Public
Edited
Nov 18, 2022
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function parse(input) {
return input
.split("\n\n")
.map((s) => s.split("\n").slice(1).map(Number))
.map((d) => new Denque(d));
}
Insert cell
Insert cell
function play(deck1, deck2) {
if (deck1.isEmpty()) {
// Game ends with player 1 running out of cards
return { winner: 2, deck: deck2, score: calcScore(deck2) };
}
if (deck2.isEmpty()) {
// Game ends with player 2 running out of cards
return { winner: 1, deck: deck1, score: calcScore(deck1) };
}

// The game continues.
const [c1, c2] = [deck1.shift(), deck2.shift()];
if (c1 > c2) {
// Player 1 wins this round
deck1.push(c1);
deck1.push(c2);
} else {
// Player 2 wins this round
deck2.push(c2);
deck2.push(c1);
}
// Play again next round.
return play(deck1, deck2);
}
Insert cell
Insert cell
function calcScore(d) {
return AOC.sum(
d
.toArray()
.reverse()
.map((n, i) => n * (i + 1))
);
}
Insert cell
function part1(input) {
return play(...parse(puzzleInput)).score;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function hand(deck) {
return JSON.stringify(deck.toArray());
}
Insert cell
Insert cell
function play2(deck1, history1, deck2, history2) {
if (history1.has(hand(deck1)) || history2.has(hand(deck2))) {
// We've been here before so player 1 wins by default
return { winner: 1, deck: deck1, score: calcScore(deck1) };
}
if (deck1.isEmpty()) {
// Round (or game if this is a top-level round) ends with player 1 running out of cards
return { winner: 2, deck: deck2, score: calcScore(deck2) };
}
if (deck2.isEmpty()) {
// Round (or game if this is a top-level round) ends with player 2 running out of cards
return { winner: 1, deck: deck1, score: calcScore(deck1) };
}

// The game continues so store current hands in history before proceeding.
history1.add(hand(deck1));
history2.add(hand(deck2));

// Draw
const [c1, c2] = [deck1.shift(), deck2.shift()];
if (deck1.toArray().length < c1 || deck2.toArray().length < c2) {
// Normal game
if (c1 > c2) {
// Player 1 wins this round
deck1.push(c1);
deck1.push(c2);
} else {
// Player 2 wins this round
deck2.push(c2);
deck2.push(c1);
}
// Play again next round.
return play2(deck1, history1, deck2, history2);
}
// Recursive game
const subGame = play2(
new Denque(deck1.toArray().slice(0, c1)),
new Set(),
new Denque(deck2.toArray().slice(0, c2)),
new Set()
);
if (subGame.winner === 1) {
// Player 1 won the sub game so they grab the two cards from this game
deck1.push(c1);
deck1.push(c2);
} else {
// Player 2 won the sub game so they grab the two cards from this game
deck2.push(c2);
deck2.push(c1);
}
// Play again next round.
return play2(deck1, history1, deck2, history2);
}
Insert cell
function part2(input) {
const [deck1, deck2] = parse(puzzleInput);
return play2(deck1, new Set(), deck2, new Set()).score;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more