Add solutions from AOC 2016-2020

This commit is contained in:
Anders Ytterström 2021-11-01 16:40:46 +01:00
parent d5a29d41e2
commit 7daf307f21
205 changed files with 10423 additions and 0 deletions

View file

@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

View file

@ -0,0 +1 @@
elixir 1.11.1

29
2015-elixir/README.md Normal file
View file

@ -0,0 +1,29 @@
# Aoc
Solutions for [Advent of Code 2020][1], this year in Elixir.
[Elixir 1.11.1][2] preferred.
## Help scripts
Run tests using `mix test` as usual.
Create input file, test file and solution file for a day,
using day 1 as example:
mix Aoc.new 1 "Name of the puzzle"
Solve a single puzzle, using puzzle from day 3 as example:
mix Aoc.solve 3
Solve all puzzles, starting at the first:
mix Aoc.solve_all
## Log
_Advent of Code has not started yet. See you in December!_
[1]: https://adventofcode.com/
[2]: https://hexdocs.pm/elixir/Kernel.html

61
2015-elixir/lib/aoc.ex Normal file
View file

@ -0,0 +1,61 @@
defmodule Aoc do
@moduledoc """
Solutions for Advent of Code 2020, written in Elixir 1.11.1.
"""
def solve_all() do
"""
ADVENT OF CODE 2020
===================
"""
|> IO.puts()
1..25 |> Enum.map(&solve/1)
end
def solve(n) do
id = n |> Integer.to_string() |> String.pad_leading(2, "0")
case File.read("./inputs/" <> id <> ".in") do
{:error, _} ->
nil
{:ok, input} ->
input
|> run(id)
|> present
|> IO.puts()
end
end
def run(input, id) do
solution_module = Module.concat(["Aoc.Solution", "Day" <> id])
data = apply(solution_module, :parse!, [input])
name = apply(solution_module, :get_name, [])
first_solution = apply(solution_module, :solve_first_part, [data])
second_solution = apply(solution_module, :solve_second_part, [data])
[name, first_solution, second_solution]
end
@doc """
Generates a CLI-friendly presentation of the solutions.
## Examples
iex> Aoc.present(["Day 1: tomten kommer", "10", "20"])
"Day 1: tomten kommer\\n--------------------\\n\\n1. 10\\n2. 20\\n"
"""
def present([name, first_solution, second_solution]) do
"""
#{name}
#{1..String.length(name) |> Enum.map_join(fn _x -> "-" end)}
1. #{first_solution}
2. #{second_solution}
"""
end
end

View file

@ -0,0 +1,89 @@
defmodule Mix.Tasks.Aoc.New do
use Mix.Task
@shortdoc "Bootstrap new solution"
@impl Mix.Task
def run([n, name]) do
id = n |> String.pad_leading(2, "0")
input_file = "./inputs/" <> id <> ".in"
test_file = "./test/day_" <> id <> "_test.exs"
solution_file = "./lib/solutions/day_" <> id <> ".ex"
IO.puts("Creating " <> input_file)
File.touch(input_file)
IO.puts("Creating " <> test_file)
File.write!(test_file, test_file_content(id))
IO.puts("Creating " <> solution_file)
File.write!(solution_file, solution_file_content(name, id))
"""
\nDone! Start coding.
Get your puzzle input here:
https://adventofcode.com/2015/day/#{id}/input
mix test -- run tests.
mix Aoc.solve_all -- run all puzzles, starting with 1
mix Aoc.solve #{id} -- run single puzzle, 1-25
"""
|> IO.puts()
end
defp test_file_content(id) do
"""
defmodule Day#{id}Test do
use ExUnit.Case
doctest Aoc.Solution.Day#{id}
import Aoc.Solution.Day#{id}
test "parses the input" do
expected = 10
assert parse!("10") == expected
end
test "solves first part" do
a = "something" |> parse!() |> solve_first_part()
expect a == :something
end
test "solves second part" do
a = "something" |> parse!() |> solve_second_part()
expect a == :something
end
end
"""
end
defp solution_file_content(name, id) do
~s"""
defmodule Aoc.Solution.Day#{id} do
@name "#{name}"
@behaviour Solution
@impl Solution
def get_name, do: @name
@impl Solution
def parse!(_raw) do
"10"
end
@impl Solution
def solve_first_part(_input) do
"(TBW)"
end
@impl Solution
def solve_second_part(_input) do
"(TBW)"
end
end
"""
end
end

View file

@ -0,0 +1,11 @@
defmodule Mix.Tasks.Aoc.Solve do
use Mix.Task
@shortdoc "Solve single puzzle"
@impl Mix.Task
def run([id]) do
id
|> String.to_integer()
|> Aoc.solve()
end
end

View file

@ -0,0 +1,9 @@
defmodule Mix.Tasks.Aoc.SolveAll do
use Mix.Task
@shortdoc "Solve all puzzles"
@impl Mix.Task
def run(_) do
Aoc.solve_all()
end
end

View file

@ -0,0 +1,6 @@
defmodule Solution do
@callback parse!(String.t()) :: Any
@callback get_name() :: String.t()
@callback solve_first_part(Any) :: String.t()
@callback solve_second_part(Any) :: String.t()
end

View file

@ -0,0 +1,57 @@
defmodule Aoc.Solution.Day01 do
@name "Day 1: Not Quite Lisp"
@behaviour Solution
@basement -1
@impl Solution
def get_name, do: @name
@impl Solution
def parse!(raw) do
raw
|> String.split("", trim: true)
end
@impl Solution
def solve_first_part(input) do
input
|> Enum.map(fn d ->
case d do
"(" -> 1
")" -> -1
_ -> 0
end
end)
|> Enum.sum()
end
@impl Solution
def solve_second_part(input) do
[change | changes] =
input
|> Enum.map(fn d ->
case d do
"(" -> 1
")" -> -1
_ -> 0
end
end)
move(change, 1, 0, changes)
end
defp move(change, pos, current, []) do
case current + change do
@basement -> pos
_ -> :basement_never_reached
end
end
defp move(change, pos, current, [next | changes]) do
case current + change do
@basement -> pos
_ -> move(next, pos + 1, current + change, changes)
end
end
end

View file

@ -0,0 +1,42 @@
defmodule Aoc.Solution.Day02 do
@name "I Was Told There Would Be No Math"
@behaviour Solution
@impl Solution
def get_name, do: @name
@impl Solution
def parse!(raw) do
raw
|> String.split()
|> Enum.map(fn p -> p |> String.split("x") |> Enum.map(&String.to_integer/1) end)
end
@impl Solution
def solve_first_part(input) do
input
|> Enum.map(fn p -> area(p) end)
|> Enum.sum()
end
@impl Solution
def solve_second_part(input) do
input
|> Enum.map(fn p -> ribbon(p) end)
|> Enum.sum()
end
defp area(p) do
[l, w, h] = p
extra = Enum.min([l * w, w * h, h * l])
2 * l * w + 2 * w * h + 2 * h * l + extra
end
defp ribbon(p) do
[l, w, h] = p
[x, y | _] = Enum.sort(p)
ribbon = 2 * x + 2 * y
bow = l * w * h
bow + ribbon
end
end

View file

@ -0,0 +1,53 @@
defmodule Aoc.Solution.Day03 do
@name "Perfectly Spherical Houses in a Vacuum"
@behaviour Solution
@impl Solution
def get_name, do: @name
@impl Solution
def parse!(raw) do
String.split(raw, "", trim: true)
end
@impl Solution
def solve_first_part(input) do
input |> deliver() |> MapSet.size()
end
@impl Solution
def solve_second_part(input) do
santa = Enum.take_every(input, 2) |> deliver()
robosanta =
input
|> Enum.with_index()
|> Enum.reject(fn {_k, v} -> rem(v, 2) == 0 end)
|> Enum.map(fn {k, _v} -> k end)
|> deliver()
MapSet.union(santa, robosanta) |> MapSet.size()
end
defp move("<", {x, y}), do: {x - 1, y}
defp move(">", {x, y}), do: {x + 1, y}
defp move("v", {x, y}), do: {x, y + 1}
defp move("^", {x, y}), do: {x, y - 1}
defp deliver(input) do
{_, seen} =
input
|> Enum.reduce(
{{0, 0}, MapSet.new([{0, 0}])},
fn d, {pos, seen} ->
next = move(d, pos)
{next, MapSet.put(seen, next)}
end
)
seen
end
end

View file

@ -0,0 +1,35 @@
defmodule Aoc.Solution.Day04 do
@name "Day 4: The Ideal Stocking Stuffer"
@behaviour Solution
@impl Solution
def get_name, do: @name
@impl Solution
def parse!(raw) do
String.trim(raw)
end
@impl Solution
def solve_first_part(input) do
find_number(input, 0, "00000")
end
@impl Solution
def solve_second_part(input) do
find_number(input, 0, "000000")
end
defp find_number(a, b, prefix) do
case valid_checksum?(a, b, prefix) do
true -> b
false -> find_number(a, b + 1, prefix)
end
end
defp valid_checksum?(a, b, prefix) do
:crypto.hash(:md5, "#{a}#{b}")
|> Base.encode16()
|> String.starts_with?(prefix)
end
end

View file

@ -0,0 +1,73 @@
defmodule Aoc.Solution.Day05 do
@name "Day 5: Doesn't He Have Intern-Elves For This?"
@behaviour Solution
@impl Solution
def get_name, do: @name
@impl Solution
def parse!(raw) do
raw
|> String.split()
end
@impl Solution
def solve_first_part(strings) do
strings
|> Enum.map(fn s -> nice?(s) end)
|> Enum.map(fn b ->
if b, do: 1, else: 0
end)
|> Enum.sum()
end
@impl Solution
def solve_second_part(strings) do
strings
|> Enum.map(fn s -> still_nice?(s) end)
|> Enum.map(fn b ->
if b, do: 1, else: 0
end)
|> Enum.sum()
end
defp nice?(subject) do
Enum.all?([
does_not_contain_the_strings(subject),
contains_at_least_three_vowels(subject),
contains_at_least_one_letter_pair(subject)
])
end
defp still_nice?(subject) do
Enum.all?([
contains_a_letter_pair_without_overlapping(subject),
contains_one_letter_pair_which_repeats_with_exactly_one_letter_between_them(subject)
])
end
defp contains_at_least_three_vowels(subject) do
r = ~r/[aeiou].*[aeiou].*[aeiou].*/
String.match?(subject, r)
end
defp contains_at_least_one_letter_pair(subject) do
r = ~r/(\w)\1/
String.match?(subject, r)
end
defp does_not_contain_the_strings(subject) do
r = ~r/ab|cd|pq|xy/
not String.match?(subject, r)
end
defp contains_a_letter_pair_without_overlapping(subject) do
r = ~r/(\w{2}).*\1/
String.match?(subject, r)
end
defp contains_one_letter_pair_which_repeats_with_exactly_one_letter_between_them(subject) do
r = ~r/(\w)\w\1/
String.match?(subject, r)
end
end

28
2015-elixir/mix.exs Normal file
View file

@ -0,0 +1,28 @@
defmodule Aoc.MixProject do
use Mix.Project
def project do
[
app: :Aoc,
version: "0.1.0",
elixir: "~> 1.10",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger, :crypto]
]
end
# Run "mix help deps" to learn about dependencies.
defp deps do
[
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
]
end
end

View file

@ -0,0 +1,4 @@
defmodule AocTest do
use ExUnit.Case
doctest Aoc
end

View file

@ -0,0 +1,51 @@
defmodule Day01Test do
use ExUnit.Case
doctest Aoc.Solution.Day01
import Aoc.Solution.Day01
test "parses the input" do
expected = ["(", "(", ")", ")"]
result = parse!("(())")
assert result == expected
end
test "solves first part" do
a = "(())" |> parse!() |> solve_first_part()
b = "()()" |> parse!() |> solve_first_part()
c = "(((" |> parse!() |> solve_first_part()
d = "(()(()(" |> parse!() |> solve_first_part()
e = "))(((((" |> parse!() |> solve_first_part()
f = "())" |> parse!() |> solve_first_part()
g = "))(" |> parse!() |> solve_first_part()
h = ")))" |> parse!() |> solve_first_part()
i = ")())())" |> parse!() |> solve_first_part()
assert a == 0
assert b == 0
assert c == 3
assert d == 3
assert e == 3
assert f == -1
assert g == -1
assert h == -3
assert i == -3
end
test "solves second part" do
a = ")" |> parse!() |> solve_second_part()
b = "()())" |> parse!() |> solve_second_part()
c = "()())()" |> parse!() |> solve_second_part()
assert a == 1
assert b == 5
assert c == 5
end
test "solves second part with examples of no-last chars" do
a = "()())()" |> parse!() |> solve_second_part()
assert a == 5
end
end

View file

@ -0,0 +1,45 @@
defmodule Day02Test do
use ExUnit.Case
doctest Aoc.Solution.Day02
import Aoc.Solution.Day02
test "parses the input" do
input = """
2x3x4
1x1x10
"""
assert parse!(input) == [
[2, 3, 4],
[1, 1, 10]
]
end
test "solves first part" do
a = "2x3x4" |> parse!() |> solve_first_part()
b = "1x1x10" |> parse!() |> solve_first_part()
assert a == 58
assert b == 43
end
test "solves first part using multiple packages" do
a = "2x3x4\n1x1x10" |> parse!() |> solve_first_part()
assert a == 58 + 43
end
test "solves second part" do
a = "2x3x4" |> parse!() |> solve_second_part()
b = "1x1x10" |> parse!() |> solve_second_part()
assert a == 34
assert b == 14
end
test "solves second part using multiple packages" do
a = "2x3x4\n1x1x10" |> parse!() |> solve_second_part()
assert a == 34 + 14
end
end

View file

