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
use std::error::Error;

use regex::Regex;

use advent_of_code::main;

main!();

fn solve(input: &str) -> Result<String, Box<Error>> {
    let re = Regex::new(r"(?P<players>\d+) players; last marble is worth (?P<points>\d+) points")
        .unwrap();
    let caps = re.captures(input).unwrap();
    let player_count = caps.name("players").unwrap().as_str().parse()?;
    let marble_count: usize = caps.name("points").unwrap().as_str().parse()?;
    let high_score = play(player_count, marble_count * 100);
    Ok(high_score.to_string())
}

fn play(player_count: usize, marble_count: usize) -> usize {
    let mut scores = vec![0; player_count];
    let mut circle = vec![Marble {
        value: 0,
        next: 0,
        prev: 0,
    }];

    let mut current_player = 0;
    let mut current_index: usize = 0;

    for value in 1..=marble_count {
        if value % 23 == 0 {
            scores[current_player] += value;
            for _ in 0..7 {
                current_index = circle[current_index].prev;
            }
            let marble = &circle[current_index];
            scores[current_player] += marble.value;
            let prev = marble.prev;
            let next = marble.next;
            circle[prev].next = next;
            circle[next].prev = prev;
            current_index = next;
        } else {
            current_index = circle[current_index].next;
            let current_marble = &circle[current_index];
            let next = current_marble.next;
            let prev = current_index;
            let marble = Marble { value, next, prev };
            circle.push(marble);
            current_index = circle.len() - 1;
            circle[prev].next = current_index;
            circle[next].prev = current_index;
        }

        // if current_index % 100000 == 0 {
        //     println!("{}", current_index);
        // }
        // println!("[{}] {:?}", current_player + 1, circle_to_string(&circle));

        current_player += 1;
        current_player %= player_count;
    }

    *scores.iter().max().unwrap()
}

#[test]
fn test_play() {
    assert_eq!(play(9, 25), 32);
    assert_eq!(play(10, 1618), 8317);
    assert_eq!(play(13, 7999), 146373);
    assert_eq!(play(17, 1104), 2764);
    assert_eq!(play(21, 6111), 54718);
    assert_eq!(play(30, 5807), 37305);
}

// fn circle_to_string(circle: &Vec<Marble>) -> String {
//     let mut i = 0;
//     let mut v = Vec::new();
//     loop {
//         v.push(circle[i].value);
//         i = circle[i].next;
//         if i == 0 {
//             break;
//         }
//     }
//     format!("{:?}", v)
// }

#[derive(Debug)]
struct Marble {
    value: usize,
    next: usize,
    prev: usize,
}