Skip to content

Commit 66caf0e

Browse files
committed
Added fillmat
1 parent ec330f7 commit 66caf0e

4 files changed

Lines changed: 197 additions & 0 deletions

File tree

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
use crate::util;
2+
use cspuz_rs::graph;
3+
use cspuz_rs::serializer::{
4+
problem_to_url, url_to_problem, Choice, Combinator, Dict, Grid, Spaces,
5+
};
6+
use cspuz_rs::solver::Solver;
7+
8+
pub fn solve_fillmat(
9+
clues: &[Vec<Option<i32>>],
10+
) -> Option<graph::BoolInnerGridEdgesIrrefutableFacts> {
11+
let (h, w) = util::infer_shape(clues);
12+
13+
let mut solver = Solver::new();
14+
let num = &solver.int_var_2d((h, w), 1, 4);
15+
let is_border = graph::BoolInnerGridEdges::new(&mut solver, (h, w));
16+
solver.add_answer_key_bool(&is_border.horizontal);
17+
solver.add_answer_key_bool(&is_border.vertical);
18+
solver.add_expr(
19+
num.slice((.., ..(w - 1)))
20+
.ne(num.slice((.., 1..)))
21+
.iff(&is_border.vertical),
22+
);
23+
solver.add_expr(
24+
num.slice((..(h - 1), ..))
25+
.ne(num.slice((1.., ..)))
26+
.iff(&is_border.horizontal),
27+
);
28+
for i in 1..=4 {
29+
solver.add_expr(!(num.eq(i).slice((..(h - 1), ..(w - 1))) & num.eq(i).slice((1.., 1..))));
30+
solver.add_expr(!(num.eq(i).slice((..(h - 1), 1..)) & num.eq(i).slice((1.., ..(w - 1)))));
31+
}
32+
graph::graph_division_2d(&mut solver, num, &is_border);
33+
34+
for y in 0..h {
35+
for x in 0..w {
36+
if let Some(n) = clues[y][x] {
37+
if n >= 0 {
38+
solver.add_expr(num.at((y, x)).eq(n));
39+
}
40+
}
41+
}
42+
}
43+
44+
// No 4 intersections (technically redundant by theory but keeping it for optimization)
45+
for y in 1..h {
46+
for x in 1..w {
47+
let left = &is_border.horizontal.at((y - 1, x - 1));
48+
let right = &is_border.horizontal.at((y - 1, x));
49+
let up = &is_border.vertical.at((y - 1, x - 1));
50+
let down = &is_border.vertical.at((y, x - 1));
51+
solver.add_expr(!(left & right & up & down));
52+
}
53+
}
54+
55+
solver.irrefutable_facts().map(|f| f.get(&is_border))
56+
}
57+
58+
type Problem = Vec<Vec<Option<i32>>>;
59+
60+
fn combinator() -> impl Combinator<Problem> {
61+
Grid::new(Choice::new(vec![
62+
Box::new(Spaces::new(None, 'a')),
63+
Box::new(Dict::new(Some(-1), ".")),
64+
Box::new(Dict::new(Some(1), "1")),
65+
Box::new(Dict::new(Some(2), "2")),
66+
Box::new(Dict::new(Some(3), "3")),
67+
Box::new(Dict::new(Some(4), "4")),
68+
]))
69+
}
70+
71+
pub fn serialize_problem(problem: &Problem) -> Option<String> {
72+
problem_to_url(combinator(), "fillmat", problem.clone())
73+
}
74+
75+
pub fn deserialize_problem(url: &str) -> Option<Problem> {
76+
url_to_problem(combinator(), &["fillmat"], url)
77+
}
78+
79+
#[cfg(test)]
80+
mod tests {
81+
use super::*;
82+
83+
fn problem_for_tests() -> Problem {
84+
vec![
85+
vec![Some(3), None, None, Some(3), None],
86+
vec![None, None, None, None, None],
87+
vec![None, None, Some(1), None, None],
88+
vec![None, None, None, None, None],
89+
vec![None, Some(1), None, None, Some(4)],
90+
]
91+
}
92+
93+
#[test]
94+
fn test_fillmat_problem() {
95+
let problem = problem_for_tests();
96+
let ans = solve_fillmat(&problem);
97+
assert!(ans.is_some());
98+
let ans = ans.unwrap();
99+
#[rustfmt::skip]
100+
let expected = graph::BoolInnerGridEdgesIrrefutableFacts {
101+
horizontal: crate::util::tests::to_option_bool_2d([
102+
[0, 0, 1, 1, 1],
103+
[0, 0, 1, 1, 0],
104+
[1, 0, 1, 0, 0],
105+
[0, 1, 0, 0, 0],
106+
]),
107+
vertical: crate::util::tests::to_option_bool_2d([
108+
[1, 1, 0, 0],
109+
[1, 1, 0, 1],
110+
[1, 1, 1, 1],
111+
[1, 1, 1, 1],
112+
[1, 1, 1, 1],
113+
]),
114+
};
115+
assert_eq!(ans, expected);
116+
}
117+
118+
#[test]
119+
fn test_fillmat_serializer() {
120+
let problem = problem_for_tests();
121+
let url = "https://puzz.link/p?fillmat/5/5/3b3h1h1b4";
122+
util::tests::serializer_test(problem, url, serialize_problem, deserialize_problem);
123+
}
124+
}