@ -0,0 +1,31 @@
defmodule Day03Test do
use ExUnit.Case
doctest Aoc.Solution.Day03
import Aoc.Solution.Day03
test "parses the input" do
expected = ["<", ">"]
assert parse!("<>") == expected
end
test "solves first part" do
a = ">" |> parse!() |> solve_first_part()
b = "^>v<" |> parse!() |> solve_first_part()
c = "^v^v^v^v^v" |> parse!() |> solve_first_part()
assert a == 2
assert b == 4
assert c == 2
end
test "solves second part" do
a = "^v" |> parse!() |> solve_second_part()
b = "^>v<" |> parse!() |> solve_second_part()
c = "^v^v^v^v^v" |> parse!() |> solve_second_part()
assert a == 3
assert b == 3
assert c == 11
end
end

View file

@ -0,0 +1,20 @@
defmodule Day04Test do
use ExUnit.Case
doctest Aoc.Solution.Day04
import Aoc.Solution.Day04
test "parses the input" do
i = "\n ab \n"
expected = "ab"
assert parse!(i) == expected
end
test "solves first part" do
a = "abcdef" |> parse!() |> solve_first_part()
b = "pqrstuv" |> parse!() |> solve_first_part()
assert a == 609_043
assert b == 1_048_970
end
end

View file

@ -0,0 +1,43 @@
defmodule Day05Test do
use ExUnit.Case
doctest Aoc.Solution.Day05
import Aoc.Solution.Day05
test "parses the input" do
input = """
a
b
c
"""
expected = ["a", "b", "c"]
assert parse!(input) == expected
end
test "solves first part" do
a = "ugknbfddgicrmopn" |> parse!() |> solve_first_part()
b = "aaa" |> parse!() |> solve_first_part()
c = "jchzalrnumimnmhp" |> parse!() |> solve_first_part()
d = "haegwjzuvuyypxyu" |> parse!() |> solve_first_part()
e = "dvszwmarrgswjxmb" |> parse!() |> solve_first_part()
assert a == 1
assert b == 1
assert c == 0
assert d == 0
assert e == 0
end
test "solves second part" do
a = "qjhvhtzxzqqjkmpb" |> parse!() |> solve_second_part()
b = "xxyxx" |> parse!() |> solve_second_part()
c = "uurcxstgmygtbstg" |> parse!() |> solve_second_part()
d = "ieodomkazucvgmuy" |> parse!() |> solve_second_part()
assert a == 1
assert b == 1
assert c == 0
assert d == 0
end
end

View file

@ -0,0 +1 @@
ExUnit.start()

22
2016-python/README.md Normal file
View file

@ -0,0 +1,22 @@
# Lösningar till Advent of Code 2016
För Python 3. För att köra alla lösningarna i ett svep,
kör `python aoc2016.py`. Ingen virtualenv krävs: alla lösningar
använder inget utöver standardbiblioteket.
För att köra en enstaka lösning, skicka med pusselfilen
som första programargument, t ex såhär: `python day_03.py puzzles/03.txt`
## Noteringar / logg
* Koden i `Day 1: No Time for a Taxicab` är rejält överdriven vad gäller
läsbarhet och en (ill-)vilja för att ha fyndiga variabelnamn.
* För `Day 3: Squares With Three Sides` fanns ett osunt behov av att lösa
allt med en rad. Förlåt ...
* Latmasken hämtade [en befintlig algoritm](https://www.reddit.com/r/adventofcode/comments/5hbygy/2016_day_9_solutions/)
för andra delen av `Day 9: Explosives in Cyberspace` på grund av tidsbrist.
## Förtydligande
Det är ok att nämna mig vid namn i podden om ni hittar något värt
att nämna i min kod. :)

17
2016-python/aoc2016.py Executable file
View file

@ -0,0 +1,17 @@
print("""
_ _ ____ ___ _ __
_| || |_ __ _ ___ ___|___ \ / _ \/ |/ /_
|_ .. _|/ _` |/ _ \ / __| __) | | | | | '_ \\
|_ _| (_| | (_) | (__ / __/| |_| | | (_) |
|_||_| \__,_|\___/ \___|_____|\___/|_|\___/
""")
for i in [str(n).zfill(2) for n in range(1, 26)]:
try:
with open('inputs/%s.txt' % i, 'r') as puzzle_input:
puzzle = puzzle_input.read()
print('\n--- %s ---' % __import__('day_%s' % i).run.__doc__)
__import__('day_%s' % i).run(puzzle)
except IOError:
pass
except ImportError:
pass

108
2016-python/day_01.py Normal file
View file

@ -0,0 +1,108 @@
import sys
from random import randint
def run(pinput):
"""Day 1: No Time for a Taxicab"""
santa_starting_position = (randint(-1024, 1024), randint(-1024, 1024))
head_quarters, first_recurrence = direct_santa(santa_starting_position, pinput)
print('Distance to Easter Bunny HQ: %s' % head_quarters)
print('Distance to first recurrence: %s' % first_recurrence)
def get_distance(start, current):
return abs(abs(start[0] - current[0]) + abs(start[1] - current[1]))
def go_north(current_block, distance):
return (current_block[0] - distance, current_block[1]), 'N'
def go_south(current_block, distance):
return (current_block[0] + distance, current_block[1]), 'S'
def go_west(current_block, distance):
return (current_block[0], current_block[1] - distance), 'W'
def go_east(current_block, distance):
return (current_block[0], current_block[1] + distance), 'E'
def go_left(current_block, facing_direction, distance):
if facing_direction == 'N':
return go_west(current_block, distance)
if facing_direction == 'W':
return go_south(current_block, distance)
if facing_direction == 'S':
return go_east(current_block, distance)
if facing_direction == 'E':
return go_north(current_block, distance)
def go_right(current_block, facing_direction, distance):
if facing_direction == 'N':
return go_east(current_block, distance)
if facing_direction == 'E':
return go_south(current_block, distance)
if facing_direction == 'S':
return go_west(current_block, distance)
if facing_direction == 'W':
return go_north(current_block, distance)
def track_santa(tracks, direction, distance):
pos = tracks[-1]
recurrence = None
i = 0
while i < distance:
if direction == 'N':
pos, f = go_north(pos, 1)
if direction == 'W':
pos, f = go_west(pos, 1)
if direction == 'S':
pos, f = go_south(pos, 1)
if direction == 'E':
pos, f = go_east(pos, 1)
if not recurrence and pos in tracks:
recurrence = pos
tracks.append(pos)
i += 1
return tracks, recurrence
def direct_santa(starting_block, directions):
gone_in_circle_at = None
current_block = starting_block
facing = 'N'
tracks = [starting_block]
for d in directions.split(', '):
distance = int(d[1:])
current_block, facing = go_left(current_block, facing, distance) if d.startswith('L') else go_right(
current_block, facing, distance)
tracks, recurrence = track_santa(tracks, facing, distance)
if not gone_in_circle_at and recurrence:
gone_in_circle_at = recurrence
head_quarters_pos = get_distance(starting_block, current_block)
first_recurrence_pos = get_distance(starting_block, gone_in_circle_at)
return head_quarters_pos, first_recurrence_pos
if __name__ == '__main__':
try:
with open(sys.argv[1], 'r') as f:
run(f.read())
except IOError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')
except IndexError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')

70
2016-python/day_02.py Normal file
View file

@ -0,0 +1,70 @@
import sys
def run(pinput):
"""Day 2: Bathroom Security"""
imaginary_code = break_code_imaginary(5, pinput)
real_code = break_code_real(5, pinput)
print('Code using imaginary pad: %s' % imaginary_code)
print('Code using real pad: %s' % real_code)
def break_code_real(start, instructions):
"""
real pad:
1
2 3 4
5 6 7 8 9
A B C
D
"""
pos = start
code = ''
for l in instructions.split('\n'):
for g in l.strip():
if g == 'U' and pos not in [1, 2, 4, 5, 9]:
pos -= 2 if pos in [13, 3] else 4
if g == 'D' and pos not in [5, 9, 10, 12, 13]:
pos += 2 if pos in [1, 11] else 4
if g == 'L' and pos not in [1, 2, 5, 10, 13]:
pos -= 1
if g == 'R' and pos not in [1, 4, 9, 12, 13]:
pos += 1
code += format(pos, 'X')
return code
def break_code_imaginary(start, instructions):
"""
imaginary pad:
1 2 3
4 5 6
7 8 9
"""
pos = start
code = ''
for l in instructions.split('\n'):
for g in l.strip():
if g == 'U' and pos > 3:
pos -= 3
if g == 'D' and pos < 7:
pos += 3
if g == 'L' and pos not in [1, 4, 7]:
pos -= 1
if g == 'R' and pos not in [3, 6, 9]:
pos += 1
code += str(pos)
return code
if __name__ == '__main__':
try:
with open(sys.argv[1], 'r') as f:
run(f.read())
except IOError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')
except IndexError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')

35
2016-python/day_03.py Normal file
View file

@ -0,0 +1,35 @@
import re
import sys
def run(pinput):
"""Day 3: Squares With Three Sides"""
h = find_fake_triangles_hor(pinput)
v = find_fake_triangles_ver(pinput)
print('Fake triangles count, horizontal: %s' % h)
print('Fake triangles count, vertical: %s' % v)
def find_fake_triangles_hor(pinput):
# see commit 8fd8787 for the more readable edition
return sum(map(lambda t: t[0] + t[1] > t[2], map(lambda l: sorted(map(int, l.split())), pinput.split('\n'))))
def find_fake_triangles_ver(pinput):
# do it in 1 loc? challenge accepted! also, I'm sorry.
return sum([map(lambda t: t[0] + t[1] > t[2],
[sorted(x[i:i + 3]) for i in range(0, len(x), 3)]) for x in
[(list(map(int, re.findall(r'^\s+(\d+)', pinput, flags=re.MULTILINE))) + list(
map(int, re.findall(r'^\s+\d+\s+(\d+)', pinput, flags=re.MULTILINE))) + list(
map(int, re.findall(r'^\s+\d+\s+\d+\s+(\d+)', pinput, flags=re.MULTILINE))))]][0])
if __name__ == '__main__':
try:
with open(sys.argv[1], 'r') as f:
run(f.read())
except IOError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')
except IndexError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')

56
2016-python/day_04.py Normal file
View file

@ -0,0 +1,56 @@
import re
import sys
from collections import Counter
def get_valid_rooms(pinput):
def only_valid_rooms(l):
code, id, checksum = re.match(r'^(\D+)(\d+)\[(\D+?)\]$', l.translate({ord('-'): None})).groups()
mc = Counter(code).most_common()
mc_sorted = sorted(mc, key=lambda e: (-e[1], e[0])) # because FU, Counter default sort ...
return ''.join(map(lambda x: x[0], mc_sorted)).startswith(checksum)
return filter(only_valid_rooms, pinput.splitlines())
def sum_sector_ids(puzzle):
return sum(map(int, map(lambda l: re.match(r'.+-(\d+)\[', l).group(1),
get_valid_rooms(puzzle))))
def decrypt_name(ciphered, id):
v = ''
for c in ciphered:
dec = ord(c) + (id % 26)
if dec > 122:
dec -= 26
v += ' ' if c == '-' else chr(dec)
return v
def find_np_room(pinput):
rooms = get_valid_rooms(pinput)
for r in rooms:
code, sid = re.match(r'^(\D+)-(\d+)\[', r).groups()
decrypted = decrypt_name(code, int(sid))
if decrypted == 'northpole object storage':
return sid
def run(pinput):
"""Day 4: Security Through Obscurity"""
s = sum_sector_ids(pinput)
np = find_np_room(pinput)
print('Sum of sector IDs, real rooms: %s' % s)
print('Section ID for North Pole storage: %s' % np)
if __name__ == '__main__':
try:
with open(sys.argv[1], 'r') as f:
run(f.read())
except IOError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')
except IndexError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')

47
2016-python/day_05.py Normal file
View file

@ -0,0 +1,47 @@
import hashlib
import sys
def hack_password(pinput):
passwd = ''
i = 0
while len(passwd) < 8:
s = hashlib.md5(bytes('%s%d' % (pinput, i), 'ascii')).hexdigest()
if s.startswith('00000'):
passwd += s[5]
i += 1
return passwd
def hack_password_again(pinput):
passwd = [' ' for n in range(0, 8)]
i = 0
while ' ' in passwd:
s = hashlib.md5(bytes('%s%d' % (pinput, i), 'ascii')).hexdigest()
try:
pos = int(s[5])
if s.startswith('00000') and pos < 8:
passwd[pos] = s[6] if passwd[pos] == ' ' else passwd[pos]
except ValueError:
pass
i += 1
return ''.join(passwd)
def run(pinput):
"""Day 5: How About a Nice Game of Chess?"""
p = hack_password(pinput)
q = hack_password_again(pinput)
print('Password, first door: %s' % p)
print('Password, second door: %s' % q)
if __name__ == '__main__':
try:
with open(sys.argv[1], 'r') as f:
run(f.read().strip())
except IOError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')
except IndexError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')

29
2016-python/day_06.py Normal file
View file

@ -0,0 +1,29 @@
import sys
from collections import Counter
def get_message(pinput):
return ''.join(map(lambda l: Counter(l).most_common()[0][0], zip(*pinput.split('\n'))))
def get_real_message(pinput):
return ''.join(map(lambda l: Counter(l).most_common()[-1][0], zip(*pinput.split('\n'))))
def run(pinput):
"""Day 6: Signals and Noise"""
m = get_message(pinput)
n = get_real_message(pinput)
print('Corrected message, most common char: %s' % m)
print('Corrected message, least common char: %s' % n)
if __name__ == '__main__':
try:
with open(sys.argv[1], 'r') as f:
run(f.read().strip())
except IOError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')
except IndexError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')

44
2016-python/day_07.py Normal file
View file

