Skip to main content

pencil_puzzles_core/
place.rs

1//! Places on a board, and what checkers and raters say about them.
2
3use std::fmt;
4
5/// A place on a rectangular board, counted from 0 (D5). Used by violations
6/// (P8) and by the steps a rater lists (L19).
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize))]
9#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
10#[non_exhaustive]
11pub enum Place {
12    /// One cell.
13    Cell {
14        /// The row, counted from 0.
15        row: u32,
16        /// The column, counted from 0.
17        col: u32,
18    },
19    /// A whole row, counted from 0.
20    Row(u32),
21    /// A whole column, counted from 0.
22    Col(u32),
23    /// A numbered region, such as a Sudoku box, counted from 0.
24    Region(u32),
25    /// The whole board.
26    Board,
27    /// A straight link between the centres of two cells in the same row or
28    /// column, such as a Hashi bridge. The first cell comes first in
29    /// row-major order.
30    Link {
31        /// The first cell's row, counted from 0.
32        row: u32,
33        /// The first cell's column, counted from 0.
34        col: u32,
35        /// The second cell's row, counted from 0.
36        row2: u32,
37        /// The second cell's column, counted from 0.
38        col2: u32,
39    },
40}
41
42impl fmt::Display for Place {
43    /// Shown to people, so rows and columns count from 1 (D5).
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        match self {
46            Self::Cell { row, col } => write!(
47                f,
48                "row {}, column {}",
49                u64::from(*row) + 1,
50                u64::from(*col) + 1
51            ),
52            Self::Row(row) => write!(f, "row {}", u64::from(*row) + 1),
53            Self::Col(col) => write!(f, "column {}", u64::from(*col) + 1),
54            Self::Region(region) => write!(f, "region {}", u64::from(*region) + 1),
55            Self::Board => f.write_str("the whole board"),
56            Self::Link {
57                row,
58                col,
59                row2,
60                col2,
61            } => write!(
62                f,
63                "between row {}, column {} and row {}, column {}",
64                u64::from(*row) + 1,
65                u64::from(*col) + 1,
66                u64::from(*row2) + 1,
67                u64::from(*col2) + 1
68            ),
69        }
70    }
71}
72
73/// A broken rule instance, found by a checker (P8).
74///
75/// It holds data, not sentences: tools turn it into a message, so that
76/// messages can be translated.
77#[derive(Clone, Debug, PartialEq, Eq, Hash)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize))]
79#[non_exhaustive]
80pub struct Violation {
81    /// The rule's number, as on the genre page (R1 is 1).
82    pub rule: u32,
83    /// Where the broken rule instance is.
84    pub place: Place,
85    /// Numbers that explain the violation, by name, such as `("total", 9)`
86    /// and `("clue", 7)`. Each genre page documents its names.
87    pub values: Vec<(&'static str, i64)>,
88}
89
90impl Violation {
91    /// A violation of rule `rule` at `place`.
92    pub fn new(rule: u32, place: Place, values: Vec<(&'static str, i64)>) -> Self {
93        Self {
94            rule,
95            place,
96            values,
97        }
98    }
99}
100
101/// A statement that a variable has a value, such as "the cell in row 1,
102/// column 2 is shaded" (a literal, in the terms of `docs/difficulty.md`).
103#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
104#[cfg_attr(feature = "serde", derive(serde::Serialize))]
105pub struct Literal {
106    /// The variable.
107    pub place: Place,
108    /// The value, written as in the genre's text format, such as `#`.
109    pub value: String,
110}
111
112/// One step of a rating, for hints and explanations (L19).
113#[derive(Clone, Debug, PartialEq, Eq, Hash)]
114#[cfg_attr(feature = "serde", derive(serde::Serialize))]
115#[non_exhaustive]
116pub struct Step {
117    /// The level of the technique used.
118    pub level: crate::Difficulty,
119    /// The technique's stable name, such as `hidden_single`.
120    pub technique: String,
121    /// The candidates the step removed.
122    pub removed: Vec<Literal>,
123    /// Pattern premises and earlier deductions supporting this step, when available.
124    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
125    pub proof: Option<PatternProof>,
126}
127
128/// Candidate information used as a premise of a human pattern.
129#[derive(Clone, Debug, PartialEq, Eq, Hash)]
130#[cfg_attr(feature = "serde", derive(serde::Serialize))]
131pub struct CandidateFact {
132    /// The cell or other variable involved.
133    pub place: Place,
134    /// All candidates remaining at this point, in canonical order.
135    pub candidates: Vec<String>,
136}
137
138/// One pattern inference in a replayable proof graph.
139#[derive(Clone, Debug, PartialEq, Eq, Hash)]
140#[cfg_attr(feature = "serde", derive(serde::Serialize))]
141pub struct PatternProof {
142    /// Exact candidate-state premises; printed clues are root facts.
143    pub facts: Vec<CandidateFact>,
144    /// Zero-based indices of earlier steps establishing these premises.
145    pub predecessors: Vec<u32>,
146    /// Size of this pattern's explanation, not a forcing-branch shortcut.
147    pub inference_nodes: u32,
148}
149
150/// The next step for a player, given their partial answer (P9, L19).
151#[derive(Clone, Debug, PartialEq, Eq, Hash)]
152#[cfg_attr(feature = "serde", derive(serde::Serialize))]
153#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
154#[non_exhaustive]
155pub enum Hint {
156    /// The answer is already complete and correct.
157    Solved,
158    /// The next step a solver would take, after routine bookkeeping.
159    Step(Step),
160}