Advent of Code 2022: Day 2
by Michael Welborn on 2022-12-19
If you haven't already, take a look at the prep work we did on day 0, and view the puzzle description on AoC.
Day 2 is another easy puzzle, involving rounds of Rock Paper Scissors. Each line of input is a round in the game.
A Y
B X
C Z
The scoring rules for each round of our game are as follows:
- We get points for our throw—1 for rock, 2 for paper, and 3 for scissors.
- We get points for the outcome of the round—0 for a loss, 3 for a draw, and 6 for a win.
Part One
In part one, our opponent's throw is the first letter of each round, and our throw is the second. A, B, and C stand for our opponent's rock, paper, and scissors throws, respectively. And the same for X, Y, and Z for our own throws. The desired output is the total score for our game of Rock Paper Scissors.
Now we could program the logic for Rock Paper Scissors, where paper beats rock and so forth. But because the rules of what throw beats what are not transitive, we can't just convert rock, paper, and scissors to numbers and represent the rules algebraically.
Instead, we can make the observation that there's only 9 permutations of rock, paper, and scissors taken 2 at a time with replacement. A precomputed lookup table of scores for the 9 possible rounds will be less code, faster to write, and faster to execute.
Since we're mapping the whole round to a score, there's no parsing required. The lines of input will be used directly.
from solver import Solver
class RockPaperScissors(Solver):
rounds: list[str]
def parse_input(self) -> None:
self.rounds = self.input_lines
Let's have a dictionary that maps rounds to scores be our lookup table. We'll calculate the scores based on the above rules, and define the solution as the sum of the mapping.
class RockPaperScissors(Solver):
...
p1_scores = {
"A X": 1 + 3, # Rock x Rock -> Draw
"A Y": 2 + 6, # Rock x Paper -> Win
"A Z": 3 + 0, # Rock x Scissors -> Lose
"B X": 1 + 0, # Paper x Rock -> Lose
"B Y": 2 + 3, # Paper x Paper -> Draw
"B Z": 3 + 6, # Paper x Scissors -> Win
"C X": 1 + 6, # Scissors x Rock -> Win
"C Y": 2 + 0, # Scissors x Paper -> Lose
"C Z": 3 + 3, # Scissors x Scissors -> Draw
}
def solve_part_one(self) -> int:
return sum(map(self.p1_scores.get, self.rounds))
Testing it with the sample input produces the correct answer 15.
Part Two
Part two changes the interpretation of the input slightly. The first letter—A, B, or C— still represents our opponents throws, but now the second letter—X, Y, or Z—represents the outcome of the round—lose, draw, or win, respectively.
Our solution will be the same, but the lookup table will change to reflect the difference in interpretation.
class RockPaperScissors(Solver):
...
p2_scores = {
"A X": 3 + 0, # Rock x Lose -> Scissors
"A Y": 1 + 3, # Rock x Draw -> Rock
"A Z": 2 + 6, # Rock x Win -> Paper
"B X": 1 + 0, # Paper x Lose -> Rock
"B Y": 2 + 3, # Paper x Draw -> Paper
"B Z": 3 + 6, # Paper x Win -> Scissors
"C X": 2 + 0, # Scissors x Lose -> Paper
"C Y": 3 + 3, # Scissors x Draw -> Scissors
"C Z": 1 + 6, # Scissors x Win -> Rock
}
def solve_part_two(self) -> int:
return sum(map(self.p2_scores.get, self.rounds))
Testing it with the sample input produces the correct answer 12.
You can try this solution in your browser with Pyodide.