summaryrefslogtreecommitdiff
path: root/2021/day21/src/main.rs
blob: 55e70f6ab71c1eb44dbbb3406d09335880e0daf1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use std::fs;
use std::path::Path;
use std::collections::HashMap;

const ROLLS: [(u64,u64);7] = [(3,1),(4,3),(5,6),(6,7),(7,6),(8,3),(9,1)];

struct Player {
    pos: u32,
    score: u32
}

struct QPlayer {
    plays: HashMap<(u8,u64,u64),u64>,
    wins: HashMap<u8,u64>,
    turn: u8
}

impl QPlayer {
    fn new(pos: u64) -> QPlayer {
        let mut plays = HashMap::new();
        plays.insert((0,pos,0), 1);
        QPlayer { plays, wins: HashMap::new(), turn: 0}
    }

    /// Return true if any current state advanced
    fn quantum_roll(&mut self) -> bool {
        let mut ret = false;
        // Compute update
        let mut plays = vec![];
        let mut wins = vec![];
        self.plays.iter().for_each(|(&(t,p,s), &n)| {
            if t == self.turn {
                if s > 20 {
                    wins.push((t,n));
                } else {
                    ret = true;
                    for (roll,card) in ROLLS.iter() {
                        let pos = (p + roll) % 10;
                        plays.push((t+1,pos,s+pos+1,n*card));
                    }
                }
            }
        });
        // Apply update
        plays.iter().for_each(|&(t,p,s,n)| *self.plays.entry((t,p,s)).or_default() += n);
        wins.iter().for_each(|&(t,n)| *self.wins.entry(t).or_default() += n);
        self.turn += 1;
        ret
    }
}

/* AOC21 Day 21: https://adventofcode.com/2021/day/21 */
fn main() {
    let input = Path::new("resources").join("input.txt");
    let content = fs::read_to_string(input).expect("Unable to read input file");
    println!("Ex1: the result is {}", ex1(&content));
    println!("Ex2: the result is {}", ex2(&content));
}

fn parse_input(input: &str) -> Vec<Player> {
    input.lines().map(|l| {
        let (_,pos) = l.split_once(": ").expect("Malformed input");
        Player { pos: pos.parse::<u32>().unwrap() - 1, score: 0 }
    }).collect()
}

fn ex1(input: &str) -> u32 {
    let mut players = parse_input(input);
    let mut p = players.len()-1;
    let mut turn = 0;
    while players[p].score < 1000 {
        p = (p+1) % players.len();
        players[p].pos = (players[p].pos + 9*turn + 6) % 10;
        players[p].score += players[p].pos + 1;
        turn += 1;
    }
    players[(p+1) % players.len()].score * turn * 3
}

fn ex2(input: &str) -> u64 {
    let mut p1 = {
        let (_,pos) = input.lines().nth(0).unwrap().split_once(": ").expect("Malformed input");
        QPlayer::new(pos.parse::<u64>().unwrap() - 1)
    };
    let mut p2 = {
        let (_,pos) = input.lines().nth(1).unwrap().split_once(": ").expect("Malformed input");
        QPlayer::new(pos.parse::<u64>().unwrap() - 1)
    };
    while p1.quantum_roll() { }
    while p2.quantum_roll() { }
    let p1wins = p1.wins.iter().map(|(&t1,&n1)| {
        p2.plays.iter().filter_map(|(&(t2,_,s2),&n2)| if t1 == t2+1 && s2 < 21 { Some(n1*n2) } else { None }).sum::<u64>()
    }).sum();
    let p2wins = p2.wins.iter().map(|(&t2,&n2)|
        p1.plays.iter().filter_map(|(&(t1,_,s1),&n1)| if t1 == t2 && s1 < 21 { Some(n1*n2) } else { None }).sum::<u64>()
    ).sum();
    u64::max(p1wins, p2wins)
}