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