Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions benches/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const ALL_STRATEGIES: &[Strategy] = &[
Strategy::Jellyfish, // 52
Strategy::HiddenQuads, // 54
//Strategy::SinglesChain,
Strategy::BruteForce
];

macro_rules! make_benches {
Expand Down
6 changes: 3 additions & 3 deletions src/board/sudoku.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,9 +378,9 @@ impl Sudoku {
}
}

let valid_ending = chars.get(81).map_or(true, |ch| {
matches!(ch, b'\t' | b' ' | b'\r' | b'\n' | b';' | b',')
});
let valid_ending = chars
.get(81)
.is_none_or(|ch| matches!(ch, b'\t' | b' ' | b'\r' | b'\n' | b';' | b','));

match valid_ending {
true => Sudoku::from_bytes(grid).map_err(drop),
Expand Down
4 changes: 3 additions & 1 deletion src/strategy/deduction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ pub enum Deduction<T> {
conflicts: T,
},
//SinglesChain(T),
BruteForce(Candidate),
}

impl Deduction<&'_ [Candidate]> {
Expand Down Expand Up @@ -191,6 +192,7 @@ impl Deduction<&'_ [Candidate]> {
3 => Strategy::XyzWing,
_ => unreachable!(),
},
BruteForce { .. } => Strategy::BruteForce,
AvoidableRectangle { .. } => unimplemented!(),
}
}
Expand Down Expand Up @@ -234,7 +236,7 @@ impl _Deduction {
conflicts
}
=> Wing { hinge, hinge_digits, pincers, conflicts: &eliminated[conflicts] },

BruteForce(c) => BruteForce(c),
AvoidableRectangle { .. } => unimplemented!(),
//SinglesChain(x) => SinglesChain(&eliminated[x]),
}
Expand Down
70 changes: 67 additions & 3 deletions src/strategy/solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ pub struct StrategySolver {
pub(crate) house_solved_digits: State<HouseArray<Set<Digit>>>,
// Mask of possible positions for a house and number
pub(crate) house_poss_positions: State<HouseArray<DigitArray<Set<Position<House>>>>>,

pub(crate) solution: Option<Sudoku>,
}

impl StrategySolver {
Expand All @@ -77,6 +79,7 @@ impl StrategySolver {
cell_poss_digits: State::from(CellArray([Set::ALL; 81])),
house_solved_digits: State::from(HouseArray([Set::NONE; 27])),
house_poss_positions: State::from(HouseArray([DigitArray([Set::ALL; 9]); 27])),
solution: None,
}
}

Expand All @@ -90,6 +93,7 @@ impl StrategySolver {

StrategySolver {
deduced_entries,
solution: sudoku.solution(),
..StrategySolver::empty()
}
}
Expand Down Expand Up @@ -122,11 +126,13 @@ impl StrategySolver {
}
}

StrategySolver {
let mut solver = StrategySolver {
deduced_entries: entries,
eliminated_entries: eliminated_candidates,
..StrategySolver::empty()
}
};
solver.solution = solver.to_sudoku().solution();
solver
}

/// Construct a new StrategySolver from a printout of cell candidates.
Expand Down Expand Up @@ -234,6 +240,13 @@ impl StrategySolver {
}
}

/// Try to solve the sudoku using the all support strategies. Returns a `Result` of the sudoku and a struct containing the series of deductions.
/// If a solution was found, `Ok(..)` is returned, otherwise `Err(..)`.
#[allow(clippy::result_large_err)] // nonsense, Ok and Err are the same size.
pub fn solve_all(self) -> Result<(Sudoku, Deductions), (Sudoku, Deductions)> {
self.solve(Strategy::ALL)
}

/// Try to solve the sudoku using the given `strategies`. Returns a `Result` of the sudoku and a struct containing the series of deductions.
/// If a solution was found, `Ok(..)` is returned, otherwise `Err(..)`.
#[allow(clippy::result_large_err)] // nonsense, Ok and Err are the same size.
Expand Down Expand Up @@ -610,7 +623,7 @@ impl StrategySolver {
{
use self::Deduction::*;
match strategy {
NakedSingles(..) | HiddenSingles(..) => (),
NakedSingles(..) | HiddenSingles(..) | BruteForce(..) => (),
_ => panic!("Internal error: Called push_new_candidate with wrong strategy type"),
};
}
Expand Down Expand Up @@ -1109,6 +1122,57 @@ impl StrategySolver {
Ok(())
}
*/
pub(crate) fn find_brute_force(&mut self, stop_after_first: bool) -> Result<(), Unsolvable> {
let solution = match self.solution {
Some(s) => s,
None => return Ok(()),
};
let cell_states = self.grid_state();

let grid = &mut self.grid.state;
let deduced_entries = &mut self.deduced_entries;
let deductions = &mut self.deductions;

let mut candidates = Vec::new();
let mut min_len = 255;
let mut candidate_index = 0;

for (i, cell_state) in cell_states.iter().enumerate() {
if let CellState::Candidates(set) = cell_state {
let candidate = Candidate::new(i as u8, solution.to_bytes()[i]);
if set.len() < min_len {
min_len = set.len();
candidate_index = candidates.len();
}
candidates.push(candidate);
}
}
if min_len == 255 {
return Ok(());
}
if stop_after_first {
deduced_entries.push(candidates[candidate_index]);
Self::push_new_candidate(
grid,
deduced_entries,
candidates[candidate_index],
deductions,
Deduction::BruteForce(candidates[candidate_index]),
)?
} else {
for candidate in candidates {
deduced_entries.push(candidate);
Self::push_new_candidate(
grid,
deduced_entries,
candidate,
deductions,
Deduction::BruteForce(candidate),
)?
}
}
Ok(())
}
}

impl std::fmt::Display for StrategySolver {
Expand Down
9 changes: 6 additions & 3 deletions src/strategy/strategies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ pub enum Strategy {
MutantSwordfish,
MutantJellyfish,
AvoidableRectangles,
//SinglesChain,
// SinglesChain,
BruteForce,
}

impl Strategy {
Expand All @@ -64,7 +65,8 @@ impl Strategy {
Strategy::NakedQuads, // 50
Strategy::Jellyfish, // 52
Strategy::HiddenQuads, // 54
//Strategy::SinglesChain,
// Strategy::SinglesChain,
Strategy::BruteForce,
];

// is_first_strategy is an optimization hint
Expand Down Expand Up @@ -96,7 +98,8 @@ impl Strategy {
XyzWing => state.find_xyz_wing(stop_after_first),
MutantSwordfish => state.find_mutant_fish(3, stop_after_first),
MutantJellyfish => state.find_mutant_fish(4, stop_after_first),
//SinglesChain => state.find_singles_chain(stop_after_first), // TODO: Implement non-eager SinglesChain
// SinglesChain => state.find_singles_chain(stop_after_first), // TODO: Implement non-eager SinglesChain
BruteForce => state.find_brute_force(stop_after_first),
_ => unimplemented!(),
}
}
Expand Down