word.rs
9.6 kB · rust · 324 lines
1pub const CODES: [u8; 15] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];2pub const LIBRARY: [u8; 10] = [3, 5, 6, 7, 9, 10, 11, 12, 13, 14];34pub fn corners(code: u8) -> Vec<(usize, usize)> {5 (0..4usize)6 .filter(|i| (code >> i) & 1 == 1)7 .map(|i| (i / 2, i % 2))8 .collect()9}1011pub struct Grid {12 pub side: usize,13 pub cells: Vec<bool>,14}1516pub fn render(word: &[u8]) -> Grid {17 let mut side = 1usize;18 let mut cells = vec![true];19 for &code in word {20 let next = side * 2;21 let mut out = vec![false; next * next];22 let filled = corners(code);23 for r in 0..side {24 for c in 0..side {25 if cells[r * side + c] {26 for &(a, b) in &filled {27 out[(2 * r + a) * next + 2 * c + b] = true;28 }29 }30 }31 }32 side = next;33 cells = out;34 }35 Grid { side, cells }36}3738impl Grid {39 pub fn at(&self, r: usize, c: usize) -> bool {40 self.cells[r * self.side + c]41 }4243 pub fn fill(&self) -> u64 {44 self.cells.iter().filter(|c| **c).count() as u6445 }4647 pub fn diagonal(&self) -> u64 {48 (0..self.side).filter(|&i| self.at(i, i)).count() as u6449 }5051 pub fn adjacent(&self) -> u64 {52 let mut count = 0u64;53 for r in 0..self.side {54 for c in 0..self.side {55 if !self.at(r, c) {56 continue;57 }58 if r + 1 < self.side && self.at(r + 1, c) {59 count += 1;60 }61 if c + 1 < self.side && self.at(r, c + 1) {62 count += 1;63 }64 }65 }66 count67 }6869 pub fn quads(&self) -> u64 {70 let mut count = 0u64;71 for r in 0..self.side.saturating_sub(1) {72 for c in 0..self.side.saturating_sub(1) {73 if self.at(r, c) && self.at(r + 1, c) && self.at(r, c + 1) && self.at(r + 1, c + 1)74 {75 count += 1;76 }77 }78 }79 count80 }8182 pub fn interior(&self) -> u64 {83 let mut count = 0u64;84 for r in 0..self.side {85 for c in 0..self.side {86 if !self.at(r, c) {87 continue;88 }89 let inside = r > 090 && c > 091 && r + 1 < self.side92 && c + 1 < self.side93 && self.at(r - 1, c)94 && self.at(r + 1, c)95 && self.at(r, c - 1)96 && self.at(r, c + 1);97 if inside {98 count += 1;99 }100 }101 }102 count103 }104105 pub fn boundary(&self) -> u64 {106 self.fill() - self.interior()107 }108109 pub fn perimeter(&self) -> u64 {110 4 * self.fill() - 2 * self.adjacent()111 }112113 pub fn euler(&self) -> i64 {114 self.fill() as i64 - self.adjacent() as i64 + self.quads() as i64115 }116117 pub fn labels(&self) -> (Vec<i64>, u64) {118 let mut label = vec![-1i64; self.cells.len()];119 let mut count = 0i64;120 let mut stack: Vec<usize> = Vec::new();121 for start in 0..self.cells.len() {122 if !self.cells[start] || label[start] >= 0 {123 continue;124 }125 label[start] = count;126 stack.push(start);127 while let Some(at) = stack.pop() {128 let r = at / self.side;129 let c = at % self.side;130 let push = |rr: usize, cc: usize, label: &mut Vec<i64>, stack: &mut Vec<usize>| {131 let next = rr * self.side + cc;132 if self.cells[next] && label[next] < 0 {133 label[next] = count;134 stack.push(next);135 }136 };137 if r > 0 {138 push(r - 1, c, &mut label, &mut stack);139 }140 if r + 1 < self.side {141 push(r + 1, c, &mut label, &mut stack);142 }143 if c > 0 {144 push(r, c - 1, &mut label, &mut stack);145 }146 if c + 1 < self.side {147 push(r, c + 1, &mut label, &mut stack);148 }149 }150 count += 1;151 }152 (label, count as u64)153 }154155 pub fn components(&self) -> u64 {156 self.labels().1157 }158159 pub fn holes(&self) -> u64 {160 let mut seen = vec![false; self.cells.len()];161 let mut stack: Vec<usize> = Vec::new();162 let open = |at: usize, seen: &mut Vec<bool>, stack: &mut Vec<usize>| {163 if !self.cells[at] && !seen[at] {164 seen[at] = true;165 stack.push(at);166 }167 };168 for i in 0..self.side {169 open(i, &mut seen, &mut stack);170 open((self.side - 1) * self.side + i, &mut seen, &mut stack);171 open(i * self.side, &mut seen, &mut stack);172 open(i * self.side + self.side - 1, &mut seen, &mut stack);173 }174 while let Some(at) = stack.pop() {175 let r = at / self.side;176 let c = at % self.side;177 let step = |rr: usize, cc: usize, seen: &mut Vec<bool>, stack: &mut Vec<usize>| {178 let next = rr * self.side + cc;179 if !self.cells[next] && !seen[next] {180 seen[next] = true;181 stack.push(next);182 }183 };184 if r > 0 {185 step(r - 1, c, &mut seen, &mut stack);186 }187 if r + 1 < self.side {188 step(r + 1, c, &mut seen, &mut stack);189 }190 if c > 0 {191 step(r, c - 1, &mut seen, &mut stack);192 }193 if c + 1 < self.side {194 step(r, c + 1, &mut seen, &mut stack);195 }196 }197 let mut count = 0u64;198 for start in 0..self.cells.len() {199 if self.cells[start] || seen[start] {200 continue;201 }202 count += 1;203 seen[start] = true;204 stack.push(start);205 while let Some(at) = stack.pop() {206 let r = at / self.side;207 let c = at % self.side;208 let step = |rr: usize, cc: usize, seen: &mut Vec<bool>, stack: &mut Vec<usize>| {209 let next = rr * self.side + cc;210 if !self.cells[next] && !seen[next] {211 seen[next] = true;212 stack.push(next);213 }214 };215 if r > 0 {216 step(r - 1, c, &mut seen, &mut stack);217 }218 if r + 1 < self.side {219 step(r + 1, c, &mut seen, &mut stack);220 }221 if c > 0 {222 step(r, c - 1, &mut seen, &mut stack);223 }224 if c + 1 < self.side {225 step(r, c + 1, &mut seen, &mut stack);226 }227 }228 }229 count230 }231232 pub fn profile(&self) -> Vec<u64> {233 let mut out = vec![0u64; 2 * self.side - 1];234 for r in 0..self.side {235 for c in 0..self.side {236 if self.at(r, c) {237 out[r + c] += 1;238 }239 }240 }241 out242 }243244 pub fn contacts(&self) -> (u64, u64) {245 let rows = (0..self.side)246 .filter(|&r| self.at(r, 0) && self.at(r, self.side - 1))247 .count() as u64;248 let cols = (0..self.side)249 .filter(|&c| self.at(0, c) && self.at(self.side - 1, c))250 .count() as u64;251 (rows, cols)252 }253254 pub fn merging(&self) -> u64 {255 let (label, _) = self.labels();256 let mut touched: Vec<i64> = Vec::new();257 let mark = |at: usize, touched: &mut Vec<i64>| {258 if label[at] >= 0 && !touched.contains(&label[at]) {259 touched.push(label[at]);260 }261 };262 for r in 0..self.side {263 if self.at(r, 0) && self.at(r, self.side - 1) {264 mark(r * self.side, &mut touched);265 mark(r * self.side + self.side - 1, &mut touched);266 }267 }268 for c in 0..self.side {269 if self.at(0, c) && self.at(self.side - 1, c) {270 mark(c, &mut touched);271 mark((self.side - 1) * self.side + c, &mut touched);272 }273 }274 touched.len() as u64275 }276}277278#[derive(Clone, PartialEq, Eq)]279pub struct Obs {280 pub fill: u64,281 pub diagonal: u64,282 pub boundary: u64,283 pub perimeter: u64,284 pub components: u64,285 pub euler: i64,286 pub holes: u64,287 pub profile: Vec<u64>,288}289290pub fn observe(word: &[u8]) -> Obs {291 let grid = render(word);292 Obs {293 fill: grid.fill(),294 diagonal: grid.diagonal(),295 boundary: grid.boundary(),296 perimeter: grid.perimeter(),297 components: grid.components(),298 euler: grid.euler(),299 holes: grid.holes(),300 profile: grid.profile(),301 }302}303304pub const SERIES: [&str; 4] = ["components", "Euler characteristic", "boundary", "holes"];305306pub fn series_value(obs: &Obs, which: usize) -> i64 {307 match which {308 0 => obs.components as i64,309 1 => obs.euler,310 2 => obs.boundary as i64,311 _ => obs.holes as i64,312 }313}314315pub fn spell(word: &[u8]) -> String {316 if word.is_empty() {317 return "e".to_string();318 }319 if word.len() == 1 {320 return format!("{}", word[0]);321 }322 let inner: Vec<String> = word.iter().map(|c| format!("{c}")).collect();323 format!("({})", inner.join(","))324}