Advent of Code 2022, Day two - Part Two
This image is generated using Dall-EPrompt: Generate an image of two elves playing rock paper scissors in minimalistic flat style
Preface
Now that we have the following data structure from our previous post, we can start by implementing the business logic to calculate the outcome of each match. Before we can do that, we need to setup three conversion tables. One for converting each strategy, one for each expected outcome and another one to convert each match outcome to points.
1
2
3
4
5
[
{ "A", "Y" }, // Match 1
{ "B", "X" }, // Match 2
{ "C", "Z" } // Match 3
]
Conversion table for strategies
A for Rock, B for Paper, and C for Scissors […] X for Rock, Y for Paper, and Z for Scissors
Rock | Paper | Scissors |
---|---|---|
A | B | C |
Conversion table for expected match outcomes
X means […] lose, Y means […] draw, and Z means […] win
Lost | Draw | Won |
---|---|---|
X | Y | Z |
Conversion table for the match outcome
score for the outcome of the round […] 0 if you lost, 3 if the round was a draw, and 6 if you won
0 points | 3 points | 6 points |
---|---|---|
Rock - Paper | Rock - Rock | Rock - Scissors |
Paper - Scissors | Paper - Paper | Paper - Rock |
Scissors - Rock | Scissors - Scissors | Scissors - Paper |
Design
So now that we know what our opponents strategy is going to be, and what the expected outcome is going to be we need to think about how we can get our move based on the expected outcome.
So we need to calculate which move we need to make to get our desired outcome. So if the opponent chooses Rock, our move is going to be Paper. And based on that outcome we can then play the match to get the points as described in the previous table and the move points we’ve been given:
the score for the shape you selected […] 1 for Rock, 2 for Paper, and 3 for Scissors
So with the given sample data, and the conversion tables we get the following diagram.
flowchart LR
subgraph matchone["Match one"]
direction LR
subgraph m1Strategy["Calculate strategy"]
direction LR
m1opponent["opponent move: <b>Rock</b>"]
m1Outcome["Expected outcome <b>Draw</b>"]
m1GetMove["GetMove()"]
m1OwnMove["Own move: <b>Rock</b>"]
m1opponent --> m1GetMove
m1Outcome --> m1GetMove
m1GetMove --> m1OwnMove
end
m1PlayMatch["PlayMatch()"]
m1Points["3 points"]
m1TotalPoints["Total <b>4 points</b>"]
m1opponent --> m1PlayMatch
m1OwnMove --> m1PlayMatch
m1PlayMatch --> m1Points
m1Points -- "Add 1 strategy point" --> m1TotalPoints
end
subgraph matchtwo["Match two"]
direction LR
subgraph m2Strategy["Calculate strategy"]
direction LR
m2opponent["opponent move: <b>Paper</b>"]
m2Outcome["Expected outcome <b>Lost</b>"]
m2GetMove["GetMove()"]
m2OwnMove["Own move: <b>Rock</b>"]
m2opponent --> m2GetMove
m2Outcome --> m2GetMove
m2GetMove --> m2OwnMove
end
m2PlayMatch["PlayMatch()"]
m2Points["0 points"]
m2TotalPoints["Total <b>1 points</b>"]
m2opponent --> m2PlayMatch
m2OwnMove --> m2PlayMatch
m2PlayMatch --> m2Points
m2Points -- "Add 1 strategy point" --> m2TotalPoints
end
subgraph matchthree["Match three"]
direction LR
subgraph m3Strategy["Calculate strategy"]
direction LR
m3opponent["opponent move: <b>Scissors</b>"]
m3Outcome["Expected outcome <b>Won</b>"]
m3GetMove["GetMove()"]
m3OwnMove["Own move: <b>Rock</b>"]
m3opponent --> m3GetMove
m3Outcome --> m3GetMove
m3GetMove --> m3OwnMove
end
m3PlayMatch["PlayMatch()"]
m3Points["6 points"]
m3TotalPoints["Total <b>7 points</b>"]
m3opponent --> m3PlayMatch
m3OwnMove --> m3PlayMatch
m3PlayMatch --> m3Points
m3Points -- "Add 1 strategy point" --> m3TotalPoints
end
sum["Sum()"]
total["Total: <b>12 points</b>"]
m1TotalPoints --> sum
m2TotalPoints --> sum
m3TotalPoints --> sum
sum --> total
Once all the matches have been played, and the points calculated we can add all outcomes togheter for our end result.
Implementation
Business logic
Now we know what we want our code to do, let’s start implementing it in our PartOne class.
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
class PartTwo(
private val sanitizer: Sanitizer
) {
fun getResult(): Int {
val data = sanitizer.getItems()
val points = data?.map {
val strategyPoints = when(getMove(it.first, it.second)) {
"A" -> 1 // Rock
"B" -> 2 // Paper
else -> 3 // Scissors
}
val roundOutcome = when(it.second) {
"X" -> 0 // Lost
"Y" -> 3 // Draw
else -> 6 // Won
}
strategyPoints + roundOutcome
}
val totalRoundsOutcome = points?.sum()
return totalRoundsOutcome ?: -1;
}
/**
* Get the expected move based on the expected outcome and the opponents move
*
* @param opponentMove the move the opponent is going to make
* @param expectedOutcome the outcome that is expected
* @return our move
*/
private fun getMove(opponentMove: String, expectedOutcome: String): String =
when(expectedOutcome) {
"X" -> when(opponentMove) { // Lost
"A" -> "C" // Opponent: Rock, own: Scissors
"B" -> "A" // Opponent: Paper, own: Rock
else -> "B" // Opponent: Scissors, own: Paper
} // Won
"Y" -> opponentMove // Draw
else -> when(opponentMove) { // Won
"A" -> "B" // Opponent: Rock, own: Paper
"B" -> "C" // Opponent: Paper, own: Scissors
else -> "A" // Opponent: Scissors, own: Rock
}
}
}
Test case
Because we know that we have a list of all round outcomes, we know that we can sum each item in the list to get the total score. As you can see in our previous diagram, the total score of the sample input will be 12.
So we can write a test case that validates our test input to the outcome of 12. Right now we can update the PartTwoTest
class with the following contents.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class PartTwoTest {
@Test
fun testGetResult() {
// Arrange
val resource = {}::class.java.getResource("/input.txt")
val sanitizer = Sanitizer(resource)
val sut = PartTwo(sanitizer)
val expectedNumberOfPoints = 12
// Act
val result = sut.getResult()
// Assert
assertEquals(expectedNumberOfPoints, result)
}
}
Categories
Related articles