diff --git a/input/21.txt b/input/21.txt new file mode 100644 index 0000000..041b578 --- /dev/null +++ b/input/21.txt @@ -0,0 +1,2 @@ +Player 1 starting position: 7 +Player 2 starting position: 8 diff --git a/src/day21.rs b/src/day21.rs new file mode 100644 index 0000000..af96bcb --- /dev/null +++ b/src/day21.rs @@ -0,0 +1,97 @@ +use std::collections::HashMap; + + +pub fn parse_input(input: &str) -> (u8, u8) { + let players: Vec = input.lines() + .map(|l| l.split_once(": ").unwrap().1.parse().unwrap()) + .collect(); + return (players[0], players[1]); +} + +pub fn part1(starting_positions: &(u8, u8)) -> u32 { + let mut player1_position = starting_positions.0 as u32; + let mut player2_position = starting_positions.1 as u32; + let mut player1_score: u32 = 0; + let mut player2_score: u32 = 0; + let mut rolled_count = 0; + + let mut is_player1_turn = true; + + while player1_score < 1000 && player2_score < 1000 { + let position; + let score; + if is_player1_turn { + position = &mut player1_position; + score = &mut player1_score; + } else { + position = &mut player2_position; + score = &mut player2_score; + } + + *position += (rolled_count + 1 - 1) % 100 + 1; + *position += (rolled_count + 2 - 1) % 100 + 1; + *position += (rolled_count + 3 - 1) % 100 + 1; + + *position = (*position - 1) % 10 + 1; + *score += *position as u32; + + rolled_count += 3; + is_player1_turn = !is_player1_turn; + } + + player1_score.min(player2_score) * rolled_count as u32 +} + +fn get_wins_amount(starting_pos1: u32, starting_pos2: u32, starting_score1: u32, starting_score2: u32, memo: &mut HashMap<(u32, u32, u32, u32), (u64, u64)>) -> (u64, u64) { + let memo_key = (starting_pos1, starting_pos2, starting_score1, starting_score2); + if memo.contains_key(&memo_key) { + return *memo.get(&memo_key).unwrap() + } + let mut total_wins1 = 0; + let mut total_wins2 = 0; + + for dice1 in 1..=3 { + for dice2 in 1..=3 { + for dice3 in 1..=3 { + let pos1 = (starting_pos1 + dice1 + dice2 + dice3 - 1) % 10 + 1; + let score1 = starting_score1 + pos1; + if score1 >= 21 { + total_wins1 += 1 + } else { + let (wins2, wins1) = get_wins_amount(starting_pos2, pos1, starting_score2, score1, memo); + total_wins1 += wins1; + total_wins2 += wins2; + } + } + } + } + + memo.insert(memo_key, (total_wins1, total_wins2)); + + (total_wins1, total_wins2) +} + +pub fn part2(positions: &(u8, u8)) -> u64 { + let mut memo = HashMap::new(); + let (wins1, wins2) = get_wins_amount(positions.0 as u32, positions.1 as u32, 0, 0, &mut memo); + wins1.max(wins2) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn part1_example() { + let input = (4, 8); + let result = part1(&input); + assert_eq!(result, 739785); + } + + #[test] + fn part2_example() { + let input = (4, 8); + let result = part2(&input); + assert_eq!(result, 444356092776315); + } +} diff --git a/src/main.rs b/src/main.rs index f71fe94..28427b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,7 @@ mod day17; mod day18; mod day19; mod day20; +mod day21; use std::{env, process}; use std::fs::File; @@ -74,6 +75,8 @@ fn run(day: i32, part: i32, input_filename: &str) { "19.2" => println!("{}", day19::part2(&day19::parse_input(&contents))), "20.1" => println!("{}", day20::part1(&day20::parse_input(&contents))), "20.2" => println!("{}", day20::part2(&day20::parse_input(&contents))), + "21.1" => println!("{}", day21::part1(&day21::parse_input(&contents))), + "21.2" => println!("{}", day21::part2(&day21::parse_input(&contents))), _ => println!("Day {} part {} not found", day, part) } }