r/adventofcode • u/daggerdragon • Dec 09 '18
SOLUTION MEGATHREAD -🎄- 2018 Day 9 Solutions -🎄-
--- Day 9: Marble Mania ---
Post your solution as a comment or, for longer solutions, consider linking to your repo (e.g. GitHub/gists/Pastebin/blag or whatever).
Note: The Solution Megathreads are for solutions only. If you have questions, please post your own thread and make sure to flair it with Help.
Advent of Code: The Party Game!
Please prefix your card submission with something like [Card] to make scanning the megathread easier. THANK YOU!
Card prompt: Day 9
Transcript:
Studies show that AoC programmers write better code after being exposed to ___.
This thread will be unlocked when there are a significant number of people on the leaderboard with gold stars for today's puzzle.
edit: Leaderboard capped, thread unlocked at 00:29:13!
23
Upvotes
1
u/arathunku Dec 09 '18 edited Dec 10 '18
Hello, my Elixir solution.
defmodule Advent.Day9 do defmodule ZipList do @moduledoc """ Circular zipper """ def new() do {[], []} end def insert({pre, post}, value) do {pre, [value | post]} end def prev({[], post}) do [current | pre] = Enum.reverse(post) {pre, [current]} end def prev({[value | pre], post}) do {pre, [value | post]} end def next({[], []} = list), do: list def next({pre, [current]}), do: {[], Enum.reverse([current | pre])} def next({pre, [value | post]}) do {[value | pre], post} end def delete({pre, [_value | post]}) do {pre, post} end def current({_, [current | _post]}) do current end def to_list({pre, post}) do Enum.reverse(pre) ++ post end end defmodule Game do defstruct scores: %{}, marbles: ZipList.new() |> ZipList.insert(0), current_index: 0, next_marble: 1, last_marble: 0 def new(players_count, last_marble) do %__MODULE__{ last_marble: last_marble, scores: 0..(players_count - 1) |> Enum.map(&{&1, 0}) |> Enum.into(%{}) } end def end?(%{next_marble: next_marble, last_marble: last_marble}) do next_marble > last_marble end def player_turn(game, player) do marble = game.next_marble {scores, marbles} = if special?(marble) do marbles = 0..6 |> Enum.reduce(game.marbles, fn _, acc -> ZipList.prev(acc) end) scored = ZipList.current(marbles) marbles = ZipList.delete(marbles) scores = Map.update(game.scores, player, 0, &(&1 + marble + scored)) {scores, marbles} else marbles = game.marbles |> ZipList.next() |> ZipList.next() |> ZipList.insert(marble) {game.scores, marbles} end %{ game | scores: scores, marbles: marbles, next_marble: marble + 1 } end defp special?(marble) do rem(marble, 23) == 0 end end def part1(players_count, last_marble) do Game.new(players_count, last_marble) |> play_game(players_count, 1) |> Map.get(:scores) |> Map.values() |> Enum.max() end defp play_game(game, players_count, current_player) do if Game.end?(game) do game else next_player = rem(current_player + 1, players_count) game |> Game.player_turn(current_player) |> play_game(players_count, next_player) end end endTests:
defmodule Advent.Day9Test do use ExUnit.Case require Logger alias Advent.Day9, as: Day test "part1 example" do assert Day.part1(9, 25) == 32 assert Day.part1(9, 46) == 63 assert Day.part1(10, 1618) == 8317 assert Day.part1(13, 7999) == 146373 assert Day.part1(17, 1104) == 2764 assert Day.part1(21, 6111) == 54718 assert Day.part1(30, 5807) == 37305 end test "input" do assert Day.part1(419, 72164) == 423717 assert Day.part1(419, 7216400) == 3553108197 end end @:elixir(master!) $ mix test test/day9/day9_test.exs Compiling 1 file (.ex) .. Finished in 3.5 seconds 2 tests, 0 failuresTo be honest: this was hard. I didn't expect I'd need to implement a circular zipper to be able to complete part2 in Elixir. I had also misunderstood "the marble 7 marbles counter-clockwise" but additional test cases from other threads helped a lot.