Skip to content

Commit

Permalink
Add Unique Rectangle solver
Browse files Browse the repository at this point in the history
- Derive `Hash` for all sets and `Shape`
- Add `IntoIterator` for all sets
- Add `rows()`, `columns()`, and `blocks()` to `CellSet`
- Add `cells` and `block_count` to `Rectangle`
  • Loading branch information
dharkness committed Aug 22, 2023
1 parent 757dae4 commit 518361e
Show file tree
Hide file tree
Showing 13 changed files with 480 additions and 30 deletions.
4 changes: 2 additions & 2 deletions src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ pub use cells::{
Rectangle,
};
pub use houses::{
Coord, House, HouseIter, HouseIteratorUnion, HouseSet, HouseSetIter,
HouseSetIteratorIntersection, HouseSetIteratorUnion, HousesIter, Shape,
Coord, House, HouseIteratorUnion, HouseSet, HouseSetIteratorIntersection,
HouseSetIteratorUnion, Shape,
};
pub use values::{
Known, KnownIteratorUnion, KnownSet, KnownSetIteratorIntersection, KnownSetIteratorUnion, Value,
Expand Down
25 changes: 21 additions & 4 deletions src/layout/cells/cell_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::ops::{
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, Index, Neg, Not, Sub, SubAssign,
};

use crate::layout::House;
use crate::layout::{House, HouseSet, Shape};
use crate::symbols::EMPTY_SET;

use super::{Bit, Cell};
Expand All @@ -18,7 +18,7 @@ type Size = u8;
type SizeAndBits = u128;

/// A set of cells implemented using a bit field.
#[derive(Clone, Copy, Default, Eq, PartialEq)]
#[derive(Clone, Copy, Default, Hash, Eq, PartialEq)]
pub struct CellSet(SizeAndBits);

const ALL_CELLS: std::ops::Range<Size> = 0..Cell::COUNT;
Expand Down Expand Up @@ -210,6 +210,23 @@ impl CellSet {
*self = self.inverted()
}

pub fn rows(&self) -> HouseSet {
self.houses(Shape::Row)
}

pub fn columns(&self) -> HouseSet {
self.houses(Shape::Column)
}

pub fn blocks(&self) -> HouseSet {
self.houses(Shape::Block)
}

pub fn houses(&self, shape: Shape) -> HouseSet {
self.iter()
.fold(HouseSet::empty(shape), |set, cell| set + cell.house(shape))
}

pub const fn iter(&self) -> CellIter {
CellIter {
iter: self.bit_iter(),
Expand Down Expand Up @@ -292,8 +309,8 @@ where
impl FromIterator<Cell> for CellSet {
fn from_iter<I: IntoIterator<Item = Cell>>(iter: I) -> Self {
let mut set = CellSet::empty();
for house in iter {
set += house;
for cell in iter {
set += cell;
}
set
}
Expand Down
69 changes: 64 additions & 5 deletions src/layout/cells/rectangle.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,86 @@
use std::fmt;
use std::hash::{Hash, Hasher};

use super::Cell;
use super::{Cell, CellSet};

/// A rectangle of four cells.
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Rectangle {
top_left: Cell,
bottom_right: Cell,
cells: CellSet,
block_count: usize,
}

impl Rectangle {
pub fn new(top_left: Cell, bottom_right: Cell) -> Rectangle {
pub const fn new(top_left: Cell, bottom_right: Cell) -> Rectangle {
let cells = CellSet::of(&[
top_left,
bottom_right,
Cell::from_coords(top_left.row_coord(), bottom_right.column_coord()),
Cell::from_coords(bottom_right.row_coord(), top_left.column_coord()),
]);

let tl_block = top_left.block_coord().usize();
let br_block = bottom_right.block_coord().usize();
let block_count = if tl_block == br_block {
1
} else if tl_block % 3 == br_block % 3 || tl_block / 3 == br_block / 3 {
2
} else {
4
};

Rectangle {
top_left,
bottom_right,
cells,
block_count,
}
}

pub fn from(c1: Cell, c2: Cell, c3: Cell, c4: Cell) -> Rectangle {
Rectangle {
top_left: c1.min(c2).min(c3).min(c4),
bottom_right: c1.max(c2).max(c3).max(c4),
Rectangle::new(c1.min(c2).min(c3).min(c4), c1.max(c2).max(c3).max(c4))
}

pub const fn cells(&self) -> CellSet {
self.cells
}

pub const fn block_count(&self) -> usize {
self.block_count
}
}

impl Hash for Rectangle {
fn hash<H: Hasher>(&self, state: &mut H) {
self.top_left.hash(state);
self.bottom_right.hash(state);
}
}

impl TryFrom<CellSet> for Rectangle {
type Error = ();

fn try_from(cells: CellSet) -> Result<Rectangle, ()> {
if cells.size() < 2 || 4 < cells.size() {
return Err(());
}

let rows = cells.rows();
let columns = cells.columns();

if rows.size() != 2 || columns.size() != 2 {
return Err(());
}

let (top, bottom) = rows.as_pair().unwrap();
let (left, right) = columns.as_pair().unwrap();

Ok(Rectangle::new(
Cell::from_coords(top.coord(), left.coord()),
Cell::from_coords(bottom.coord(), right.coord()),
))
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/layout/houses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ pub use coord::Coord;
pub use coord_set::CoordSet;
pub use house::{House, HouseIter, HousesIter};
pub use house_set::{
HouseIteratorUnion, HouseSet, HouseSetIter, HouseSetIteratorIntersection, HouseSetIteratorUnion,
HouseIteratorUnion, HouseSet, HouseSetIteratorIntersection, HouseSetIteratorUnion, Iter,
};
pub use shape::Shape;
15 changes: 12 additions & 3 deletions src/layout/houses/coord_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::symbols::{EMPTY_SET, MISSING};
use super::Coord;

/// A set of coordinates in a [`House`] implemented using a bit field.
#[derive(Clone, Copy, Default, Eq, PartialEq)]
#[derive(Clone, Copy, Default, Hash, Eq, PartialEq)]
pub struct CoordSet(u16);

const FULL: u16 = (1 << 9) - 1;
Expand Down Expand Up @@ -218,6 +218,15 @@ impl From<i32> for CoordSet {
}
}

impl IntoIterator for CoordSet {
type Item = Coord;
type IntoIter = Iter;

fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}

pub trait CoordIteratorUnion {
fn union(self) -> CoordSet;
}
Expand Down Expand Up @@ -260,8 +269,8 @@ where
impl FromIterator<Coord> for CoordSet {
fn from_iter<I: IntoIterator<Item = Coord>>(iter: I) -> Self {
let mut set = Self::empty();
for house in iter {
set += house;
for coord in iter {
set += coord;
}
set
}
Expand Down
8 changes: 4 additions & 4 deletions src/layout/houses/house.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::ops::{Add, Neg};
use crate::layout::houses::house_set::{blocks, cols, rows};
use crate::layout::{Cell, CellSet, Coord};

use super::{HouseSet, HouseSetIter, Shape};
use super::{HouseSet, Iter, Shape};

/// One of the nine rows, columns, or blocks on the board.
#[derive(Clone, Copy, Debug, Default)]
Expand Down Expand Up @@ -158,7 +158,7 @@ impl House {
}
}

pub fn row_iter(&self) -> HouseSetIter {
pub fn row_iter(&self) -> Iter {
self.rows().iter()
}

Expand All @@ -170,7 +170,7 @@ impl House {
}
}

pub fn column_iter(&self) -> HouseSetIter {
pub fn column_iter(&self) -> Iter {
self.columns().iter()
}

Expand All @@ -182,7 +182,7 @@ impl House {
}
}

pub fn block_iter(&self) -> HouseSetIter {
pub fn block_iter(&self) -> Iter {
self.blocks().iter()
}
}
Expand Down
19 changes: 14 additions & 5 deletions src/layout/houses/house_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use super::{Coord, CoordSet, House, Shape};

const FULL: u16 = (1 << 9) - 1;

#[derive(Clone, Copy, Default, Eq, PartialEq)]
#[derive(Clone, Copy, Default, Hash, Eq, PartialEq)]
pub struct HouseSet {
shape: Shape,
coords: CoordSet,
Expand Down Expand Up @@ -231,8 +231,8 @@ impl HouseSet {
*self = self.inverted()
}

pub const fn iter(&self) -> HouseSetIter {
HouseSetIter {
pub const fn iter(&self) -> Iter {
Iter {
shape: self.shape,
coords: self.coords.bits(),
}
Expand All @@ -258,6 +258,15 @@ impl From<&str> for HouseSet {
}
}

impl IntoIterator for HouseSet {
type Item = House;
type IntoIter = Iter;

fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}

pub trait HouseIteratorUnion {
fn union(self) -> HouseSet;
}
Expand Down Expand Up @@ -518,12 +527,12 @@ macro_rules! blocks {
#[allow(unused_imports)]
pub(crate) use {blocks, cols, houses, rows};

pub struct HouseSetIter {
pub struct Iter {
shape: Shape,
coords: u16,
}

impl Iterator for HouseSetIter {
impl Iterator for Iter {
type Item = House;

fn next(&mut self) -> Option<Self::Item> {
Expand Down
2 changes: 1 addition & 1 deletion src/layout/houses/shape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::layout::{Cell, CellSet, Coord};
use super::{House, HouseIter};

/// The three house shapes on the board.
#[derive(Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd)]
#[derive(Clone, Copy, Default, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum Shape {
#[default]
Row,
Expand Down
15 changes: 12 additions & 3 deletions src/layout/values/known_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type Bits = u16;
type SizeAndBits = u16;

/// A set of knowns implemented using a bit field.
#[derive(Clone, Copy, Default, Eq, PartialEq)]
#[derive(Clone, Copy, Default, Hash, Eq, PartialEq)]
pub struct KnownSet(SizeAndBits);

const BITS_MASK: Bits = (1 << 9) - 1;
Expand Down Expand Up @@ -196,6 +196,15 @@ impl From<&str> for KnownSet {
}
}

impl IntoIterator for KnownSet {
type Item = Known;
type IntoIter = Iter;

fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}

pub trait KnownIteratorUnion {
fn union(self) -> KnownSet;
}
Expand Down Expand Up @@ -238,8 +247,8 @@ where
impl FromIterator<Known> for KnownSet {
fn from_iter<I: IntoIterator<Item = Known>>(iter: I) -> Self {
let mut set = KnownSet::empty();
for house in iter {
set += house;
for known in iter {
set += known;
}
set
}
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; 15] = [
const SOLVERS: [Solver; 16] = [
crate::solvers::intersection_removals::find_intersection_removals,
crate::solvers::naked_tuples::find_naked_pairs,
crate::solvers::naked_tuples::find_naked_triples,
Expand All @@ -25,9 +25,10 @@ const SOLVERS: [Solver; 15] = [
crate::solvers::skyscrapers::find_skyscrapers,
crate::solvers::y_wings::find_y_wings,
crate::solvers::xyz_wings::find_xyz_wings,
crate::solvers::unique_rectangles::find_unique_rectangles,
crate::solvers::empty_rectangles::find_empty_rectangles,
];
const SOLVER_LABELS: [&str; 15] = [
const SOLVER_LABELS: [&str; 16] = [
"intersection removal",
"naked pair",
"naked triple",
Expand All @@ -42,6 +43,7 @@ const SOLVER_LABELS: [&str; 15] = [
"skyscraper",
"y-wing",
"xyz-wing",
"unique rectangles",
"empty rectangles",
];

Expand Down
1 change: 1 addition & 0 deletions src/puzzle/strategy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,5 @@ pub enum Strategy {
XYZWing, // (Known, pivot Cell, arms (Cell, Cell))

EmptyRectangle, // (Known, Block, Row, Column, Cell) - CellSet instead of three houses
UniqueRectangle, // (Cell, Cell, Cell, Cell)
}
1 change: 1 addition & 0 deletions src/solvers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod intersection_removals;
pub mod naked_tuples;
pub mod singles_chains;
pub mod skyscrapers;
pub mod unique_rectangles;
pub mod xyz_wings;
pub mod y_wings;

Expand Down
Loading

0 comments on commit 518361e

Please sign in to comment.