wiki-transfer-matrix.rs

3.5 kB · rust · 103 lines

1use mrlycore::errors::Result;2use mrlyfig::{ink, plot, save, Board, Grid};3use std::f64::consts::TAU;45const STATES: usize = 3;6const LENGTH: usize = 8;7const RULE: [[u64; STATES]; STATES] = [[1, 1, 0], [0, 0, 1], [1, 0, 0]];89fn times(a: &[[u64; STATES]; STATES], b: &[[u64; STATES]; STATES]) -> [[u64; STATES]; STATES] {10    let mut out = [[0u64; STATES]; STATES];11    for row in 0..STATES {12        for mid in 0..STATES {13            for col in 0..STATES {14                out[row][col] += a[row][mid] * b[mid][col];15            }16        }17    }18    out19}2021fn total(a: &[[u64; STATES]; STATES]) -> u64 {22    a.iter().map(|row| row.iter().sum::<u64>()).sum()23}2425fn dart(board: &mut Board, tip: (f64, f64), way: (f64, f64), head: f64) {26    let side = (-way.1, way.0);27    let base = (tip.0 - way.0 * head, tip.1 - way.1 * head);28    board.triangle(29        tip,30        (base.0 + side.0 * head * 0.5, base.1 + side.1 * head * 0.5),31        (base.0 - side.0 * head * 0.5, base.1 - side.1 * head * 0.5),32        ink::yellow(),33    );34}3536fn main() -> Result<()> {37    let mut power = RULE;38    let mut walks = Vec::with_capacity(LENGTH);39    for _ in 0..LENGTH {40        walks.push(total(&power));41        power = times(&power, &RULE);42    }43    assert_eq!(walks, vec![4, 6, 9, 13, 19, 28, 41, 60]);44    let ratio = walks[LENGTH - 1] as f64 / walks[LENGTH - 2] as f64;45    assert!((ratio - 1.465571).abs() < 0.01);4647    let mut board = Board::square();48    let frame = board.frame(0.08);49    let halves = frame.rows(2);50    let panels = halves[0].cols(2);5152    let stage = panels[0].square().inset(panels[0].w * 0.06);53    let (cx, cy) = stage.center();54    let reach = stage.w * 0.34;55    let knob = stage.w * 0.10;56    let seats: Vec<(f64, f64)> = (0..STATES)57        .map(|state| {58            let turn = TAU * (state as f64 / STATES as f64 - 0.25);59            (cx + reach * turn.cos(), cy + reach * turn.sin())60        })61        .collect();62    let head = knob * 0.62;63    for (from, to) in [(0usize, 1usize), (1, 2), (2, 0)] {64        let (a, b) = (seats[from], seats[to]);65        let span = ((b.0 - a.0).powi(2) + (b.1 - a.1).powi(2)).sqrt();66        let way = ((b.0 - a.0) / span, (b.1 - a.1) / span);67        let start = (a.0 + way.0 * knob * 1.12, a.1 + way.1 * knob * 1.12);68        let tip = (b.0 - way.0 * knob * 1.12, b.1 - way.1 * knob * 1.12);69        board.segment(start, tip, knob * 0.22, ink::yellow());70        dart(&mut board, tip, way, head);71    }72    let loop_at = (seats[0].0, seats[0].1 - knob * 1.35);73    board.ring(loop_at.0, loop_at.1, knob * 0.92, knob * 0.22, ink::yellow());74    for seat in &seats {75        board.disc(seat.0, seat.1, knob, ink::blue());76    }77    let turn = TAU / 6.0;78    let tip = (79        loop_at.0 + knob * 0.92 * turn.cos(),80        loop_at.1 + knob * 0.92 * turn.sin(),81    );82    dart(&mut board, tip, (-turn.sin(), turn.cos()), head);8384    let table = panels[1].square().inset(panels[1].w * 0.12);85    let grid = Grid::new(table, STATES, STATES, 0.10);86    for row in 0..STATES {87        for col in 0..STATES {88            let paint = if RULE[row][col] == 1 {89                ink::yellow()90            } else {91                ink::fade(ink::line(), 0.45)92            };93            grid.fill(&mut board, col, row, paint);94        }95    }9697    let chart = halves[1].inset(halves[1].h * 0.14);98    let tall: Vec<f64> = walks.iter().map(|&n| n as f64).collect();99    plot::bars(&mut board, chart, &tall, 0.34, ink::blue());100    plot::baseline(&mut board, chart, ink::line());101    save("wiki-transfer-matrix", &board)?;102    Ok(())103}