diff --git a/inputs/day22.txt b/inputs/day22.txt new file mode 100644 index 0000000..fbad1be --- /dev/null +++ b/inputs/day22.txt @@ -0,0 +1,53 @@ +Player 1: +43 +36 +13 +11 +20 +25 +37 +38 +4 +18 +1 +8 +27 +23 +7 +22 +10 +5 +50 +40 +45 +26 +15 +32 +33 + +Player 2: +21 +29 +12 +28 +46 +9 +44 +6 +16 +39 +19 +24 +17 +14 +47 +48 +42 +34 +31 +3 +41 +35 +2 +30 +49 diff --git a/inputs/day22_test.txt b/inputs/day22_test.txt new file mode 100644 index 0000000..24d78bf --- /dev/null +++ b/inputs/day22_test.txt @@ -0,0 +1,13 @@ +Player 1: +9 +2 +6 +3 +1 + +Player 2: +5 +8 +4 +7 +10 \ No newline at end of file diff --git a/inputs/day22_test_halt.txt b/inputs/day22_test_halt.txt new file mode 100644 index 0000000..dce7e1a --- /dev/null +++ b/inputs/day22_test_halt.txt @@ -0,0 +1,8 @@ +Player 1: +43 +19 + +Player 2: +2 +29 +14 \ No newline at end of file diff --git a/src/bin/crab_combat.rs b/src/bin/crab_combat.rs new file mode 100644 index 0000000..95045e9 --- /dev/null +++ b/src/bin/crab_combat.rs @@ -0,0 +1,212 @@ +use advent2020::errors::TopLevelError; +use std::env; +use std::fmt; +use std::fs; +use std::iter::FromIterator; +use std::str::FromStr; +use std::{ + collections::{HashMap, VecDeque}, + sync::atomic::{AtomicUsize, Ordering}, +}; + +#[derive(Clone, Eq, Hash, PartialEq)] +struct Deck { + player: usize, + cards: VecDeque, +} + +impl Deck { + fn read<'a, I: Iterator>(lines: &mut I) -> Result { + match lines.next() { + None => Err(TopLevelError::NoInputFound), + Some("") => Deck::read(lines), + Some(x) if x.starts_with("Player ") => { + let numeric_string = x.trim_start_matches("Player ").trim_end_matches(':'); + let player = usize::from_str(numeric_string)?; + let mut cards = VecDeque::new(); + + loop { + match lines.next() { + None => break, + Some("") => break, + Some(x) => cards.push_back(usize::from_str(x)?), + } + } + + Ok(Deck { player, cards }) + } + Some(_) => Err(TopLevelError::UnknownError), + } + } + + fn top(&mut self) -> Option { + self.cards.pop_front() + } + + fn add_cards(&mut self, winning_card: usize, cards: &mut Vec) { + cards.sort_unstable(); + cards.reverse(); + self.cards.push_back(winning_card); + for card in cards.drain(..).filter(|x| *x != winning_card) { + self.cards.push_back(card); + } + } + + fn size(&self) -> usize { + self.cards.len() + } + + fn resize(&mut self, new_size: usize) { + self.cards.truncate(new_size); + } +} + +impl fmt::Display for Deck { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Player {}'s deck: {:?}", self.player, self.cards) + } +} + +struct Game { + id: usize, + round: usize, + history: Vec>, + decks: HashMap, +} + +static NEXT_GAME_NUMBER: AtomicUsize = AtomicUsize::new(1); + +impl Game { + fn new(decks: &[Deck]) -> Game { + Game { + id: NEXT_GAME_NUMBER.fetch_add(1, Ordering::SeqCst), + history: Vec::new(), + round: 0, + decks: HashMap::from_iter(decks.iter().map(|x| (x.player, x.clone()))), + } + } + + fn winner(&self) -> Option<&Deck> { + let mut result = None; + + for deck in self.decks.values() { + if deck.size() > 0 && result.is_none() { + result = Some(deck); + } else if deck.size() > 0 { + return None; + } + } + + result + } + + fn play(&mut self, recursive: bool) -> Result { + loop { + // first, see if we're done + if let Some(winner) = self.winner() { + return Ok(winner.clone()); + } + + // OK, let's print out some state information + self.round += 1; + println!( + "-- Game {}, Round {} {}--", + self.id, + self.round, + if recursive { "[recursive]" } else { "" } + ); + for deck in self.decks.values() { + println!("{}", deck); + } + + // if this is the recursive version of the game and we've been here before, + // just stop, and player 1 won. + if recursive && self.history.contains(&self.decks) { + return self + .decks + .get(&1) + .cloned() + .ok_or(TopLevelError::UnknownError); + } + + if recursive { + self.history.push(self.decks.clone()); + } + + // otherwise, grab the first card off each of the decks + let mut top_card_info = Vec::new(); + + for (player, deck) in self.decks.iter_mut() { + if let Some(top_card) = deck.top() { + let recurse_check_value = deck.size() >= top_card; + println!( + "Top card for Player {}: {} [recurse check: {}]", + player, top_card, recurse_check_value + ); + top_card_info.push((deck, top_card, recurse_check_value)); + } + } + + let mut top_cards = top_card_info.iter().map(|(_, x, _)| *x).collect(); + + // if we're in a recursive game, and we meet the length conditions, recurse + if recursive && top_card_info.iter().all(|(_, _, x)| *x) { + let new_decks: Vec = top_card_info + .iter() + .map(|(deck, newlen, _)| { + let mut new_deck = (&**deck).clone(); + new_deck.resize(*newlen); + new_deck + }) + .collect(); + println!("Creating recursive game!"); + let mut subgame = Game::new(&new_decks); + let subgame_result = subgame.play(true)?; + + for (deck, top, _) in top_card_info.drain(..) { + if deck.player == subgame_result.player { + deck.add_cards(top, &mut top_cards); + } + } + } else { + // this is just a normal case + let winning_card = top_card_info + .iter() + .map(|(_, x, _)| *x) + .max() + .ok_or(TopLevelError::UnknownError)?; + println!("The winning card is {}", winning_card); + let (winner, _, _) = top_card_info + .drain(..) + .filter(|(_, x, _)| *x == winning_card) + .next() + .ok_or(TopLevelError::UnknownError)?; + winner.add_cards(winning_card, &mut top_cards); + } + + println!(); + } + } +} + +fn main() -> Result<(), TopLevelError> { + let filename = env::args().nth(1).expect("No file argument given."); + let contents = fs::read_to_string(filename)?; + let mut lines = contents.lines(); + let deck1 = Deck::read(&mut lines)?; + let deck2 = Deck::read(&mut lines)?; + let decks = [deck1, deck2]; + let mut game1 = Game::new(&decks); + + let result = game1.play(true)?; + let mut sum = 0; + + for (num, card) in result.cards.iter().rev().enumerate() { + sum += (num + 1) * card; + } + + println!("Winning deck: {}", result); + println!("Final puzzle result: {}", sum); + + Ok(()) +}