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}