2020: day22: part 2
This commit is contained in:
parent
fe1f56bd7d
commit
17bf26ea14
|
@ -1,5 +1,5 @@
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::VecDeque;
|
use std::collections::{HashSet, VecDeque};
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
@ -10,11 +10,12 @@ pub fn run() -> Result<String> {
|
||||||
let mut res = String::with_capacity(128);
|
let mut res = String::with_capacity(128);
|
||||||
|
|
||||||
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
||||||
|
writeln!(res, "part 2: {}", part2(INPUT)?)?;
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn play_game<'a>(deck_a: &'a mut Deck, deck_b: &'a mut Deck) -> &'a Deck {
|
fn play_game(mut deck_a: Deck, mut deck_b: Deck) -> Deck {
|
||||||
while !(deck_a.0.is_empty() || deck_b.0.is_empty()) {
|
while !(deck_a.0.is_empty() || deck_b.0.is_empty()) {
|
||||||
let card_a = deck_a.0.pop_front().unwrap();
|
let card_a = deck_a.0.pop_front().unwrap();
|
||||||
let card_b = deck_b.0.pop_front().unwrap();
|
let card_b = deck_b.0.pop_front().unwrap();
|
||||||
|
@ -39,23 +40,104 @@ fn play_game<'a>(deck_a: &'a mut Deck, deck_b: &'a mut Deck) -> &'a Deck {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(input: &str) -> Result<u64> {
|
fn play_recursive_game(mut deck_a: Deck, mut deck_b: Deck) -> (Deck, bool) {
|
||||||
let mut decks = input.split("\n\n");
|
let mut seen: HashSet<(Deck, Deck)> = HashSet::new();
|
||||||
|
|
||||||
let mut deck_a: Deck = decks.next().context("couldn't get first deck")?.parse()?;
|
while !(deck_a.0.is_empty() || deck_b.0.is_empty()) {
|
||||||
let mut deck_b: Deck = decks.next().context("couldn't get second deck")?.parse()?;
|
// Before either player deals a card, if there was a previous round in this game that had
|
||||||
|
// exactly the same cards in the same order in the same players' decks, the game instantly
|
||||||
|
// ends in a win for player 1. Previous rounds from other games are not considered. (This
|
||||||
|
// prevents infinite games of Recursive Combat, which everyone agrees is a bad idea.)
|
||||||
|
if seen.contains(&(deck_a.clone(), deck_b.clone())) {
|
||||||
|
return (deck_a, true);
|
||||||
|
} else {
|
||||||
|
seen.insert((deck_a.clone(), deck_b.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
let winning_deck = play_game(&mut deck_a, &mut deck_b);
|
// Otherwise, this round's cards must be in a new configuration; the players begin the round
|
||||||
|
// by each drawing the top card of their deck as normal.
|
||||||
|
let card_a = deck_a.0.pop_front().unwrap();
|
||||||
|
let card_b = deck_b.0.pop_front().unwrap();
|
||||||
|
|
||||||
Ok(winning_deck
|
// true if first player won, false otherwise
|
||||||
.0
|
let winner: bool;
|
||||||
|
|
||||||
|
if deck_a.0.len() >= card_a as usize && deck_b.0.len() >= card_b as usize {
|
||||||
|
// If both players have at least as many cards remaining in their deck as the value of
|
||||||
|
// the card they just drew, the winner of the round is determined by playing a new game
|
||||||
|
// of Recursive Combat (see below).
|
||||||
|
|
||||||
|
// To play a sub-game of Recursive Combat, each player creates a new deck by making a
|
||||||
|
// copy of the next cards in their deck (the quantity of cards copied is equal to the
|
||||||
|
// number on the card they drew to trigger the sub-game). During this sub-game, the game
|
||||||
|
// that triggered it is on hold and completely unaffected; no cards are removed from
|
||||||
|
// players' decks to form the sub-game. (For example, if player 1 drew the 3 card, their
|
||||||
|
// deck in the sub-game would be copies of the next three cards in their deck.)
|
||||||
|
let mut new_deck_a = deck_a.clone();
|
||||||
|
let mut new_deck_b = deck_b.clone();
|
||||||
|
new_deck_a.0.truncate(card_a as usize);
|
||||||
|
new_deck_b.0.truncate(card_b as usize);
|
||||||
|
|
||||||
|
let (_, deck_a_won) = play_recursive_game(new_deck_a, new_deck_b);
|
||||||
|
winner = deck_a_won;
|
||||||
|
} else {
|
||||||
|
// Otherwise, at least one player must not have enough cards left in their deck to
|
||||||
|
// recurse; the winner of the round is the player with the higher-value card.
|
||||||
|
match card_a.cmp(&card_b) {
|
||||||
|
Ordering::Greater => winner = true,
|
||||||
|
Ordering::Less => winner = false,
|
||||||
|
Ordering::Equal => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if winner {
|
||||||
|
deck_a.0.push_back(card_a);
|
||||||
|
deck_a.0.push_back(card_b);
|
||||||
|
} else {
|
||||||
|
deck_b.0.push_back(card_b);
|
||||||
|
deck_b.0.push_back(card_a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if deck_a.0.is_empty() {
|
||||||
|
(deck_b, false)
|
||||||
|
} else {
|
||||||
|
(deck_a, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deck_score(deck: &Deck) -> u64 {
|
||||||
|
deck.0
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, card)| card * (i as u64 + 1))
|
.map(|(i, card)| card * (i as u64 + 1))
|
||||||
.sum())
|
.sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn part1(input: &str) -> Result<u64> {
|
||||||
|
let mut decks = input.split("\n\n");
|
||||||
|
|
||||||
|
let deck_a: Deck = decks.next().context("couldn't get first deck")?.parse()?;
|
||||||
|
let deck_b: Deck = decks.next().context("couldn't get second deck")?.parse()?;
|
||||||
|
|
||||||
|
let winning_deck = play_game(deck_a, deck_b);
|
||||||
|
|
||||||
|
Ok(deck_score(&winning_deck))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(input: &str) -> Result<u64> {
|
||||||
|
let mut decks = input.split("\n\n");
|
||||||
|
|
||||||
|
let deck_a: Deck = decks.next().context("couldn't get first deck")?.parse()?;
|
||||||
|
let deck_b: Deck = decks.next().context("couldn't get second deck")?.parse()?;
|
||||||
|
|
||||||
|
let (winning_deck, _) = play_recursive_game(deck_a, deck_b);
|
||||||
|
|
||||||
|
Ok(deck_score(&winning_deck))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
struct Deck(VecDeque<u64>);
|
struct Deck(VecDeque<u64>);
|
||||||
|
|
||||||
impl Deck {}
|
impl Deck {}
|
||||||
|
@ -92,4 +174,15 @@ mod tests {
|
||||||
fn part1_real() {
|
fn part1_real() {
|
||||||
assert_eq!(part1(INPUT).unwrap(), 30780);
|
assert_eq!(part1(INPUT).unwrap(), 30780);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_provided() {
|
||||||
|
assert_eq!(part2(PROVIDED).unwrap(), 291);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn part2_real() {
|
||||||
|
assert_eq!(part2(INPUT).unwrap(), 36621);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue