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}