Skip to content

Commit

Permalink
Day 11 in Erlang
Browse files Browse the repository at this point in the history
  • Loading branch information
Anthony Hayward committed May 15, 2020
1 parent 7932081 commit 9b39650
Show file tree
Hide file tree
Showing 6 changed files with 442 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.beam
*.exe
*.pdb
*.pyc
target
24 changes: 24 additions & 0 deletions day11_robot/erlang/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
```
erl
```

then

```
c("computer").
c("day11_robot_part_1").
day11_robot_part_1:paint_hull().
# 1747
c("day11_robot_part_2").
day11_robot_part_2:paint_hull().
XXXXXXXX XXXX XXXX XXXXXX XX XX XX XX XX XXXXXX
XX XX XX XX XX XX XX XX XX XX XX XX XX XX
XX XX XX XX XX XXXXXXXX XXXX XX XXXXXX
XX XX XX XXXX XXXXXX XX XX XX XX XX XX XX
XX XX XX XX XX XX XX XX XX XX XX XX XX XX
XXXXXXXX XXXX XXXXXX XX XX XX XX XX XX XXXXXXXX XXXXXX
173 changes: 173 additions & 0 deletions day11_robot/erlang/computer.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
-module(computer).

-export([load_program/1, set_inputs/2, exec/1, get_outputs/1, clear_outputs/1, get_state/1]).


-record(computer, {
mem, % memory contents
ip, % instruction pointer
relative_base, % base for relative mode access
state, % waiting for input or halted?
inputs, % list of available input values
outputs, % list of available output values
output_connections
}).


peek(Mem, Address) when Address < length(Mem) ->
lists:nth(Address + 1, Mem);
peek(_, _) ->
0.


read(Mem, Address, RelativeBase, Mode) ->
Index = peek(Mem, Address),
case Mode of
indirect -> peek(Mem, Index);
direct -> Index;
relative -> peek(Mem, RelativeBase + Index)
end.


address_from(Mem, Address, RelativeBase, Mode) ->
Index = peek(Mem, Address),
case Mode of
indirect -> Index;
direct -> error;
relative -> RelativeBase + Index
end.


poke(Mem, Address, Value) when Address < length(Mem) ->
lists:sublist(Mem, Address) ++ [Value] ++ lists:nthtail(Address + 1, Mem);
poke(Mem, Address, Value) when Address =:= length(Mem) ->
Mem ++ [Value];
poke(Mem, Address, Value) ->
poke(Mem ++ [0], Address, Value).


param_mode(ParamSpec) ->
case ParamSpec of
0 -> indirect;
1 -> direct;
2 -> relative
end.


read_param_1(#computer{mem=Mem, ip=IP, relative_base=RelativeBase}) ->
read(Mem, IP + 1, RelativeBase, param_mode((peek(Mem, IP) div 100) rem 10)).


read_param_2(#computer{mem=Mem, ip=IP, relative_base=RelativeBase}) ->
read(Mem, IP + 2, RelativeBase, param_mode((peek(Mem, IP) div 1000) rem 10)).


address_from_param_1(#computer{mem=Mem, ip=IP, relative_base=RelativeBase}) ->
address_from(Mem, IP + 1, RelativeBase, param_mode((peek(Mem, IP) div 100) rem 10)).


address_from_param_3(#computer{mem=Mem, ip=IP, relative_base=RelativeBase}) ->
address_from(Mem, IP + 3, RelativeBase, param_mode((peek(Mem, IP) div 10000) rem 10)).


exec(Computer) ->
Mem = Computer#computer.mem,
IP = Computer#computer.ip,
Cmd = peek(Mem, IP),
case Cmd rem 100 of
1 -> % add
A = read_param_1(Computer),
B = read_param_2(Computer),
ResultAddress = address_from_param_3(Computer),
exec(Computer#computer{mem=poke(Mem, ResultAddress, A + B), ip=IP + 4});
2 -> % mul
A = read_param_1(Computer),
B = read_param_2(Computer),
ResultAddress = address_from_param_3(Computer),
exec(Computer#computer{mem=poke(Mem, ResultAddress, A * B), ip=IP + 4});
3 -> % input
ResultAddress = address_from_param_1(Computer),
case Computer#computer.inputs of
[InputValue|Rest] ->
exec(Computer#computer{mem=poke(Mem, ResultAddress, InputValue), ip=IP + 2, inputs=Rest});
[] ->
Computer#computer{state=waiting_for_input}
end;
4 -> % output
A = read_param_1(Computer),
exec(Computer#computer{ip=IP + 2, outputs=Computer#computer.outputs ++ [A]});
5 -> % jump if true
A = read_param_1(Computer),
B = read_param_2(Computer),
case A of
0 ->
exec(Computer#computer{ip=IP + 3});
_ ->
exec(Computer#computer{ip=B})
end;
6 -> % jump if false
A = read_param_1(Computer),
B = read_param_2(Computer),
case A of
0 ->
exec(Computer#computer{ip=B});
_ ->
exec(Computer#computer{ip=IP + 3})
end;
7 -> % less than
A = read_param_1(Computer),
B = read_param_2(Computer),
ResultAddress = address_from_param_3(Computer),
R = if A < B -> 1; true -> 0 end,
exec(Computer#computer{mem=poke(Mem, ResultAddress, R), ip=IP + 4});
8 -> % equals
A = read_param_1(Computer),
B = read_param_2(Computer),
ResultAddress = address_from_param_3(Computer),
R = if A =:= B -> 1; true -> 0 end,
exec(Computer#computer{mem=poke(Mem, ResultAddress, R), ip=IP + 4});
9 -> % add to relative base
A = read_param_1(Computer),
exec(Computer#computer{relative_base=Computer#computer.relative_base + A, ip=IP + 2});
99 -> % halt
Computer#computer{state=halted}
end.


parse_line(CurrentInstruction, [$,|Rest]) ->
[list_to_integer(CurrentInstruction)] ++ parse_line("", Rest);
parse_line(CurrentInstruction, "\n") ->
[list_to_integer(CurrentInstruction)];
parse_line(CurrentInstruction, "") ->
[list_to_integer(CurrentInstruction)];
parse_line(CurrentInstruction, [A|Rest]) ->
parse_line(CurrentInstruction ++ [A], Rest).


load_program(Device) ->
case io:get_line(Device, "") of
eof ->
error;
Line ->
#computer{
mem=parse_line("", Line), ip=0, relative_base=0, state=ready,
inputs=[],
outputs=[], output_connections=output
}
end.


set_inputs(Computer, Inputs) ->
Computer#computer{inputs=Inputs}.


get_outputs(#computer{outputs=Outputs}) ->
Outputs.


clear_outputs(Computer) ->
Computer#computer{outputs=[]}.


get_state(#computer{state=State}) ->
State.
48 changes: 48 additions & 0 deletions day11_robot/erlang/day11_robot_part_1.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
-module(day11_robot_part_1).

-import(computer, [load_program/1, set_inputs/2, exec/1, get_outputs/1, clear_outputs/1, get_state/1]).
-export([paint_hull/0]).


-record(robot, {
x,
y,
xd,
yd
}).


paint_hull() ->
{ok, Device} = file:open("../data/puzzle_input.csv", [read]),
Computer = try computer:load_program(Device)
after file:close(Device)
end,
map_size(do_paint_hull(Computer, #robot{x=0, y=0, xd=0, yd=-1}, #{})).


get_panel_colour(Hull, X, Y) ->
try maps:get({X, Y}, Hull)
catch
error:_ -> 0
end.


paint_panel(Hull, X, Y, Colour) ->
maps:put({X, Y}, Colour, Hull).


do_paint_hull(Computer, #robot{x=X, y=Y, xd=XD, yd=YD}, Hull) ->
Colour = get_panel_colour(Hull, X, Y),
NewComputer = computer:exec(computer:set_inputs(Computer, [Colour])),
case computer:get_state(NewComputer) of
halted ->
Hull;
_ ->
[NewColour, Turn] = computer:get_outputs(NewComputer),
NewHull = paint_panel(Hull, X, Y, NewColour),
NewRobot = case Turn of
0 -> #robot{x=X + YD, y=Y - XD, xd=YD, yd=-XD};
1 -> #robot{x=X - YD, y=Y + XD, xd=-YD, yd=XD}
end,
do_paint_hull(computer:clear_outputs(NewComputer), NewRobot, NewHull)
end.
92 changes: 92 additions & 0 deletions day11_robot/erlang/day11_robot_part_2.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
-module(day11_robot_part_2).

-import(computer, [load_program/1, set_inputs/2, exec/1, get_outputs/1, clear_outputs/1, get_state/1]).
-export([paint_hull/0]).


-record(robot, {
x,
y,
xd,
yd
}).


paint_hull() ->
{ok, Device} = file:open("../data/puzzle_input.csv", [read]),
Computer = try computer:load_program(Device)
after file:close(Device)
end,
Hull = do_paint_hull(Computer, #robot{x=0, y=0, xd=0, yd=-1},
paint_panel(#{}, 0, 0, 1)),
print_hull_panel_pattern(Hull).


get_panel_colour(Hull, X, Y) ->
try maps:get({X, Y}, Hull)
catch
error:_ -> 0
end.


paint_panel(Hull, X, Y, Colour) ->
maps:put({X, Y}, Colour, Hull).


do_paint_hull(Computer, #robot{x=X, y=Y, xd=XD, yd=YD}, Hull) ->
Colour = get_panel_colour(Hull, X, Y),
NewComputer = computer:exec(computer:set_inputs(Computer, [Colour])),
case computer:get_state(NewComputer) of
halted ->
Hull;
_ ->
[NewColour, Turn] = computer:get_outputs(NewComputer),
NewHull = paint_panel(Hull, X, Y, NewColour),
NewRobot = case Turn of
0 -> #robot{x=X + YD, y=Y - XD, xd=YD, yd=-XD};
1 -> #robot{x=X - YD, y=Y + XD, xd=-YD, yd=XD}
end,
do_paint_hull(computer:clear_outputs(NewComputer), NewRobot, NewHull)
end.


print_hull_panel_pattern(Hull) ->
{_, _, MinY, _} = Extent = find_pattern_extent({0, 0, 0, 0}, maps:to_list(Hull)),
print_next_hull_row(MinY, Extent, Hull).


find_pattern_extent({MinX, MaxX, MinY, MaxY}, [{{X, Y}, _}|Rest]) ->
find_pattern_extent({
if X < MinX -> X; true -> MinX end,
if X > MaxX -> X; true -> MaxX end,
if Y < MinY -> Y; true -> MinY end,
if Y > MaxY -> Y; true -> MaxY end
}, Rest);
find_pattern_extent(Extent, []) ->
Extent.


print_next_hull_row(Y, {MinX, MaxX, _, MaxY} = Extent, Hull) ->
print_hull_row(MinX, Y, MaxX, Hull),
io:fwrite("~n"),
if
Y < MaxY ->
print_next_hull_row(Y + 1, Extent, Hull);
true ->
{}
end.


print_hull_row(X, Y, MaxX, Hull) ->
io:fwrite("~s", [
case get_panel_colour(Hull, X, Y) of
0 -> " ";
1 -> "XX"
end
]),
if
X < MaxX ->
print_hull_row(X + 1, Y, MaxX, Hull);
true ->
{}
end.
Loading

0 comments on commit 9b39650

Please sign in to comment.