summaryrefslogtreecommitdiff
path: root/2021/rust
diff options
context:
space:
mode:
authorcos <cos>2021-12-04 10:23:45 +0100
committercos <cos>2021-12-04 11:29:53 +0100
commit86f696a318001f1e6f1d063ba42dee1f5da13477 (patch)
tree64f7d7258b76d789b1dc276ea70b53eb36864a61 /2021/rust
parent57870e0429cbfa85b08d891900ca7999d6ae7b23 (diff)
downloadadventofcode-86f696a318001f1e6f1d063ba42dee1f5da13477.zip
Add day04, 2021
Diffstat (limited to '2021/rust')
-rw-r--r--2021/rust/Cargo.toml2
-rw-r--r--2021/rust/day04/Cargo.toml9
-rw-r--r--2021/rust/day04/src/main.rs217
3 files changed, 227 insertions, 1 deletions
diff --git a/2021/rust/Cargo.toml b/2021/rust/Cargo.toml
index ac7f9ee..aed5051 100644
--- a/2021/rust/Cargo.toml
+++ b/2021/rust/Cargo.toml
@@ -3,7 +3,7 @@ members = [
"day01",
"day02",
"day03",
-# "day04",
+ "day04",
# "day05",
# "day06",
# "day07",
diff --git a/2021/rust/day04/Cargo.toml b/2021/rust/day04/Cargo.toml
new file mode 100644
index 0000000..5f11cc4
--- /dev/null
+++ b/2021/rust/day04/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "day04"
+version = "0.1.0"
+authors = ["cos <cos>"]
+edition = "2018"
+
+[dependencies]
+aoc = { path = "../../../common/rust/aoc" }
+anyhow = "1.0"
diff --git a/2021/rust/day04/src/main.rs b/2021/rust/day04/src/main.rs
new file mode 100644
index 0000000..e0457de
--- /dev/null
+++ b/2021/rust/day04/src/main.rs
@@ -0,0 +1,217 @@
+use {
+ anyhow::{
+ anyhow,
+ Context,
+ Result,
+ },
+ std::{
+ env::args,
+ fmt,
+ fmt::Display,
+ fs::File,
+ io::{
+ BufRead,
+ BufReader,
+ },
+ path::Path,
+ },
+};
+
+#[derive(Clone)]
+struct BingoBoard {
+ size: usize,
+ values: Vec<u8>,
+ called: Vec<bool>,
+ completed: Option<u8>,
+}
+
+impl BingoBoard {
+ fn new<'a, I: IntoIterator<Item = &'a u8> + Copy>(grid: I) -> Self {
+ let size = 5;
+ let values = (&grid).into_iter().copied().collect();
+ let called = vec![false; grid.into_iter().count()];
+ let completed = None;
+ Self {
+ size,
+ values,
+ called,
+ completed,
+ }
+ }
+
+ fn play(&mut self, num: &u8) -> Result<bool> {
+ if self.completed.is_some() {
+ return Ok(false);
+ }
+
+ for y in 0..self.size {
+ for x in 0..self.size {
+ let value = self.values.get(y * self.size + x)
+ .ok_or(anyhow!("Invalid BingoBoard"))?;
+
+ if value == num {
+ self.called[y * self.size + x] = true;
+ }
+ }
+ }
+ for y in 0..self.size {
+ let mut count = 0;
+ for x in 0..self.size {
+ if *self.called.get(y * self.size + x).ok_or(anyhow!("Invalid BingoBoard"))? {
+ count += 1;
+ }
+ if count == self.size {
+ self.completed = Some(*num);
+ return Ok(true);
+ }
+ }
+ }
+ for x in 0..self.size {
+ let mut count = 0;
+ for y in 0..self.size {
+ if *self.called.get(y * self.size + x).ok_or(anyhow!("Invalid BingoBoard"))? {
+ count += 1;
+ }
+ if count == self.size {
+ self.completed = Some(*num);
+ return Ok(true);
+ }
+ }
+ }
+ Ok(false)
+ }
+
+ fn sum_unmarked(&self) -> Result<usize> {
+ let mut sum = 0;
+ for y in 0..self.size {
+ for x in 0..self.size {
+ let value = self.values.get(y * self.size + x)
+ .ok_or(anyhow!("Invalid BingoBoard"))?;
+
+ if ! *self.called.get(y * self.size + x).ok_or(anyhow!("Invalid BingoBoard"))? {
+ sum += *value as usize;
+ }
+ }
+ }
+ Ok(sum)
+ }
+}
+
+impl Display for BingoBoard {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut s = String::new();
+ for y in 0..self.size {
+ for x in 0..self.size {
+ let value = self.values.get(y * self.size + x).ok_or(std::fmt::Error)?;
+ if *self.called.get(y * self.size + x).ok_or(std::fmt::Error)? {
+ s += &format!("[{:2}] ", value);
+ } else {
+ s += &format!(" {:2} ", value);
+ }
+ }
+ s += "\n";
+ }
+ if let Some(won) = self.completed {
+ s += &format!("Winning number: {}\n", won);
+ }
+ write!(f, "{}", s)
+ }
+}
+
+#[derive(Clone)]
+struct BingoGame {
+ numbers: Vec<u8>,
+ boards: Vec<BingoBoard>,
+}
+
+impl BingoGame {
+ fn new() -> Self {
+ Self {
+ numbers: vec![],
+ boards: vec![],
+ }
+ }
+}
+
+fn read_input<T: AsRef<Path>>(filename: T) -> Result<BingoGame> {
+ let reader = BufReader::new(File::open(filename)?);
+ let mut game = BingoGame::new();
+ let mut boards: Vec<BingoBoard> = vec![];
+
+ let mut lines = reader.lines();
+ let draw_line = lines.next().ok_or(anyhow!("Could not parse draw_line"))?
+ .map_err(|err| anyhow!("{}", err))?;
+ let mut buffer: Vec<u8> = vec![];
+ for line in lines {
+ let l = line?;
+ if l.is_empty() {
+ if ! buffer.is_empty() {
+ let board = BingoBoard::new(&buffer);
+ boards.push(board);
+ }
+ buffer = vec![];
+ } else {
+ buffer.append(&mut l.split_whitespace().map(|v| v.parse()
+ .map_err(|_| anyhow!("Parse error"))).collect::<Result<_>>()?);
+ }
+ }
+ let board = BingoBoard::new(&buffer);
+ boards.push(board);
+
+ game.numbers = draw_line.split(',').map(|v| v.parse().map_err(|err| anyhow!("{}", err)))
+ .collect::<Result<_>>()?;
+ game.boards = boards;
+
+ Ok(game)
+}
+
+fn part1(game: &BingoGame) -> Result<usize> {
+ let mut game_mut = game.clone();
+ for num in &game_mut.numbers {
+ for board in &mut game_mut.boards {
+ if board.play(num)? {
+ // println!("{}", board);
+ return Ok(board.sum_unmarked()? * *num as usize);
+ }
+ }
+ }
+ Err(anyhow!("No winner"))
+}
+
+fn part2(game: &BingoGame) -> Result<usize> {
+ let mut game_mut = game.clone();
+ let mut last_winner = None;
+ for num in &game_mut.numbers {
+ for (index, board) in &mut game_mut.boards.iter_mut().enumerate() {
+ if board.play(num)? {
+ last_winner = Some(index);
+ }
+ }
+ }
+ if let Some(winner) = last_winner {
+ let board = &game_mut.boards[winner];
+ let unmarked_sum = board.sum_unmarked()?;
+ let won = board.completed.ok_or(anyhow!("No winning number"))? as usize;
+ // println!("Winning board: {} ({} * {})", winner + 1, unmarked_sum, won);
+ // println!("{}", board);
+ Ok(unmarked_sum * won)
+ } else {
+ Err(anyhow!("No winner"))
+ }
+}
+
+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 input = read_input(filename).context("Could not read input")?;
+ if do_part_1 {
+ let solution = part1(&input).context("No solution for part 1")?;
+ println!("Part1, solution found to be: {}", solution);
+ }
+ if do_part_2 {
+ let solution = part2(&input).context("No solution for part 2")?;
+ println!("Part2, solution found to be: {}", solution);
+ }
+ Ok(())
+}