Bart Kessels
Bart Kessels
Passionate open source software engineer who loves to go backpacking

Advent of Code 2022, Day two - Part One

Advent of Code 2022, Day two - Part One
This image is generated using Dall-E
  • Prompt: 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 two conversion tables. One for converting each strategy 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
    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

    Now we know how our data is structured and we gave it a meaning. We can start to think about our business logic. The steps we need to take is that we need to get the points for each match and combine them with the points for each strategy we played.

    the score for the shape you selected […] 1 for Rock, 2 for Paper, and 3 for Scissors

    So for the sample input we get the following diagram.

    flowchart LR
      subgraph matchone["Match 1"]
        direction LR
    
        subgraph strategyone["Strategy"]
          direction TB
    
          ao["opponent: <b>Rock</b>"]
          ay["you: <b>Paper</b>\n<i>2 strategy points</i>"]
    
          ao ~~~ ay
        end
    
        outone["6 points"]
        strategyone -- "Play match" --> outone
    
        totalone["Total <b>8 point</b>"]
        outone -- "Add <b>2</b> strategy point" --> totalone
      end
    
      subgraph matchtwo["Match 2"]
        direction LR
    
        subgraph strategytwo["Strategy"]
          direction TB
          bo["opponent: <b>Paper</b>"]
          by["you: <b>Rock</b>\n<i>1 strategy points</i>"]
    
          bo ~~~ by
        end
    
        outtwo["0 points"]
        strategytwo -- "Play match" --> outtwo
    
        totaltwo["Total <b>1 points</b>"]
        outtwo -- "Add <b>1</b> strategy points" --> totaltwo
      end
    
      subgraph matchthree["Match 3"]
        direction LR
        
        subgraph strategythree["Strategy"]
          direction TB
          co["opponent: <b>Scissors</b>"]
          cy["you: <b>Scissors</b>\n<i>3 strategy points</i>"]
          
          co ~~~ cy
        end
    
        outthree["3 points"]
        strategythree -- "Play match" --> outthree
        
        totalthree["Total <b>6 points</b>"]
        outthree -- "Add <b>3</b> strategy points" --> totalthree
      end
    
      sum["Sum()"]
      total["Total: <b>15 points</b>"]
    
      matchone --> sum
      matchtwo --> sum
      matchthree --> 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
    
    class PartOne(
        private val sanitizer: Sanitizer
    ) {
        fun getResult(): Int {
            val data = sanitizer.getItems()
            val points = data?.map {
                val strategyPoints = when(it.second) { // 1
                    "X" -> 1    // Rock
                    "Y" -> 2    // Paper
                    else -> 3   // Scissors
                }
                val roundOutcome = when(it) { // 2
                    // Lost
                    Pair("A", "Z"),
                    Pair("B", "X"),
                    Pair("C", "Y") -> 0
    
                    // Draw
                    Pair("A", "X"),
                    Pair("B", "Y"),
                    Pair("C", "Z") -> 3
    
                    // Won
                    else -> 6
                }
    
                strategyPoints + roundOutcome
            }
    
            val totalRoundsOutcome = points?.sum()
    
            return totalRoundsOutcome ?: -1;
        }
    }
    

    What our code does, is, it turns our matches list into a list of round outcomes. At step 1 the strategy points are calculated based on the information we’ve gotten from the assignment. We only check for our own moves, because we don’t get points for the move the opponent made.

    Once we’ve gotten the score from our strategy, we calculate the points based on the round outcome. At step 2 we only check for winnings and a draw. This can be cleaned up by splitting it into a seperate method or normalizing the data into Rock, Paper and Scissors instead of the current A, B, X etc.

    So this will give us the following data structure.

    1
    2
    3
    4
    5
    
    [
      8,
      1,
      6
    ]
    

    This list is finally summed up and return as the assignment outcome.

    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 15.

    So we can write a test case that validates our test input to the outcome of 15. Right now we can update the PartOneTest class with the following contents.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    class PartOneTest {
        @Test
        fun testGetResult() {
            // Arrange
            val resource = {}::class.java.getResource("/input.txt")
            val sanitizer = Sanitizer(resource)
            val sut = PartOne(sanitizer)
            val expectedNumberOfPoints = 15
    
            // Act
            val result = sut.getResult()
    
            // Assert
            assertEquals(expectedNumberOfPoints, result)
        }
    }
    

    Categories

    Related articles

    Advent of Code 2022, Day two - Sanitizer

    Turn the input into a list of rock-paper-scissor games.

    Advent of Code 2022, Day two - Part Two

    Calculate the strategy we need to play to get the expected outcome based on our opponents move.