Skip to content

Commit

Permalink
Add XYZ-Wing solver
Browse files Browse the repository at this point in the history
- Add `as_single()` for all sets
- Do not add empty `Action` to `Effects`
  • Loading branch information
dharkness committed Aug 20, 2023
1 parent 7b9dd7d commit 5ebce7d
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 15 deletions.
8 changes: 8 additions & 0 deletions src/layout/cells/cell_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ impl CellSet {
self.0 & cell.bit().bit() != 0
}

pub const fn as_single(&self) -> Option<Cell> {
if self.size() != 1 {
None
} else {
Some(Cell::new(self.bits().trailing_zeros() as u8))
}
}

pub const fn as_pair(&self) -> Option<(Cell, Cell)> {
if self.size() != 2 {
None
Expand Down
8 changes: 8 additions & 0 deletions src/layout/houses/coord_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ impl CoordSet {
self.0 & coord.bit() != 0
}

pub const fn as_single(&self) -> Option<Coord> {
if self.size() != 1 {
None
} else {
Some(Coord::from_index(self.bits().trailing_zeros()))
}
}

pub const fn as_pair(&self) -> Option<(Coord, Coord)> {
if self.size() != 2 {
None
Expand Down
28 changes: 16 additions & 12 deletions src/layout/houses/house_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,25 +83,29 @@ impl HouseSet {
self.iter().fold(CellSet::empty(), |acc, h| acc | h.cells())
}

pub const fn as_pair(&self) -> Option<(House, House)> {
match self.coords.as_pair() {
Some((first, second)) => Some((
pub fn as_single(&self) -> Option<House> {
self.coords
.as_single()
.map(|coord| House::new(self.shape, coord))
}

pub fn as_pair(&self) -> Option<(House, House)> {
self.coords.as_pair().map(|(first, second)| {
(
House::new(self.shape, first),
House::new(self.shape, second),
)),
_ => None,
}
)
})
}

pub const fn as_triple(&self) -> Option<(House, House, House)> {
match self.coords.as_triple() {
Some((first, second, third)) => Some((
pub fn as_triple(&self) -> Option<(House, House, House)> {
self.coords.as_triple().map(|(first, second, third)| {
(
House::new(self.shape, first),
House::new(self.shape, second),
House::new(self.shape, third),
)),
_ => None,
}
)
})
}

pub fn with(&self, house: House) -> Self {
Expand Down
8 changes: 8 additions & 0 deletions src/layout/values/known_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ impl KnownSet {
self.0 & known.bit() != 0
}

pub const fn as_single(&self) -> Option<Known> {
if self.size() != 1 {
None
} else {
Some(Known::from_index(self.bits().trailing_zeros()))
}
}

pub const fn as_pair(&self) -> Option<(Known, Known)> {
if self.size() != 2 {
None
Expand Down
6 changes: 4 additions & 2 deletions src/play.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::symbols::UNKNOWN_VALUE;

const URL: &str = "https://www.sudokuwiki.org/sudoku.htm?bd=";

const SOLVERS: [Solver; 12] = [
const SOLVERS: [Solver; 13] = [
crate::solvers::intersection_removals::find_intersection_removals,
crate::solvers::naked_tuples::find_naked_pairs,
crate::solvers::naked_tuples::find_naked_triples,
Expand All @@ -23,8 +23,9 @@ const SOLVERS: [Solver; 12] = [
crate::solvers::fish::find_jellyfish,
crate::solvers::singles_chains::find_singles_chains,
crate::solvers::y_wings::find_y_wings,
crate::solvers::xyz_wings::find_xyz_wings,
];
const SOLVER_LABELS: [&str; 12] = [
const SOLVER_LABELS: [&str; 13] = [
"intersection removal",
"naked pair",
"naked triple",
Expand All @@ -37,6 +38,7 @@ const SOLVER_LABELS: [&str; 12] = [
"jellyfish",
"singles chain",
"y-wing",
"xyz-wing",
];

pub fn play() {
Expand Down
4 changes: 3 additions & 1 deletion src/puzzle/effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ impl Effects {
}

pub fn add_action(&mut self, action: Action) {
self.actions.push(action);
if !action.is_empty() {
self.actions.push(action);
}
}

pub fn add_set(&mut self, strategy: Strategy, cell: Cell, known: Known) {
Expand Down
1 change: 1 addition & 0 deletions src/puzzle/strategy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,5 @@ pub enum Strategy {

SinglesChain, // (Known, Vec<Cell>)
YWing, // (Known, pivot Cell, arms (Cell, Cell))
XYZWing, // (Known, pivot Cell, arms (Cell, Cell))
}
1 change: 1 addition & 0 deletions src/solvers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod hidden_tuples;
pub mod intersection_removals;
pub mod naked_tuples;
pub mod singles_chains;
pub mod xyz_wings;
pub mod y_wings;

use crate::layout::*;
Expand Down
67 changes: 67 additions & 0 deletions src/solvers/xyz_wings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use super::*;

pub fn find_xyz_wings(board: &Board) -> Option<Effects> {
let mut effects = Effects::new();

// for each tri-value cell
// peers = bi-value cells & cell peers
// for each pair of peers
// if count of cells that see all three cells is not 2
// degenerate naked triple (continue)
// if union of peer candidates is not cell candidates
// continue
// found xyz-wing

let mut log = false;

let tri_values = board.cells_with_n_candidates(3);
if tri_values.is_empty() {
return None;
}
let bi_values = board.cells_with_n_candidates(2);
if bi_values.is_empty() {
return None;
}

tri_values.iter().for_each(|cell| {
// let (k1, k2) = board.candidates(cell).as_pair().unwrap();
(cell.peers() & bi_values)
.iter()
.combinations(2)
.map(|pair| pair.iter().copied().union() as CellSet)
.for_each(|pair| {
let (c1, c2) = pair.as_pair().expect("cell pair");
let candidates = cell.peers() & c1.peers() & c2.peers();
if candidates.size() != 2 {
// degenerate naked triple
return;
}

let ks = board.candidates(cell);
let ks1 = board.candidates(c1);
let ks2 = board.candidates(c2);
if ks1 | ks2 != ks {
// degenerate naked pair or totally unrelated candidates
return;
}

let k = (ks1 & ks2).as_single().expect("one candidate in common");
if log {
println!(
"{}-{}: {}-{} {}-{} - {}",
cell, ks, c1, ks1, c2, ks2, candidates
)
}

let mut action = Action::new(Strategy::XYZWing);
action.erase_cells(candidates & board.candidate_cells(k), k);
effects.add_action(action);
});
});

if effects.has_actions() {
Some(effects)
} else {
None
}
}

0 comments on commit 5ebce7d

Please sign in to comment.