-
Notifications
You must be signed in to change notification settings - Fork 2
/
day21.js
98 lines (87 loc) · 2.7 KB
/
day21.js
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 strict';
const fs = require('fs');
const cl = console.log;
const boardSize = 10;
fs.readFile('day21.txt', 'utf-8', (err, input) => {
if (err) throw err;
const players = input.trim().split(/\r?\n/).map(newPlayer);
cl(diracDice1(players.map(copyPlayer)));
cl(diracDice2(players));
});
function diracDice2(players) {
const wins = play(players[0], players[1], new Map(), calcDiceFutures());
return Math.max(...wins);
}
function play(player, otherPlayer, history, diceFutures) {
let wins;
let histKey = historyKey(player, otherPlayer);
if (otherPlayer.score >= 21) {
return [0, 1];
} else if (wins = history.get(histKey)) {
return wins.reverse(); // I don't get why I need this reverse :-(
} else {
wins = diceFutures.map(([moves, frequency]) => {
const futurePlayer = copyPlayer(player);
futurePlayer.pos = circAdd(futurePlayer.pos, moves, boardSize);
futurePlayer.score += futurePlayer.pos;
let futureWins =
play(otherPlayer, futurePlayer, history, diceFutures)
.reverse();
return scale(futureWins, frequency);
}).reduce(([a, b], [c, d]) => [a + c, b + d]);
history.set(histKey, wins);
return history.get(histKey);
}
}
function historyKey(p1, p2) {
return `${p1.score},${p1.pos},${p2.score},${p2.pos}`
}
function scale([a, b], factor) {
return [a * factor, b * factor];
}
function calcDiceFutures() {
let totals = new Array(10).fill(0);
for (let i = 1; i <= 3; i++) {
for (let j = 1; j <= 3; j++) {
for (let k = 1; k <= 3; k++) {
totals[i + j + k]++;
}
}
}
let futures = [];
for (let i = 0; i < totals.length; i++) {
let count = totals[i];
if (count > 0) {
futures.push([i, count])
}
}
return futures;
}
function diracDice1(players) {
let diceVal = 0;
let diceRolls = 0;
while (true) {
for (let player of players) {
let moves = 0;
for (let i = 0; i < 3; i++) {
moves += (diceVal = circAdd(diceVal, 1, 100));
diceRolls++;
}
player.score +=
(player.pos = circAdd(player.pos, moves, boardSize));
if (player.score >= 1000) {
const loser = players.filter(p => p.score < 1000)[0];
return loser.score * diceRolls;
}
}
}
}
function newPlayer(ln) {
return { pos: parseInt(ln.at(-1)), score: 0 };
}
function copyPlayer(player) {
return { pos: player.pos, score: player.score };
}
function circAdd(a, b, limit) {
return ((a + b - 1) % limit) + 1;
}