laulens_move/src/main.rs
2024-08-05 13:04:16 +02:00

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);
}
}