@ -0,0 +1,44 @@
import re
import sys
def count_ips(pinput):
def tls(l):
a = re.sub(r'\[.+?\]', lambda x: ' ', l)
abbas = sum(map(lambda i: a[i:i + 4] == a[i:i + 4][::-1] and a[i:i + 1] != a[i + 1:i + 2],
range(0, len(a) - 3)))
total = sum(map(lambda i: l[i:i + 4] == l[i:i + 4][::-1] and l[i:i + 1] != l[i + 1:i + 2],
range(0, len(l) - 3)))
return abbas == total and abbas > 0
def ssl(l):
a = re.sub(r'\[.+?\]', lambda x: ' ', l)
b = ' '.join(re.findall(r'\[(.+?)\]', l))
abas = set(filter(lambda v: v, map(
lambda i: a[i:i + 3] if a[i:i + 3] == a[i:i + 3][::-1] and a[i:i + 1] != a[i + 1:i + 2] else None,
range(0, len(a) - 2))))
babs = set(filter(lambda v: v, map(
lambda i: b[i + 1:i + 3] + b[i + 1] if b[i:i + 3] == b[i:i + 3][::-1] and b[i:i + 1] != b[i + 1:i + 2] else None,
range(0, len(b) - 2))))
return len(abas.intersection(babs)) > 0
return sum(map(tls, pinput.split('\n'))), sum(map(ssl, pinput.split('\n')))
def run(pinput):
"""Day 7: Internet Protocol Version 7"""
tls, ssl = count_ips(pinput)
print('IPs supporting TLS: %s' % tls)
print('IPs supporting SSL: %s' % ssl)
if __name__ == '__main__':
try:
with open(sys.argv[1], 'r') as f:
run(f.read().strip())
except IOError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')
except IndexError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')

75
2016-python/day_08.py Normal file
View file

@ -0,0 +1,75 @@
import re
import sys
def rotate(o, w, h, matrix):
e = re.match(r'rotate.+([x|y])=(\d+) by (\d+)', o)
if e:
t, s, d = e.groups()
lit = list()
m = list()
if t == 'x':
for c in range(0, w):
lit.append(matrix[c::w])
for r in range(0, int(d)):
lit[int(s)] = lit[int(s)][h - 1:] + lit[int(s)][0:h - 1]
for c in zip(*lit):
m.extend(c)
if t == 'y':
for c in range(0, w):
lit.append(matrix[c::w])
lit = list(zip(*lit))
for r in range(0, int(d)):
lit[int(s)] = lit[int(s)][w - 1:] + lit[int(s)][0:w - 1]
for c in lit:
m.extend(c)
return m
else:
return matrix
def rect(o, w, h, matrix):
e = re.match(r'rect (\d+)x(\d+)', o)
if e:
x, y = e.groups()
lit = list()
r = list(range(0, w * h))
for c in range(0, int(x)):
lit += r[c::w][:int(y)]
for n in lit:
matrix[n] = True
return matrix
else:
return matrix
def lit_screen(w, h, pinput):
matrix = [False for i in range(0, w * h)]
for o in pinput:
matrix = rect(o, w, h, matrix)
matrix = rotate(o, w, h, matrix)
out = list()
for l in range(0, w * h, w):
out.append(''.join(map(lambda l: '#' if l else ' ', matrix[l:l+w])))
return sum(matrix), '\n'.join(out)
def run(pinput):
"""Day 8: Two-Factor Authentication"""
pl, out = lit_screen(50, 6, pinput.strip().split('\n'))
print('\n%s\n' % out)
print('Pixels lit: %s' % pl)
if __name__ == '__main__':
try:
with open(sys.argv[1], 'r') as f:
run(f.read().strip())
except IOError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')
except IndexError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')

52
2016-python/day_09.py Normal file
View file

@ -0,0 +1,52 @@
import re
import sys
def decompress(pinput):
parts = list()
m = re.match(r'.*\((\d+)x(\d+)\)', pinput)
if m:
while re.match(r'.*\((\d+)x(\d+)\)', pinput):
a, c, r, b = re.split(r'\((\d+)x(\d+)\)', pinput, maxsplit=1)
parts.append(a)
parts.append(''.join([b[0:int(c)] for i in range(0, int(r))]))
pinput = b[int(c):]
return sum(map(lambda s: len(s), parts)) + len(pinput)
else:
return len(pinput)
def decompress_V2(pinput):
# Algorithm found at:
# https://www.reddit.com/r/adventofcode/comments/5hbygy/2016_day_9_solutions/
l = 0
w = [1 for c in pinput]
cur = 0
while cur < len(pinput):
if pinput[cur] == '(':
s, r = re.match(r'\((\d+)x(\d+)\)', pinput[cur:]).groups()
cur += len('(%sx%s)' % (s, r))
for i in range(cur, cur + int(s)):
w[i] *= int(r)
else:
l += w[cur]
cur += 1
return l
def run(pinput):
"""Day 9: Explosives in Cyberspace"""
v1 = decompress(pinput)
v2 = decompress_V2(pinput)
print('Decrompressed length, v1: %s' % v1)
print('Decrompressed length, v2: %s' % v2)
if __name__ == '__main__':
try:
with open(sys.argv[1], 'r') as f:
run(f.read().strip())
except IOError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')
except IndexError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')

65
2016-python/day_10.py Normal file
View file

@ -0,0 +1,65 @@
import re
import sys
value_delegate = re.compile(r'value (\d+) goes to bot (\d+)')
bot_delegate = re.compile(r'bot (\d+) gives low to \b(bot|output) (\d+) and high to \b(bot|output) (\d+)')
def delegate(pinput, x, y):
bots = dict()
outputs = dict()
for l in pinput:
if value_delegate.match(l):
fv, b = map(int, value_delegate.match(l).groups())
bots[b] = bots[b] + [fv] if b in bots else [fv]
bot = ''
go = True
while go:
botz = bots.copy().items()
for bid, bvs in botz:
if len(bvs) == 2:
for l in pinput:
if l.startswith('bot %d ' % bid) and bot_delegate.match(l):
fv, l, li, h, hi = bot_delegate.match(l).groups()
fv, li, hi = map(int, (fv, li, hi))
try:
if l == 'output':
outputs[li] = outputs[li] + [min(bots[fv])] if li in outputs else [min(bots[fv])]
bots[fv] = list(filter(lambda x: x != min(bots[fv]), bots[fv]))
else:
bots[li] = bots[li] + [min(bots[fv])] if li in bots else [min(bots[fv])]
bots[fv] = list(filter(lambda x: x != min(bots[fv]), bots[fv]))
if h == 'output':
outputs[hi] = outputs[hi] + [max(bots[fv])] if hi in outputs else [max(bots[fv])]
bots[fv] = list(filter(lambda x: x != max(bots[fv]), bots[fv]))
else:
bots[hi] = bots[hi] + [max(bots[fv])] if hi in bots else [max(bots[fv])]
bots[fv] = list(filter(lambda x: x != max(bots[fv]), bots[fv]))
except ValueError:
pass
break
go = len(list(filter(lambda b: len(b[1]) == 2, bots.items()))) != 0
bt = ''.join([str(z[0]) for z in filter(lambda z: x in z[1] and y in z[1], bots.items())])
if len(bt) > 0:
bot = bt
return bot, outputs[0][0] * outputs[2][0] * outputs[1][0]
def run(pinput):
"""Day 10: Balance Bots"""
bot, m = delegate(pinput.strip().splitlines(), 61, 17)
print('Bot that deals with %s and %s: %s' % (61, 17, bot))
print('Multiple the values of 0-2: %s' % m)
if __name__ == '__main__':
try:
with open(sys.argv[1], 'r') as f:
run(f.read().strip())
except IOError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')
except IndexError:
print('please provide a file path to puzzle file, example: ./puzzle.txt')

3
2017-python/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
.idea
*.pyc
__pycache__

54
2017-python/README.md Normal file
View file

