Compare commits
4 Commits
372bc43623
...
trunk
Author | SHA1 | Date | |
---|---|---|---|
bf6b034768 | |||
0fb63e65e9 | |||
8359a6b308 | |||
e64edbd201 |
@ -1,7 +1,7 @@
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
/// Increments count if each sample is larger than the previous.
|
/// Increments count if each sample is larger than the previous.
|
||||||
pub fn day1_p1(input: String) {
|
pub fn part1(input: String) {
|
||||||
let mut prev = None;
|
let mut prev = None;
|
||||||
let mut count: u32 = 0;
|
let mut count: u32 = 0;
|
||||||
for line in input.lines() {
|
for line in input.lines() {
|
||||||
@ -22,7 +22,7 @@ pub fn day1_p1(input: String) {
|
|||||||
/// 3-sample windowing average (no need to average, just use sum here)
|
/// 3-sample windowing average (no need to average, just use sum here)
|
||||||
///
|
///
|
||||||
/// We need to start comparing when we have all of
|
/// We need to start comparing when we have all of
|
||||||
pub fn day1_p2(input: String) {
|
pub fn part2(input: String) {
|
||||||
let mut storage = VecDeque::with_capacity(5);
|
let mut storage = VecDeque::with_capacity(5);
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|
||||||
|
54
src/day2.rs
Normal file
54
src/day2.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
struct Position {
|
||||||
|
pub x: i64,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub y: i64,
|
||||||
|
pub z: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::default::Default for Position {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { x: 0, y: 0, z: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part1(input: String) {
|
||||||
|
let mut pos = Position::default();
|
||||||
|
|
||||||
|
for line in input.lines() {
|
||||||
|
let mut command = line.split(' ');
|
||||||
|
let dir = command.next().unwrap();
|
||||||
|
let distance = command.next().unwrap().parse::<i64>().unwrap();
|
||||||
|
|
||||||
|
match dir {
|
||||||
|
"forward" => pos.x += distance,
|
||||||
|
"up" => pos.z += distance,
|
||||||
|
"down" => pos.z -= distance,
|
||||||
|
_ => panic!("Invalid input!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{}", pos.x * -pos.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part2(input: String) {
|
||||||
|
let mut pos = Position::default();
|
||||||
|
let mut attitude = 0i64;
|
||||||
|
|
||||||
|
for line in input.lines() {
|
||||||
|
let mut command = line.split(' ');
|
||||||
|
let dir = command.next().unwrap();
|
||||||
|
let distance = command.next().unwrap().parse::<i64>().unwrap();
|
||||||
|
|
||||||
|
match dir {
|
||||||
|
"forward" => {
|
||||||
|
pos.x += distance;
|
||||||
|
pos.z += distance * attitude;
|
||||||
|
}
|
||||||
|
"up" => attitude += distance,
|
||||||
|
"down" => attitude -= distance,
|
||||||
|
_ => panic!("Invalid input!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{}", pos.x * -pos.z);
|
||||||
|
}
|
135
src/day3.rs
Normal file
135
src/day3.rs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
/// I thought this would be useful for both parts but I was very wrong
|
||||||
|
fn bit_count(input: &String) -> (Vec<i64>, usize) {
|
||||||
|
let byte_len = input.lines().next().unwrap().len();
|
||||||
|
let mut count = vec![0i64; byte_len];
|
||||||
|
|
||||||
|
for line in input.lines() {
|
||||||
|
let mut i = 0;
|
||||||
|
for c in line.chars() {
|
||||||
|
if c == '1' {
|
||||||
|
count[i] += 1;
|
||||||
|
} else if c == '0' {
|
||||||
|
count[i] -= 1;
|
||||||
|
} else {
|
||||||
|
panic! {"Malformed input!"};
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(count, byte_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the product of gamma and epsilon.
|
||||||
|
///
|
||||||
|
/// Gamma is determined by the most common bit values at each position in all of the
|
||||||
|
/// input.
|
||||||
|
///
|
||||||
|
/// Epsilon is the inverse, determined by the least common bit values.
|
||||||
|
pub fn part1(input: String) {
|
||||||
|
let (count, byte_len) = bit_count(&input);
|
||||||
|
|
||||||
|
let mut gamma = 0u64;
|
||||||
|
let mut epsilon = 0u64;
|
||||||
|
for i in 0..byte_len {
|
||||||
|
// ERROR: wrong with even counts
|
||||||
|
gamma |= ((count[i] > 0) as u64) << (byte_len - (i + 1));
|
||||||
|
epsilon |= ((count[i] <= 0) as u64) << (byte_len - (i + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{}", gamma * epsilon);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Counts high/low bits in
|
||||||
|
fn count_bits(data: &Vec<u64>, bit_index: usize, mask: u64, bit_diag: u64) -> (u64, i64) {
|
||||||
|
let mut bit_prevalance = 0i64;
|
||||||
|
let mut valid_count = 0u64;
|
||||||
|
|
||||||
|
for byte in data {
|
||||||
|
//println!("byte: {}", bit_diag ^ (byte & mask));
|
||||||
|
if bit_diag ^ (byte & mask) == 0 {
|
||||||
|
valid_count += 1;
|
||||||
|
if (byte & (1 << bit_index)) > 0 {
|
||||||
|
bit_prevalance += 1;
|
||||||
|
} else {
|
||||||
|
bit_prevalance -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//println!("bit_index: {}, mask: {}, bit_diag: {}, valid_count: {}, bit_prevalance: {}", bit_index, mask, bit_diag, valid_count, bit_prevalance);
|
||||||
|
(valid_count, bit_prevalance)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// I *could* document this, but why would I want to? :P
|
||||||
|
/// AOC itself serves as documentation.
|
||||||
|
pub fn part2(input: String) {
|
||||||
|
let byte_len = input.lines().next().unwrap().len();
|
||||||
|
let data: Vec<u64> = input
|
||||||
|
.lines()
|
||||||
|
.map(|l| u64::from_str_radix(l, 2).unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut generator_mask = 0u64;
|
||||||
|
let mut generator_diag = 0u64;
|
||||||
|
let mut generator_value = None;
|
||||||
|
let mut scrubber_mask = 0u64;
|
||||||
|
let mut scrubber_diag = 0u64;
|
||||||
|
let mut scrubber_value = None;
|
||||||
|
|
||||||
|
// Diagnostic bit sequence will be different for generator and scrubber
|
||||||
|
// Logic for counting bits will be the same, just need to keep diagnostic bits seperate
|
||||||
|
for i in 0..byte_len {
|
||||||
|
let bit_index = byte_len - i - 1;
|
||||||
|
|
||||||
|
// Set diagnostic bit for each piece of data individually
|
||||||
|
if generator_value.is_none() {
|
||||||
|
let (valid_count, bit_prevalance) =
|
||||||
|
count_bits(&data, bit_index, generator_mask, generator_diag);
|
||||||
|
|
||||||
|
// We want to match against the most common bit, which is 1 when prevalance is positive.
|
||||||
|
// We also want to match on one when they are equal.
|
||||||
|
if bit_prevalance >= 0 {
|
||||||
|
generator_diag |= 1 << bit_index;
|
||||||
|
}
|
||||||
|
generator_mask |= 1 << bit_index;
|
||||||
|
|
||||||
|
// Only one valid solution, grab it and get out
|
||||||
|
if valid_count <= 2 || i == byte_len - 1 {
|
||||||
|
generator_value = Some(
|
||||||
|
data.iter()
|
||||||
|
.filter(|d| generator_diag ^ (*d & generator_mask) == 0)
|
||||||
|
.next()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if scrubber_value.is_none() {
|
||||||
|
let (valid_count, bit_prevalance) =
|
||||||
|
count_bits(&data, bit_index, scrubber_mask, scrubber_diag);
|
||||||
|
|
||||||
|
// We want to match against the least common bit, which is 1 when prevalance is negative.
|
||||||
|
// When equal we match against 0.
|
||||||
|
if bit_prevalance < 0 {
|
||||||
|
scrubber_diag |= 1 << bit_index;
|
||||||
|
}
|
||||||
|
scrubber_mask |= 1 << bit_index;
|
||||||
|
|
||||||
|
// Only one valid solution, grab it and get out
|
||||||
|
if valid_count <= 2 || i == byte_len - 1 {
|
||||||
|
scrubber_value = Some(
|
||||||
|
data.iter()
|
||||||
|
.filter(|d| scrubber_diag ^ (*d & scrubber_mask) == 0)
|
||||||
|
.next()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let gen = generator_value.unwrap();
|
||||||
|
let scrub = scrubber_value.unwrap();
|
||||||
|
|
||||||
|
println!("{}", gen * scrub);
|
||||||
|
}
|
135
src/day4.rs
Normal file
135
src/day4.rs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
struct BingoBoard {
|
||||||
|
board: [[u8; 5]; 5],
|
||||||
|
marked: [[bool; 5]; 5],
|
||||||
|
bingo: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BingoBoard {
|
||||||
|
fn new(board: [[u8; 5]; 5]) -> Self {
|
||||||
|
Self {
|
||||||
|
board,
|
||||||
|
marked: [[false; 5]; 5],
|
||||||
|
bingo: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks to see if bingo has been achieved.
|
||||||
|
///
|
||||||
|
/// Returns true if any row or column is fully marked. (Ignores diagonals)
|
||||||
|
fn check_bingo(&mut self) -> bool {
|
||||||
|
// Check rows
|
||||||
|
for row in self.marked {
|
||||||
|
if row.iter().all(|marked| *marked) {
|
||||||
|
self.bingo = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check columns
|
||||||
|
for i in 0..self.marked.len() {
|
||||||
|
if self.marked.iter().map(|row| row[i]).all(|marked| marked) {
|
||||||
|
self.bingo = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_bingo(&self) -> bool {
|
||||||
|
self.bingo
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks number for existance on board and marks off any instances of it.
|
||||||
|
fn check_number(&mut self, number: u8) {
|
||||||
|
for i in 0..self.board.len() {
|
||||||
|
for j in 0..self.board[i].len() {
|
||||||
|
if self.board[i][j] == number {
|
||||||
|
self.marked[i][j] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scores board based on input number
|
||||||
|
fn score(&self, number: u8) -> usize {
|
||||||
|
let mut score = 0usize;
|
||||||
|
for i in 0..self.board.len() {
|
||||||
|
for j in 0..self.board[i].len() {
|
||||||
|
if !self.marked[i][j] {
|
||||||
|
score += self.board[i][j] as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
score * number as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input(input: String) -> (Vec<u8>, Vec<BingoBoard>) {
|
||||||
|
let mut lines = input.lines();
|
||||||
|
|
||||||
|
let input: Vec<u8> = lines
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.split(',')
|
||||||
|
.map(|s| str::parse::<u8>(s).unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Keep building boards until we don't have any more lines to parse
|
||||||
|
let mut boards = Vec::<BingoBoard>::new();
|
||||||
|
while let Some(_) = lines.next() {
|
||||||
|
let mut board = [[0u8; 5]; 5];
|
||||||
|
for i in 0..board.len() {
|
||||||
|
let line: Vec<u8> = lines
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.split_whitespace()
|
||||||
|
.map(|s| str::parse::<u8>(s).unwrap())
|
||||||
|
.collect();
|
||||||
|
board[i] = line.try_into().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
boards.push(BingoBoard::new(board));
|
||||||
|
}
|
||||||
|
|
||||||
|
(input, boards)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part1(input: String) {
|
||||||
|
let (input, mut boards) = parse_input(input);
|
||||||
|
|
||||||
|
for call in input {
|
||||||
|
// Mark and check boards
|
||||||
|
// NOTE: making big assumption here that only one board wins
|
||||||
|
for board in &mut boards {
|
||||||
|
board.check_number(call);
|
||||||
|
|
||||||
|
if board.check_bingo() {
|
||||||
|
println!("Final score: {}", board.score(call));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part2(input: String) {
|
||||||
|
let (input, mut boards) = parse_input(input);
|
||||||
|
|
||||||
|
let mut final_score = 0usize;
|
||||||
|
for call in input {
|
||||||
|
for board in &mut boards {
|
||||||
|
if !board.is_bingo() {
|
||||||
|
board.check_number(call);
|
||||||
|
|
||||||
|
if board.check_bingo() {
|
||||||
|
final_score = board.score(call);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Last winner's score: {}", final_score);
|
||||||
|
}
|
38
src/main.rs
38
src/main.rs
@ -1,6 +1,9 @@
|
|||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
|
|
||||||
mod day1;
|
mod day1;
|
||||||
|
mod day2;
|
||||||
|
mod day3;
|
||||||
|
mod day4;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let matches = App::new("AOC 2021 Code")
|
let matches = App::new("AOC 2021 Code")
|
||||||
@ -30,23 +33,30 @@ fn main() {
|
|||||||
let part = matches.value_of("part").unwrap_or("1");
|
let part = matches.value_of("part").unwrap_or("1");
|
||||||
let part = str::parse::<u8>(part).expect("Invalid part provided (1 or 2 expected)");
|
let part = str::parse::<u8>(part).expect("Invalid part provided (1 or 2 expected)");
|
||||||
|
|
||||||
let input = read_input(day, part).unwrap();
|
let input = read_input(day).unwrap();
|
||||||
|
|
||||||
match day {
|
// TODO macro-ify
|
||||||
1 => match part {
|
match_days!(1, day1, 2, day2, 3, day3, 4, day4);
|
||||||
1 => day1::day1_p1(input),
|
|
||||||
2 => day1::day1_p2(input),
|
|
||||||
_ => (),
|
|
||||||
},
|
|
||||||
_ => println!("Day {} not completed yet!", day),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO do the inputs ever change inside of a day?
|
// TODO do the inputs ever change inside of a day?
|
||||||
/// Read inputs into a string
|
/// Read inputs into a string
|
||||||
fn read_input(day: u8, part: u8) -> std::io::Result<String> {
|
fn read_input(day: u8) -> std::io::Result<String> {
|
||||||
std::fs::read_to_string(format!(
|
std::fs::read_to_string(format!("inputs/day{}", day))
|
||||||
"inputs/day{}p{}",
|
}
|
||||||
day, part
|
|
||||||
))
|
#[macro_export]
|
||||||
|
macro_rules! match_days {
|
||||||
|
( $($num:expr, $day:ident),* ) => {
|
||||||
|
match day {
|
||||||
|
$(
|
||||||
|
$num => match part {
|
||||||
|
1 => $day::part1(input),
|
||||||
|
2 => $day::part2(input),
|
||||||
|
_ => println!("Part {} invalid!", part),
|
||||||
|
},
|
||||||
|
)*
|
||||||
|
_ => println!("Day {} not completed yet!", day),
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user