Skip to content

Commit

Permalink
feat: Day 4 2021, ⭐ ⭐
Browse files Browse the repository at this point in the history
  • Loading branch information
icyJoseph committed Dec 4, 2021
1 parent fc5ac30 commit 5cc3acc
Show file tree
Hide file tree
Showing 3 changed files with 238 additions and 0 deletions.
9 changes: 9 additions & 0 deletions 2021/day-4/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
authors = ["Joseph Chamochumbi <[email protected]>"]
edition = "2018"
name = "day-4"
version = "0.1.0"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
aoc = {git = "https://github.com/icyJoseph/advent-of-code.git"}
132 changes: 132 additions & 0 deletions 2021/day-4/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
use aoc;

use std::cell::Cell;

#[derive(Debug)]
struct Entry {
checked: Cell<bool>,
value: u32,
}

impl Entry {
fn get_checked(&self) -> bool {
self.checked.get()
}
fn update(&mut self, next: u32) {
self.checked.set(self.checked.get() || self.value == next);
}
}

fn solve(raw: String) -> () {
let blocks: Vec<&str> = raw.trim().split("\n\n").collect();

let sequence = blocks[0]
.split(',')
.map(|x| parse_num::<u32>(x))
.collect::<Vec<u32>>();

let mut cards = blocks[1..]
.iter()
.map(|section| {
section
.split("\n")
.map(|row| {
row.split(" ")
.filter(|x| !x.is_empty())
.map(|x| Entry {
value: parse_num::<u32>(x),
checked: Cell::new(false),
})
.collect::<Vec<Entry>>()
})
.collect::<Vec<Vec<Entry>>>()
})
.collect::<Vec<Vec<Vec<Entry>>>>();

use std::collections::HashSet;
let mut set: HashSet<usize> = HashSet::new();
let total_cards = cards.len();

let run = || {
for next in sequence {
for (card_index, card) in cards.iter_mut().enumerate() {
if set.contains(&card_index) {
continue;
}

for row in card.iter_mut() {
for entry in row.iter_mut() {
entry.update(next)
}
}
// check if the card wins
let row_win = card
.iter()
.any(|row| row.iter().all(|entry| entry.get_checked()));

let col_win = (0..5)
.collect::<Vec<usize>>()
.iter()
.map(|index| card.iter().map(|row| &row[*index]).collect::<Vec<&Entry>>())
.any(|col| col.iter().all(|entry| entry.get_checked()));

if row_win || col_win {
if !set.contains(&card_index) {
set.insert(card_index);

if set.len() == 1 || set.len() == total_cards {
let score = next
* card.iter().fold(0, |prev, row| {
prev + row
.iter()
.filter(|entry| !entry.get_checked())
.map(|entry| entry.value)
.sum::<u32>()
});

println!(
"Part {}: {}",
if set.len() == 1 { "one" } else { "two" },
score
);
}
}
}
}
}
};

run();
}

fn main() {
let input = aoc::get_input(2021, 4);
// let input = std::fs::read_to_string("./input/day-4.in").expect("Error reading input");

solve(input);
}

// Utilities

#[allow(dead_code)]
fn parse_num<T: std::str::FromStr>(str: &str) -> T {
match str.trim().parse::<T>() {
Ok(n) => n,
_ => panic!("Error parsing"),
}
}
#[allow(dead_code)]
fn to_int(bin: &str) -> u32 {
match u32::from_str_radix(bin, 2) {
Ok(n) => n,
_ => panic!("Error parsing binary to integer"),
}
}

#[allow(dead_code)]
fn string_vec<T: std::string::ToString>(vec: &Vec<T>, separator: &str) -> String {
vec.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join(separator)
}
97 changes: 97 additions & 0 deletions 2021/deno/day-4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
const input = await Deno.readTextFile("./input/day-4.in");

const blocks = input.split("\n\n");

/**
* Part One
*/

const [numbers, ...rest] = blocks;

const sequence = numbers.split(",").map(Number);

type Entry = { value: number; checked: boolean };
type Card = Entry[][];

const cards = rest.map((block) =>
block.split("\n").map((row) =>
row
.split(" ")
.filter(Boolean)
.map(Number)
.map((value) => ({ checked: false, value }))
)
);

// wins happens in rows and columns
const checkWin = (card: { checked: boolean; value: number }[][]) => {
// check rows
const rowWin = card.some((row) => row.every(({ checked }) => checked));

if (rowWin) return rowWin;

for (const index of [0, 1, 2, 3, 4]) {
const colWin = card
.map((row) => row[index])
.every(({ checked }) => checked);

if (colWin) return colWin;
}

return false;
};

let firstWinner: Card = [];
let lastWinner: Card = [];

let firstWinnerNumber = sequence[0];
let lastWinnerNumber = sequence[0];

const winners = new Set<Card>();

outer: for (const next of sequence) {
lastWinnerNumber = next;

const nonWinners = cards.filter((card) => !winners.has(card));

for (const card of nonWinners) {
card.forEach((row, rowIndex) =>
row.forEach((entry, entryIndex) => {
card[rowIndex][entryIndex].checked =
entry.checked || entry.value === next;
})
);

if (checkWin(card)) {
if (firstWinner.length === 0) {
firstWinner = card;
firstWinnerNumber = next;
}

lastWinner = card;

winners.add(card);

if (winners.size === cards.length) {
break outer;
}
}
}
}

const checkedSum = (prev: number, { value, checked }: Entry) =>
checked ? prev : prev + value;

const sum = (a: number, b: number) => a + b;

console.log(
"Part One:",
firstWinner.map((row) => row.reduce(checkedSum, 0)).reduce(sum, 0) *
firstWinnerNumber
);

console.log(
"Part Two:",
lastWinner.map((row) => row.reduce(checkedSum, 0)).reduce(sum, 0) *
lastWinnerNumber
);

0 comments on commit 5cc3acc

Please sign in to comment.