Compare commits

...

2 Commits

Author SHA1 Message Date
Shadowfacts f10b2fdf99
Day 8 2019-12-08 11:00:17 -05:00
Shadowfacts d4e68f6062
Add assembler macros 2019-12-08 10:36:49 -05:00
6 changed files with 206 additions and 63 deletions

View File

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

71
lib/day8/day8.ex Normal file
View File

@ -0,0 +1,71 @@
defmodule Day8 do
def part1 do
File.read!("lib/day8/input.txt")
|> parse_input()
|> split_into_layers(25, 6)
|> min_zeros()
|> ones_times_twos()
# "123456789012"
# |> parse_input()
# |> split_into_layers(3, 2)
# |> min_zeros()
# |> ones_times_twos()
end
def part2 do
File.read!("lib/day8/input.txt")
|> parse_input()
|> split_into_layers(25, 6)
|> combine_layers()
|> Enum.map(fn
0 -> ?\s
1 -> ?X
end)
|> Enum.chunk_every(25)
|> Enum.each(&IO.puts/1)
# "0222112222120000"
# |> parse_input()
# |> split_into_layers(2, 2)
# |> combine_layers()
end
def parse_input(str) do
str
|> String.trim()
|> String.to_charlist()
|> Enum.map(fn digit -> digit - ?0 end)
end
def split_into_layers(input, layer_width, layer_height) do
Enum.chunk_every(input, layer_width * layer_height)
end
def min_zeros(layers) do
layers
|> Enum.min_by(fn layer ->
Enum.count(layer, &(&1 == 0))
end)
end
def ones_times_twos(layer) do
ones = Enum.count(layer, &(&1 == 1))
twos = Enum.count(layer, &(&1 == 2))
ones * twos
end
def combine_layers(layers) do
Enum.zip(layers)
|> Enum.map(&combine_pixel/1)
end
def combine_pixel(pixel) do
0..(tuple_size(pixel) - 1)
|> Enum.map(fn index -> elem(pixel, index) end)
|> Enum.reduce(2, fn
val, 2 -> val
_, it -> it
end)
end
end

