use std::{env::args, fs}; fn main() { let file = args().nth(1).expect("Usage : boxon "); let content = fs::read_to_string(&file).unwrap_or_else(|_| panic!("Could not read file '{file}'.")); let mut grid = Grid::from(content); grid.fix(); let content: String = grid.into(); fs::write(&file, content).unwrap_or_else(|_| panic!("Could not write result to file '{file}'.")); } #[derive(Debug, Clone)] struct Grid(Vec>); impl> From for Grid { fn from(value: T) -> Self { Self(value.as_ref().lines().map(str::chars).map(Iterator::collect).collect()) } } impl From for String { fn from(val: Grid) -> Self { val.0 .iter() .map(|l| l.iter().collect::()) .collect::>() .join("\n") } } impl Grid { fn fix(&mut self) -> usize { let mut result = 0; let copy = self.clone(); for (y, line) in self.0.iter_mut().enumerate() { for (x, character) in line.iter_mut().enumerate() { if let Some(cross) = copy.cross_at(x as i32, y as i32) { let new_char = match cross { Cross( ((), MM, ()), // (MM, MM, MM), // ((), MM, ()), // ) => '┼', Cross( ((), MM, ()), // (F_, MM, MM), // ((), MM, ()), // ) => '├', Cross( ((), MM, ()), // (MM, MM, F_), // ((), MM, ()), // ) => '┤', Cross( ((), MM, ()), // (MM, MM, MM), // ((), F_, ()), // ) => '┴', Cross( ((), F_, ()), // (MM, MM, MM), // ((), MM, ()), // ) => '┬', Cross( ((), F_, ()), // (MM, MM, MM), // ((), F_, ()), // ) => '─', Cross( ((), MM, ()), // (F_, MM, F_), // ((), MM, ()), // ) => '│', Cross( ((), MM, ()), // (MM, MM, F_), // ((), F_, ()), // ) => '┘', Cross( ((), F_, ()), // (MM, MM, F_), // ((), MM, ()), // ) => '┐', Cross( ((), F_, ()), // (F_, MM, MM), // ((), MM, ()), // ) => '┌', Cross( ((), MM, ()), // (F_, MM, MM), // ((), F_, ()), // ) => '└', _ => continue, }; *character = new_char; result += 1; } } } result } #[rustfmt::skip] fn cross_at(&self, x: i32, y: i32) -> Option { fn at(g: &Grid, x: i32, y: i32) -> Option { if x < 0 || y < 0 { None } else { Some(connects(*g.0.get(y as usize)?.get(x as usize)?)) } } let get = |x: i32, y: i32| -> Cross { at(self, x, y).unwrap_or(connects(' ')) }; let c = get(x, y); let its_y = y; Some(Cross( ( (), get(x, y - 1).d() && c.u(), ()), (get(x - 1, y).r() && c.l(), get(x, its_y).c(), get(x + 1, y).l() && c.r()), ( (), get(x, y + 1).u() && c.d(), ()), )) } } fn connects(c: char) -> Cross { match c { '-' => Cross( ((), F_, ()), // (MM, MM, MM), // ((), F_, ()), // ), '|' => Cross( ((), MM, ()), // (F_, MM, F_), // ((), MM, ()), // ), '+' => Cross( ((), MM, ()), // (MM, MM, MM), // ((), MM, ()), // ), '┼' => Cross( ((), MM, ()), // (MM, MM, MM), // ((), MM, ()), // ), '├' => Cross( ((), MM, ()), // (F_, MM, MM), // ((), MM, ()), // ), '┤' => Cross( ((), MM, ()), // (MM, MM, F_), // ((), MM, ()), // ), '┴' => Cross( ((), MM, ()), // (MM, MM, MM), // ((), F_, ()), // ), '┬' => Cross( ((), F_, ()), // (MM, MM, MM), // ((), MM, ()), // ), '─' => Cross( ((), F_, ()), // (MM, MM, MM), // ((), F_, ()), // ), '│' => Cross( ((), MM, ()), // (F_, MM, F_), // ((), MM, ()), // ), '┘' => Cross( ((), MM, ()), // (MM, MM, F_), // ((), F_, ()), // ), '┐' => Cross( ((), F_, ()), // (MM, MM, F_), // ((), MM, ()), // ), '┌' => Cross( ((), F_, ()), // (F_, MM, MM), // ((), MM, ()), // ), '└' => Cross( ((), MM, ()), // (F_, MM, MM), // ((), F_, ()), // ), _ => Cross( ((), F_, ()), // (F_, F_, F_), // ((), F_, ()), // ), } } const F_: bool = false; const MM: bool = true; type Empt = (); struct Cross( (Empt, bool, Empt), // (bool, bool, bool), // (Empt, bool, Empt), // ); impl Cross { fn u(&self) -> bool { self.0.1 } fn d(&self) -> bool { self.2.1 } fn l(&self) -> bool { self.1.0 } fn r(&self) -> bool { self.1.2 } fn c(&self) -> bool { self.1.1 } }