[Advent Of Code 2022] Day 02 Discussion [SPOILERS]

https://adventofcode.com/2022/day/1

Boy, what a simple problem. And yet those details got complicated! I had to reread the values over and over! I did manage to solve part 1 in the top 1,000, so that’s nice. And I got a one-liner for part 2.

Solution Code
LOSE, DRAW, WIN = "XYZ"
POINTS = {LOSE: 0, DRAW: 3, WIN: 6}

def part1(self, parsed_input: InputType) -> int:
    """Score a tournament with the columns representing the choices."""
    score = 0
    for elf_move, my_move in parsed_input:
        pos_a = "ABC".index(elf_move)
        pos_b = "XYZ".index(my_move)
        score += pos_b + 1
        if pos_a == pos_b:
            score += POINTS[DRAW]
        elif pos_b == (pos_a + 1) % 3:
            score += POINTS[WIN]
    return score


def part2(self, parsed_input: InputType) -> int:
    """Score a tournament with the second column representing the outcome."""
    score = 0
    for elf_move, outcome in parsed_input:
        pos_a = "ABC".index(elf_move)
        score += POINTS[outcome]
        # The outcome determines if my move is the object before/after/same as the elf's.
        shift = {DRAW: 0, WIN: 1, LOSE: 2}[outcome]
        score += ((pos_a + shift) % 3) + 1
    return score


def part2_short(self, parsed_input: InputType) -> int:
    return sum(
        POINTS[outcome] + ("ABC".index(elf_move) + {DRAW: 0, WIN: 1, LOSE: 2}[outcome]) % 3
        for elf_move, outcome in parsed_input
    ) + len(parsed_input)
2 Likes

This was an interesting one indeed. Like you say, simple on the surface, but with a lot of little details that prompted a lot of decisions. For that reason, I expect to see very different approaches to this one.

I ended up creating a lot of mappings, enums and types in general to represent the data. It was fun, I learned things!

Since the solution ended up bigger than I expected, I’ll just link the file instead of posting the code here: day02.ts

1 Like

I read the text from today and had to bike for 20 minutes. During the ride I was pondering if it was of any benefit to make some enums or convert the strings to atoms. In the end I think the direct string comparison is still pretty readable. So I left it at that and had 9 guarded function calls. On reddit I have seen some nice modulo solutions though…

Elixir Code Solution
defmodule AdventOfCode.Day02 do
  def part1(args) do
    convertToLine(args)
    |> Enum.map(&getScore/1)
    |> Enum.sum()
  end

  def part2(args) do
    convertToLine(args)
    |> Enum.map(&getScore2/1)
    |> Enum.sum()
  end

  defp convertToLine(str),
    do:
      str
      |> String.trim()
      |> String.split("\n")

  defp getScore("A Y"), do: 2 + 6
  defp getScore("B X"), do: 1 + 0
  defp getScore("C Z"), do: 3 + 3

  defp getScore("B Y"), do: 2 + 3
  defp getScore("C X"), do: 1 + 6
  defp getScore("A Z"), do: 3 + 0

  defp getScore("C Y"), do: 2 + 0
  defp getScore("A X"), do: 1 + 3
  defp getScore("B Z"), do: 3 + 6

  defp getScore(_else), do: "Error"


  defp getScore2("A Y"), do: 3 + 1
  defp getScore2("B X"), do: 0 + 1
  defp getScore2("C Z"), do: 6 + 1

  defp getScore2("B Y"), do: 3 + 2
  defp getScore2("C X"), do: 0 + 2
  defp getScore2("A Z"), do: 6 + 2

  defp getScore2("C Y"), do: 3 + 3
  defp getScore2("A X"), do: 0 + 3
  defp getScore2("B Z"), do: 6 + 3

  defp getScore2(_else), do: "Error"
end
1 Like

Saw some solutions using that strategy too and I really like it. Simplifies the problem massively without any major drawbacks really.

