Skip to content
Threadsnappers

Day 2 - Rock Paper Scissors ✂️

In this post I’ll be explaining my day 2 solution to Advent of Code 2022. First things first, lets read the input! Once again I’ll be using the readInput()method provided in the AoC Kotlin template by JetBrains.

val input = readInput("Day02")

Part 1

Analyzing the data

The meaning of the input is completely different in part 1 and part 2 which is why I’m not explaining the input before solving each part.

A Y
B X
C Z

The input is an encrypted strategy guide where the first column is the move made by your opponent and second column has the moves you should do.

A -> rock     <- X (1 point)
B -> paper    <- Y (2 points)
C -> scissors <- Z (3 points)

A, B and C represent your opponent’s moves and X, Y and Z show your moves. Each move you make has a score associated with it as shown above. Apart from that, you’ll get extra points for a win (6 points) and draw (3 points).

For example in the first round, the opponent chooses Rock and you play Paper. Since you played Paper, you get 2 points. Paper beats Rock which means you won the round, giving 6 points for a total of 8.

fold() -ing the data

Instead of breaking the solution part by part, the whole process can be finished in just one operation on the input using the convenient fold() function in the Collections API.

val part1 = input.fold(0) { sum, l ->
    sum + ...
}

The initial value of sum is set to 0 and l represents each line in input. From here on we’ll be using ASCII to help with the code logic.

Computing the result

l[0] represents the first character which is the opponent’s move (A, B or C). Likewise l[2] represents your move (X, Y or Z). We can assign number 0 for Rock, 1 for Paper and 2 for Scissors. l[2] - 'X' and l[0] - 'A' use ASCII codes to convert the input to this form.

If the number of the opponent’s move and your move turns out to be the same, the round ends in a draw giving 3 points.

Now that we know who makes which move, there are three possible scenarios - win, loss or draw. There are three ways to win - Paper (1) beats Rock (0), Scissors (2) beats Paper (1) and Rock (0) beats Scissors(2). Whenever we win, the difference between your move and the opponent’s is 1 or -2. This gives 6 points.

Finally, if the difference between the codes turns out to be anything else, the round ends in a loss giving 0 points.

when ((l[2] - 'X') - (l[0] - 'A')) {
    0 -> 3
    1, -2 -> 6
    else -> 0
}

The only thing left is to add the points of our move as per the scheme. X gives one point, Y gives 2 and Z gives three. An easy way to get that number would be to subtract the ASCII of 'W' from l[2].

(l[2] - 'W')

Adding these to sum we reach the solution of part 1.

Solution

val part1 = input.fold(0) { sum, l ->
    sum + when ((l[2] - 'X') - (l[0] - 'A')) {
        0 -> 3
        1, -2 -> 6
        else -> 0
    } + (l[2] - 'W')
}

Part 2

Analyzing the data

In part 2 we come to know that the second column of the strategy guide encodes the outcome of a round where X means loss, Y - draw and Z - win.

A Y     # rival - rock, result - draw => you - rock
B X     # rival - paper, result - loss => you - rock 
C Z     # rival - scissors, result - win => you - rock

The scoring system is same as before. The real challenge here is mapping the choice you need to make for achieving the outcome. Once again fold() can be used to make the code concise.

Finding the result

First we store the result of the round to a variable res where 0 represents loss, 1 means draw and 2 signifies a win.

val res = l[2] - 'X'

res * 3 gives the score of the result of the round.

Deciding your move

Using the number codes for rock paper and scissors from computing the result section of part 1 we can find the opponent’s move which will decide yours. l[0] - 'A' returns the opponent’s move in the number code.

Loss

When the result is a loss there are three possibilities:

Rival - Rock (0)    =>  You - Scissors (3 points)
Rival - Paper (1)   =>  You - Rock (1 point)
Rival - Scissor (2) =>  You - Paper (2 points)

Looking at this we can say that when the code is 0 (Rock), you get 3 points. Otherwise the score is same as the number code of the opponent’s move. This can be reduced to:

if (l[0] != 'A') l[0] - 'A' else 3

Draw

A draw means the score is the same as the score of the opponent’s move which is code + 1. For example when the opponent chooses Rock (0) to make it a draw you choose Rock too which gives 1 point.

l[0] - 'A' + 1

Win

There are three ways you can win:

Rival - Rock (0)    =>  You - Paper (2 points)
Rival - Paper (1)   =>  You - Scissors (3 point)
Rival - Scissor (2) =>  You - Rock (1 points)

As long as the opponent doesn’t choose Scissors (2), the score from the will be code + 2. When the opponent’s choice is Scissors (0) the score becomes 1.

if (l[0] != 'C') l[0] - 'A' + 2 else 1

Summing it up

Wrapping the above conditionals inside when we get:

when (res) {
    0 -> if (l[0] != 'A') l[0] - 'A' else 3
    1 -> l[0] - 'A' + 1
    2 -> if (l[0] != 'C') l[0] - 'A' + 2 else 1
    else -> 0
}

The else block is never executed because all the possibilities have been exhausted. It is only to ensure that the compiler detects that it always returns an Int.

Now that we have all the elements needed to compute the score, all that remains is to fold() the data once again to get the total score.

Solution

 val part2 = input.fold(0) { sum, l ->
    val res = l[2] - 'X'
    sum + res * 3 + when (res) {
        0 -> if (l[0] != 'A') l[0] - 'A' else 3
        1 -> l[0] - 'A' + 1
        2 -> if (l[0] != 'C') l[0] - 'A' + 2 else 1
        else -> 0
    }
}

Congrats on completing day 2 in Kotlin!

Full Solution

fun main() {
    val input = readInput("Day02")
    val part1 = input.fold(0) { sum, l ->
        sum + when ((l[2] - 'X') - (l[0] - 'A')) {
            0 -> 3
            1, -2 -> 6
            else -> 0
        } + (l[2] - 'W')
    }
    val part2 = input.fold(0) { sum, l ->
        val res = l[2] - 'X'
        sum + res * 3 + when (res) {
            0 -> if (l[0] != 'A') l[0] - 'A' else 3
            1 -> l[0] - 'A' + 1
            2 -> if (l[0] != 'C') l[0] - 'A' + 2 else 1
            else -> 0
        }
    }
    part1.println()
    part2.println()
}

Open in Playground GitHub