use std::env::args; use std::fs::File; use std::io::BufRead; use std::io::BufReader; struct Area { x1: i64, x2: i64, y1: i64, y2: i64, z1: i64, z2: i64, } impl Area { fn parse(s: &str) -> Self { let parsed = s .split(",") .map(|r| match r[2..] .split("..") .collect::>()[..] { [left, right] => ( left.parse::().unwrap(), right.parse::().unwrap(), ), _ => unreachable!(), } ) .collect::>(); return match parsed[..] { [x, y, z] => Self { x1: x.0, x2: x.1, y1: y.0, y2: y.1, z1: z.0, z2: z.1, }, _ => unreachable!(), }; } fn diff(&self, other: &Self) -> Vec { // break down self into shards that do not overlap with other let mut shards = vec![]; let x1 = if self.x1 > other.x1 {self.x1} else {other.x1}; let x2 = if self.x2 < other.x2 {self.x2} else {other.x2}; let y1 = if self.y1 > other.y1 {self.y1} else {other.y1}; let y2 = if self.y2 < other.y2 {self.y2} else {other.y2}; let z1 = if self.z1 > other.z1 {self.z1} else {other.z1}; let z2 = if self.z2 < other.z2 {self.z2} else {other.z2}; if self.x1 < x1 { shards.push(Area { x1: self.x1, x2: x1 - 1, y1: self.y1, y2: self.y2, z1: self.z1, z2: self.z2 }); } if self.y1 < y1 { shards.push(Area { x1, x2, y1: self.y1, y2: y1 - 1, z1: self.z1, z2: self.z2 }); } if self.z1 < z1 { shards.push(Area { x1, x2, y1, y2, z1: self.z1, z2: z1 - 1 }); } if z2 < self.z2 { shards.push(Area { x1, x2, y1, y2, z1: z2 + 1, z2: self.z2 }); } if y2 < self.y2 { shards.push(Area { x1, x2, y1: y2 + 1, y2: self.y2, z1: self.z1, z2: self.z2 }); } if x2 < self.x2 { shards.push(Area { x1: x2 + 1, x2: self.x2, y1: self.y1, y2: self.y2, z1: self.z1, z2: self.z2 }); } return shards; } fn overlap(&self, other: &Self) -> bool { return self.x1 <= other.x2 && other.x1 <= self.x2 && self.y1 <= other.y2 && other.y1 <= self.y2 && self.z1 <= other.z2 && other.z1 <= self.z2; } fn count(&self) -> i64 { return (self.x2 - self.x1 + 1) * (self.y2 - self.y1 + 1) * (self.z2 - self.z1 + 1); } } fn add_area(area: Area, mut areas: &mut Vec) { if area.x1 > area.x2 || area.y1 > area.y2 || area.z1 > area.z2 { return; } let mut i = 0; while i < areas.len() { let other = &areas[i]; if area.overlap(other) { let shards1 = area.diff(other); let shards2 = other.diff(&area); // PERF: optimize for a smaller areas.len() if shards1.len() < shards2.len() { for shard in shards1 { add_area(shard, &mut areas); } return; } else { areas.remove(i); for shard in shards2 { areas.push(shard); } } } else { i += 1; } } areas.push(area); } fn remove_area(area: Area, areas: &mut Vec) { let mut i = 0; while i < areas.len() { if area.overlap(&areas[i]) { let other = areas.remove(i); for shard in other.diff(&area) { areas.push(shard); } } else { i += 1; } } } fn main() { let path = args().nth(1).unwrap(); let file = File::open(path).unwrap(); let mut areas = vec![]; for line in BufReader::new(file).lines() { match line.unwrap().split(" ").collect::>()[..] { [a, b] => { let on = a == "on"; let area = Area::parse(b); if on { add_area(area, &mut areas); } else { remove_area(area, &mut areas); } }, _ => unreachable!(), } } println!("{}", areas.iter().map(|area| area.count()).sum::()); }