# Day 21: Dirac Dice

Please post your solutions in your own top-level comment. Here's a template you can copy-paste into your comment to format it nicely, with the code collapsed by default inside an expandable section with syntax highlighting (you can replace `python` with any of the "short names" listed in this page of supported languages):

``````<details>
<summary>Part 1</summary>

```python
```

</details>
``````

1. Crespyl
Well part one is easy enough, part two looks like the modular arithmetic problem from Day 22 of 2019, and is probably beyond my abilities (certainly beyond my abilities for tonight!). I'll...

Well part one is easy enough, part two looks like the modular arithmetic problem from Day 22 of 2019, and is probably beyond my abilities (certainly beyond my abilities for tonight!). I'll probably do some reading over the next few days and see if I can make some headway, but I'm also heading up to visit the family for the rest of the holidays, so this will probably mark the end of my day-by-day solving.

Happy Christmas and a merry New Year to everyone, it's been fun solving with y'all!

Part 1 Ruby

I went with a somewhat overly verbose object oriented approach, thinking that maybe part two would involve rolling lots of separate dice, deterministic or otherwise. I was sort of right, but not in a way that made this particular `DDice` class useful.

``````class DDice
def initialize()
@max = 100
@next_roll = 1
@total_rolls = 0
end

def total_rolls
@total_rolls
end

def roll
@total_rolls += 1
result = @next_roll

@next_roll = @next_roll + 1
@next_roll = 1 if @next_roll > @max

return result
end

def roll_n(n)
(0...n).map { roll }
end
end

def wrap(n)
n -= 10 until n <= 10
n
end

def compute_p1(input)
p1_pos = input.lines[0].chomp.chars.last.to_i
p2_pos = input.lines[1].chomp.chars.last.to_i

p1_score = 0
p2_score = 0

die = DDice.new

loop do
p1_rolls = die.roll_n(3)
p1_step = p1_rolls.sum
p1_pos = wrap(p1_pos + p1_step)
p1_score += p1_pos
# puts "Player 1 rolls #{p1_rolls.join('+')} and moves to space #{p1_pos} for a total score of #{p1_score}"
break if p1_score >= 1000

p2_rolls = die.roll_n(3)
p2_step = p2_rolls.sum
p2_pos = wrap(p2_pos + p2_step)
p2_score += p2_pos
# puts "Player 2 rolls #{p2_rolls.join('+')} and moves to space #{p2_pos} for a total score of #{p2_score}"
break if p2_score >= 1000
end

winner = p1_score >= 1000 ? 'p1' : 'p2'
loser  = p1_score >= 1000 ? 'p2' : 'p1'
puts "#{winner} wins with #{[p1_score, p2_score].max} points, after #{die.total_rolls} rolls"
puts "#{loser} loses with #{[p1_score, p2_score].min} points, after #{die.total_rolls} rolls"

(([p1_score, p2_score].min) * die.total_rolls)
end
``````
2. wycy
(edited )
Rust Finally got it, though not sure that I did it the intended way. It takes 2sec to run on my hardware. DetailsI implemented this as a recursive function to play through all the scenarios and...

Rust

Finally got it, though not sure that I did it the intended way. It takes 2sec to run on my hardware.

DetailsI implemented this as a recursive function to play through all the scenarios and used Rust's `cached` crate to memoize the results of the function.
Rust
``````use std::env;
use std::io::{self};

const CIRCLE_SIZE: u64 = 10;
const DICE_ROLLS_PER_TURN: u64 = 3;

const DETERM_DICE_SIDES: u64 = 100;
const WIN_SCORE_PART1: u64 = 1000;

const WIN_SCORE_PART2: u64 = 21;

fn add_arrays(first: [u64;2], second: [u64;2]) -> [u64;2] {
let mut new: [u64;2] = [0;2];
new[0] = first[0] + second[0];
new[1] = first[1] + second[1];
new
}

#[macro_use] extern crate lazy_static;
lazy_static! {
static ref ROLLS: Vec<[u64; 3]> = {
let mut poss: Vec<[u64; 3]> = Vec::new();
for d1 in 1..=3 {
for d2 in 1..=3 {
for d3 in 1..=3 {
poss.push([d1, d2, d3]);
}
}
}
poss
};
}

#[macro_use] extern crate cached;

cached! {
PD;
fn play_from(starting: [u64;2], scores: [u64;2], turn: usize, rolls: Option<[u64; 3]>) -> [u64; 2] = {
let mut wins: [u64; 2] = [0; 2];

// Initialize game
let mut pos:   [u64; 2] = starting;
let mut score: [u64; 2] = scores;

// Play next move first
match rolls {
Some(roll) => {
pos[turn] = (pos[turn] + roll[0]+roll[1]+roll[2] - 1) % CIRCLE_SIZE + 1;
score[turn] += pos[turn];
if score[turn] >= WIN_SCORE_PART2 {
wins[turn] += 1;
return wins;
}
},
_ => {},
}

// Keep playing
let player = if turn == 0 { 1 } else { 0 };
for roll in ROLLS.iter() {
}
wins
}
}

fn play_part1(starting: [u64;2]) -> u64 {
// Play game
let mut pos:   [u64; 2] = starting;
let mut score: [u64; 2] = [0; 2];
let mut rolls = 0;
let mut dice = 0;
'play: loop {

// Player Loop
for player in 0..2 {
let mut roll = 0;
for _ in 0..DICE_ROLLS_PER_TURN {
dice += 1;
if dice > DETERM_DICE_SIDES { dice = 1; }
roll += dice;
}
pos[player] = (pos[player] + roll - 1) % CIRCLE_SIZE + 1;
score[player] += pos[player];
rolls += DICE_ROLLS_PER_TURN;
if score[player] >= WIN_SCORE_PART1 { break 'play; }
}
}
let loser = score.iter().min().unwrap();
loser * rolls
}

fn solve(input: &str) -> io::Result<()> {
// Input
let input_str = input_str.trim();

// Starting positions
let starting = input_str
.split("\n")
.map(|l| l.chars().last().unwrap().to_digit(10).unwrap())
.collect::<Vec<_>>();

let starting: [u64; 2] = [starting[0].into(), starting[1].into()];

// Part 1
let part1 = play_part1(starting.into());
println!("Part 1: {}", part1); // 1004670

// Part 2
let part2 = play_from(starting, [0;2], 1, None); // start with player 1 because rolls=None (effectively starts with player 0)
let part2 = part2.iter().max().unwrap();
println!("Part 2: {}", part2); // 492043106122795

Ok(())
}

fn main() {
let args: Vec<String> = env::args().collect();
let filename = &args[1];
solve(&filename).unwrap();
}
``````
1 vote