diff --git a/src/main.rs b/src/main.rs index dc8d6a6..e5d1328 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,9 +2,11 @@ use std::{ error::Error, fmt::{self, Display}, io::{self, prelude::*}, + process, }; use bitvec::vec::BitVec; +use pathfinding::prelude::dijkstra; type Pos = (i16, i16); @@ -50,7 +52,7 @@ impl BoardConsts { } } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Clone, Hash)] struct Board { pub player: Pos, boxes: BitVec, @@ -89,6 +91,41 @@ impl Board { *self.boxes.get(consts.index_of(pos)).unwrap() } + pub fn is_won(&self, consts: &BoardConsts) -> bool { + consts.goals.iter_ones().eq(self.boxes.iter_ones()) + } + + pub fn solid_at(&self, consts: &BoardConsts, pos: Pos) -> bool { + self.box_at(consts, pos) || consts.wall_at(pos) + } + + pub fn do_move(&self, consts: &BoardConsts, (dx, dy): Pos) -> Option { + let p0 = self.player; + let p1 = (p0.0 + dx, p0.1 + dy); + let p2 = (p1.0 + dx, p1.1 + dy); + + if !self.solid_at(consts, p1) { + let mut new = self.clone(); + new.player = p1; + Some(new) + } else if self.box_at(consts, p1) && !self.solid_at(consts, p2) { + let mut new = self.clone(); + new.player = p1; + new.boxes.set(consts.index_of(p1), false); + new.boxes.set(consts.index_of(p2), true); + Some(new) + } else { + None + } + } + + pub fn successors(&self, consts: &BoardConsts) -> Vec { + [(-1, 0), (1, 0), (0, -1), (0, 1)] + .into_iter() + .filter_map(|delta| self.do_move(consts, delta)) + .collect() + } + pub fn parse(src: &str) -> Result<(BoardConsts, Board), BoardParseError> { let width = src.lines().map(str::len).max().unwrap_or(0) as i16; let mut player = Err(BoardParseError::NoPlayer); @@ -174,5 +211,17 @@ fn main() -> Result<(), Box> { let src = io::read_to_string(io::stdin())?; let (consts, board) = Board::parse(&src)?; board.write(&consts, &mut io::stdout())?; + let Some((steps, _step_count)) = dijkstra( + &board, + |b| b.successors(&consts).into_iter().map(move |b| (b, 1)), + |b| b.is_won(&consts), + ) else { + eprintln!("no solution found :("); + process::exit(1); + }; + for (i, step) in steps.into_iter().enumerate() { + eprintln!("step #{i}"); + step.write(&consts, &mut io::stdout())?; + } Ok(()) }