diff --git a/cspuz_rs_puzzles/src/puzzles/ripple.rs b/cspuz_rs_puzzles/src/puzzles/ripple.rs index 70aeb4e2..38f4151c 100644 --- a/cspuz_rs_puzzles/src/puzzles/ripple.rs +++ b/cspuz_rs_puzzles/src/puzzles/ripple.rs @@ -1,22 +1,48 @@ use cspuz_rs::graph; use cspuz_rs::serializer::{ - problem_to_url_with_context, url_to_problem, Choice, Combinator, Context, ContextBasedGrid, - Dict, HexInt, Optionalize, Rooms, Size, Spaces, Tuple2, + problem_to_url_with_context, url_to_problem, Choice, Choice2, Combinator, Context, + ContextBasedGrid, Dict, HexInt, Map, MultiDigit, Optionalize, Rooms, Size, Spaces, Tuple3, }; use cspuz_rs::solver::Solver; pub fn solve_ripple( borders: &graph::InnerGridEdges>>, clues: &[Vec>], + is_hole: &Option>>, ) -> Option>>> { let (h, w) = borders.base_shape(); - let rooms = graph::borders_to_rooms(borders); + // Making a copy of the borders for holes + let mut borders_with_holes = graph::InnerGridEdges { + horizontal: borders.horizontal.clone(), + vertical: borders.vertical.clone(), + }; + + // If there are holes, add a border between cells with holes and cells with no holes + if let Some(is_hole) = is_hole { + for y in 0..h { + for x in 0..w { + if is_hole[y][x] ^ is_hole[y][(x + 1).min(w - 1)] { + borders_with_holes.vertical[y][x] = true; + } + if is_hole[(y + 1).min(h - 1)][x] ^ is_hole[y][x] { + borders_with_holes.horizontal[y][x] = true; + } + } + } + } + + let rooms = graph::borders_to_rooms(&borders_with_holes); let mut ranges = vec![vec![(1, 1); w]; h]; for room in &rooms { for &(y, x) in room { - ranges[y][x] = (1, room.len() as i32); + let hole = if let Some(is_hole) = is_hole { + is_hole[y][x] + } else { + false + }; + ranges[y][x] = if hole { (0, 0) } else { (1, room.len() as i32) }; } } @@ -35,6 +61,16 @@ pub fn solve_ripple( } for room in &rooms { + let hole_only = room.iter().all(|&(y, x)| { + if let Some(is_hole) = is_hole { + is_hole[y][x] + } else { + false + } + }); + if hole_only { + continue; + } let room_nums = num.select(room); for i in 1..=room.len() { solver.add_expr(room_nums.eq(i as i32).count_true().eq(1)); @@ -70,16 +106,28 @@ pub fn solve_ripple( solver.irrefutable_facts().map(|f| f.get(num)) } -pub type Problem = (graph::InnerGridEdges>>, Vec>>); +pub type Problem = ( + graph::InnerGridEdges>>, + Vec>>, + Option>>, +); fn combinator() -> impl Combinator { - Size::new(Tuple2::new( + Size::new(Tuple3::new( Rooms, ContextBasedGrid::new(Choice::new(vec![ Box::new(Optionalize::new(HexInt)), Box::new(Spaces::new(None, 'g')), Box::new(Dict::new(Some(-1), ".")), ])), + Choice2::new( + Optionalize::new(ContextBasedGrid::new(Map::new( + MultiDigit::new(2, 5), + |x: bool| Some(if x { 1 } else { 0 }), + |n: i32| Some(n == 1), + ))), + Dict::new(None, ""), + ), )) } @@ -124,13 +172,13 @@ mod tests { vec![None, None, None, None, Some(1)], ]; - (borders, clues) + (borders, clues, None) } #[test] fn test_ripple_problem() { - let (borders, clues) = problem_for_tests(); - let ans = solve_ripple(&borders, &clues); + let (borders, clues, is_hole) = problem_for_tests(); + let ans = solve_ripple(&borders, &clues, &is_hole); assert!(ans.is_some()); let ans = ans.unwrap(); diff --git a/cspuz_solver_backend/src/puzzle/ripple.rs b/cspuz_solver_backend/src/puzzle/ripple.rs index 6f2ae613..7d2c764a 100644 --- a/cspuz_solver_backend/src/puzzle/ripple.rs +++ b/cspuz_solver_backend/src/puzzle/ripple.rs @@ -3,8 +3,8 @@ use crate::uniqueness::check_uniqueness; use cspuz_rs_puzzles::puzzles::ripple; pub fn solve(url: &str) -> Result { - let (borders, clues) = ripple::deserialize_problem(url).ok_or("invalid url")?; - let ans = ripple::solve_ripple(&borders, &clues); + let (borders, clues, is_hole) = ripple::deserialize_problem(url).ok_or("invalid url")?; + let ans = ripple::solve_ripple(&borders, &clues, &is_hole); let height = clues.len(); let width = clues[0].len(); @@ -14,6 +14,12 @@ pub fn solve(url: &str) -> Result { for y in 0..height { for x in 0..width { + if let Some(is_hole) = &is_hole { + if is_hole[y][x] { + board.push(Item::cell(y, x, "black", ItemKind::Fill)); + continue; + } + } if let Some(n) = clues[y][x] { if n >= 0 { board.push(Item::cell(y, x, "black", ItemKind::Num(n)));