summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcos <cos>2021-12-13 07:46:50 +0100
committercos <cos>2021-12-13 12:50:49 +0100
commit224001efc7aae4c5ec875f99c5f39e50c372c36a (patch)
tree2fc20c94e88ee819105f59ec9705014f28e96adc
parentefb6f73fdb7f512b419547c0baa7913e6b92d539 (diff)
downloadadventofcode-224001efc7aae4c5ec875f99c5f39e50c372c36a.zip
Add day13, 2021
-rw-r--r--2021/rust/Cargo.toml2
-rw-r--r--2021/rust/day13/Cargo.toml9
-rw-r--r--2021/rust/day13/src/main.rs183
3 files changed, 193 insertions, 1 deletions
diff --git a/2021/rust/Cargo.toml b/2021/rust/Cargo.toml
index 06ea192..8c18a33 100644
--- a/2021/rust/Cargo.toml
+++ b/2021/rust/Cargo.toml
@@ -12,7 +12,7 @@ members = [
"day10",
"day11",
"day12",
-# "day13",
+ "day13",
# "day14",
# "day15",
# "day16",
diff --git a/2021/rust/day13/Cargo.toml b/2021/rust/day13/Cargo.toml
new file mode 100644
index 0000000..8408975
--- /dev/null
+++ b/2021/rust/day13/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "day13"
+version = "0.1.0"
+authors = ["cos <cos>"]
+edition = "2021"
+
+[dependencies]
+aoc = { path = "../../../common/rust/aoc" }
+anyhow = "1.0"
diff --git a/2021/rust/day13/src/main.rs b/2021/rust/day13/src/main.rs
new file mode 100644
index 0000000..c87373c
--- /dev/null
+++ b/2021/rust/day13/src/main.rs
@@ -0,0 +1,183 @@
+use {
+ anyhow::{
+ anyhow,
+ Context,
+ Result,
+ },
+ std::{
+ collections::BTreeSet,
+ env::args,
+ fmt::{
+ Display,
+ Error as FmtError,
+ Formatter,
+ Result as FmtResult,
+ },
+ fs::File,
+ io::{
+ BufRead,
+ BufReader,
+ },
+ path::Path,
+ },
+};
+
+#[derive(Clone,Copy,Debug,Eq,Ord,PartialEq,PartialOrd)]
+struct Point {
+ y: usize, // Please note that ordering is derived with y prior to x, as required for the
+ x: usize, // Display trait to work as currently implemented.
+}
+
+impl Point {
+ fn fold(&self, fold: Fold) -> Self {
+ match fold {
+ Fold::X(num) => {
+ if self.x > num {
+ return Point{x: num - (self.x - num), y: self.y};
+ }
+ }
+ Fold::Y(num) => {
+ if self.y > num {
+ return Point{x: self.x, y: num-(self.y - num)};
+ }
+ }
+ }
+ *self
+ }
+}
+
+#[derive(Clone,Copy)]
+enum Fold {
+ X(usize),
+ Y(usize),
+}
+
+struct Paper {
+ dots: BTreeSet<Point>,
+}
+
+impl Paper {
+ fn fold(&self, fold: &Fold) -> Result<Paper> {
+ let mut folded = Paper {
+ dots: BTreeSet::new(),
+ };
+
+ for point in &self.dots {
+ let mirror = point.fold(*fold);
+ if self.dots.contains(point) {
+ folded.dots.insert(mirror);
+ }
+ }
+
+ Ok(folded)
+ }
+
+ fn dot_count(&self) -> usize {
+ self.dots.len()
+ }
+}
+
+impl Display for Paper {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ let mut s = String::new();
+ let (mut x, mut y) = (0, 0);
+ let max_x = self.dots.iter().map(|p| p.x).max().ok_or(FmtError)?;
+ for p in self.dots.iter() {
+ if p.y == y {
+ (x..p.x).for_each(|_| s += "." );
+ } else {
+ (x..=max_x).for_each(|_| s += "." );
+ s += "\n";
+ for _ in y..(p.y-1) {
+ (0..=max_x).for_each(|_| s += "." );
+ s += "\n";
+ }
+ (0..p.x).for_each(|_| s += "." );
+ }
+ s += "#";
+
+ x = p.x+1;
+ y = p.y;
+ if x > max_x {
+ x = 0;
+ y += 1;
+ s += "\n";
+ }
+ }
+ (x..=max_x).for_each(|_| s += "." );
+ write!(f, "{}", s)
+ }
+}
+
+fn read_input<T: AsRef<Path>>(filename: T) -> Result<(Paper, Vec<Fold>)> {
+ let reader = BufReader::new(File::open(filename)?);
+ let mut lines = reader.lines();
+ let mut dots: BTreeSet<Point> = BTreeSet::new();
+
+ for line in lines.by_ref() {
+ let l = line.map_err(|err| anyhow!("Failed reading from file: {}", err))?;
+ if l.is_empty() {
+ break;
+ }
+
+ let values: Vec<_> = l.split(',')
+ .map(|v| v.parse().map_err(|err| anyhow!("{}: {}", l, err))).collect::<Result<_>>()?;
+ if values.len() != 2 {
+ return Err(anyhow!("Failed parsing coordinates from line: {}", l));
+ }
+ let (x, y) = match (values.get(0), values.get(1)) {
+ (Some(parsed_x), Some(parsed_y)) => Ok((parsed_x, parsed_y)),
+ (_, _) => Err(anyhow!("Invalid coordinates: {}", l)),
+ }?;
+ dots.insert(Point{x: *x, y: *y});
+ }
+
+ let folds = lines.map(|line| {
+ let l = line?;
+ let s = l.strip_prefix("fold along ")
+ .ok_or_else(|| anyhow!("Could not find fold: {}", l))?;
+ let (axis, position) = s.split_once('=')
+ .ok_or_else(|| anyhow!("Could not parse fold: {}", l))?;
+ let num = position.parse()
+ .map_err(|err| anyhow!("Invalid fold position: {}, {}", l, err))?;
+ let fold = match axis {
+ "x" => Ok(Fold::X(num)),
+ "y" => Ok(Fold::Y(num)),
+ _ => Err(anyhow!("Invalid fold axis: {}", axis)),
+ }?;
+
+ Ok(fold)
+ }).collect::<Result<Vec<_>>>()?;
+
+ Ok((Paper { dots, }, folds))
+}
+
+fn part1(paper: &Paper, folds: &[Fold]) -> Result<usize> {
+ let fold = folds.get(0).ok_or_else(|| anyhow!("Could not find any fold"))?;
+ let folded = paper.fold(fold)?;
+ Ok(folded.dot_count())
+}
+
+fn part2(paper: Paper, folds: &[Fold]) -> Result<String> {
+ let folded = folds.iter().fold(Ok(paper), |p: Result<Paper>, fold| {
+ let folded = p?.fold(fold)?;
+ Ok(folded)
+ })?;
+ Ok(format!("{}", folded))
+}
+
+fn main() -> Result<()> {
+ let ( do_part_1, do_part_2 ) = aoc::do_parts();
+
+ let filename = args().nth(1).ok_or(anyhow!("Missing input filename"))?;
+ let (paper, folds) = read_input(filename).context("Could not read input")?;
+ if do_part_1 {
+ let solution = part1(&paper, &folds).context("No solution for part 1")?;
+ println!("Part1, solution found to be: {}", solution);
+ }
+ if do_part_2 {
+ let solution = part2(paper, &folds).context("No solution for part 2")?;
+ println!("Part2, solution found to be:\n{}", solution);
+ }
+ Ok(())
+}