Day 2 — Rock Paper Scissors
Part 1
A little step up from yesterday! The first thing I did was assign the letters to their respective point values. I also created a couple constants for win points and draw points.
const WIN = 6;
const DRAW = 3;
// A, X = Rock
// B, Y = Paper
// C, Z = Scissors
const scoreMap = {
A: 1,
B: 2,
C: 3,
X: 1,
Y: 2,
Z: 3,
};
Next, we should split the input into rounds, and filter out any falsey values.
const rounds = input.split("\n").filter(Boolean);
Finally we can use reduce to calculate the score for each round. We determine the winner by using a clever trick with modulus to determine the outcome of the round.
const finalScore = rounds.reduce((score, round) => {
const [oMove, pMove] = round.split(" ");
if (scoreMap[pMove] % 3 === scoreMap[oMove] - 1) {
// Player lost :(
score += scoreMap[pMove];
} else if (scoreMap[pMove] === scoreMap[oMove]) {
// Round is a draw :|
score += scoreMap[pMove] + DRAW;
} else {
// Player wins! :D
score += scoreMap[pMove] + WIN;
}
return score;
}, 0);
Final answer: 9,241 ✅
Part 2
This isn’t so bad. We simply need to work backwards to determine how to force a a win. loss, or draw. Once again we’ll use reduce and the modulus trick to determine the outcome of the round.
rounds.reduce((score, round) => {
const [oMove, desiredResult] = round.split(" ");
}, 0);
This time we’re treating the second item in the round tuple as the desired outcome. We’ll go in alphabetical order here, and handle forcing a loss first. To do this we simply need to use the scoreMap object as a lookup and use the same algorithm as before to determine which move results in a loss.
rounds.reduce((score, round) => {
const [oMove, desiredResult] = round.split(" ");
if (desiredResult === "X") {
//force a loss
score +=
Object.values(scoreMap).find(
(points) => points % 3 === scoreMap[oMove] - 1
) ?? 0;
}
}, 0);
Handling a draw is even easier. We just need to use the lookup to find the value that matches the same value of the move the opponent made.
rounds.reduce((score, round) => {
const [oMove, desiredResult] = round.split(" ");
if (desiredResult === "X") {
//force a loss
score +=
Object.values(scoreMap).find(
(points) => points % 3 === scoreMap[oMove] - 1
) ?? 0;
} else if (desiredResult === "Y") {
// force a draw
score += scoreMap[oMove] + DRAW;
}
}, 0);
Finally, we can determine the winning move by using nearly the exact same method as losing, but with a small change to the algorithm.
rounds.reduce((score, round) => {
const [oMove, desiredResult] = round.split(" ");
if (desiredResult === "X") {
//force a loss
score +=
Object.values(scoreMap).find(
(points) => points % 3 === scoreMap[oMove] - 1
) ?? 0;
} else if (desiredResult === "Y") {
// force a draw
score += scoreMap[oMove] + DRAW;
} else {
score +=
(Object.values(scoreMap).find(
(points) => points === (scoreMap[oMove] % 3) + 1
) ?? 0) + WIN;
}
}, 0);
The only difference is we’re looking for the scoreMap value that equals move % 3 + 1 instead of move % 3 - 1.
With that our final outcome is 14,610! 🥳