@ -0,0 +1,54 @@
Advent of Code 2017
===================
Lösningar för #aoc2017 i Python 3 (testat mot 3.5.2).
Logg
----
* Särskilt svåra dagar (tidsåtgång mer än 60 min) vid kodtillfället:
* Dag 3,
* Dag 6,
* Dag 7,
* Dag 14,
* Dag 18 och
* Dag 21.
* Dag 3: del 2 kopierades från en [färdig sekvens](https://oeis.org/A141481). Restpunkt är att faktiskt
implementera den.
* Dag 5: del 2 tar väldigt lång tid att exekvera. Restpunkt är att optimera denna!
* Dag 11: Allt som behövs finns beskrivet på
[Hexagonal Grids](https://www.redblobgames.com/grids/hexagons/).
* Dag 13: del 2 misslyckande att hitta något som var snabbt nog att exekvera.
Restpunkt är skriva något som inte tar en timme att köra!
* Dag 14 del 2: Läs på om [Union-find algoritmen](https://en.wikipedia.org/wiki/Disjoint-set_data_structure)
* Dag 15: borde skrivas om att använda generatorer för att snabbas upp.
* Dag 18 del 2: ["värdet av" kan betyda flera saker](https://www.reddit.com/r/adventofcode/comments/7kj35s/2017_day_18_solutions/dreth75/) ... jävla etta.
* Dag 21: Blocken ska divergera och ska därmed testas separat mot reglerna. Detta missade jag, och antog istället att samma regel applicerade på alla block ...
Hjälpscript
-----------
För att köra alla lösningar:
python aoc.py
För att starta en ny dag (skapar och populerar filerna `inputs/<dagnummer>.txt`, `solutions/day_<dagnummer>.py` och
`tests/day_<dagnummer>_tests.py`):
python aoc.py <dagnummer> "<namn dag>"
Öppna puzzle input manuellt och kopiera innehållet till `inputs/<dagnummer>.txt` som ett sista manuellt steg.
För att köra separat lösning (ersätt `XX` med dagens nummer):
PYTHONPATH=$(pwd) python solutions/day_XX.py
Starta automatisk testkörare (ersätt `XX` med dagens nummer):
export PYTHONPATH=$(pwd)
ls solutions/**/*.py | entr -c -r python tests/day_XX_tests.py

65
2017-python/aoc.py Normal file
View file

@ -0,0 +1,65 @@
import sys
try:
_, day_no, name = sys.argv
except ValueError:
day_no = None
name = None
if day_no and name:
with open('solutions/day_{}.py'.format(day_no.zfill(2)), 'w') as s:
s.write('''
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '{day_no}.txt'
def __str__(self):
return 'Day {day}: {name}'
def solve(self, puzzle_input):
pass
def solve_again(self, puzzle_input):
pass
if __name__ == '__main__':
solution = Solution()
solution.show_results()
'''.strip().format(day=day_no, day_no=day_no.zfill(2), name=name) + '\n')
with open('tests/day_{}_tests.py'.format(day_no.zfill(2)), 'w') as t:
t.write('''
import unittest
from solutions.day_{day_no} import Solution
class Day{day_no}TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_something(self):
assert self.solution.solve('puzzle_input') == True
if __name__ == '__main__':
unittest.main()
'''.strip().format(day_no=day_no.zfill(2)) + '\n')
with open('inputs/{}.txt'.format(day_no.zfill(2)), 'w') as i:
i.write('')
exit(0)
print('\nAdvent of Code 2017'
'\n###################'
'\n\nby Anders Ytterström (@madr)')
for i in [str(n).zfill(2) for n in range(1, 26)]:
try:
solution = __import__('solutions.day_{}'.format(i), globals(), locals(), ['Solution'], 0).Solution()
solution.show_results()
except IOError:
pass
except ImportError:
pass

View file

@ -0,0 +1,26 @@
class BaseSolution:
puzzle_input = ""
input_file = None
trim_input = True
def parse_input(self, filename):
filepath = './inputs/{}'.format(filename)
with open(filepath, 'r') as f:
self.puzzle_input = f.read()
if self.trim_input:
self.puzzle_input = self.puzzle_input.strip()
def show_results(self):
self.parse_input(self.input_file)
print('\n\n{}\n{}\n\nPart 1: {}\nPart 2: {}'.format(
str(self),
'-' * len(str(self)),
self.solve(self.puzzle_input),
self.solve_again(self.puzzle_input),
))
def solve(self, puzzle_input):
raise NotImplemented
def solve_again(self, puzzle_input):
raise NotImplemented

View file

@ -0,0 +1,22 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '01.txt'
def __str__(self):
return 'Day 1: Inverse Captcha'
def solve(self, puzzle_input, distance=1):
pi_length = len(puzzle_input)
return sum(int(puzzle_input[pos]) for pos in range(pi_length) if
puzzle_input[pos] == puzzle_input[(pos + distance) % pi_length])
def solve_again(self, puzzle_input):
distance = len(puzzle_input) // 2
return self.solve(puzzle_input, distance)
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,34 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '02.txt'
def __str__(self):
return 'Day 2: Corruption Checksum'
def _get_rows(self, puzzle_input):
return [list(map(int, rows.split())) for rows in puzzle_input.splitlines()]
def get_even_divisible(self, columns):
l = len(columns)
for col in range(l):
for i in range(l):
if not col == i and columns[col] % columns[i] == 0:
return columns[col] // columns[i]
def get_diff(self, columns):
return max(columns) - min(columns)
def solve(self, puzzle_input):
rows = self._get_rows(puzzle_input)
return sum(self.get_diff(columns) for columns in rows)
def solve_again(self, puzzle_input):
rows = self._get_rows(puzzle_input)
return sum(self.get_even_divisible(columns) for columns in rows)
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,37 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '03.txt'
def __str__(self):
return 'Day 3: Spiral Memory'
def _get_rounds(self, value):
n = 0
while (2 * n + 1) ** 2 < value:
n += 1
return n
def solve(self, puzzle_input):
directions = [ # instructions are laterally reversed
lambda x: (2 * x + 1) ** 2 - x, # Go down x times
lambda x: (2 * x + 1) ** 2 - 3 * x, # Go left x times
lambda x: (2 * x + 1) ** 2 - 5 * x, # Go up x times
lambda x: (2 * x + 1) ** 2 - 7 * x, # Go right x times
]
target = int(puzzle_input)
steps = self._get_rounds(target)
steps += min(target - l(steps) for l in directions if target - l(steps) >= 0)
return steps
def solve_again(self, puzzle_input):
# see OEIS sequence no. A141481,
# "Square spiral of sums of selected preceding terms, starting at 1."
# https://oeis.org/A141481
return 279138
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,25 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '04.txt'
def __str__(self):
return 'Day 4: High-Entropy Passphrases'
def validate(self, passphrase, extended=False):
words = passphrase.split()
if extended:
words = [''.join(sorted(w)) for w in words]
return sorted(list(set(words))) == sorted(words)
def solve(self, puzzle_input):
return sum(self.validate(p) for p in puzzle_input.splitlines())
def solve_again(self, puzzle_input):
return sum(self.validate(p, True) for p in puzzle_input.splitlines())
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,28 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '05.txt'
def __str__(self):
return 'Day 5: A Maze of Twisty Trampolines, All Alike'
def solve(self, puzzle_input, strange_jumps=False):
pos = 0
steps = 0
offset_values = [int(ov) for ov in puzzle_input.splitlines()]
r = range(len(offset_values))
while pos in r:
jump_by = offset_values[pos]
offset_values[pos] += -1 if strange_jumps and jump_by > 2 else 1
pos += jump_by
steps += 1
return steps
def solve_again(self, puzzle_input):
return self.solve(puzzle_input, True)
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,48 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '06.txt'
def __str__(self):
return 'Day 6: Memory Reallocation'
def redistribute(self, banks):
banks = list(banks)
hi = banks.index(max(banks))
l = len(banks)
v = banks[hi] - banks[hi] % (l - 1)
if v == 0:
v = banks[hi]
banks[hi] -= v
pos = (hi + 1) % l
while v > 0:
if pos != hi:
banks[pos] += 1
v -= 1
pos = (pos + 1) % l
return tuple(banks)
def _allocate(self, puzzle_input):
banks = list(map(int, puzzle_input.split()))
seen = [tuple(banks)]
not_seen = True
while not_seen:
banks = self.redistribute(banks)
if banks in seen:
not_seen = False
seen.append(banks)
return seen
def solve(self, puzzle_input):
return len(self._allocate(puzzle_input)) - 1
def solve_again(self, puzzle_input):
seen = self._allocate(puzzle_input)
seen_last = ' '.join(str(n) for n in seen[-1])
return len(self._allocate(seen_last)) - 1
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,94 @@
from solutions import BaseSolution
class Program:
def __init__(self, data=None):
if data:
name, weight, disc = self._parse(data)
self.name = name
self.weight = weight
self.disc = disc
def __repr__(self):
return str(self.name)
def _parse(self, data):
disc = []
try:
name, weight = data.split()
except ValueError:
name, weight, _, *disc = data.split()
weight = int(weight[1:len(weight) - 1])
disc = tuple(p.replace(',', '') for p in disc)
return name, weight, disc
def has_disc(self):
return len(self.disc) > 0
def unseen_discs(self, seen_discs):
return [t for t in self.disc if t not in seen_discs]
class Solution(BaseSolution):
input_file = '07.txt'
def __str__(self):
return 'Day 7: Recursive Circus'
def _get_programs(self, puzzle_input):
return [Program(data) for data in puzzle_input.splitlines()]
def _get_discs(self, disc, programs):
subdisc = [{'own_weight': p.weight, 'obj': p} for p in programs if p.name in disc]
for t in subdisc:
t['weight'] = t['own_weight']
if t['obj'].has_disc():
t['disc'] = self._get_discs(t['obj'].disc, programs)
t['weight'] += sum([st['weight'] for st in t['disc']])
del (t['obj'])
return subdisc
def _find_unbalanced_disc(self, disc):
disc = sorted(disc, key=lambda t: t['weight'])
if not disc[0]['weight'] < disc[1]['weight']:
disc = sorted(disc, key=lambda t: t['weight'], reverse=True)
return disc[0], disc[1]['weight'] - disc[0]['weight']
def solve(self, puzzle_input):
programs = self._get_programs(puzzle_input)
programs_with_discs = []
seen = []
for p in programs:
if p.has_disc():
programs_with_discs.append(p)
else:
seen.append(p.name)
for p in programs_with_discs:
unseen = p.unseen_discs(seen)
if len(unseen) > 0:
seen += unseen
bottom_program = list(filter(lambda p: p.name not in seen, programs_with_discs))[0]
return bottom_program
def solve_again(self, puzzle_input):
programs = self._get_programs(puzzle_input)
bottom_program = self.solve(puzzle_input)
disc_tree = {
'own_weight': bottom_program.weight,
'disc': self._get_discs(bottom_program.disc, programs)
}
diff = -1
unbalanced = True
while unbalanced:
disc, new_diff = self._find_unbalanced_disc(disc_tree['disc'])
if new_diff == 0:
unbalanced = False
else:
disc_tree = disc
diff = new_diff
return disc_tree['own_weight'] + diff
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,40 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '08.txt'
registry = {}
def __str__(self):
return 'Day 8: I Heard You Like Registers'
def _should_modify(self, x, comp, y):
if comp in ('==', '!=', '>=', '<=', '>', '<'):
return eval('{:d} {} {:d}'.format(x, comp, y))
return False
def _update_registry(self, registry, instruction):
r, action, n, _, k, comp, v = instruction.split()
current = registry.get(r, 0)
if self._should_modify(registry.get(k, 0), comp, int(v)):
registry[r] = current + int(n) if action == 'inc' else current - int(n)
def solve(self, puzzle_input):
registry = {}
for instruction in puzzle_input.splitlines():
self._update_registry(registry, instruction)
return max(registry.values())
def solve_again(self, puzzle_input):
registry = {}
max_value = 0
for instruction in puzzle_input.splitlines():
self._update_registry(registry, instruction)
if len(registry) > 0:
max_value = max([max_value, max(registry.values())])
return max_value
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,33 @@
import re
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '09.txt'
def __str__(self):
return 'Day 9: Stream Processing'
def solve(self, puzzle_input):
level = 0
groups = []
stream = re.sub(r'!.', '', puzzle_input)
stream = re.sub(r'<[^>]*>', '', stream)
for c in stream:
if c == '{':
level += 1
if c == '}':
groups.append(level)
level -= 1
return sum(groups)
def solve_again(self, puzzle_input):
stream = re.sub(r'!.', '', puzzle_input)
garbage = re.findall(r'<([^>]*)>', stream)
return sum([len(g) for g in garbage])
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,47 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '10.txt'
list = []
l = 0
skip_size = 0
pos = 0
def __str__(self):
return 'Day 10: Knot Hash'
def reset(self, l=256):
self.list = list(range(l))
self.l = l
self.skip_size = 0
self.pos = 0
def reverse(self, sublist_length):
sublist = []
for i in range(sublist_length):
sublist.append(self.list[(self.pos + i) % self.l])
for i, n in enumerate(reversed(sublist)):
self.list[(self.pos + i) % self.l] = n
self.pos = (self.pos + sublist_length + self.skip_size) % self.l
self.skip_size += 1
def solve(self, puzzle_input, r=256):
self.reset(r)
for sublist_length in map(int, puzzle_input.split(',')):
self.reverse(sublist_length)
return self.list[0] * self.list[1]
def solve_again(self, puzzle_input, r=256):
puzzle_input = [ord(c) for c in puzzle_input] + [17, 31, 73, 47, 23]
self.reset(r)
for _ in range(64):
for sublist_length in puzzle_input:
self.reverse(sublist_length)
dense_hash = [eval('^'.join(list(map(str, self.list[seq:seq+16])))) for seq in range(0, r, 16)]
return ''.join(['{:x}'.format(int(i)).zfill(2) for i in dense_hash])
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,41 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '11.txt'
furthest_away = 0
# https://www.redblobgames.com/grids/hexagons/#coordinates
DIRECTIONS = {
'n': lambda x, y, z: (x, y + 1, z - 1),
'ne': lambda x, y, z: (x + 1, y, z - 1),
'se': lambda x, y, z: (x + 1, y - 1, z),
's': lambda x, y, z: (x, y - 1, z + 1),
'sw': lambda x, y, z: (x - 1, y, z + 1),
'nw': lambda x, y, z: (x - 1, y + 1, z),
}
def __str__(self):
return 'Day 11: Hex Ed'
def _get_end(self, steps):
x = 0
y = 0
z = 0
self.furthest_away = 0
for step in steps:
x, y, z = self.DIRECTIONS[step](x, y, z)
self.furthest_away = max(self.furthest_away, abs(x), abs(y), abs(z))
return abs(x), abs(y), abs(z)
def solve(self, puzzle_input):
x, y, z = self._get_end(puzzle_input.split(','))
return max(x, y, z)
def solve_again(self, puzzle_input):
_, *__ = self._get_end(puzzle_input.split(','))
return self.furthest_away
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,39 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '12.txt'
seen = []
def __str__(self):
return 'Day 12: Digital Plumber'
def _walk(self, i, programs):
line = next(filter(lambda l: l.startswith('{} <->'.format(i)), programs))
piped = line.split()[2:]
self.seen.add(i)
for p in [int(p.replace(',', '')) for p in piped]:
if p not in self.seen:
self._walk(p, programs)
def solve(self, puzzle_input):
programs = [pi.strip() for pi in puzzle_input.splitlines()]
self.seen = set()
self._walk(0, programs)
return len(self.seen)
def solve_again(self, puzzle_input):
programs = [pi.strip() for pi in puzzle_input.splitlines()]
self.seen = set()
groups = 0
for line in programs:
pid = int(line.split()[0])
if pid not in self.seen:
self._walk(pid, programs)
groups += 1
return groups
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,67 @@
import itertools
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '13.txt'
layers = []
scanners = []
def __str__(self):
return 'Day 13: Packet Scanners'
def _move_scanners(self):
for p, l in enumerate(self.layers):
if l != 0:
self.scanners[p][0] += self.scanners[p][1]
if self.scanners[p][0] == l - 1 or self.scanners[p][0] == 0:
self.scanners[p][1] = -self.scanners[p][1]
def _init_scanners(self):
self.scanners = [[0, 1] for l in self.layers]
def _setup(self, puzzle_input):
pi = [tuple(map(int, line.strip().split(': '))) for line in puzzle_input.splitlines()]
self.layers = [0 for _ in range(pi[-1][0] + 1)]
self._init_scanners()
for k, v in pi:
self.layers[k] = v
def _get_severity(self):
severity = 0
for pos in range(len(self.layers)):
if self.scanners[pos][0] == 0 and self.layers[pos] > 0:
severity += self.layers[pos] * pos
self._move_scanners()
return severity
def _will_be_caught(self):
caught = False
for pos, l in enumerate(self.layers):
if l > 0 and self.scanners[pos][0] == 0:
caught = True
self._move_scanners()
return caught
def solve(self, puzzle_input):
self._setup(puzzle_input)
severity = self._get_severity()
return severity
def _scan(self, height, time):
offset = time % ((height - 1) * 2)
return 2 * (height - 1) - offset if offset > height - 1 else offset
def solve_again(self, puzzle_input):
# todo: rewrite!
lines = [line.split(': ') for line in puzzle_input.splitlines()]
heights = {int(pos): int(height) for pos, height in lines}
wait = next(
wait for wait in itertools.count() if not any(self._scan(heights[pos], wait + pos) == 0 for pos in heights))
return wait
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,56 @@
from solutions import BaseSolution
from solutions.day_10 import Solution as KnotHash
class Solution(BaseSolution):
input_file = '14.txt'
def __str__(self):
return 'Day 14: Disk Defragmentation'
def _get_grid(self, pi):
grid = ''
ks = KnotHash()
for n in range(128):
s = '-'.join([pi, str(n)])
knothash = ks.solve_again(s)
grid += '{:0128b}'.format(int(knothash, 16))
return grid
def _find_regions(self, squares):
seen = set()
regions = 0
def get_adjacent_square(i):
if i in seen or i not in squares:
return
seen.add(i)
if i % 128 > 0:
get_adjacent_square(i - 1)
if i > 127:
get_adjacent_square(i - 128)
if i % 128 < 127:
get_adjacent_square(i + 1)
if i < 128 ** 2 - 128:
get_adjacent_square(i + 128)
for i in range(128 ** 2):
if i in seen or i not in squares:
continue
regions += 1
get_adjacent_square(i)
return regions
def solve(self, puzzle_input):
grid = self._get_grid(puzzle_input)
return sum(map(int, grid))
def solve_again(self, puzzle_input):
grid = self._get_grid(puzzle_input)
squares = [i for i, s in enumerate(list(grid)) if s == '1']
return self._find_regions(squares)
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,42 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '15.txt'
def __str__(self):
return 'Day 15: Dueling Generators'
def _calc(self, x, f, m=1):
x = (x * f) % 2147483647
if x % m != 0:
return self._calc(x, f, m)
else:
return x
def solve(self, puzzle_input):
af, bf = (16807, 48271)
a, b = [int(pi.split()[-1]) for pi in puzzle_input.splitlines()]
j = 0
for _ in range(40 * 10**6):
a = self._calc(a, af)
b = self._calc(b, bf)
if '{:b}'.format(a)[-16:] == '{:b}'.format(b)[-16:]:
j += 1
return j
def solve_again(self, puzzle_input):
af, bf = (16807, 48271)
a, b = [int(pi.split()[-1]) for pi in puzzle_input.splitlines()]
j = 0
for _ in range(5 * 10 ** 6):
a = self._calc(a, af, 4)
b = self._calc(b, bf, 8)
if '{:b}'.format(a)[-16:] == '{:b}'.format(b)[-16:]:
j += 1
return j
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,55 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '16.txt'
def __str__(self):
return 'Day 16: Permutation Promenade'
def _move(self, programs, m, i):
l = len(programs)
if m == 's':
r = int(i)
return programs[-r:] + programs[:l - r]
if m == 'x':
x, y = [int(s) for s in i.split('/')]
z = programs[x]
programs[x] = programs[y]
programs[y] = z
return programs
if m == 'p':
xp, yp = i.split('/')
x = programs.index(xp)
y = programs.index(yp)
z = programs[x]
programs[x] = programs[y]
programs[y] = z
return programs
def _dance(self, programs, moves):
for m in moves:
programs = self._move(programs, m[0], m[1:])
return programs
def solve(self, puzzle_input, n=16):
programs = [chr(c) for c in range(97, 97 + n)]
moves = puzzle_input.split(',')
return ''.join(self._dance(programs, moves))
def solve_again(self, puzzle_input, n=16):
moves = puzzle_input.split(',')
initial = [chr(c) for c in range(97, 97 + n)]
programs = list(self.solve(puzzle_input))
dances = 1
while not programs == initial:
programs = self._dance(programs, moves)
dances += 1
for _ in range(10 ** 9 % dances):
programs = self._dance(programs, moves)
return ''.join(programs)
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,32 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '17.txt'
def __str__(self):
return 'Day 17: Spinlock'
def solve(self, puzzle_input):
n = int(puzzle_input)
l = [0]
pos = 0
for i in range(1, 2018):
pos = (pos + n) % i + 1
l.insert(pos, i)
return l[pos + 1 % 2017]
def solve_again(self, puzzle_input):
pos = 0
n = int(puzzle_input)
last_seen = 0
for i in range(1, 5 * 10 ** 7 + 1):
pos = (pos + n) % i + 1
if pos == 1:
last_seen = i
return last_seen
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,94 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '18.txt'
sound_freq = 0
queue = [], []
sent = [0, 0]
def __str__(self):
return 'Day 18: Duet'
def _run(self, line, registry, swag_mode=True):
actions = 'add jgz mod mul rcv set snd'
a, *kv = line.split()
if len(kv) == 2:
k, v = kv
else:
k = kv[0]
v = None
if a in actions:
if a == 'add' and k in registry:
registry[k] += registry[v] if v in registry else int(v)
if a == 'jgz': # damn you, 'jgz 1 3'
try:
k = int(k)
except ValueError:
k = registry[k] if k in registry else 0
try:
v = int(v)
except ValueError:
v = registry[v] if v in registry else 1
return v if k > 0 else 1
if a == 'mod' and k in registry:
registry[k] %= registry[v] if v in registry else int(v)
if a == 'mul' and k in registry:
registry[k] *= registry[v] if v in registry else int(v)
if a == 'set':
registry[k] = registry[v] if v in registry else int(v)
if swag_mode: # Part 1: scientific wild-ass guess
if a == 'rcv' and registry[k] != 0:
return self.STOP_SIGNAL
if a == 'snd' and k in registry:
self.sound_freq = registry[k]
else: # part 2, actual instructions
if a == 'rcv':
if len(self.queue[registry['_id']]) == 0:
return 0
registry[k] = self.queue[registry['_id']].pop(0)
if a == 'snd':
self.sent[registry['_id']] += 1
q = (registry['_id'] + 1) % 2
kk = registry[k] if k in registry else int(k)
self.queue[q].append(kk)
return 1
def solve(self, puzzle_input):
lines = puzzle_input.splitlines()
stop = len(lines)
self.STOP_SIGNAL = stop
i = 0
registry = {}
while i < stop:
i += self._run(lines[i], registry)
return self.sound_freq
def solve_again(self, puzzle_input):
registry = {'p': 0, '_id': 0}, {'p': 1, '_id': 1}
lines = puzzle_input.splitlines()
i = 0
j = 0
p0_queue = True
p1_queue = False
while p1_queue or p0_queue:
while p0_queue:
x = self._run(lines[i], registry[0], swag_mode=False)
if x == 0:
p0_queue = False
else:
i += x
p1_queue = len(self.queue[1]) > 0
while p1_queue:
x = self._run(lines[j], registry[1], swag_mode=False)
if x == 0:
p1_queue = False
else:
j += x
p0_queue = len(self.queue[0]) > 0
return self.sent[1]
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,63 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '19.txt'
trim_input = False
def __str__(self):
return 'Day 19: A Series of Tubes'
def _walk_maze(self, puzzle_input):
DIRECTIONS = {
'D': (1, 0),
'U': (-1, 0),
'R': (0, 1),
'L': (0, -1),
}
maze = puzzle_input.splitlines()
pos = (0, list(maze[0]).index('|'))
d = DIRECTIONS['D']
paths = '-|'
steps = 0
seen = ''
def _nc(nu):
np = (pos[0] + nu[0], pos[1] + nu[1])
if np[0] < len(maze) and np[1] < len(maze[np[0]]):
return maze[np[0]][np[1]]
else:
return ' '
while True:
pos = (pos[0] + d[0], pos[1] + d[1])
c = _nc((0, 0))
steps += 1
if c == '+':
nc = _nc(d)
if nc == ' ':
for v in DIRECTIONS.values():
if -v[0] == d[0] and -v[1] == d[1]:
continue
nc = _nc(v)
if nc != ' ':
d = v
break
elif c == ' ':
break
elif c not in paths:
seen += c
return seen, steps
def solve(self, puzzle_input):
seen, _ = self._walk_maze(puzzle_input)
return seen
def solve_again(self, puzzle_input):
_, steps = self._walk_maze(puzzle_input)
return steps
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,56 @@
import re
import collections
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '20.txt'
def __str__(self):
return 'Day 20: Particle Swarm'
def _get_particle(self, line):
return [list(map(int, coordinate.split(','))) for coordinate in re.findall(r'.=<([^>]+)>', line)]
def solve(self, puzzle_input, ticks=10 ** 4):
particles = [self._get_particle(line) for line in puzzle_input.splitlines()]
distances = [[] for _ in range(len(particles))]
for _ in range(ticks):
for i, particle in enumerate(particles):
p, v, a = particle
v[0] += a[0]
v[1] += a[1]
v[2] += a[2]
p[0] += v[0]
p[1] += v[1]
p[2] += v[2]
distances[i].append(sum(map(abs, p)))
d = sorted(map(lambda d: (d[0], sum(d[1]) / len(d[1])), enumerate(distances)), key=lambda x: x[1])
return d[0][0]
def solve_again(self, puzzle_input, ticks=10 ** 3):
particles = [self._get_particle(line) for line in puzzle_input.splitlines()]
for _ in range(ticks):
positions = collections.defaultdict(list)
for particle in particles:
p, v, a = particle
v[0] += a[0]
v[1] += a[1]
v[2] += a[2]
p[0] += v[0]
p[1] += v[1]
p[2] += v[2]
k = '-'.join(map(str, p))
positions[k].append(particle)
for duplicates in positions.values():
if len(duplicates) > 1:
for d in duplicates:
particles.remove(d)
return len(particles)
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,89 @@
import collections
from solutions import BaseSolution
class Rule:
size = 0
def __init__(self, line):
pattern, output = line.split(' => ')
self.pattern = pattern.replace('/', '')
self.output = output.replace('/', '')
self.size = int(len(self.pattern) ** 0.5)
self.patterns = [
self.pattern,
self._f(self.pattern),
self._r(self.pattern),
self._f(self._r(self.pattern)),
self._r(self._f(self.pattern)),
self._r(self._r(self.pattern)),
]
def __repr__(self):
return '[{}] {} -> {}'.format(self.size, self.pattern, self.output)
def _r(self, b):
return ''.join(map(lambda g: ''.join(g), zip(*[b[s:s + self.size] for s in range(0, len(b), self.size)][::-1])))
def _f(self, b):
return b[-self.size:] + b[self.size:len(b) - self.size] + b[0:self.size]
def matches(self, canvas):
return canvas in self.patterns
def enhance(self, canvas):
return self.output
class Solution(BaseSolution):
input_file = '21.txt'
def __str__(self):
return 'Day 21: Fractal Art'
def _get_block(self, canvas, size, n=0):
s = ''
cl = int(len(canvas) ** 0.5)
x = n % (cl // size)
y = n // (cl // size)
for r in range(size):
start = cl * ((y * size) + r) + (x * size)
end = start + size
s += canvas[start:end]
return s
def _join_blocks(self, blocks):
bl = len(blocks)
c = int(bl ** 0.5)
rl = int(len(blocks[0]) ** 0.5)
canvas = ''
for i in range(0, bl, c):
for j in range(rl):
canvas += ''.join([block[j * rl:(j + 1) * rl] for block in blocks[i:i + c]])
return canvas
def solve(self, puzzle_input, iterations=5):
canvas = '.#...####'
rules = [Rule(l.strip()) for l in puzzle_input.splitlines()]
for _ in range(iterations):
size = 2 if len(canvas) % 2 == 0 else 3
blocks = len(canvas) // (size ** 2)
cb = []
for b in range(blocks):
bc = self._get_block(canvas, size, b)
rule = [r for r in rules if r.matches(bc) and r.size == size]
if len(rule) > 0:
r = rule[0]
cb.append(r.enhance(bc))
canvas = self._join_blocks(cb)
return collections.Counter(canvas)['#']
def solve_again(self, puzzle_input):
return self.solve(puzzle_input, 18)
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,98 @@
from collections import Counter
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '22.txt'
def __str__(self):
return 'Day 22: Sporifica Virus'
infected = 0
def solve(self, puzzle_input, bursts=10000):
return self._calculate_infected(puzzle_input, bursts)
def solve_again(self, puzzle_input, bursts=10000000):
return self._calculate_infected(puzzle_input, bursts, evolved=True)
def _calculate_infected(self, puzzle_input, bursts, evolved=False):
dirs, amap, pos = self._setup(puzzle_input)
for _ in range(bursts):
amap, state = self._get_state(amap, pos)
dirs = self._change_dir(dirs, state)
if not evolved:
amap = self._update_state(amap, pos)
else:
amap = self._update_state_evolved(amap, pos)
pos = self._move(pos, dirs[0])
return self.infected
def _setup(self, puzzle_input):
self.infected = 0
dirs = [
(-1, 0), # up
(0, -1), # right
(1, 0), # down
(0, 1), # left
]
amap = []
pil = puzzle_input.strip().splitlines()
pos = [len(pil) // 2, len(pil[0]) // 2]
for n, line in enumerate(pil):
for nn, c in enumerate(line.strip()):
amap.append([n, nn, c])
return dirs, amap, pos
def _change_dir(self, dirs, state):
if state == ".":
dirs = dirs[1:] + [dirs[0]]
elif state == "#":
dirs = [dirs[3]] + dirs[:3]
elif state == 'F':
dirs = dirs[2:] + dirs[:2]
return dirs
def _get_state(self, amap, pos):
t = lambda x: x[1][0] == pos[0] and x[1][1] == pos[1]
existing = next(filter(t, enumerate(amap)), None)
if existing:
i, p = existing
return amap, p[2]
else:
amap.append([pos[0], pos[1], '.'])
return amap, '.'
def _update_state(self, amap, pos):
t = lambda x: x[1][0] == pos[0] and x[1][1] == pos[1]
existing = next(filter(t, enumerate(amap)))
i, p = existing
if p[2] == '.':
self.infected += 1
amap[i][2] = '.' if p[2] == '#' else '#'
return amap
def _move(self, pos, d):
return [pos[0] + d[0], pos[1] + d[1]]
def _update_state_evolved(self, amap, pos):
t = lambda x: x[1][0] == pos[0] and x[1][1] == pos[1]
existing = next(filter(t, enumerate(amap)))
i, p = existing
if p[2] == '.':
ns = 'W'
elif p[2] == 'W':
self.infected += 1
ns = "#"
elif p[2] == '#':
ns = "F"
else:
ns = "."
amap[i][2] = ns
return amap
if __name__ == '__main__':
solution = Solution()
solution.show_results()

View file

View file

@ -0,0 +1,25 @@
import unittest
from solutions.day_01 import Solution
class Day1TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_sums_equal_pairs(self):
assert self.solution.solve('1122') == 3
assert self.solution.solve('1111') == 4
assert self.solution.solve('1234') == 0
assert self.solution.solve('91212129') == 9
def test_sums_equal_pairs_halvway_around(self):
assert self.solution.solve_again('1212') == 6
assert self.solution.solve_again('1221') == 0
assert self.solution.solve_again('123425') == 4
assert self.solution.solve_again('123123') == 12
assert self.solution.solve_again('12131415') == 4
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,31 @@
import unittest
from solutions.day_02 import Solution
class Day2TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_calculates_row_difference(self):
assert self.solution.get_diff([5, 1, 9, 5]) == 8
assert self.solution.get_diff([7, 5, 3]) == 4
assert self.solution.get_diff([2, 4, 6, 8]) == 6
def test_calculates_checksum(self):
puzzle_input = '\n'.join(['5 1 9 5', '7 5 3', '2 4 6 8'])
assert self.solution.solve(puzzle_input) == 18
def test_calculates_row_even_divisible(self):
puzzle_input = '\n'.join(['5 9 2 8', '9 4 7 3', '3 8 6 5'])
assert self.solution.get_even_divisible([5, 9, 2, 8]) == 4
assert self.solution.get_even_divisible([9, 4, 7, 3]) == 3
assert self.solution.get_even_divisible([3, 8, 6, 5]) == 2
def test_calculates_row_result_sum(self):
puzzle_input = '\n'.join(['5 9 2 8', '9 4 7 3', '3 8 6 5'])
assert self.solution.solve_again(puzzle_input) == 9
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,18 @@
import unittest
from solutions.day_03 import Solution
class Day3TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_shortest_manhattan_distance(self):
assert self.solution.solve('1') == 0
assert self.solution.solve('12') == 3
assert self.solution.solve('23') == 2
assert self.solution.solve('1024') == 31
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,37 @@
import unittest
from solutions.day_04 import Solution
class Day4TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_passphrase_has_only_unique_words(self):
passphrases = [
'aa bb cc dd ee',
'aa bb cc dd aa',
'aa bb cc dd aaa',
]
assert self.solution.validate(passphrases[0]) == True
assert self.solution.validate(passphrases[1]) == False
assert self.solution.validate(passphrases[2]) == True
assert self.solution.solve('\n'.join(passphrases)) == 2
def test_passphrase_has_no_anagrams(self):
passphrases = [
'abcde fghij',
'abcde xyz ecdab',
'a ab abc abd abf abj',
'iiii oiii ooii oooi oooo',
'oiii ioii iioi iiio',
]
assert self.solution.validate(passphrases[0], True) == True
assert self.solution.validate(passphrases[1], True) == False
assert self.solution.validate(passphrases[2], True) == True
assert self.solution.validate(passphrases[3], True) == True
assert self.solution.validate(passphrases[4], True) == False
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,20 @@
import unittest
from solutions.day_05 import Solution
class Day5TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_calculate_exit_distance(self):
puzzle_input = '\n'.join(['0', '3', '0', '1', '-3',])
assert self.solution.solve(puzzle_input) == 5
def test_calculate_stranger_exit_distance(self):
puzzle_input = '\n'.join(['0', '3', '0', '1', '-3',])
assert self.solution.solve_again(puzzle_input) == 10
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,26 @@
import unittest
from solutions.day_06 import Solution
class Day6TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_count_redistribution_cycles(self):
puzzle_input = '0 2 7 0'
banks = list(map(int, puzzle_input.split()))
assert self.solution.redistribute(banks) == (2, 4, 1, 2)
assert self.solution.redistribute((2, 4, 1, 2)) == (3, 1, 2, 3)
assert self.solution.redistribute((3, 1, 2, 3)) == (0, 2, 3, 4)
assert self.solution.redistribute((0, 2, 3, 4)) == (1, 3, 4, 1)
assert self.solution.redistribute((1, 3, 4, 1)) == (2, 4, 1, 2)
assert self.solution.solve(puzzle_input) == 5
def test_count_redistribution_cycles_again(self):
puzzle_input = '0 2 7 0'
assert self.solution.solve_again(puzzle_input) == 4
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,42 @@
import unittest
from solutions.day_07 import Solution, Program
class Day7TestCase(unittest.TestCase):
def setUp(self):
self.puzzle_input = '''
pbga (66)
xhth (57)
ebii (61)
havc (66)
ktlj (57)
fwft (72) -> ktlj, cntj, xhth
qoyq (66)
padx (45) -> pbga, havc, qoyq
tknk (41) -> ugml, padx, fwft
jptl (61)
ugml (68) -> gyxo, ebii, jptl
gyxo (61)
cntj (57)
'''.strip()
self.solution = Solution()
def test_find_bottom_tower(self):
p = Program('ugml (68) -> gyxo, ebii, jptl')
assert p.name == 'ugml'
assert p.weight == 68
assert p.disc == ('gyxo', 'ebii', 'jptl')
p = Program('jptl (61)')
assert p.name == 'jptl'
assert p.weight == 61
assert p.disc == ()
assert self.solution.solve(self.puzzle_input).name == 'tknk'
def test_find_weight_correction(self):
corrected = self.solution.solve_again(self.puzzle_input)
assert corrected == 60
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,30 @@
import unittest
from solutions.day_08 import Solution
class Day8TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_largest_registry_value(self):
puzzle_input = '''
b inc 5 if a > 1
a inc 1 if b < 5
c dec -10 if a >= 1
c inc -20 if c == 10
'''.strip()
assert self.solution.solve(puzzle_input) == 1
def test_largest_ath_registry_value(self):
puzzle_input = '''
b inc 5 if a > 1
a inc 1 if b < 5
c dec -10 if a >= 1
c inc -20 if c == 10
'''.strip()
assert self.solution.solve_again(puzzle_input) == 10
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,31 @@
import unittest
from solutions.day_09 import Solution
class Day9TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_calculates_score(self):
assert self.solution.solve('{}') == 1
assert self.solution.solve('{{{}}}') == 6
assert self.solution.solve('{{},{}}') == 5
assert self.solution.solve('{{{},{},{{}}}}') == 16
assert self.solution.solve('{<a>,<a>,<a>,<a>}') == 1
assert self.solution.solve('{{<ab>},{<ab>},{<ab>},{<ab>}}') == 9
assert self.solution.solve('{{<!!>},{<!!>},{<!!>},{<!!>}}') == 9
assert self.solution.solve('{{<a!>},{<a!>},{<a!>},{<ab>}}') == 3
def test_count_garbage(self):
assert self.solution.solve_again('<>') == 0
assert self.solution.solve_again('<random characters>') == 17
assert self.solution.solve_again('<<<<>') == 3
assert self.solution.solve_again('<{!>}>') == 2
assert self.solution.solve_again('<!!>') == 0
assert self.solution.solve_again('<!!!>>') == 0
assert self.solution.solve_again('<{o"i!a,<{i<a>') == 10
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,38 @@
import unittest
from solutions.day_10 import Solution
class Day10TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_process_and_check(self):
self.solution.reset(5)
self.solution.reverse(3)
assert self.solution.list == [2, 1, 0, 3, 4]
assert self.solution.skip_size == 1
assert self.solution.pos == 3
self.solution.reverse(4)
assert self.solution.list == [4, 3, 0, 1, 2]
assert self.solution.skip_size == 2
assert self.solution.pos == 3
self.solution.reverse(1)
assert self.solution.list == [4, 3, 0, 1, 2]
assert self.solution.skip_size == 3
assert self.solution.pos == 1
self.solution.reverse(5)
assert self.solution.list == [3, 4, 2, 1, 0]
assert self.solution.skip_size == 4
assert self.solution.pos == 4
assert self.solution.solve('3,4,1,5', r=5) == 12
def test_dense_hash(self):
assert self.solution.solve_again('') == 'a2582a3a0e66e6e86e3812dcb672a272'
assert self.solution.solve_again('AoC 2017') == '33efeb34ea91902bb2f59c9920caa6cd'
assert self.solution.solve_again('1,2,3') == '3efbe78a8d82f29979031a4aa0b16a9d'
assert self.solution.solve_again('1,2,4') == '63960835bcdc130f0b66d7ff4f6a5a8e'
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,22 @@
import unittest
from solutions.day_11 import Solution
class Day11TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_distance(self):
assert self.solution.solve('ne,ne,ne') == 3
assert self.solution.solve('ne,ne,sw,sw') == 0
assert self.solution.solve('ne,ne,s,s') == 2
assert self.solution.solve('se,sw,se,sw,sw') == 3
def test_furthest_away(self):
assert self.solution.solve_again('ne,ne,sw,sw') == 2
assert self.solution.solve_again('se,sw,se,sw,sw') == 3
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,36 @@
import unittest
from solutions.day_12 import Solution
class Day12TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_connected_to_program0(self):
puzzle_input = '''
0 <-> 2
1 <-> 1
2 <-> 0, 3, 4
3 <-> 2, 4
4 <-> 2, 3, 6
5 <-> 6
6 <-> 4, 5
'''.strip()
assert self.solution.solve(puzzle_input) == 6
def test_group_coun(self):
puzzle_input = '''
0 <-> 2
1 <-> 1
2 <-> 0, 3, 4
3 <-> 2, 4
4 <-> 2, 3, 6
5 <-> 6
6 <-> 4, 5
'''.strip()
assert self.solution.solve_again(puzzle_input) == 2
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,30 @@
import unittest
from solutions.day_13 import Solution
class Day13TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_get_through_firewall(self):
puzzle_input = '''
0: 3
1: 2
4: 4
6: 4
'''.strip()
assert self.solution.solve(puzzle_input) == 24
def test_wait(self):
puzzle_input = '''
0: 3
1: 2
4: 4
6: 4
'''.strip()
assert self.solution.solve_again(puzzle_input) == 10
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,18 @@
import unittest
from solutions.day_14 import Solution
class Day14TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_used_squares(self):
assert self.solution.solve('flqrgnkx') == 8108
def test_regions(self):
assert self.solution.solve_again('flqrgnkx') == 1242
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,26 @@
import unittest
from solutions.day_15 import Solution
class Day15TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_40m_pairs(self):
puzzle_input = '''
Generator A starts with 65
Generator B starts with 8921
'''.strip()
#assert self.solution.solve(puzzle_input) == 588
def test_5k_pairs(self):
puzzle_input = '''
Generator A starts with 65
Generator B starts with 8921
'''.strip()
assert self.solution.solve_again(puzzle_input) == 309
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,16 @@
import unittest
from solutions.day_16 import Solution
class Day16TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_something(self):
puzzle_input = '''s1,x3/4,pe/b'''.strip()
assert self.solution.solve(puzzle_input, 5) == 'baedc'
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,15 @@
import unittest
from solutions.day_17 import Solution
class Day17TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_something(self):
assert self.solution.solve('3') == 638
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,39 @@
import unittest
from solutions.day_18 import Solution
class Day18TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_something(self):
puzzle_input = '''
set a 1
add a 2
mul a a
mod a 5
snd a
set a 0
rcv a
jgz a -1
set a 1
jgz a -2
'''.strip()
assert self.solution.solve(puzzle_input) == 4
def test_something_else(self):
puzzle_input = '''
snd 1
snd 2
snd p
rcv a
rcv b
rcv c
rcv d
'''.strip()
assert self.solution.solve_again(puzzle_input) == 3
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,34 @@
import unittest
from solutions.day_19 import Solution
class Day19TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_seen(self):
puzzle_input = ''' |
| +--+
A | C
F---|----E|--+
| | | D
+B-+ +--+
'''
assert self.solution.solve(puzzle_input) == 'ABCDEF'
def test_steps(self):
puzzle_input = ''' |
| +--+
A | C
F---|----E|--+
| | | D
+B-+ +--+
'''
assert self.solution.solve_again(puzzle_input) == 38
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,28 @@
import unittest
from solutions.day_20 import Solution
class Day20TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_shortest_distance_over_time(self):
puzzle_input = '''
p=< 3,0,0>, v=< 2,0,0>, a=<-1,0,0>
p=< 4,0,0>, v=< 0,0,0>, a=<-2,0,0>
'''.strip()
assert self.solution.solve(puzzle_input, 4) == 0
def test_something(self):
puzzle_input = '''
p=<-6,0,0>, v=< 3,0,0>, a=< 0,0,0>
p=<-4,0,0>, v=< 2,0,0>, a=< 0,0,0>
p=<-2,0,0>, v=< 1,0,0>, a=< 0,0,0>
p=< 3,0,0>, v=<-1,0,0>, a=< 0,0,0>
'''.strip()
assert self.solution.solve_again(puzzle_input, 4) == 1
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,19 @@
import unittest
from solutions.day_21 import Solution
class Day21TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_something(self):
puzzle_input = '''
../.# => ##./#../...
.#./..#/### => #..#/..../..../#..#
'''.strip()
assert self.solution.solve(puzzle_input, 2) == 12
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,30 @@
import unittest
from solutions.day_22 import Solution
class Day22TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_infected(self):
puzzle_input = """
..#
#..
...
"""
assert self.solution.solve(puzzle_input, 7) == 5
assert self.solution.solve(puzzle_input, 70) == 41
assert self.solution.solve(puzzle_input, 10000) == 5587
def test_evolved_infected(self):
puzzle_input = """
..#
#..
...
"""
assert self.solution.solve_again(puzzle_input, 100) == 26
assert self.solution.solve_again(puzzle_input, 10000000) == 2511944
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

25
2018-elixir/.gitignore vendored Normal file
View file

@ -0,0 +1,25 @@
# The directory Mix will write compiled artifacts to.
/_build/
# If you run "mix test --cover", coverage assets end up here.
/cover/
# The directory Mix downloads your dependencies sources to.
/deps/
# Where 3rd-party dependencies like ExDoc output generated docs.
/doc/
# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch
# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez
# Ignore package tarball (built via "mix hex.build").
aoc2018e-*.tar
.idea

21
2018-elixir/README.md Normal file
View file

@ -0,0 +1,21 @@
# Aoc2018e
**TODO: Add description**
## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `aoc2018e` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:aoc2018e, "~> 0.1.0"}
]
end
```
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at [https://hexdocs.pm/aoc2018e](https://hexdocs.pm/aoc2018e).

View file

@ -0,0 +1,30 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config
# This configuration is loaded before any dependency and is restricted
# to this project. If another project depends on this project, this
# file won't be loaded nor affect the parent project. For this reason,
# if you want to provide default values for your application for
# 3rd-party users, it should be done in your "mix.exs" file.
# You can configure your application as:
#
# config :aoc2018e, key: :value
#
# and access this configuration in your application as:
#
# Application.get_env(:aoc2018e, :key)
#
# You can also configure a 3rd-party app:
#
# config :logger, level: :info
#
# It is also possible to import configuration files, relative to this
# directory. For example, you can emulate configuration per environment
# by uncommenting the line below and defining dev.exs, test.exs and such.
# Configuration from the imported file will override the ones defined
# here (which is why it is important to import them last).
#
# import_config "#{Mix.env()}.exs"

993
2018-elixir/data/01.in Normal file
View file

@ -0,0 +1,993 @@
+3
+15
-1
-18
+3
-10
+3
+10
-8
-20
+13
+11
-21
-10
-16
-2
-6
+19
-8
+12
-13
+4
-9
-20
+12
+15
-17
-18
+11
-17
-9
-12
-9
+3
+9
-6
+18
+16
+9
-11
-1
+8
-20
+17
-3
-16
-7
+10
+6
+3
+15
+3
-2
+9
-8
-6
+11
+5
+24
+10
+4
-8
+2
+13
-16
+13
-16
+11
+7
-9
-13
-12
+17
-4
-5
-1
+16
+22
+8
+12
+9
+2
-3
-5
+12
+4
+4
+8
+18
+9
-14
-11
-5
-11
+15
-18
-7
-12
+1
+5
-18
+14
-13
-10
+17
+8
+11
-12
+20
-16
+22
+5
+1
-9
+16
+13
+6
+5
+7
-4
-4
-7
+16
+4
-1
-15
-11
+19
+9
+17
-15
+21
-7
+4
-16
-3
-9
+4
+11
+12
+11
+7
+12
+14
-6
-1
-18
-3
-13
+4
+10
-15
-10
-8
+15
+15
+19
+16
-3
-14
+13
+16
+1
+10
+9
+7
-11
-19
+2
-3
+10
-2
+13
-19
-7
+3
+18
+1
+9
+2
+12
-13
-9
-7
-15
-18
-6
-1
-8
-12
+19
-1
-14
-6
-16
-4
+1
+4
-22
-19
-2
+4
+18
-8
-19
-16
+6
+16
-7
+4
+8
-20
-11
-11
+20
+15
+19
+30
+19
+19
-11
-18
+4
+1
+21
-9
-9
+12
+18
-1
-6
+12
+16
+12
+3
+1
-9
-15
-15
-1
-1
-18
-32
-11
-17
-20
+4
+14
-1
+16
+20
+53
+19
+16
+14
+14
+6
-8
+14
-17
-5
+3
+14
-3
-15
-10
-19
+17
+3
+17
+5
+14
-7
+5
+14
+18
-2
-19
+8
+10
+15
-6
+13
-11
+19
+10
-15
-15
+2
-4
-3
+11
+2
+18
+3
+17
+9
-16
-8
+6
+15
-11
-3
+20
-1
+12
+1
+8
+11
-8
+6
+7
+17
-12
+13
-8
+16
+15
-8
-12
+7
-12
-18
+5
+5
+7
-8
+6
-4
-15
+17
-15
-10
-1
+17
+18
-21
-19
-9
+19
+16
+18
-5
-15
+19
-11
+10
+9
-1
+4
+4
+9
-5
+20
+4
-18
+17
+12
-18
+14
-21
-15
-5
-18
-17
-19
+11
+12
-20
-19
+12
-9
+8
-1
-22
+4
+15
+14
+9
-10
-26
-22
-10
+7
+9
-1
-16
-21
-18
+8
+6
+18
+1
+5
+11
+18
+6
+20
-7
-10
-5
+33
-21
-8
-13
-5
+7
+1
-7
-39
-14
-12
+16
-14
-15
+4
+2
+12
-11
+16
+8
+15
-1
+21
-5
+13
-18
-7
+2
-15
+19
+8
+26
-24
+37
+59
+21
-19
+18
+6
-11
+15
-2
+5
-2
+20
+8
+2
-7
+18
-8
-1
-8
+3
+17
+12
+18
+2
-8
+3
+40
+3
+21
+5
-1
+15
+2
+16
-50
+15
+5
+21
-31
-17
-15
-10
-9
-2
-18
-14
+7
-14
+6
-20
-8
+13
-27
+8
+2
-16
+3
-7
-6
+23
+31
+52
-6
-1
+2
-54
-42
-6
+18
+2
+1
-29
+36
-42
+7
-146
-2
-14
-13
+19
-8
+19
-15
-18
-4
-18
-1
+2
-3
+11
-16
-23
-9
-30
-31
+58
+18
-19
+45
+11
+14
-16
+19
-10
+17
+12
-2
-8
+19
-50
+8
-114
-40
-62
-153
-60
+12
-23
-40
-87
+377
-79652
-12
+1
-9
-5
-12
-14
+9
-3
-2
+8
-15
-18
+19
-6
+4
-14
-2
-18
-19
-8
+2
+11
-19
-11
+5
-3
+4
+1
-3
+9
+7
+15
-9
-2
-6
+3
+2
+10
+9
+3
-5
-4
+12
+15
-6
-11
-9
-15
+9
-19
-15
+4
+7
+7
-16
+15
-4
-19
-8
-11
-18
-14
-18
-16
+5
+5
+12
-9
-16
+2
-6
-2
+20
-13
-9
-16
+4
-1
+2
-3
-17
-2
-11
+1
+6
+17
-4
-15
-3
-19
+6
-16
-18
-6
+2
+12
-9
+6
+7
-6
+7
-9
-18
-3
+10
-17
+11
+16
+16
+6
+13
-17
+2
+3
+17
-10
+8
-1
-9
-15
+11
+2
-14
+7
-12
-19
-3
-7
-7
-15
-17
+5
+3
+14
+6
-5
+17
-9
-5
+1
-8
-13
+7
+17
-12
-14
-18
+10
+16
-3
-19
+7
+14
+14
+11
-1
+18
-16
-4
+10
-14
+9
-17
-1
+4
-14
-16
+1
-14
-7
+1
+7
-11
-4
-14
+19
-11
+8
+14
+8
-15
-18
+2
-17
-19
+13
+3
+6
+8
-10
+7
-3
-9
+8
+12
-4
+15
+7
+10
+11
-10
-20
-16
+11
-1
-21
-13
-19
-15
-15
-7
+10
+19
-17
-6
-10
-13
+12
-18
+16
+10
-13
+12
-1
+12
+12
+10
+16
-18
-5
-10
-13
-7
+19
+14
+18
-2
-4
-13
+14
+18
-7
+11
-13
-21
-12
+14
+16
+7
-19
+16
+7
+2
-5
+1
+19
+16
+14
+9
+12
+14
-8
-28
+13
+11
+13
-17
-21
+15
-37
-11
+19
+1
+8
+12
-1
-27
-21
-19
-21
+15
-24
-7
-11
-17
-15
-11
-11
+8
-9
-9
-4
-6
+15
-10
-3
+19
+9
+8
+6
+11
-4
-14
-1
+3
-15
+17
+18
-10
-22
-15
+14
+6
-14
-11
+4
-18
+5
-16
-9
-13
+14
+3
+9
+17
-9
+22
-12
-8
-5
-1
-6
-6
-19
+17
+9
+28
+20
+14
+11
+6
+7
-2
+17
+17
-12
+7
+21
-2
-4
+10
+2
+12
-19
-22
-8
-20
+25
-17
-15
-8
-1
+13
-15
+16
+15
+19
-128
-4
-16
-4
+13
-10
+20
-16
-13
+15
+9
+1
-11
-16
+13
+1
+15
+1
-31
+11
-53
-11
-9
-16
-11
+7
+6
+13
+6
-12
-3
+16
+2
-5
+7
-16
+17
-13
-9
+15
-3
+13
+8
+5
-8
-1
-5
-21
+25
+23
+12
+1
-2
+7
-25
-51
-5
+4
+14
-8
+4
+11
-5
-21
-14
+9
+8
-13
+4
+80915

250
2018-elixir/data/02.in Normal file
View file

@ -0,0 +1,250 @@
crruafyzloguvxwctqmphenbkd
srcjafyzlcguvrwctqmphenbkd
srijafyzlogbpxwctgmphenbkd
zrijafyzloguvxrctqmphendkd
srijabyzloguvowcqqmphenbkd
srijafyzsoguvxwctbmpienbkd
srirtfyzlognvxwctqmphenbkd
srijafyzloguvxwctgmphenbmq
senjafyzloguvxectqmphenbkd
srijafyeloguvxwwtqmphembkd
srijafyzlogurxtctqmpkenbkd
srijafyzlkguvxictqhphenbkd
srijafgzlogunxwctqophenbkd
shijabyzloguvxwctqmqhenbkd
srjoafyzloguvxwctqmphenbwd
srijafyhloguvxwmtqmphenkkd
srijadyzlogwvxwctqmphenbed
brijafyzloguvmwctqmphenhkd
smijafyzlhguvxwctqmphjnbkd
sriqafvzloguvxwctqmpheebkd
srijafyzloguvxwisqmpuenbkd
mrijakyuloguvxwctqmphenbkd
srnfafyzloguvxwctqmphgnbkd
srijadyzloguvxwhfqmphenbkd
srijafhzloguvxwctdmlhenbkd
srijafyzloguvxwcsqmphykbkd
srijafyzlogwvxwatqmphhnbkd
srijafyzlozqvxwctqmphenbku
srijafyzloguvxwcbamphenbgd
srijafyzlfguvxwctqmphzybkd
srijafyzloguqxwetqmphenkkd
srijafyylogubxwttqmphenbkd
srijafyzloguvxzctadphenbkd
srijafyzloguoxwhtqmchenbkd
srijafyzloguvxwcvqmzhenbko
srijnfyzloguvxwctqmchenjkd
srijaryzloggvxwctqzphenbkd
srijafhzleguvxwcxqmphenbkd
ssijafyzllguvxfctqmphenbkd
srijafyzloguvxdctqmfhenbcd
srijafyzloguvxfctqmplynbkd
srijaftzlogavxwcrqmphenbkd
sriwaoyzloguvxwctqmphenbtd
srijahyzlogunxwctqmphenbvd
srjjafyzloguzxwctumphenbkd
nrijafyzlxguvxwctqmphanbkd
srijafezlqguyxwctqmphenbkd
srijafygloguvxwjtqcphenbkd
erijafyzloguvxoctqmnhenbkd
ssijafyzllguvxwbtqmphenbkd
sriaafyzloguvxwctqqphenbkv
frijafyzloguvswctwmphenbkd
srijafyzyogkvxwctqmprenbkd
syijafyzuoguvxwctqmkhenbkd
srijafyzloganxwctqmphenbkf
srijafyzloguvxwftqmxhenbkq
srijafyflogxvxwctqmghenbkd
srijafyzsoguvxwctqmpjenwkd
srujafylloguvxwctqmphenckd
srijafyzlpzuvxwctqmphenbud
srijafyzlogfvxwctqmhhenbwd
srijafjzlogusxwctqmphepbkd
srijlfyzloguvxwctqfphenzkd
srijafyzlogwvxwctqyphenbqd
srijafyzloluvxwctqtphenukd
srizafyzlowuvxwctqmphqnbkd
sritafkzlkguvxwctqmphenbkd
sbijafdzloguvxgctqmphenbkd
crijafyeloguvxwctqmpsenbkd
srijafyvlogulxwctqmphenbkk
srijafyologuvxwctqmehegbkd
siijafyzloguvxwctjmphenbmd
srijafyzlupuvxwctqmpheabkd
srijafyzlogumxwctqqphanbkd
srijxfyzlogujxwcqqmphenbkd
irijafizeoguvxwctqmphenbkd
sgijafyzloguvtwctqmpfenbkd
srijzfyzloguvmwctnmphenbkd
srijafyzwohuvxwctqmthenbkd
srijafyzlhguvxoctqwphenbkd
srgjafyplogxvxwctqmphenbkd
srijafyqlogovxwctqzphenbkd
srijafjzloguvlnvtqmphenbkd
srijafyzooguvxwctqmphenvud
srijafyzgoguvxwctumphgnbkd
srijaffzloguvxwdqqmphenbkd
srijafyzlogugxwctqxphenbkr
srijafyzlogutxwctqmmcenbkd
srifafyzlhguwxwctqmphenbkd
mrimajyzloguvxwctqmphenbkd
sriyafyzloguvxwcthmphejbkd
srieakyzlokuvxwctqmphenbkd
srisafyzloguhxwctqmphecbkd
srijanyzloguvxcctqmxhenbkd
srijafyzypguvxwctqmqhenbkd
sryjtfyzlvguvxwctqmphenbkd
srijafyzlsguvxwctqmqfenbkd
srijafyzlogudxwbtqwphenbkd
srijysyzloguvxwctqmpvenbkd
srijafyzloggvxwjtqmphegbkd
srijgfyzloguvxwctqmbhdnbkd
ssijufyzloguvawctqmphenbkd
skojafyzloguvxwctqmphenbnd
srijafylloguvxwcqqmpienbkd
trioafyzloguvqwctqmphenbkd
srijafydloguvxwctqmpzjnbkd
saijafvzloguvxwcqqmphenbkd
srhjapyzloguvxwctqmbhenbkd
srijafyzlfguvxwcsqmpwenbkd
shijafyzboguvxwctqmphenbmd
srizafysloguvxwrtqmphenbkd
srijafyzloguvxwciqmwhenbkj
qrijafyzloduvxwctqmphenbko
srijefyuloguvxwctqmphenbed
srijafyzlobuvxwctqmphenhbd
srijafyzloxuvxwctqmpheabkq
srijafyzloguvrwctqmghenkkd
sfisafywloguvxwctqmphenbkd
srgjafyzlogurxwctqmphenbkp
srijafhzloguvxwcjqmphenhkd
srijafyylogufxwrtqmphenbkd
srijafyzvoguvxwzkqmphenbkd
sqijafyzloguvxwctqmpheqbxd
srijafyvloguvxwctqzpherbkd
srijufyzloguvxlcsqmphenbkd
srijafykloguvxlccqmphenbkd
srijafyzloguexwcrqmphenzkd
sridifyzloguyxwctqmphenbkd
srijafyzlogfvxwctqlphenbkl
srijafyzlodqdxwctqmphenbkd
srijafyzloruvxactqmphenekd
grijafyzloguvxpctmmphenbkd
srsjakyzloguvxwctqmphvnbkd
srikafyvloguvxwrtqmphenbkd
srijafyzloguvxwctqjpserbkd
jrijafyzloguvxwctqmpgesbkd
swijafyzluguvxwctqmfhenbkd
srijanynlogovxwctqmphenbkd
jrijafyzloguvxwctymphrnbkd
srinafyzloguvewctqmphenbzd
srijakyzloguvxwctqmphcnbka
srijafyhlobuvxwctqmphenbka
srijafyzcogusxwctqmphwnbkd
srijavyzlosuvxwctqmphjnbkd
orijafyzxoguvxwcnqmphenbkd
srijafyzlogcvxwvtqmthenbkd
srijapyzloauvxwctqmphenvkd
srijaflzloguhxwctqmphenbwd
smijafyzlonuvxwctqmphenbkw
jrijafyzloguvxwclqmnhenbkd
srijaqyzloguvqwctqmphenskd
srijasyzloguvxwctqmvhenbku
crijtfyzloguvxwctqmthenbkd
srrkafyzvoguvxwctqmphenbkd
srijatyzloguvewctqmphenbld
srfjafyyloguvnwctqmphenbkd
srijafyzloguvxwctqjpbenbkt
hrijafyzooguvxwctqmphenbld
srijafbzlogscxwctqmphenbkd
srinafyzlogxvxwctqqphenbkd
slijafyzloglvxwctqmphenbdd
srijafyzlogjvxwcsqmphenbld
sryjcfyzloguvewctqmphenbkd
srijafyzloguexwctqmohknbkd
jaijafyzlogevxwctqmphenbkd
srijafbzlogavxwctqmphenbki
srijafozlogpvxwctqmphgnbkd
srijdfyzloguvxwczqmphenbkm
srijafyzlobuvxwctqmphxndkd
mrijifyzlhguvxwctqmphenbkd
srijafyzloguvxbctumphjnbkd
srijafyzloyuvxwptqmphlnbkd
arijafyzloguvxwcsqmohenbkd
srijaftzioguvxwttqmphenbkd
srijafyzlqsuvxwctqmphxnbkd
srijafyzioguvxwctqnphetbkd
prijafbzloguvxdctqmphenbkd
srijaeyzlnguvxwmtqmphenbkd
srijofyzloguvqwctqmphonbkd
srixaryzpoguvxwctqmphenbkd
srijafyzlowuvxwcwhmphenbkd
srijafydloguvxwctqmptenikd
srijqfyzlogtvfwctqmphenbkd
srijafyzloguvxlctqmpvenbgd
srijafyzlbguvxwjtqgphenbkd
srijafyzlohuqxwctqmphenbka
srijafyzroguvxictqmphynbkd
srijafyzloguvxdctjmphenjkd
srijaoczloguvxwctqmphenbjd
srajafhzloguvxwctqmphenbke
srijofyzloduvxwctqmphanbkd
srijafytloguvxwmtnmphenbkd
srijafyzuoguvxwceqmpgenbkd
rrijafyzloyuvxwctqmphlnbkd
srljafyzloguvxictqmohenbkd
srijafyzlogulxwcrqrphenbkd
srajafyzloguvxwctqmphanbke
srijafyzlhguvxwxtqmpheabkd
sxijafyzloggwxwctqmphenbkd
srijafyultguvxwctqmphinbkd
srijafyzloguvtwctqmfhvnbkd
srijafwzloruvxwctquphenbkd
srbjafyzxoguuxwctqmphenbkd
erijafyzlxguvxbctqmphenbkd
srijagyzlojubxwctqmphenbkd
srijafyzloguvxwdtqmchenakd
srijafkzlogukxwctqiphenbkd
mridafyzloguvxwctqmphenrkd
szqjafyzloguvxwctqmpheibkd
srijahyzloguvxwctcmphenekd
srijafyzloguvxwczpuphenbkd
srijafyzcoguvfwctqmphenbkq
qriiafyzloguvxwctqmpheebkd
srijpfyzloguvxlctqmphenokd
srijzfyzlotuvxwcjqmphenbkd
srinafyqloguvxwctfmphenbkd
srijafyzlogjvxpltqmphenbkd
srijafyzlotuvxwutqmphenbtd
sridafyzloguvxwctqmpyenokd
srxjafyzqogyvxwctqmphenbkd
ssijafyzzoguvxwctqmphenbad
srijafrzloguvxwctqmphekpkd
srijafyzlfgrvxactqmphenbkd
srijafyzroguvxwttqmphekbkd
srijefyzloguvxwctqmpqenbrd
srijefycloguvxwctqmchenbkd
srzjafyzloguvxwcqqmphanbkd
srijauyzlhguvxwctqmphenbgd
srijafyzloguvmwvnqmphenbkd
srihafyzloguvlwotqmphenbkd
srigafyzloguvxwctqmphennsd
sriuafzzloguvxwcuqmphenbkd
srijavuzllguvxwctqmphenbkd
srijafjzloguvlnctqmphenbkd
lrirafyzloguvxwctqmphenbld
soijarxzloguvxwctqmphenbkd
srijapyzlnguvxwctqmdhenbkd
srijafyzkogujxmctqmphenbkd
srijafuzloguvxwcsqvphenbkd
srijagyzzoguvxwctqmpvenbkd
srijafyzlovuvxwctqmrhenbxd
srijafyzqoguvxwctwmpienbkd
sxijafyzloguvxwutqmphenlkd
srijafyzlhgzvxwctqmphqnbkd
srijajyzloguvxwcbwmphenbkd
srijazyzloguvxwhtqmphenbkx
srgjafyzloguvvwctqmphdnbkd
rrivafyzloguvxjctqmphenbkd
srijifyzdoguvxwctqmphenbka
hrijafyzloguvxectqmpheybkd

View file

@ -0,0 +1,7 @@
defmodule Aoc2018e do
alias Day01
def show_all do
Day01.show
end
end
Aoc2018e.show_all

44
2018-elixir/lib/day01.ex Normal file
View file

@ -0,0 +1,44 @@
defmodule Day01 do
defp find_recurring(data) do
Enum.reduce_while(data, {0, MapSet.new([0])}, fn term, {prev, known} ->
freq = prev + term
if MapSet.member?(known, freq) do
{:halt, freq}
else
{:cont, {freq, MapSet.put(known, freq)}}
end
end)
end
defp parse(data) do
String.split(data, "\n")
|> Enum.map(&String.trim/1)
|> Enum.map(&String.to_integer/1)
end
def solve(data) do
data
|> parse
|> Enum.sum
end
def solve_again(data) do
data
|> parse
|> Stream.cycle
|> find_recurring
end
def show do
data = File.read!("data/01.in")
:timer.tc(fn -> solve(data) end)
|> elem(0)
|> Kernel./(1_000_000)
|> IO.inspect
:timer.tc(fn -> solve_again(data) end)
|> elem(0)
|> Kernel./(1_000_000)
|> IO.inspect
end
end
Day01.show()

40
2018-elixir/lib/day02.ex Normal file
View file

@ -0,0 +1,40 @@
defmodule Day02 do
defp parse(data) do
data
|> String.split("\n", trim: true)
|> Enum.map(&String.trim/1)
end
def countn(data, n) do
data
|> Enum.filter(fn s -> MapSet.size(MapSet.new(String.to_charlist(s))) != String.length(s) - n + 1 end)
|> Enum.count
end
def solve(data) do
parsed = data
|> parse
f2 = parsed
|> countn(2)
f3 = parsed
|> countn(3)
f2 * f3
end
def solve_again(data) do
data
|> parse
end
def show do
data = File.read!("data/02.in")
:timer.tc(fn -> solve(data) end)
|> elem(0)
|> Kernel./(1_000_000)
|> IO.inspect
:timer.tc(fn -> solve_again(data) end)
|> elem(0)
|> Kernel./(1_000_000)
|> IO.inspect
end
end

28
2018-elixir/mix.exs Normal file
View file

@ -0,0 +1,28 @@
defmodule Aoc2018e.MixProject do
use Mix.Project
def project do
[
app: :aoc2018e,
version: "0.1.0",
elixir: "~> 1.7",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger]
]
end
# Run "mix help deps" to learn about dependencies.
defp deps do
[
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
]
end
end

View file

@ -0,0 +1,17 @@
defmodule Day01Test do
use ExUnit.Case
doctest Day01
test "adjust frequency" do
assert Day01.solve("+1\n+1\n+1") == 3
assert Day01.solve("+1\n+1\n-2") == 0
assert Day01.solve("-1\n-2\n-3") == -6
end
test "finds recurring frequency" do
assert Day01.solve_again("+1\n-1") == 0
assert Day01.solve_again("+3\n+3\n+4\n-2\n-4") == 10
assert Day01.solve_again("-6\n+3\n+8\n+5\n-6") == 5
assert Day01.solve_again("+7\n+7\n-2\n-7\n-4") == 14
end
end

View file

@ -0,0 +1,38 @@
defmodule Day02Test do
use ExUnit.Case
doctest Day02
setup do
puzzle_input = [
'abcdef',
'bababc',
'abbcde',
'abcccd',
'aabcdd',
'abcdee',
'ababab'
]
|> Enum.join("\n")
{:ok, puzzle_input: puzzle_input }
end
test "box id checksum", meta do
puzzle_input = [
"abcdef",
"bababc",
"abbcde",
"abcccd",
"aabcdd",
"abcdee",
"ababab"
]
assert Day02.countn(puzzle_input, 2) == 3
assert Day02.countn(puzzle_input, 3) == 4
#assert Day02.solve(puzzle_input) == 12
end
test "finds recurring frequency", meta do
puzzle_input = meta[:puzzle_input]
#assert Day02.solve_again(puzzle_input) == "fgij"
end
end

View file

@ -0,0 +1 @@
ExUnit.start()

3
2018-python/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
.idea
*.pyc
__pycache__

52
2018-python/README.md Normal file
View file

@ -0,0 +1,52 @@
Advent of Code 2018
===================
Lösningar för #aoc2018 i Python 3 (testat mot 3.7.1).
Hjälpscript
-----------
För att köra alla lösningar:
python aoc.py
För att starta en ny dag (skapar och populerar filerna `inputs/<dagnummer>.txt`, `solutions/day_<dagnummer>.py` och
`tests/day_<dagnummer>_tests.py`):
python aoc.py <dagnummer_utan_nolla> "<namn dag>"
Öppna puzzle input manuellt och kopiera innehållet till `inputs/<dagnummer>.txt` som ett sista manuellt steg.
För att köra separat lösning (ersätt `XX` med dagens nummer):
PYTHONPATH=$(pwd) python solutions/day_XX.py
Starta automatisk testkörare (ersätt `XX` med dagens nummer):
export PYTHONPATH=$(pwd)
ls solutions/**/*.py | entr -c -r python tests/day_XX_tests.py
Logg
----
* Dag 1: Insikten när en for-loop kan ersättas med en `sum`. xD Dagens `itertools`: `cycle()`
* Dag 2: Dagens `itertools`: `combinations()`, efter många försök att vara för smart med `zip()`.
* Dag 3: Dagens `itertools` (ja, det verkar vara ett tema!): `product()`. Två nästlade for-loopar kändes trist.
* Dag 4: Mycket text och mycket kod. Dagens `itertools`: `chain()`.
* Dag 5: Krånglade till saker genom att köra listor istället för strängar, skrev om till att istället använda en
`reduce`. Inga `itertools`. :(
* Dag 6: Längsta körtiden hittills och kan högst troligtvis optimeras.
* Dag 7: Svårtolkad uppgift. Otäckt med workers. Rant i [kodkommentar om icke hjälpsamt
exempel](https://github.com/madr/redesigned-system/blob/master/solutions/day_07.py#L63-L70).
* Dag 8: Rekursion! Invigning av `sys.setrecursionlimit()`.
* Dag 9: Harakiri.
* Dag 10: Kult med visualisering! Fick tipset att leta efter minsta bounds, och kom då fram till den magiska siffran
`10391`. Det krävdes en hel del optimering, då första versionen var slö.
* Dag 11: [Summed-area table](https://en.wikipedia.org/wiki/Summed-area_table). Lösning 2 behöver refaktoriseras för
att använda partial sum och summed-area table, den tar 8-12h att köra med brute force.
* Dag 12: Otydliga instruktioner. [Inte bara enligt mig](https://www.reddit.com/r/adventofcode/comments/a5gt7h/day12_part_1_explanation_for_the_example/),
[vad det verkar](https://www.reddit.com/r/adventofcode/comments/a5eztl/2018_day_12_solutions/).
* Day 13: Ännu en dag med ett exempel som var vilseledande för att testa ens kod.
[Rant i kodkommentar](https://github.com/madr/redesigned-system/blob/master/solutions/day_13.py#L108-L141).
* Day 14:
* Dag 15:

66
2018-python/aoc.py Normal file
View file

@ -0,0 +1,66 @@
import sys
try:
_, day_no, name = sys.argv
except ValueError:
day_no = None
name = None
if day_no and name:
with open('solutions/day_{}.py'.format(day_no.zfill(2)), 'w') as s:
s.write('''
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '{day_no}.in'
def __str__(self):
return 'Day {day}: {name}'
def solve(self, puzzle_input):
pass
def solve_again(self, puzzle_input):
pass
if __name__ == '__main__':
solution = Solution()
solution.show_results()
'''.strip().format(day=day_no, day_no=day_no.zfill(2), name=name) + '\n')
with open('tests/day_{}_tests.py'.format(day_no.zfill(2)), 'w') as t:
t.write('''
import unittest
from solutions.day_{day_no} import Solution
class Day{day_no}TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_something(self):
puzzle_input = ''
assert self.solution.solve(puzzle_input) == True
if __name__ == '__main__':
unittest.main()
'''.strip().format(day_no=day_no.zfill(2)) + '\n')
with open('inputs/{}.in'.format(day_no.zfill(2)), 'w') as i:
i.write('')
exit(0)
print('\nAdvent of Code 2018'
'\n###################'
'\n\nby Anders Ytterström (@madr)')
for i in [str(n).zfill(2) for n in range(1, 26)]:
try:
solution = __import__('solutions.day_{}'.format(i), globals(), locals(), ['Solution'], 0).Solution()
solution.show_results()
except IOError:
pass
except ImportError:
pass

View file

@ -0,0 +1,36 @@
import time
from datetime import timedelta
class BaseSolution:
puzzle_input = ""
input_file = None
trim_input = True
def parse_input(self, filename):
filepath = './inputs/{}'.format(filename)
with open(filepath, 'r') as f:
self.puzzle_input = f.read()
if self.trim_input:
self.puzzle_input = self.puzzle_input.strip()
def show_results(self):
self.parse_input(self.input_file)
start_time = time.monotonic()
p1 = self.solve(self.puzzle_input)
p2 = self.solve_again(self.puzzle_input)
end_time = time.monotonic()
duration = timedelta(seconds=end_time - start_time)
print('\n\n{}\n{}\n\nPart 1: {}\nPart 2: {}\n\nDuration: {}'.format(
str(self),
'-' * len(str(self)),
p1,
p2,
duration,
))
def solve(self, puzzle_input):
raise NotImplemented
def solve_again(self, puzzle_input):
raise NotImplemented

View file

@ -0,0 +1,28 @@
import itertools
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '01.in'
def __str__(self):
return 'Day 1: Chronal Calibration'
def solve(self, puzzle_input, freq=0):
return sum(map(int, puzzle_input.splitlines()))
def solve_again(self, puzzle_input, freq=0):
freq_changes = map(int, puzzle_input.splitlines())
known = {0}
for n in itertools.cycle(freq_changes):
freq += n
if freq in known:
break
known.add(freq)
return freq
if __name__ == '__main__':
solution = Solution()
solution.show_results()

Some files were not shown because too many files have changed in this diff Show more