345 lines
9.7 KiB
Rust
345 lines
9.7 KiB
Rust
use smallvec::SmallVec;
|
|
use std::collections::{hash_map::Entry::Vacant, HashMap, VecDeque};
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
struct Board {
|
|
board: [[u8; 5]; 4],
|
|
pieces: [[u8; 2]; 11],
|
|
}
|
|
|
|
impl Board {
|
|
fn new() -> Self {
|
|
Self {
|
|
board: [
|
|
[1, 1, 0, 2, 2],
|
|
[3, 3, 4, 5, 6],
|
|
[3, 3, 4, 7, 8],
|
|
[9, 9, 0, 10, 10],
|
|
],
|
|
pieces: [
|
|
[0, 0],
|
|
[0, 0],
|
|
[3, 0],
|
|
[0, 1],
|
|
[2, 1],
|
|
[3, 1],
|
|
[4, 1],
|
|
[3, 2],
|
|
[4, 2],
|
|
[0, 3],
|
|
[3, 3],
|
|
],
|
|
}
|
|
}
|
|
|
|
fn apply_move(&mut self, m: &Move) {
|
|
let pos = self.pieces[m.piece as usize];
|
|
let tiles: &[[u8; 2]] = match &m.piece {
|
|
1 | 2 | 9 | 10 => &[[0, 0], [1, 0]],
|
|
3 => &[[0, 0], [1, 0], [0, 1], [1, 1]],
|
|
4 => &[[0, 0], [0, 1]],
|
|
5..=8 => &[[0, 0]],
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
for [x, y] in tiles {
|
|
self.board[(pos[1] + y) as usize][(pos[0] + x) as usize] = 0;
|
|
}
|
|
|
|
let newpos = match &m.dir {
|
|
Dir::Up => [pos[0], pos[1] - 1],
|
|
Dir::Right => [pos[0] + 1, pos[1]],
|
|
Dir::Down => [pos[0], pos[1] + 1],
|
|
Dir::Left => [pos[0] - 1, pos[1]],
|
|
};
|
|
|
|
for [x, y] in tiles {
|
|
self.board[(newpos[1] + y) as usize][(newpos[0] + x) as usize] = m.piece;
|
|
}
|
|
|
|
self.pieces[m.piece as usize] = newpos;
|
|
}
|
|
|
|
fn revert_move(&mut self, m: &Move) {
|
|
let im = Move {
|
|
dir: match m.dir {
|
|
Dir::Up => Dir::Down,
|
|
Dir::Right => Dir::Left,
|
|
Dir::Down => Dir::Up,
|
|
Dir::Left => Dir::Right,
|
|
},
|
|
piece: m.piece,
|
|
};
|
|
self.apply_move(&im);
|
|
}
|
|
|
|
fn get_hash(&self) -> u64 {
|
|
let mut r = 0;
|
|
let p = self.pieces[3];
|
|
r |= (p[0] << 2 | p[1]) as u64;
|
|
let p = self.pieces[4];
|
|
r |= ((p[0] << 2 | p[1]) as u64) << 5;
|
|
|
|
let mut p = [
|
|
self.pieces[1],
|
|
self.pieces[2],
|
|
self.pieces[9],
|
|
self.pieces[10],
|
|
];
|
|
p.sort();
|
|
for (i, p) in p.into_iter().enumerate() {
|
|
r |= ((p[0] << 2 | p[1]) as u64) << (i * 5 + 10);
|
|
}
|
|
|
|
let mut p = [
|
|
self.pieces[5],
|
|
self.pieces[6],
|
|
self.pieces[7],
|
|
self.pieces[8],
|
|
];
|
|
p.sort();
|
|
for (i, p) in p.into_iter().enumerate() {
|
|
r |= ((p[0] << 2 | p[1]) as u64) << (i * 5 + 30);
|
|
}
|
|
|
|
r
|
|
}
|
|
|
|
fn possible_moves(&self) -> SmallVec<[Move; 8]> {
|
|
let mut v = SmallVec::new();
|
|
|
|
for i in 1..=10 {
|
|
let [px, py] = self.pieces[i as usize];
|
|
match i {
|
|
1 | 2 | 9 | 10 => {
|
|
if px > 0 && self.board[py as usize][(px - 1) as usize] == 0 {
|
|
v.push(Move {
|
|
dir: Dir::Left,
|
|
piece: i,
|
|
});
|
|
}
|
|
|
|
if px < 3 && self.board[py as usize][(px + 2) as usize] == 0 {
|
|
v.push(Move {
|
|
dir: Dir::Right,
|
|
piece: i,
|
|
});
|
|
}
|
|
|
|
if py > 0
|
|
&& self.board[(py - 1) as usize][px as usize] == 0
|
|
&& self.board[(py - 1) as usize][(px + 1) as usize] == 0
|
|
{
|
|
v.push(Move {
|
|
dir: Dir::Up,
|
|
piece: i,
|
|
});
|
|
}
|
|
|
|
if py < 3
|
|
&& self.board[(py + 1) as usize][px as usize] == 0
|
|
&& self.board[(py + 1) as usize][(px + 1) as usize] == 0
|
|
{
|
|
v.push(Move {
|
|
dir: Dir::Down,
|
|
piece: i,
|
|
});
|
|
}
|
|
}
|
|
3 => {
|
|
if px > 0
|
|
&& self.board[py as usize][(px - 1) as usize] == 0
|
|
&& self.board[(py + 1) as usize][(px - 1) as usize] == 0
|
|
{
|
|
v.push(Move {
|
|
dir: Dir::Left,
|
|
piece: i,
|
|
});
|
|
}
|
|
|
|
if px < 3
|
|
&& self.board[py as usize][(px + 2) as usize] == 0
|
|
&& self.board[(py + 1) as usize][(px + 2) as usize] == 0
|
|
{
|
|
v.push(Move {
|
|
dir: Dir::Right,
|
|
piece: i,
|
|
});
|
|
}
|
|
if py > 0
|
|
&& self.board[(py - 1) as usize][px as usize] == 0
|
|
&& self.board[(py - 1) as usize][(px + 1) as usize] == 0
|
|
{
|
|
v.push(Move {
|
|
dir: Dir::Up,
|
|
piece: i,
|
|
});
|
|
}
|
|
|
|
if py < 2
|
|
&& self.board[(py + 2) as usize][px as usize] == 0
|
|
&& self.board[(py + 2) as usize][(px + 1) as usize] == 0
|
|
{
|
|
v.push(Move {
|
|
dir: Dir::Down,
|
|
piece: i,
|
|
});
|
|
}
|
|
}
|
|
4 => {
|
|
if px > 0
|
|
&& self.board[py as usize][(px - 1) as usize] == 0
|
|
&& self.board[(py + 1) as usize][(px - 1) as usize] == 0
|
|
{
|
|
v.push(Move {
|
|
dir: Dir::Left,
|
|
piece: i,
|
|
});
|
|
}
|
|
|
|
if px < 4
|
|
&& self.board[py as usize][(px + 1) as usize] == 0
|
|
&& self.board[(py + 1) as usize][(px + 1) as usize] == 0
|
|
{
|
|
v.push(Move {
|
|
dir: Dir::Right,
|
|
piece: i,
|
|
});
|
|
}
|
|
|
|
if py > 0 && self.board[(py - 1) as usize][px as usize] == 0 {
|
|
v.push(Move {
|
|
dir: Dir::Up,
|
|
piece: i,
|
|
});
|
|
}
|
|
|
|
if py < 2 && self.board[(py + 2) as usize][px as usize] == 0 {
|
|
v.push(Move {
|
|
dir: Dir::Down,
|
|
piece: i,
|
|
});
|
|
}
|
|
}
|
|
5..=8 => {
|
|
if px > 0 && self.board[py as usize][(px - 1) as usize] == 0 {
|
|
v.push(Move {
|
|
dir: Dir::Left,
|
|
piece: i,
|
|
});
|
|
}
|
|
|
|
if px < 4 && self.board[py as usize][(px + 1) as usize] == 0 {
|
|
v.push(Move {
|
|
dir: Dir::Right,
|
|
piece: i,
|
|
});
|
|
}
|
|
|
|
if py > 0 && self.board[(py - 1) as usize][px as usize] == 0 {
|
|
v.push(Move {
|
|
dir: Dir::Up,
|
|
piece: i,
|
|
});
|
|
}
|
|
|
|
if py < 3 && self.board[(py + 1) as usize][px as usize] == 0 {
|
|
v.push(Move {
|
|
dir: Dir::Down,
|
|
piece: i,
|
|
});
|
|
}
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
v
|
|
}
|
|
|
|
fn print(&self) {
|
|
for y in 0..4 {
|
|
for x in 0..5 {
|
|
print!("{:x}", self.board[y][x]);
|
|
}
|
|
println!();
|
|
}
|
|
println!();
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
enum Dir {
|
|
Up,
|
|
Right,
|
|
Down,
|
|
Left,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
struct Move {
|
|
dir: Dir,
|
|
piece: u8,
|
|
}
|
|
|
|
fn solver() -> Option<(HashMap<u64, (u64, Move)>, Board)> {
|
|
let mut solution = HashMap::new();
|
|
let mut queue = VecDeque::new();
|
|
|
|
solution.insert(
|
|
Board::new().get_hash(),
|
|
(
|
|
0,
|
|
Move {
|
|
dir: Dir::Up,
|
|
piece: 0,
|
|
},
|
|
),
|
|
);
|
|
|
|
queue.push_back(Board::new());
|
|
|
|
while let Some(q) = queue.pop_front() {
|
|
let moves = q.possible_moves();
|
|
|
|
for m in moves {
|
|
let mut b = q;
|
|
|
|
b.apply_move(&m);
|
|
|
|
if let Vacant(e) = solution.entry(b.get_hash()) {
|
|
e.insert((q.get_hash(), m));
|
|
if b.pieces[3] == [3, 1] {
|
|
return Some((solution, b));
|
|
}
|
|
queue.push_back(b);
|
|
}
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
fn main() {
|
|
if let Some((s, b)) = solver() {
|
|
let mut h = b.get_hash();
|
|
|
|
let mut r = Vec::new();
|
|
while let Some(&(i, m)) = s.get(&h) {
|
|
// println!("{:b}", i);
|
|
r.push(m);
|
|
h = i;
|
|
}
|
|
|
|
r.reverse();
|
|
|
|
let mut b = Board::new();
|
|
for m in &r[1..] {
|
|
b.print();
|
|
b.apply_move(m);
|
|
println!("Move: {} {:?}", m.piece, m.dir);
|
|
}
|
|
b.print();
|
|
println!("Length: {}", r.len() - 1);
|
|
}
|
|
}
|