-
-
Save ferd/c43066944de8c7283f1b82d5a998e012 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%%% @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