Compare commits
2 commits
8da65689bc
...
0863785c00
Author | SHA1 | Date | |
---|---|---|---|
0863785c00 | |||
4729fa062d |
107
src/day_03.rs
Normal file
107
src/day_03.rs
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
|
||||||
|
#[aoc_generator(day3)]
|
||||||
|
pub fn parse_diagnostics(input: &str) -> Vec<String> {
|
||||||
|
input.lines().map(String::from).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day3, part1)]
|
||||||
|
pub fn calculate_power_consumption(input: &[String]) -> usize {
|
||||||
|
let gamma_factor = most_common_bit_string(input);
|
||||||
|
let epsilon_factor = invert_binary_string(&gamma_factor);
|
||||||
|
let gamma_factor = usize::from_str_radix(&gamma_factor, 2).unwrap();
|
||||||
|
let epsilon_factor = usize::from_str_radix(&epsilon_factor, 2).unwrap();
|
||||||
|
gamma_factor * epsilon_factor
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn most_common_bit(input: &[String], pos: usize) -> char {
|
||||||
|
let mut count = 0;
|
||||||
|
for val in input {
|
||||||
|
if val.get(pos..=pos).unwrap() == "1" {
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if count * 2 >= input.len() {
|
||||||
|
'1'
|
||||||
|
} else {
|
||||||
|
'0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn most_common_bit_string(input: &[String]) -> String {
|
||||||
|
let mut ret_val = String::new();
|
||||||
|
for i in 0..input[0].len() {
|
||||||
|
ret_val.push(most_common_bit(input, i))
|
||||||
|
}
|
||||||
|
ret_val
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invert_binary_string(val: &str) -> String {
|
||||||
|
let mut ret_val = String::new();
|
||||||
|
for charactor in val.chars() {
|
||||||
|
match charactor {
|
||||||
|
'0' => ret_val.push('1'),
|
||||||
|
'1' => ret_val.push('0'),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret_val
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_oxygen_generator_rating(input: &[String]) -> usize {
|
||||||
|
get_recursive_prefix_match(input, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_co2_scrubber_rating(input: &[String]) -> usize {
|
||||||
|
get_recursive_prefix_match(input, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_recursive_prefix_match(input: &[String], invert: bool) -> usize {
|
||||||
|
let mut input = Vec::from(input);
|
||||||
|
for i in 0..input[0].len() {
|
||||||
|
if input.len() > 1 {
|
||||||
|
let mut target = String::from(most_common_bit(&input, i));
|
||||||
|
if invert {
|
||||||
|
target = invert_binary_string(&target);
|
||||||
|
}
|
||||||
|
input.retain(|val| val.get(i..=i) == Some(&target));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_eq!(input.len(), 1);
|
||||||
|
usize::from_str_radix(&input[0], 2).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day3, part2)]
|
||||||
|
pub fn calculate_life_support_rating(input: &[String]) -> usize {
|
||||||
|
get_oxygen_generator_rating(input) * get_co2_scrubber_rating(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
const EXAMPLE_INPUT: &str = "00100
|
||||||
|
11110
|
||||||
|
10110
|
||||||
|
10111
|
||||||
|
10101
|
||||||
|
01111
|
||||||
|
00111
|
||||||
|
11100
|
||||||
|
10000
|
||||||
|
11001
|
||||||
|
00010
|
||||||
|
01010";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_power_consumption_example() {
|
||||||
|
let result = super::calculate_power_consumption(&super::parse_diagnostics(EXAMPLE_INPUT));
|
||||||
|
assert_eq!(result, 198);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_life_support_example() {
|
||||||
|
let result = super::calculate_life_support_rating(&super::parse_diagnostics(EXAMPLE_INPUT));
|
||||||
|
assert_eq!(result, 230);
|
||||||
|
}
|
||||||
|
}
|
195
src/day_04.rs
Normal file
195
src/day_04.rs
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Field {
|
||||||
|
number: usize,
|
||||||
|
marked: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct BingoBoard {
|
||||||
|
fields: [[Field; 5]; 5],
|
||||||
|
finished: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for BingoBoard {
|
||||||
|
type Err = ();
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let fields: [[Field; 5]; 5] = s
|
||||||
|
.lines()
|
||||||
|
.map(|line| line.trim())
|
||||||
|
.filter(|line| line.len() != 0)
|
||||||
|
.map(|line| -> [Field; 5] {
|
||||||
|
let line: Vec<Field> = line
|
||||||
|
.split(' ')
|
||||||
|
.filter(|num| num.len() != 0)
|
||||||
|
.map(|field| {
|
||||||
|
let number = field.parse().unwrap();
|
||||||
|
Field {
|
||||||
|
number,
|
||||||
|
marked: false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
line.try_into().unwrap()
|
||||||
|
})
|
||||||
|
.collect::<Vec<[Field; 5]>>()
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
Ok(BingoBoard { fields, finished: false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BingoBoard {
|
||||||
|
fn mark(&mut self, num: usize) -> Option<usize> {
|
||||||
|
if self.finished == true {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
for lines in self.fields.iter_mut() {
|
||||||
|
for field in lines.iter_mut() {
|
||||||
|
if field.number == num {
|
||||||
|
field.marked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(score) = self.bingo().map(|pre_score| pre_score * num) {
|
||||||
|
self.finished = true;
|
||||||
|
Some(score)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bingo(&self) -> Option<usize> {
|
||||||
|
let mut completed = false;
|
||||||
|
for i in 0..5 {
|
||||||
|
if (self.fields[i][0].marked
|
||||||
|
&& self.fields[i][1].marked
|
||||||
|
&& self.fields[i][2].marked
|
||||||
|
&& self.fields[i][3].marked
|
||||||
|
&& self.fields[i][4].marked)
|
||||||
|
|| (self.fields[0][i].marked
|
||||||
|
&& self.fields[1][i].marked
|
||||||
|
&& self.fields[2][i].marked
|
||||||
|
&& self.fields[3][i].marked
|
||||||
|
&& self.fields[4][i].marked)
|
||||||
|
{
|
||||||
|
completed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if completed {
|
||||||
|
Some(self.pre_score())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pre_score(&self) -> usize {
|
||||||
|
self.fields
|
||||||
|
.iter()
|
||||||
|
.flat_map(|lines| lines.iter())
|
||||||
|
.map(|field| if field.marked { 0 } else { field.number })
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct BingoGame {
|
||||||
|
boards: Vec<BingoBoard>,
|
||||||
|
input_numbers: Vec<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BingoGame {
|
||||||
|
fn get_scores(mut self, max: Option<usize>) -> Vec<usize> {
|
||||||
|
let mut scores = Vec::new();
|
||||||
|
for num in self.input_numbers {
|
||||||
|
for board in &mut self.boards {
|
||||||
|
if let Some(score) = board.mark(num) {
|
||||||
|
scores.push(score);
|
||||||
|
if let Some(max) = max {
|
||||||
|
if scores.len() >= max {
|
||||||
|
return scores
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scores
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day4)]
|
||||||
|
pub fn parse_input(input: &str) -> BingoGame {
|
||||||
|
let (input_numbers_str, boards_str) = input.split_once('\n').unwrap();
|
||||||
|
let input_numbers: Vec<usize> = input_numbers_str
|
||||||
|
.split(',')
|
||||||
|
.map(|num| num.parse().unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let boards: Vec<BingoBoard> = boards_str
|
||||||
|
.split_terminator("\n\n")
|
||||||
|
.map(|board| board.parse().unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
BingoGame {
|
||||||
|
boards,
|
||||||
|
input_numbers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day4, part1)]
|
||||||
|
pub fn part1(input: &BingoGame) -> usize {
|
||||||
|
let game = input.clone();
|
||||||
|
*game.get_scores(Some(1)).first().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day4, part2)]
|
||||||
|
pub fn part2(input: &BingoGame) -> usize {
|
||||||
|
let game = input.clone();
|
||||||
|
*game.get_scores(None).last().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
const EXAMPLE_INPUT: &str =
|
||||||
|
"7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1
|
||||||
|
|
||||||
|
22 13 17 11 0
|
||||||
|
8 2 23 4 24
|
||||||
|
21 9 14 16 7
|
||||||
|
6 10 3 18 5
|
||||||
|
1 12 20 15 19
|
||||||
|
|
||||||
|
3 15 0 2 22
|
||||||
|
9 18 13 17 5
|
||||||
|
19 8 7 25 23
|
||||||
|
20 11 10 24 4
|
||||||
|
14 21 16 12 6
|
||||||
|
|
||||||
|
14 21 17 24 4
|
||||||
|
10 16 15 9 19
|
||||||
|
18 8 23 26 20
|
||||||
|
22 11 13 6 5
|
||||||
|
2 0 12 3 7
|
||||||
|
|
||||||
|
";
|
||||||
|
const RESULT_PART_1: usize = 4512;
|
||||||
|
const RESULT_PART_2: usize = 1924;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
let result = super::part1(&super::parse_input(EXAMPLE_INPUT));
|
||||||
|
assert_eq!(result, RESULT_PART_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
let result = super::part2(&super::parse_input(EXAMPLE_INPUT));
|
||||||
|
assert_eq!(result, RESULT_PART_2);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,5 +2,7 @@ use aoc_runner_derive::aoc_lib;
|
||||||
|
|
||||||
pub mod day_01;
|
pub mod day_01;
|
||||||
pub mod day_02;
|
pub mod day_02;
|
||||||
|
pub mod day_03;
|
||||||
|
pub mod day_04;
|
||||||
|
|
||||||
aoc_lib! { year = 2021 }
|
aoc_lib! { year = 2021 }
|
||||||
|
|
Loading…
Reference in a new issue