Post

# Advent of Code 2022, Day 2 - Part Two

## 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

RockPaperScissors
ABC

### Conversion table for expected match outcomes

X means […] lose, Y means […] draw, and Z means […] win

LostDrawWon
XYZ

### 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 points3 points6 points
Rock - PaperRock - RockRock - Scissors
Paper - ScissorsPaper - PaperPaper - Rock
Scissors - RockScissors - ScissorsScissors - 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

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) } } ```