1
lib/day8/input.txt Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,17 @@
defmodule Assembler.Helpers do
defmacro assemble_macro(match, asm) do
quote do
def assemble_insn(unquote(match), memory) do
{macro_mem, _} = do_assemble(unquote(asm), length(memory))
macro_mem ++ memory
end
end
end
end
defmodule Assembler do
import NimbleParsec
import Assembler.Helpers
label =
ascii_string([?a..?z, ?A..?Z, ?_], min: 1)
@ -21,7 +33,7 @@ defmodule Assembler do
|> ascii_string([{:not, ?,}], min: 1)
instruction =
ignore(ascii_string([?\s], 2))
ignore(ascii_string([?\s, ?\t], min: 0))
|> ascii_string([?a..?z], min: 1)
|> repeat(param |> ignore(ascii_char([?,])))
|> optional(param)
@ -37,45 +49,14 @@ defmodule Assembler do
])
)
@asm """
in res
clt $res, _self + 1, cmpRes
jnz $cmpRes, lessThan
ceq $res, 8, cmpRes
jnz $cmpRes, equal
out 1001
hlt
lessThan:
out 999
hlt
equal:
out 1000
hlt
res: 0
cmpRes: 0
"""
def test do
assemble(@asm)
File.read!("test/intcode/cmp8.asm")
|> assemble()
|> Day5.run_cli()
end
def assemble(asm) do
{memory, labels} =
asm
|> String.trim_trailing("\n")
|> String.split("\n")
|> Enum.map(fn line ->
case parse_line(line) do
{:ok, res, _, _, _, _} ->
res
err ->
raise "Unable to parse line: '#{line}':\n#{inspect(err)}"
end
end)
|> Enum.reduce({[], %{}}, &assemble_line/2)
def assemble(asm, offset \\ 0) do
{memory, labels} = do_assemble(asm, offset)
memory
|> Enum.reverse()
@ -92,23 +73,47 @@ defmodule Assembler do
end)
end
def assemble_line([], acc), do: acc
def do_assemble(asm, offset) do
asm
|> String.trim_trailing("\n")
|> String.split("\n")
|> Enum.map(fn line ->
case parse_line(line) do
{:ok, res, _, _, _, _} ->
res
def assemble_line([label: [name]], {memory, labels}) do
err ->
raise "Unable to parse line: '#{line}':\n#{inspect(err)}"
end
end)
|> Enum.reduce({[], %{}}, fn line, acc ->
assemble_line(line, acc, offset)
end)
end
def assemble_line([], acc, _), do: acc
def assemble_line([label: [name]], {memory, labels}, offset) do
{
memory,
Map.put(labels, name, length(memory))
Map.put(labels, name, length(memory) + offset)
}
end
def assemble_line([data_label: [name, value]], {memory, labels}) do
def assemble_line([data_label: [name, value]], {memory, labels}, _) do
value =
case value do
{:neg, [val]} -> -val
val when is_integer(val) -> val
end
{
[value | memory],
Map.put(labels, name, length(memory))
}
end
def assemble_line([insn: insn], {memory, labels}) do
def assemble_line([insn: insn], {memory, labels}, _) do
{
assemble_insn(insn, memory),
labels
@ -159,6 +164,46 @@ defmodule Assembler do
[99 | memory]
end
# macros
assemble_macro ["mov", src, dest],
"""
add 0, #{src}, #{dest}
"""
assemble_macro ["jmp", dest],
"""
jnz _self, #{dest}
"""
assemble_macro ["sub", a, b, dest],
"""
mul #{b}, -1, #{dest}
add #{a}, $#{dest}, #{dest}
"""
assemble_macro ["cle", a, b, dest],
"""
clt #{a}, #{b}, #{dest}
jnz $#{dest}, end
ceq #{a}, #{b}, #{dest}
end:
"""
assemble_macro ["cgt", a, b, dest],
"""
clt #{a}, #{b}, #{dest}
jnz $#{dest}, false
ceq #{a}, #{b}, #{dest}
jnz $#{dest}, false
mov 1, #{dest}
jmp end
false:
mov 0, #{dest}
end:
"""
@doc """
Raises the base to to the given power.

View File

@ -8,28 +8,34 @@ defmodule AssemblerTest do
end
test "assembles instructions correctly" do
assert assemble(" add 1, 2, 3") == [1101, 1, 2, 3]
assert assemble(" mul 1, 2, 3") == [1102, 1, 2, 3]
assert assemble(" in 7") == [3, 7]
assert assemble(" out 7") == [104, 7]
assert assemble(" out $7") == [4, 7]
assert assemble(" jnz 1, 2") == [1105, 1, 2]
assert assemble(" jnz $1, 2") == [1005, 1, 2]
assert assemble(" jez 1, 2") == [1106, 1, 2]
assert assemble(" jez $1, 2") == [1006, 1, 2]
assert assemble(" clt 1, 2, 3") == [1107, 1, 2, 3]
assert assemble(" clt $1, $2, $3") == [7, 1, 2, 3]
assert assemble(" ceq 1, 2, 3") == [1108, 1, 2, 3]
assert assemble(" ceq $1, $2, $3") == [8, 1, 2, 3]
assert assemble(" hlt") == [99]
assert assemble("add 1, 2, 3") == [1101, 1, 2, 3]
assert assemble("mul 1, 2, 3") == [1102, 1, 2, 3]
assert assemble("in 7") == [3, 7]
assert assemble("out 7") == [104, 7]
assert assemble("out $7") == [4, 7]
assert assemble("jnz 1, 2") == [1105, 1, 2]
assert assemble("jnz $1, 2") == [1005, 1, 2]
assert assemble("jez 1, 2") == [1106, 1, 2]
assert assemble("jez $1, 2") == [1006, 1, 2]
assert assemble("clt 1, 2, 3") == [1107, 1, 2, 3]
assert assemble("clt $1, $2, $3") == [7, 1, 2, 3]
assert assemble("ceq 1, 2, 3") == [1108, 1, 2, 3]
assert assemble("ceq $1, $2, $3") == [8, 1, 2, 3]
assert assemble("hlt") == [99]
end
test "assembles macros" do
assert assemble("mov 1, 2") == [1101, 0, 1, 2]
assert assemble("jmp 2") == [1105, 1, 2]
assert assemble("sub 5, 3, res\nres: 0") == [1102, 3, -1, 8, 101, 5, 8, 8, 0]
end
test "assembles simple program" do
program = """
add 1, 2, 10
mul 2, 3, 11
add $10, $11, 12
hlt
add 1, 2, 10
mul 2, 3, 11
add $10, $11, 12
hlt
"""
assert assemble(program) == [
@ -96,8 +102,8 @@ defmodule AssemblerTest do
end
test "assembles parameters with expressions" do
assert assemble(" add 1, _self + 1, 3") == [1101, 1, 3, 3]
assert assemble(" add 1, $(_self + 1), 3") == [101, 1, 3, 3]
assert assemble("add 1, _self + 1, 3") == [1101, 1, 3, 3]
assert assemble("add 1, $(_self + 1), 3") == [101, 1, 3, 3]
assert assemble("""
add 1, label + 4 - 2, 3

19
test/intcode/cmp8.asm Normal file
View File

@ -0,0 +1,19 @@
in res
clt $res, 8, cmpRes
jnz $(res + 1), lessThan
ceq $res, 8, cmpRes
jnz $cmpRes, equal
out 1001
hlt
lessThan:
out 999
hlt
equal:
out 1000
hlt
res: 0
cmpRes: 0