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}