basic autosolving
This commit is contained in:
parent
767f3bc3b6
commit
b0418f3bcd
1 changed files with 50 additions and 1 deletions
51
src/main.rs
51
src/main.rs
|
|
@ -2,9 +2,11 @@ use std::{
|
||||||
error::Error,
|
error::Error,
|
||||||
fmt::{self, Display},
|
fmt::{self, Display},
|
||||||
io::{self, prelude::*},
|
io::{self, prelude::*},
|
||||||
|
process,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bitvec::vec::BitVec;
|
use bitvec::vec::BitVec;
|
||||||
|
use pathfinding::prelude::dijkstra;
|
||||||
|
|
||||||
type Pos = (i16, i16);
|
type Pos = (i16, i16);
|
||||||
|
|
||||||
|
|
@ -50,7 +52,7 @@ impl BoardConsts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||||
struct Board {
|
struct Board {
|
||||||
pub player: Pos,
|
pub player: Pos,
|
||||||
boxes: BitVec,
|
boxes: BitVec,
|
||||||
|
|
@ -89,6 +91,41 @@ impl Board {
|
||||||
*self.boxes.get(consts.index_of(pos)).unwrap()
|
*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<Self> {
|
||||||
|
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<Self> {
|
||||||
|
[(-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> {
|
pub fn parse(src: &str) -> Result<(BoardConsts, Board), BoardParseError> {
|
||||||
let width = src.lines().map(str::len).max().unwrap_or(0) as i16;
|
let width = src.lines().map(str::len).max().unwrap_or(0) as i16;
|
||||||
let mut player = Err(BoardParseError::NoPlayer);
|
let mut player = Err(BoardParseError::NoPlayer);
|
||||||
|
|
@ -174,5 +211,17 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let src = io::read_to_string(io::stdin())?;
|
let src = io::read_to_string(io::stdin())?;
|
||||||
let (consts, board) = Board::parse(&src)?;
|
let (consts, board) = Board::parse(&src)?;
|
||||||
board.write(&consts, &mut io::stdout())?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue