Skip to content

Commit f9ebf47

Browse files
authored
support holes in Ripple Effect solver (#276)
1 parent 4c08bfe commit f9ebf47

2 files changed

Lines changed: 65 additions & 11 deletions

File tree

cspuz_rs_puzzles/src/puzzles/ripple.rs

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,48 @@
11
use cspuz_rs::graph;
22
use cspuz_rs::serializer::{
3-
problem_to_url_with_context, url_to_problem, Choice, Combinator, Context, ContextBasedGrid,
4-
Dict, HexInt, Optionalize, Rooms, Size, Spaces, Tuple2,
3+
problem_to_url_with_context, url_to_problem, Choice, Choice2, Combinator, Context,
4+
ContextBasedGrid, Dict, HexInt, Map, MultiDigit, Optionalize, Rooms, Size, Spaces, Tuple3,
55
};
66
use cspuz_rs::solver::Solver;
77

88
pub fn solve_ripple(
99
borders: &graph::InnerGridEdges<Vec<Vec<bool>>>,
1010
clues: &[Vec<Option<i32>>],
11+
is_hole: &Option<Vec<Vec<bool>>>,
1112
) -> Option<Vec<Vec<Option<i32>>>> {
1213
let (h, w) = borders.base_shape();
1314

14-
let rooms = graph::borders_to_rooms(borders);
15+
// Making a copy of the borders for holes
16+
let mut borders_with_holes = graph::InnerGridEdges {
17+
horizontal: borders.horizontal.clone(),
18+
vertical: borders.vertical.clone(),
19+
};
20+
21+
// If there are holes, add a border between cells with holes and cells with no holes
22+
if let Some(is_hole) = is_hole {
23+
for y in 0..h {
24+
for x in 0..w {
25+
if is_hole[y][x] ^ is_hole[y][(x + 1).min(w - 1)] {
26+
borders_with_holes.vertical[y][x] = true;
27+
}
28+
if is_hole[(y + 1).min(h - 1)][x] ^ is_hole[y][x] {
29+
borders_with_holes.horizontal[y][x] = true;
30+
}
31+
}
32+
}
33+
}
34+
35+
let rooms = graph::borders_to_rooms(&borders_with_holes);
1536
let mut ranges = vec![vec![(1, 1); w]; h];
1637

1738
for room in &rooms {
1839
for &(y, x) in room {
19-
ranges[y][x] = (1, room.len() as i32);
40+
let hole = if let Some(is_hole) = is_hole {
41+
is_hole[y][x]
42+
} else {
43+
false
44+
};
45+
ranges[y][x] = if hole { (0, 0) } else { (1, room.len() as i32) };
2046
}
2147
}
2248

@@ -35,6 +61,16 @@ pub fn solve_ripple(
3561
}
3662

3763
for room in &rooms {
64+
let hole_only = room.iter().all(|&(y, x)| {
65+
if let Some(is_hole) = is_hole {
66+
is_hole[y][x]
67+
} else {
68+
false
69+
}
70+
});
71+
if hole_only {
72+
continue;
73+
}
3874
let room_nums = num.select(room);
3975
for i in 1..=room.len() {
4076
solver.add_expr(room_nums.eq(i as i32).count_true().eq(1));
@@ -70,16 +106,28 @@ pub fn solve_ripple(
70106
solver.irrefutable_facts().map(|f| f.get(num))
71107
}
72108

73-
pub type Problem = (graph::InnerGridEdges<Vec<Vec<bool>>>, Vec<Vec<Option<i32>>>);
109+
pub type Problem = (
110+
graph::InnerGridEdges<Vec<Vec<bool>>>,
111+
Vec<Vec<Option<i32>>>,
112+
Option<Vec<Vec<bool>>>,
113+
);
74114

75115
fn combinator() -> impl Combinator<Problem> {
76-
Size::new(Tuple2::new(
116+
Size::new(Tuple3::new(
77117
Rooms,
78118
ContextBasedGrid::new(Choice::new(vec![
79119
Box::new(Optionalize::new(HexInt)),
80120
Box::new(Spaces::new(None, 'g')),
81121
Box::new(Dict::new(Some(-1), ".")),
82122
])),
123+
Choice2::new(
124+
Optionalize::new(ContextBasedGrid::new(Map::new(
125+
MultiDigit::new(2, 5),
126+
|x: bool| Some(if x { 1 } else { 0 }),
127+
|n: i32| Some(n == 1),
128+
))),
129+
Dict::new(None, ""),
130+
),
83131
))
84132
}
85133

@@ -124,13 +172,13 @@ mod tests {
124172
vec![None, None, None, None, Some(1)],
125173
];
126174

127-
(borders, clues)
175+
(borders, clues, None)
128176
}
129177

130178
#[test]
131179
fn test_ripple_problem() {
132-
let (borders, clues) = problem_for_tests();
133-
let ans = solve_ripple(&borders, &clues);
180+
let (borders, clues, is_hole) = problem_for_tests();
181+
let ans = solve_ripple(&borders, &clues, &is_hole);
134182
assert!(ans.is_some());
135183
let ans = ans.unwrap();
136184

cspuz_solver_backend/src/puzzle/ripple.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use crate::uniqueness::check_uniqueness;
33
use cspuz_rs_puzzles::puzzles::ripple;
44

55
pub fn solve(url: &str) -> Result<Board, &'static str> {
6-
let (borders, clues) = ripple::deserialize_problem(url).ok_or("invalid url")?;
7-
let ans = ripple::solve_ripple(&borders, &clues);
6+
let (borders, clues, is_hole) = ripple::deserialize_problem(url).ok_or("invalid url")?;
7+
let ans = ripple::solve_ripple(&borders, &clues, &is_hole);
88

99
let height = clues.len();
1010
let width = clues[0].len();
@@ -14,6 +14,12 @@ pub fn solve(url: &str) -> Result<Board, &'static str> {
1414

1515
for y in 0..height {
1616
for x in 0..width {
17+
if let Some(is_hole) = &is_hole {
18+
if is_hole[y][x] {
19+
board.push(Item::cell(y, x, "black", ItemKind::Fill));
20+
continue;
21+
}
22+
}
1723
if let Some(n) = clues[y][x] {
1824
if n >= 0 {
1925
board.push(Item::cell(y, x, "black", ItemKind::Num(n)));

0 commit comments

Comments
 (0)