Skip to content

Commit 67beaa0

Browse files
authored
Add solver for double back (#205)
* Added solver for double back * Simplified constraints
1 parent f02d3a0 commit 67beaa0

4 files changed

Lines changed: 198 additions & 0 deletions

File tree

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
use cspuz_rs::graph;
2+
use cspuz_rs::serializer::{
3+
problem_to_url_with_context, url_to_problem, Combinator, Context, Rooms, Size,
4+
};
5+
use cspuz_rs::solver::{count_true, Solver};
6+
7+
pub fn solve_doubleback(
8+
borders: &graph::InnerGridEdges<Vec<Vec<bool>>>,
9+
) -> Option<graph::BoolGridEdgesIrrefutableFacts> {
10+
let (h, w) = borders.base_shape();
11+
12+
let mut solver = Solver::new();
13+
let rooms = graph::borders_to_rooms(borders);
14+
let is_line = &graph::BoolGridEdges::new(&mut solver, (h - 1, w - 1));
15+
solver.add_answer_key_bool(&is_line.horizontal);
16+
solver.add_answer_key_bool(&is_line.vertical);
17+
18+
let is_passed = &graph::single_cycle_grid_edges(&mut solver, is_line);
19+
let mut room_id = vec![vec![0; w]; h];
20+
21+
for i in 0..rooms.len() {
22+
for &(y, x) in &rooms[i] {
23+
room_id[y][x] = i;
24+
}
25+
}
26+
27+
let mut room_entrance = vec![vec![]; rooms.len()];
28+
for y in 0..h {
29+
for x in 0..w {
30+
if y < h - 1 && room_id[y][x] != room_id[y + 1][x] {
31+
room_entrance[room_id[y][x]].push(is_line.vertical.at((y, x)));
32+
room_entrance[room_id[y + 1][x]].push(is_line.vertical.at((y, x)));
33+
}
34+
if x < w - 1 && room_id[y][x] != room_id[y][x + 1] {
35+
room_entrance[room_id[y][x]].push(is_line.horizontal.at((y, x)));
36+
room_entrance[room_id[y][x + 1]].push(is_line.horizontal.at((y, x)));
37+
}
38+
}
39+
}
40+
41+
for i in 0..rooms.len() {
42+
// Check every room is entered twice
43+
solver.add_expr(count_true(&room_entrance[i]).eq(4));
44+
}
45+
46+
for i in 0..rooms.len() {
47+
let mut cells = vec![];
48+
for &pt in &rooms[i] {
49+
cells.push(is_passed.at(pt));
50+
}
51+
}
52+
53+
solver.add_expr(is_passed);
54+
55+
solver.irrefutable_facts().map(|f| f.get(is_line))
56+
}
57+
58+
type Problem = graph::InnerGridEdges<Vec<Vec<bool>>>;
59+
60+
fn combinator() -> impl Combinator<Problem> {
61+
Size::new(Rooms)
62+
}
63+
64+
pub fn serialize_problem(problem: &Problem) -> Option<String> {
65+
let height = problem.vertical.len();
66+
let width = problem.vertical[0].len() + 1;
67+
problem_to_url_with_context(
68+
combinator(),
69+
"doubleback",
70+
problem.clone(),
71+
&Context::sized(height, width),
72+
)
73+
}
74+
75+
pub fn deserialize_problem(url: &str) -> Option<Problem> {
76+
url_to_problem(combinator(), &["doubleback"], url)
77+
}
78+
79+
#[cfg(test)]
80+
mod tests {
81+
use super::*;
82+
use crate::util;
83+
84+
fn problem_for_tests() -> Problem {
85+
let borders = graph::InnerGridEdges {
86+
horizontal: crate::util::tests::to_bool_2d([[0, 0, 1, 1], [0, 0, 0, 0], [1, 1, 1, 1]]),
87+
vertical: crate::util::tests::to_bool_2d([[0, 0, 0], [0, 1, 0], [0, 1, 0], [0, 0, 0]]),
88+
};
89+
borders
90+
}
91+
92+
#[test]
93+
fn test_doubleback_problem() {
94+
let borders = problem_for_tests();
95+
let ans = solve_doubleback(&borders);
96+
assert!(ans.is_some());
97+
let ans = ans.unwrap();
98+
99+
#[rustfmt::skip]
100+
let expected = graph::BoolGridEdgesIrrefutableFacts {
101+
horizontal: crate::util::tests::to_option_bool_2d([
102+
[1, 1, 1],
103+
[0, 1, 0],
104+
[0, 0, 0],
105+
[1, 0, 1],
106+
]),
107+
vertical: crate::util::tests::to_option_bool_2d([
108+
[1, 0, 0, 1],
109+
[1, 1, 1, 1],
110+
[1, 1, 1, 1],
111+
]),
112+
};
113+
assert_eq!(ans, expected);
114+
}
115+
#[test]
116+
fn test_doubleback_serializer() {
117+
let problem = problem_for_tests();
118+
let url = "https://puzz.link/p?doubleback/4/4/14063o"; // puzz.link example puzzle
119+
util::tests::serializer_test(problem, url, serialize_problem, deserialize_problem);
120+
}
121+
}

cspuz_rs_puzzles/src/puzzles/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub mod dbchoco;
3636
pub mod dominion;
3737
pub mod doppelblock;
3838
pub mod double_lits;
39+
pub mod doubleback;
3940
pub mod energywalk;
4041
pub mod evolmino;
4142
pub mod exercise;
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use crate::board::{Board, BoardKind};
2+
use crate::uniqueness::check_uniqueness;
3+
use cspuz_rs_puzzles::puzzles::doubleback;
4+
5+
pub fn solve(url: &str) -> Result<Board, &'static str> {
6+
let borders = doubleback::deserialize_problem(url).ok_or("invalid url")?;
7+
let ans = doubleback::solve_doubleback(&borders);
8+
9+
let height = borders.vertical.len();
10+
let width = borders.vertical[0].len() + 1;
11+
let mut board = Board::new(BoardKind::Grid, height, width, check_uniqueness(&ans));
12+
board.add_borders(&borders, "black");
13+
14+
if let Some(is_line) = &ans {
15+
board.add_lines_irrefutable_facts(is_line, "green", None);
16+
}
17+
18+
Ok(board)
19+
}
20+
21+
#[cfg(test)]
22+
mod tests {
23+
use super::solve;
24+
use crate::board::*;
25+
use crate::compare_board_and_check_no_solution_case;
26+
use crate::uniqueness::Uniqueness;
27+
28+
#[test]
29+
#[rustfmt::skip]
30+
fn test_solve() {
31+
compare_board_and_check_no_solution_case!(
32+
solve("https://puzz.link/p?doubleback/4/4/14063o"),
33+
Board {
34+
kind: BoardKind::Grid,
35+
height: 4,
36+
width: 4,
37+
data: vec![
38+
Item { y: 2, x: 5, color: "black", kind: ItemKind::BoldWall },
39+
Item { y: 2, x: 7, color: "black", kind: ItemKind::BoldWall },
40+
Item { y: 3, x: 4, color: "black", kind: ItemKind::BoldWall },
41+
Item { y: 6, x: 1, color: "black", kind: ItemKind::BoldWall },
42+
Item { y: 6, x: 3, color: "black", kind: ItemKind::BoldWall },
43+
Item { y: 5, x: 4, color: "black", kind: ItemKind::BoldWall },
44+
Item { y: 6, x: 5, color: "black", kind: ItemKind::BoldWall },
45+
Item { y: 6, x: 7, color: "black", kind: ItemKind::BoldWall },
46+
Item { y: 2, x: 1, color: "green", kind: ItemKind::Line },
47+
Item { y: 2, x: 3, color: "green", kind: ItemKind::Cross },
48+
Item { y: 2, x: 5, color: "green", kind: ItemKind::Cross },
49+
Item { y: 2, x: 7, color: "green", kind: ItemKind::Line },
50+
Item { y: 4, x: 1, color: "green", kind: ItemKind::Line },
51+
Item { y: 4, x: 3, color: "green", kind: ItemKind::Line },
52+
Item { y: 4, x: 5, color: "green", kind: ItemKind::Line },
53+
Item { y: 4, x: 7, color: "green", kind: ItemKind::Line },
54+
Item { y: 6, x: 1, color: "green", kind: ItemKind::Line },
55+
Item { y: 6, x: 3, color: "green", kind: ItemKind::Line },
56+
Item { y: 6, x: 5, color: "green", kind: ItemKind::Line },
57+
Item { y: 6, x: 7, color: "green", kind: ItemKind::Line },
58+
Item { y: 1, x: 2, color: "green", kind: ItemKind::Line },
59+
Item { y: 1, x: 4, color: "green", kind: ItemKind::Line },
60+
Item { y: 1, x: 6, color: "green", kind: ItemKind::Line },
61+
Item { y: 3, x: 2, color: "green", kind: ItemKind::Cross },
62+
Item { y: 3, x: 4, color: "green", kind: ItemKind::Line },
63+
Item { y: 3, x: 6, color: "green", kind: ItemKind::Cross },
64+
Item { y: 5, x: 2, color: "green", kind: ItemKind::Cross },
65+
Item { y: 5, x: 4, color: "green", kind: ItemKind::Cross },
66+
Item { y: 5, x: 6, color: "green", kind: ItemKind::Cross },
67+
Item { y: 7, x: 2, color: "green", kind: ItemKind::Line },
68+
Item { y: 7, x: 4, color: "green", kind: ItemKind::Cross },
69+
Item { y: 7, x: 6, color: "green", kind: ItemKind::Line },
70+
],
71+
uniqueness: Uniqueness::Unique,
72+
},
73+
);
74+
}
75+
}

cspuz_solver_backend/src/puzzle/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ puzzle_list!(puzz_link,
118118
(dbchoco, ["dbchoco"], "Double Choco", "ダブルチョコ"),
119119
(dominion, ["dominion"], "Dominion", "ドミニオン"),
120120
(doppelblock, ["doppelblock"], "Doppelblock", "ビトゥイーン・サム"),
121+
(doubleback, ["doubleback"], "Double Back", "Double Back"),
121122
(energywalk, ["energywalk"], "Energy Walk", "Energy Walk"),
122123
(evolmino, ["evolmino"], "Evolmino", "シンカミノ"),
123124
(fillomino, ["fillomino"], "Fillomino", "フィルオミノ"),

0 commit comments

Comments
 (0)