Skip to content

Instantly share code, notes, and snippets.

@ferd

ferd/day11.erl Secret

Last active December 11, 2019 14:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ferd/c43066944de8c7283f1b82d5a998e012 to your computer and use it in GitHub Desktop.
Save ferd/c43066944de8c7283f1b82d5a998e012 to your computer and use it in GitHub Desktop.
%%% @doc
%%% welcome to day ELEVEN, the day after the longest day so far
%%% @end
-module(day11).
-export([p1/0, p2/0]).
p1() ->
Prog = parse_program(advent:input("day11")),
Parent = self(),
Bot = spawn_link(fun() ->
Parent ! {result, self(), init_fsm(#{{0,0} => black})}
end),
Pid = spawn_program(Prog, Bot),
Bot ! {intcode_pid, Pid},
collect_result(Pid),
Bot ! halt,
maps:size(collect_result(Bot)).
p2() ->
Prog = parse_program(advent:input("day11")),
Parent = self(),
Bot = spawn_link(fun() ->
Parent ! {result, self(), init_fsm(#{{0,0} => white})}
end),
Pid = spawn_program(Prog, Bot),
Bot ! {intcode_pid, Pid},
collect_result(Pid),
Bot ! halt,
print(collect_result(Bot)).
print(Map) ->
Coords = maps:keys(Map),
SmallX = lists:min([X || {X,_} <- Coords]),
SmallY = lists:min([Y || {_,Y} <- Coords]),
BigX = lists:max([X || {X,_} <- Coords]),
BigY = lists:max([Y || {_,Y} <- Coords]),
print({SmallX, BigY}, {BigX, SmallY}, SmallX, Map).
print({_,H}, {_,MinH}, _, _) when H < MinH ->
ok;
print({W,H}, {W,_}=Dim, MinW, Map) ->
io:format("\n"),
print({MinW, H-1}, Dim, MinW, Map);
print({W,H}, Dim, MinW, Map) ->
case get_color({W,H}, Map) of
white -> io:format("*");
black -> io:format(" ")
end,
print({W+1,H}, Dim, MinW, Map).
%%%%%%%%%%%%%%%%%%
%%% ROBOT CODE %%%
%%%%%%%%%%%%%%%%%%
init_fsm(InitMap) ->
receive
{intcode_pid, Pid} ->
fsm(up, {0,0}, InitMap, Pid)
end.
fsm(Dir, {X,Y}, Map, IntCodePid) ->
CurrentColor = get_color({X,Y}, Map),
IntCodePid ! {io, color_to_paint(CurrentColor)},
receive
{io, ColorDigit} ->
Color = color(ColorDigit),
NewMap = Map#{{X,Y} => Color},
receive
{io, TurnDigit} ->
NewDir = direction(Dir, turn(TurnDigit)),
NewCoord = forwards({X,Y}, NewDir),
fsm(NewDir, NewCoord, NewMap, IntCodePid)
end;
halt ->
Map
end.
color(0) -> black;
color(1) -> white.
get_color(Coord, Map) ->
maps:get(Coord, Map, black).
color_to_paint(black) -> 0;
color_to_paint(white) -> 1.
turn(0) -> left;
turn(1) -> right.
direction(up, left) -> left;
direction(left, left) -> down;
direction(down, left) -> right;
direction(right, left) -> up;
direction(up, right) -> right;
direction(right, right) -> down;
direction(down, right) -> left;
direction(left, right) -> up.
forwards({X,Y}, up) -> {X, Y+1};
forwards({X,Y}, left) -> {X-1, Y};
forwards({X,Y}, down) -> {X, Y-1};
forwards({X,Y}, right) -> {X+1, Y}.
%%%%%%%%%%%%%%%%%%%%%%%%
%%% INTCODE COMPUTER %%%
%%%%%%%%%%%%%%%%%%%%%%%%
parse_program(Str) ->
source_to_map(str_to_source(Str)).
str_to_source(Str) ->
String = string:trim(Str),
[list_to_integer(S) || S <- string:lexemes(String, ",")].
spawn_program(Map, IO) ->
Parent = self(),
Pid = spawn_link(fun() ->
Parent ! {result, self(), run_program(Map#{output => IO})}
end),
Pid.
collect_result(Pid) ->
receive
{result, Pid, Result} -> Result
end.
run_program(Map) -> run_program(0, Map).
run_program(P, Map) ->
{Op, ArgTypes} = instruction(maps:get(P, Map)),
Args = get_args(P+1, ArgTypes, Map),
Instr = {Op, Args},
case apply_instruction(Instr, Map) of
halt -> Map;
{jmp, NewP} -> run_program(NewP, Map);
{ok, NewMap} -> run_program(P+1+length(Args), NewMap)
end.
apply_instruction({'+', [A,B,R]}, Map) ->
{ok, Map#{ret_pos(R, Map) => read_arg(A, Map) + read_arg(B, Map)}};
apply_instruction({'*', [A,B,R]}, Map) ->
{ok, Map#{ret_pos(R, Map) => read_arg(A,Map) * read_arg(B, Map)}};
apply_instruction({input, [R]}, Map) ->
receive
{io, N} -> {ok, Map#{ret_pos(R, Map) => N}}
end;
apply_instruction({output, [R]}, Map) ->
OutputPid = maps:get(output, Map, self()),
OutputPid ! {io, read_arg(R, Map)},
{ok, Map};
apply_instruction({jmp_if_true, [Cond,R]}, Map) ->
case read_arg(Cond, Map) of
0 -> {ok, Map};
_ -> {jmp, read_arg(R, Map)}
end;
apply_instruction({jmp_if_false, [Cond,R]}, Map) ->
case read_arg(Cond, Map) of
0 -> {jmp, read_arg(R, Map)};
_ -> {ok, Map}
end;
apply_instruction({'<', [A,B,R]}, Map) ->
Res = case read_arg(A, Map) < read_arg(B, Map) of
true -> 1;
false -> 0
end,
{ok, Map#{ret_pos(R, Map) => Res}};
apply_instruction({'=:=', [A,B,R]}, Map) ->
Res = case read_arg(A, Map) =:= read_arg(B, Map) of
true -> 1;
false -> 0
end,
{ok, Map#{ret_pos(R, Map) => Res}};
apply_instruction({set_rel, [A]}, Map) ->
Base = maps:get(relative_base, Map, 0),
{ok, Map#{relative_base => Base + read_arg(A, Map)}};
apply_instruction({'exit', []}, _) ->
halt.
read_arg({val, N}, _Map) ->
N;
read_arg({addr, N}, Map) ->
maps:get(N, Map, 0);
read_arg({rel, N}, Map) ->
Base = maps:get(relative_base, Map, 0),
maps:get(Base+N, Map, 0).
ret_pos({addr, N}, _Map) ->
N;
ret_pos({rel, N}, Map) ->
Base = maps:get(relative_base, Map, 0),
Base + N.
get_args(_, [], _) -> [];
get_args(P, [H|T], Map) -> [{H, maps:get(P, Map, 0)} | get_args(P+1, T, Map)].
instruction(N) ->
OpcodeDigit = extract_opcode(N),
{Op, ParamCount} = opcode(OpcodeDigit),
{Op, params_types(N, ParamCount)}.
params_types(N, ParamCount) -> params_types(N, 0, ParamCount).
params_types(_, C, C) ->
[];
params_types(N, C, ParamCount) ->
[extract_param(N, C+1) | params_types(N, C+1, ParamCount)].
opcode(1) -> {'+', 3};
opcode(2) -> {'*', 3};
opcode(3) -> {input, 1};
opcode(4) -> {output, 1};
opcode(5) -> {jmp_if_true, 2};
opcode(6) -> {jmp_if_false, 2};
opcode(7) -> {'<', 3};
opcode(8) -> {'=:=', 3};
opcode(9) -> {set_rel, 1};
opcode(99) -> {'exit', 0}.
extract_opcode(N) -> N rem 100.
extract_param(N, Nth) ->
Multiplier = trunc(math:pow(10, Nth)),
Rem = 100 * Multiplier,
Div = 10 * Multiplier,
case (N rem Rem) div Div of
0 -> addr;
1 -> val;
2 -> rel
end.
source_to_map(Input) ->
{_, Map} = lists:foldl(
fun(N, {Index, Map}) -> {Index+1, Map#{Index => N}} end,
{0, #{}},
Input
),
Map.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment