235 lines
4.5 KiB
Rust
235 lines
4.5 KiB
Rust
use std::{env::args, fs};
|
|
|
|
fn main() {
|
|
let file = args().nth(1).expect("Usage : boxon <file>");
|
|
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<Vec<char>>);
|
|
|
|
impl<T: AsRef<str>> From<T> for Grid {
|
|
fn from(value: T) -> Self {
|
|
Self(value.as_ref().lines().map(str::chars).map(Iterator::collect).collect())
|
|
}
|
|
}
|
|
|
|
impl From<Grid> for String {
|
|
fn from(val: Grid) -> Self {
|
|
val.0
|
|
.iter()
|
|
.map(|l| l.iter().collect::<String>())
|
|
.collect::<Vec<_>>()
|
|
.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<Cross> {
|
|
fn at(g: &Grid, x: i32, y: i32) -> Option<Cross> {
|
|
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
|
|
}
|
|
}
|