Day 22. Clearly some optimizing could be done, but it works.
This commit is contained in:
53
inputs/day22.txt
Normal file
53
inputs/day22.txt
Normal file
@@ -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
|
||||||
13
inputs/day22_test.txt
Normal file
13
inputs/day22_test.txt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
Player 1:
|
||||||
|
9
|
||||||
|
2
|
||||||
|
6
|
||||||
|
3
|
||||||
|
1
|
||||||
|
|
||||||
|
Player 2:
|
||||||
|
5
|
||||||
|
8
|
||||||
|
4
|
||||||
|
7
|
||||||
|
10
|
||||||
8
inputs/day22_test_halt.txt
Normal file
8
inputs/day22_test_halt.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Player 1:
|
||||||
|
43
|
||||||
|
19
|
||||||
|
|
||||||
|
Player 2:
|
||||||
|
2
|
||||||
|
29
|
||||||
|
14
|
||||||
212
src/bin/crab_combat.rs
Normal file
212
src/bin/crab_combat.rs
Normal file
@@ -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<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deck {
|
||||||
|
fn read<'a, I: Iterator<Item = &'a str>>(lines: &mut I) -> Result<Deck, TopLevelError> {
|
||||||
|
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<usize> {
|
||||||
|
self.cards.pop_front()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_cards(&mut self, winning_card: usize, cards: &mut Vec<usize>) {
|
||||||
|
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<HashMap<usize, Deck>>,
|
||||||
|
decks: HashMap<usize, Deck>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Deck, TopLevelError> {
|
||||||
|
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<Deck> = 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(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user