I started in Typescript to try out Deno and quickly ended up with a confusing amount of mappings by trying to be clever. I flipped back to Julia to start over from scratch, and I’m still suspicious that there’s a clever trick hidden in plain sight since A B C and Y X Z as opposite ends of the alphabet were unlikely chosen arbitrarily.

Julia Solution
module day02

    #@enum Game Loss=0 Draw=3 Win=6
    #@enum Move Rock=1 Paper=2 Scissors=3

    LOSS = 0
    DRAW = 3
    WIN = 6

    ROCK = 1
    PAPER = 2
    SCISSORS = 3

    SCORES_ROUND1 = Dict(
        "A X" => (DRAW + ROCK),
        "A Y" => (WIN + PAPER),
        "A Z" => (LOSS  + SCISSORS),
        "B X" => (LOSS  + ROCK),
        "B Y" => (DRAW + PAPER),
        "B Z" => (WIN + SCISSORS),
        "C X" => (WIN + ROCK),
        "C Y" => (LOSS + PAPER),
        "C Z" => (DRAW + SCISSORS),
    )

    SCORES_ROUND2 = Dict(
        "A X" => (LOSS + SCISSORS),
        "A Y" => (DRAW + ROCK),
        "A Z" => (WIN  + PAPER),
        "B X" => (LOSS  + ROCK),
        "B Y" => (DRAW + PAPER),
        "B Z" => (WIN + SCISSORS),
        "C X" => (LOSS + PAPER),
        "C Y" => (DRAW + SCISSORS),
        "C Z" => (WIN + ROCK),
    )

    function part01()
        score = 0
        for l in eachline("/Users/anagy/Documents/AdventOfCode2022/AdventOfCode2022-Julia/input/02/input.in")
            if length(l) != 0
                #a, b = split(l)
                score += SCORES_ROUND1[l]
            end
        end
        score
    end

    function part02()
        score = 0
        for l in eachline("/Users/anagy/Documents/AdventOfCode2022/AdventOfCode2022-Julia/input/02/input.in")
            if length(l) != 0
                score += SCORES_ROUND2[l]
            end
        end
        score
    end
end

println("P01: $(day02.part01())")
println("P02: $(day02.part02())")
1 Like

I haven’t seen any clever tricks yet, but string.index() and modulus arithmetic makes this puzzle relatively simple (as per my solution).

I just realized for part 2, I could use index * 3 for the points, too…

Map-free Part 2 Solution
def part2_readable(self, parsed_input: InputType) -> int:
    score = 0
    for elf_move, outcome in parsed_input:
        pos_a = "ABC".index(elf_move)
        pos_outcome = "XYZ".index(outcome)
        score += 3 * pos_outcome
        score += ((pos_a + pos_outcome + 2) % 3) + 1
    return score

def part2_short(self, parsed_input: InputType) -> int:
    return sum(
        "XYZ".index(outcome) * 3 + ("ABC".index(elf_move) + "XYZ".index(outcome) + 2) % 3
        for elf_move, outcome in parsed_input
    ) + len(parsed_input)
1 Like

I think just having a map listing the 9 possible cases for each part is pretty clever. Usually, in programming by clever we also implicitly mean “not clear”, but this is one of few instances where the clever solution might actually be the clearer one.

I think I’ll keep the mappings and the over-engineering of my solution though, it was a great learning experience and I’m proud of my mistakes learning experiences. :smiley:

I also noticed this! Just shows the amount of attention to detail that goes into creating these puzzles. It’s a greater enabler of different solutions. Another pattern a few puzzles from previous years have is to have some input that when converted to binary gives you a “suspiciously convenient” representation for which you can then apply bitwise operations to solve the puzzle instead of the “normal way”.

1 Like
Ruby Solution
score_a = %w[nil BX CY AZ AX BY CZ CX AY BZ]
score_b = %w[nil BX CX AX AY BY CY CZ AZ BZ]

instructions = File.readlines('inputs/02.txt', chomp: true).map { |instruction| instruction.delete(' ') }

a = instructions.sum { |instruction| score_a.index(instruction) }
b = instructions.sum { |instruction| score_b.index(instruction) }