cspuz_rs_puzzles/src/puzzles/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ pub mod easyasabc;
4343
pub mod energywalk;
4444
pub mod evolmino;
4545
pub mod exercise;
46+
pub mod fillmat;
4647
pub mod fillomino;
4748
pub mod firefly;
4849
pub mod firewalk;
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use crate::board::{Board, BoardKind, Item, ItemKind};
2+
use crate::uniqueness::check_uniqueness;
3+
use cspuz_rs_puzzles::puzzles::fillmat;
4+
5+
pub fn solve(url: &str) -> Result<Board, &'static str> {
6+
let problem = fillmat::deserialize_problem(url).ok_or("invalid url")?;
7+
let ans = fillmat::solve_fillmat(&problem);
8+
9+
let height = problem.len();
10+
let width = problem[0].len();
11+
let mut board = Board::new(
12+
BoardKind::ColoredGrid("#cccccc"),
13+
height,
14+
width,
15+
check_uniqueness(&ans),
16+
);
17+
18+
for y in 0..height {
19+
for x in 0..width {
20+
if let Some(n) = problem[y][x] {
21+
if n >= 0 {
22+
board.push(Item::cell(y, x, "black", ItemKind::Num(n)));
23+
} else if n == -2 {
24+
board.push(Item::cell(y, x, "black", ItemKind::Fill));
25+
}
26+
}
27+
}
28+
}
29+
30+
board.add_borders_as_answer(ans.as_ref());
31+
32+
Ok(board)
33+
}
34+
35+
#[cfg(test)]
36+
mod tests {
37+
use super::solve;
38+
use crate::board::*;
39+
use crate::compare_board_and_check_no_solution_case;
40+
use crate::uniqueness::Uniqueness;
41+
42+
#[test]
43+
#[rustfmt::skip]
44+
fn test_solve() {
45+
compare_board_and_check_no_solution_case!(
46+
solve("https://puzz.link/p?fillmat/3/3/c2b3b"),
47+
Board {
48+
kind: BoardKind::ColoredGrid("#cccccc"),
49+
height: 3,
50+
width: 3,
51+
data: vec![
52+
Item { y: 3, x: 1, color: "black", kind: ItemKind::Num(2) },
53+
Item { y: 5, x: 1, color: "black", kind: ItemKind::Num(3) },
54+
Item { y: 2, x: 1, color: "green", kind: ItemKind::BoldWall },
55+
Item { y: 1, x: 2, color: "green", kind: ItemKind::Cross },
56+
Item { y: 2, x: 3, color: "green", kind: ItemKind::BoldWall },
57+
Item { y: 1, x: 4, color: "green", kind: ItemKind::Cross },
58+
Item { y: 2, x: 5, color: "green", kind: ItemKind::BoldWall },
59+
Item { y: 4, x: 1, color: "green", kind: ItemKind::BoldWall },
60+
Item { y: 3, x: 2, color: "green", kind: ItemKind::Cross },
61+
Item { y: 4, x: 3, color: "green", kind: ItemKind::BoldWall },
62+
Item { y: 3, x: 4, color: "green", kind: ItemKind::BoldWall },
63+
Item { y: 4, x: 5, color: "green", kind: ItemKind::BoldWall },
64+
Item { y: 5, x: 2, color: "green", kind: ItemKind::Cross },
65+
Item { y: 5, x: 4, color: "green", kind: ItemKind::Cross },
66+
],
67+
uniqueness: Uniqueness::Unique,
68+
},
69+
);
70+
}
71+
}

cspuz_solver_backend/src/puzzle/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ puzzle_list!(puzz_link,
124124
(easyasabc, ["easyasabc"], "Easy as ABC", "ABCプレース"),
125125
(energywalk, ["energywalk"], "Energy Walk", "Energy Walk"),
126126
(evolmino, ["evolmino"], "Evolmino", "シンカミノ"),
127+
(fillmat, ["fillmat"], "Fillmat", "フィルマット"),
127128
(fillomino, ["fillomino"], "Fillomino", "フィルオミノ"),
128129
(firefly, ["firefly"], "Firefly", "ホタルビーム"),
129130
(firewalk, ["firewalk"], "Firewalk", "ファイアウォーク"),

0 commit comments

Comments
 (0)