wiki-wang-tiles.rs
4.5 kB · rust · 164 lines
1use figures::{ink, save, Board, Color};2use mrlyrs::core::error::Result;3use mrlyrs::core::Rng;45const SIDE: usize = 10;6const TILES: [[u8; 4]; 11] = [7 [3, 1, 1, 1],8 [3, 1, 2, 2],9 [3, 3, 3, 1],10 [2, 2, 1, 0],11 [2, 2, 0, 2],12 [0, 0, 1, 0],13 [0, 3, 2, 1],14 [1, 0, 2, 2],15 [1, 1, 0, 2],16 [1, 3, 2, 3],17 [3, 0, 1, 1],18];19const LEFT: usize = 0;20const RIGHT: usize = 1;21const BOTTOM: usize = 2;22const TOP: usize = 3;2324fn fits(patch: &[usize], at: usize, tile: usize) -> bool {25 let (row, col) = (at / SIDE, at % SIDE);26 let t = TILES[tile];27 let left = col == 0 || TILES[patch[at - 1]][RIGHT] == t[LEFT];28 let up = row == 0 || TILES[patch[at - SIDE]][BOTTOM] == t[TOP];29 left && up30}3132fn solve(rng: &mut Rng) -> Vec<usize> {33 let cells = SIDE * SIDE;34 let mut orders: Vec<Vec<usize>> = Vec::with_capacity(cells);35 let mut tried = vec![0usize; cells];36 let mut patch = vec![usize::MAX; cells];37 let mut at = 0;38 while at < cells {39 if orders.len() == at {40 let mut order: Vec<usize> = (0..TILES.len()).collect();41 rng.shuffle(&mut order);42 orders.push(order);43 tried[at] = 0;44 }45 let mut placed = false;46 while tried[at] < TILES.len() {47 let tile = orders[at][tried[at]];48 tried[at] += 1;49 if fits(&patch, at, tile) {50 patch[at] = tile;51 placed = true;52 break;53 }54 }55 if placed {56 at += 1;57 } else {58 orders.pop();59 patch[at] = usize::MAX;60 assert!(at > 0);61 at -= 1;62 }63 }64 patch65}6667fn kinds(patch: &[usize]) -> usize {68 let mut used = [false; TILES.len()];69 for &tile in patch {70 used[tile] = true;71 }72 used.iter().filter(|&&u| u).count()73}7475fn hue(colour: u8) -> Color {76 match colour {77 0 => ink::dim(),78 1 => ink::blue(),79 2 => ink::yellow(),80 _ => ink::orange(),81 }82}8384fn main() -> Result<()> {85 let mut colours: Vec<u8> = TILES.iter().flatten().copied().collect();86 colours.sort_unstable();87 colours.dedup();88 assert_eq!(colours, vec![0, 1, 2, 3]);89 let mut seed = 1u64;90 let mut patch = solve(&mut Rng::new(seed));91 while kinds(&patch) < TILES.len() {92 seed += 1;93 assert!(seed < 1000);94 patch = solve(&mut Rng::new(seed));95 }96 let mut inner = 0;97 for row in 0..SIDE {98 for col in 0..SIDE {99 let t = TILES[patch[row * SIDE + col]];100 if col + 1 < SIDE {101 assert_eq!(t[RIGHT], TILES[patch[row * SIDE + col + 1]][LEFT]);102 inner += 1;103 }104 if row + 1 < SIDE {105 assert_eq!(t[BOTTOM], TILES[patch[(row + 1) * SIDE + col]][TOP]);106 inner += 1;107 }108 }109 }110 assert_eq!(inner, 2 * SIDE * (SIDE - 1));111 assert_eq!(kinds(&patch), TILES.len());112113 let mut board = Board::square();114 let frame = board.frame(0.08);115 let cell = frame.w / SIDE as f64;116 let half = cell * 0.27;117 let deep = cell * 0.22;118 let pad = cell * 0.035;119 for row in 0..SIDE {120 for col in 0..SIDE {121 let t = TILES[patch[row * SIDE + col]];122 let (x, y) = (frame.x + col as f64 * cell, frame.y + row as f64 * cell);123 let (cx, cy) = (x + cell / 2.0, y + cell / 2.0);124 board.round_rect(125 x + pad,126 y + pad,127 cell - 2.0 * pad,128 cell - 2.0 * pad,129 pad,130 ink::line(),131 );132 let top = y + pad;133 let bottom = y + cell - pad;134 let left = x + pad;135 let right = x + cell - pad;136 board.triangle(137 (cx - half, top),138 (cx + half, top),139 (cx, top + deep),140 hue(t[TOP]),141 );142 board.triangle(143 (cx - half, bottom),144 (cx + half, bottom),145 (cx, bottom - deep),146 hue(t[BOTTOM]),147 );148 board.triangle(149 (left, cy - half),150 (left, cy + half),151 (left + deep, cy),152 hue(t[LEFT]),153 );154 board.triangle(155 (right, cy - half),156 (right, cy + half),157 (right - deep, cy),158 hue(t[RIGHT]),159 );160 }161 }162 save("wiki-wang-tiles", &board)?;163 Ok(())164}