├── 03_tcl ├── input ├── Makefile ├── 03a.tcl ├── README.md └── 03b.tcl ├── 14_cuda ├── input ├── .gitignore ├── Makefile ├── 14a.cu ├── README.md └── 14b.cu ├── 15_pony ├── 15a │ ├── .gitignore │ └── Main.pony ├── 15b │ ├── .gitignore │ └── Main.pony ├── input ├── Makefile └── README.md ├── 24_java ├── .gitignore ├── Makefile ├── input ├── README.md ├── Aoc24a.java └── Aoc24b.java ├── 08_tex ├── .gitignore ├── Makefile ├── 08b.tex ├── 08a.tex └── README.md ├── 10_erlang ├── .gitignore ├── input ├── Makefile ├── aoc10a.erl ├── aoc10b.erl └── README.md ├── 16_fsharp ├── .gitignore ├── Makefile ├── 16a.fs ├── 16b.fs └── README.md ├── 22_elixir ├── example ├── Makefile ├── input ├── README.md ├── 22a.exs └── 22b.exs ├── 20_nim ├── .gitignore ├── Makefile ├── example_20b ├── aoc20a.nim ├── aoc20b.nim └── README.md ├── 06_smalltalk ├── input ├── Makefile ├── 06a.st ├── 06b.st └── README.md ├── 18_eiffel ├── .gitignore ├── 18b │ ├── 18b │ ├── value.e │ ├── instruction.e │ ├── constant_value.e │ ├── register_value.e │ ├── snd_instruction.e │ ├── set_instruction.e │ ├── add_instruction.e │ ├── mod_instruction.e │ ├── mul_instruction.e │ ├── jgz_instruction.e │ ├── rcv_instruction.e │ ├── state.e │ └── main.e ├── example_18b ├── Makefile ├── example_18a ├── 18a │ ├── value.e │ ├── instruction.e │ ├── constant_value.e │ ├── register_value.e │ ├── snd_instruction.e │ ├── set_instruction.e │ ├── add_instruction.e │ ├── mod_instruction.e │ ├── mul_instruction.e │ ├── rcv_instruction.e │ ├── jgz_instruction.e │ ├── state.e │ └── main.e ├── input └── README.md ├── 21_icon ├── example ├── Makefile ├── 21a.icn ├── 21b.icn ├── input └── README.md ├── 17_bbcbasic ├── 17.ssd ├── Makefile ├── screenshot_17a.png ├── screenshot_17b.png ├── screenshot_beeb.png └── README.md ├── 04_awk ├── Makefile ├── 04a.awk ├── 04b.awk └── README.md ├── 11_j ├── Makefile ├── 11a.ijs ├── 11b.ijs └── README.md ├── 23_php ├── Makefile ├── 23b.php ├── input ├── 23a.php └── README.md ├── 05_algol68 ├── Makefile ├── 05a.alg ├── 05b.alg └── README.md ├── 09_julia ├── Makefile ├── 09a.jl ├── 09b.jl └── README.md ├── 25_kotlin ├── Makefile ├── example ├── input ├── 25a.kts └── README.md ├── 02_forth ├── Makefile ├── input ├── 02a.fs ├── 02b.fs └── README.md ├── 19_clojure ├── Makefile ├── example ├── 19b.clj ├── README.md └── 19a.clj ├── 01_postgresql ├── Makefile ├── 01a.sql ├── 01b.sql ├── README.md └── input ├── 07_coffeescript ├── Makefile ├── 07a.coffee ├── 07b.coffee └── README.md ├── 13_octave ├── Makefile ├── 13a.m ├── 13b.m ├── input └── README.md ├── 12_dart ├── Makefile ├── 12a.dart ├── 12b.dart └── README.md ├── README.md └── RETROSPECTIVE.md /03_tcl/input: -------------------------------------------------------------------------------- 1 | 361527 2 | -------------------------------------------------------------------------------- /14_cuda/input: -------------------------------------------------------------------------------- 1 | amgozmfv 2 | -------------------------------------------------------------------------------- /15_pony/15a/.gitignore: -------------------------------------------------------------------------------- 1 | 15a 2 | -------------------------------------------------------------------------------- /15_pony/15b/.gitignore: -------------------------------------------------------------------------------- 1 | 15b 2 | -------------------------------------------------------------------------------- /24_java/.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | -------------------------------------------------------------------------------- /08_tex/.gitignore: -------------------------------------------------------------------------------- 1 | *.dvi 2 | *.log 3 | -------------------------------------------------------------------------------- /14_cuda/.gitignore: -------------------------------------------------------------------------------- 1 | 14a 2 | 14b 3 | -------------------------------------------------------------------------------- /10_erlang/.gitignore: -------------------------------------------------------------------------------- 1 | *.beam 2 | *.dump 3 | -------------------------------------------------------------------------------- /16_fsharp/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.dll 3 | -------------------------------------------------------------------------------- /22_elixir/example: -------------------------------------------------------------------------------- 1 | ..# 2 | #.. 3 | ... 4 | -------------------------------------------------------------------------------- /20_nim/.gitignore: -------------------------------------------------------------------------------- 1 | aoc20a 2 | aoc20b 3 | nimcache/ 4 | -------------------------------------------------------------------------------- /06_smalltalk/input: -------------------------------------------------------------------------------- 1 | 4 10 4 1 8 4 9 14 5 1 14 15 0 15 3 5 2 | -------------------------------------------------------------------------------- /18_eiffel/.gitignore: -------------------------------------------------------------------------------- 1 | *.c 2 | *.h 3 | *.o 4 | *.id 5 | 18a 6 | -------------------------------------------------------------------------------- /10_erlang/input: -------------------------------------------------------------------------------- 1 | 34,88,2,222,254,93,150,0,199,255,39,32,137,136,1,167 2 | -------------------------------------------------------------------------------- /15_pony/input: -------------------------------------------------------------------------------- 1 | Generator A starts with 277 2 | Generator B starts with 349 3 | -------------------------------------------------------------------------------- /21_icon/example: -------------------------------------------------------------------------------- 1 | ../.# => ##./#../... 2 | .#./..#/### => #..#/..../..../#..# 3 | -------------------------------------------------------------------------------- /17_bbcbasic/17.ssd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttencate/aoc2017/HEAD/17_bbcbasic/17.ssd -------------------------------------------------------------------------------- /18_eiffel/18b/18b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttencate/aoc2017/HEAD/18_eiffel/18b/18b -------------------------------------------------------------------------------- /18_eiffel/example_18b: -------------------------------------------------------------------------------- 1 | snd 1 2 | snd 2 3 | snd p 4 | rcv a 5 | rcv b 6 | rcv c 7 | rcv d 8 | -------------------------------------------------------------------------------- /03_tcl/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run03a run03b 3 | 4 | run%: %.tcl 5 | tclsh $< < input 6 | -------------------------------------------------------------------------------- /04_awk/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run04a run04b 3 | 4 | run%: %.awk 5 | awk -f $< < input 6 | -------------------------------------------------------------------------------- /11_j/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run11a run11b 3 | 4 | run%: %.ijs 5 | $$j64 $< < input 6 | -------------------------------------------------------------------------------- /17_bbcbasic/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run17a # run17b 3 | 4 | run%: %.EXT 5 | # RECIPE 6 | -------------------------------------------------------------------------------- /21_icon/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run21a run21b 3 | 4 | run%: %.icn 5 | icon $< < input 6 | -------------------------------------------------------------------------------- /23_php/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run23a run23b 3 | 4 | run%: %.php 5 | php $< < input 6 | -------------------------------------------------------------------------------- /05_algol68/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run05a run05b 3 | 4 | run%: %.alg 5 | a68g $< < input 6 | -------------------------------------------------------------------------------- /06_smalltalk/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run06a run06b 3 | 4 | run%: %.st 5 | gst -f $< < input 6 | -------------------------------------------------------------------------------- /09_julia/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run09a run09b 3 | 4 | run%: %.jl 5 | julia -- $< < input 6 | -------------------------------------------------------------------------------- /22_elixir/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run22a run22b 3 | 4 | run%: %.exs 5 | elixir $< < input 6 | -------------------------------------------------------------------------------- /25_kotlin/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run25a 3 | 4 | run%: %.kts 5 | kotlinc -script $< < input 6 | -------------------------------------------------------------------------------- /02_forth/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run02a run02b 3 | 4 | run%: %.fs 5 | gforth $< -e bye < input 6 | -------------------------------------------------------------------------------- /19_clojure/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run19a # run19b 3 | 4 | run%: %.clj 5 | clojure $< < input 6 | -------------------------------------------------------------------------------- /01_postgresql/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run01a run01b 3 | 4 | run%: %.sql 5 | psql -U postgres -f $< 6 | -------------------------------------------------------------------------------- /07_coffeescript/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run07a run07b 3 | 4 | run%: %.coffee 5 | coffee $< < input 6 | -------------------------------------------------------------------------------- /13_octave/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run13a run13b 3 | 4 | run%: %.m 5 | octave-cli --quiet $< < input 6 | -------------------------------------------------------------------------------- /14_cuda/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run14a run14b 3 | 4 | run%: %.cu 5 | nvcc $< -o $* && ./$* < input 6 | -------------------------------------------------------------------------------- /17_bbcbasic/screenshot_17a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttencate/aoc2017/HEAD/17_bbcbasic/screenshot_17a.png -------------------------------------------------------------------------------- /17_bbcbasic/screenshot_17b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttencate/aoc2017/HEAD/17_bbcbasic/screenshot_17b.png -------------------------------------------------------------------------------- /20_nim/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run20a run20b 3 | 4 | run%: aoc%.nim 5 | nim compile --run $< < input 6 | -------------------------------------------------------------------------------- /16_fsharp/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run16a run16b 3 | 4 | run%: %.fs 5 | fsharpc $< && mono $*.exe < input 6 | -------------------------------------------------------------------------------- /17_bbcbasic/screenshot_beeb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttencate/aoc2017/HEAD/17_bbcbasic/screenshot_beeb.png -------------------------------------------------------------------------------- /24_java/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run24a run24b 3 | 4 | run%: Aoc%.java 5 | javac $< && java Aoc$* < input 6 | -------------------------------------------------------------------------------- /08_tex/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run08a run08b 3 | 4 | run%: %.tex 5 | tex --interaction=nonstopmode $< < input 6 | -------------------------------------------------------------------------------- /10_erlang/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run10a run10b 3 | 4 | run%: aoc%.erl 5 | erlc $< && erl -noshell -s aoc$* start < input 6 | -------------------------------------------------------------------------------- /18_eiffel/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run18a run18b 3 | 4 | run%: %/*.e 5 | cd $* && se c MAIN make -o $* && ./$* < ../input 6 | -------------------------------------------------------------------------------- /15_pony/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run15a run15b 3 | 4 | run%: %/Main.pony 5 | cd $* && CC=gcc ponyc --pic && ./$* < ../input 6 | -------------------------------------------------------------------------------- /18_eiffel/example_18a: -------------------------------------------------------------------------------- 1 | set a 1 2 | add a 2 3 | mul a a 4 | mod a 5 5 | snd a 6 | set a 0 7 | rcv a 8 | jgz a -1 9 | set a 1 10 | jgz a -2 11 | -------------------------------------------------------------------------------- /18_eiffel/18a/value.e: -------------------------------------------------------------------------------- 1 | class VALUE 2 | 3 | feature {ANY} 4 | 5 | evaluate(state: STATE): INTEGER_64 6 | deferred 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /18_eiffel/18b/value.e: -------------------------------------------------------------------------------- 1 | class VALUE 2 | 3 | feature {ANY} 4 | 5 | evaluate(state: STATE): INTEGER_64 6 | deferred 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /12_dart/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: run12a run12b 3 | 4 | run%: %.dart 5 | dartanalyzer --strong --fatal-infos --fatal-warnings $< && dart $< --checked < input 6 | -------------------------------------------------------------------------------- /18_eiffel/18a/instruction.e: -------------------------------------------------------------------------------- 1 | deferred class INSTRUCTION 2 | 3 | feature {ANY} 4 | 5 | execute(state: STATE) 6 | deferred 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /19_clojure/example: -------------------------------------------------------------------------------- 1 | | 2 | | +--+ 3 | A | C 4 | F---|----E|--+ 5 | | | | D 6 | +B-+ +--+ 7 | 8 | -------------------------------------------------------------------------------- /20_nim/example_20b: -------------------------------------------------------------------------------- 1 | p=<-6,0,0>, v=< 3,0,0>, a=< 0,0,0> 2 | p=<-4,0,0>, v=< 2,0,0>, a=< 0,0,0> 3 | p=<-2,0,0>, v=< 1,0,0>, a=< 0,0,0> 4 | p=< 3,0,0>, v=<-1,0,0>, a=< 0,0,0> 5 | -------------------------------------------------------------------------------- /04_awk/04a.awk: -------------------------------------------------------------------------------- 1 | #!/bin/awk -f 2 | 3 | BEGIN { count = 0 } 4 | { 5 | split("", seen) 6 | for (i = 1; i <= NF; i++) { 7 | if (seen[$i]) { 8 | next 9 | } 10 | seen[$i] = 1 11 | } 12 | count++ 13 | } 14 | END { print count } 15 | -------------------------------------------------------------------------------- /18_eiffel/18b/instruction.e: -------------------------------------------------------------------------------- 1 | deferred class INSTRUCTION 2 | 3 | feature {ANY} 4 | 5 | is_blocked(state: STATE): BOOLEAN 6 | do 7 | Result := False 8 | end 9 | 10 | execute(state: STATE) 11 | deferred 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /13_octave/13a.m: -------------------------------------------------------------------------------- 1 | input = reshape(scanf('%d: %d'), 2, []); 2 | depths = input(1, :); 3 | ranges = input(2, :); 4 | total_depth = depths(length(depths)); 5 | severities = depths .* ranges; 6 | caught = mod(depths, 2 * ranges - 2) == 0; 7 | disp(sum(caught .* severities)); 8 | -------------------------------------------------------------------------------- /23_php/23b.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | 16 | -------------------------------------------------------------------------------- /13_octave/13b.m: -------------------------------------------------------------------------------- 1 | input = reshape(scanf('%d: %d'), 2, []); 2 | depths = input(1, :); 3 | ranges = input(2, :); 4 | periods = 2 * ranges - 2; 5 | delay = 0; 6 | while ~all(bsxfun(@mod, bsxfun(@plus, delay, depths), periods)) 7 | delay += 1; 8 | endwhile; 9 | disp(delay); 10 | -------------------------------------------------------------------------------- /18_eiffel/18a/constant_value.e: -------------------------------------------------------------------------------- 1 | class CONSTANT_VALUE 2 | inherit VALUE 3 | 4 | create {ANY} 5 | make 6 | 7 | feature {ANY} 8 | 9 | make(v: INTEGER_64) 10 | do 11 | value := v 12 | end 13 | 14 | evaluate(state: STATE): INTEGER_64 15 | do 16 | Result := value 17 | end 18 | 19 | feature {} 20 | value: INTEGER_64 21 | 22 | end 23 | -------------------------------------------------------------------------------- /18_eiffel/18b/constant_value.e: -------------------------------------------------------------------------------- 1 | class CONSTANT_VALUE 2 | inherit VALUE 3 | 4 | create {ANY} 5 | make 6 | 7 | feature {ANY} 8 | 9 | make(v: INTEGER_64) 10 | do 11 | value := v 12 | end 13 | 14 | evaluate(state: STATE): INTEGER_64 15 | do 16 | Result := value 17 | end 18 | 19 | feature {} 20 | value: INTEGER_64 21 | 22 | end 23 | -------------------------------------------------------------------------------- /18_eiffel/18a/register_value.e: -------------------------------------------------------------------------------- 1 | class REGISTER_VALUE 2 | inherit VALUE 3 | 4 | create {ANY} 5 | make 6 | 7 | feature {ANY} 8 | 9 | make(r: STRING) 10 | do 11 | register := r 12 | end 13 | 14 | evaluate(state: STATE): INTEGER_64 15 | do 16 | Result := state.get_register(register) 17 | end 18 | 19 | feature {} 20 | register: STRING 21 | 22 | end 23 | 24 | -------------------------------------------------------------------------------- /18_eiffel/18b/register_value.e: -------------------------------------------------------------------------------- 1 | class REGISTER_VALUE 2 | inherit VALUE 3 | 4 | create {ANY} 5 | make 6 | 7 | feature {ANY} 8 | 9 | make(r: STRING) 10 | do 11 | register := r 12 | end 13 | 14 | evaluate(state: STATE): INTEGER_64 15 | do 16 | Result := state.get_register(register) 17 | end 18 | 19 | feature {} 20 | register: STRING 21 | 22 | end 23 | 24 | -------------------------------------------------------------------------------- /18_eiffel/18b/snd_instruction.e: -------------------------------------------------------------------------------- 1 | class SND_INSTRUCTION 2 | inherit INSTRUCTION 3 | 4 | create {ANY} 5 | make 6 | 7 | feature {ANY} 8 | 9 | make(v: VALUE) 10 | do 11 | value := v 12 | end 13 | 14 | execute(state: STATE) 15 | do 16 | state.send(value.evaluate(state)) 17 | state.increment_pc 18 | end 19 | 20 | feature {} 21 | 22 | value: VALUE 23 | 24 | end 25 | -------------------------------------------------------------------------------- /18_eiffel/18a/snd_instruction.e: -------------------------------------------------------------------------------- 1 | class SND_INSTRUCTION 2 | inherit INSTRUCTION 3 | 4 | create {ANY} 5 | make 6 | 7 | feature {ANY} 8 | 9 | make(f: VALUE) 10 | do 11 | frequency := f 12 | end 13 | 14 | execute(state: STATE) 15 | do 16 | state.play_sound(frequency.evaluate(state)) 17 | state.increment_pc 18 | end 19 | 20 | feature {} 21 | 22 | frequency: VALUE 23 | 24 | end 25 | -------------------------------------------------------------------------------- /23_php/input: -------------------------------------------------------------------------------- 1 | set b 65 2 | set c b 3 | jnz a 2 4 | jnz 1 5 5 | mul b 100 6 | sub b -100000 7 | set c b 8 | sub c -17000 9 | set f 1 10 | set d 2 11 | set e 2 12 | set g d 13 | mul g e 14 | sub g b 15 | jnz g 2 16 | set f 0 17 | sub e -1 18 | set g e 19 | sub g b 20 | jnz g -8 21 | sub d -1 22 | set g d 23 | sub g b 24 | jnz g -13 25 | jnz f 2 26 | sub h -1 27 | set g b 28 | sub g c 29 | jnz g 2 30 | jnz 1 3 31 | sub b -17 32 | jnz 1 -23 33 | -------------------------------------------------------------------------------- /18_eiffel/18a/set_instruction.e: -------------------------------------------------------------------------------- 1 | class SET_INSTRUCTION 2 | inherit INSTRUCTION 3 | 4 | create {ANY} 5 | make 6 | 7 | feature {ANY} 8 | 9 | make(r: STRING; v: VALUE) 10 | do 11 | register := r 12 | value := v 13 | end 14 | 15 | execute(state: STATE) 16 | do 17 | state.set_register(register, value.evaluate(state)) 18 | state.increment_pc 19 | end 20 | 21 | feature {} 22 | 23 | register: STRING 24 | value: VALUE 25 | 26 | end 27 | -------------------------------------------------------------------------------- /18_eiffel/18b/set_instruction.e: -------------------------------------------------------------------------------- 1 | class SET_INSTRUCTION 2 | inherit INSTRUCTION 3 | 4 | create {ANY} 5 | make 6 | 7 | feature {ANY} 8 | 9 | make(r: STRING; v: VALUE) 10 | do 11 | register := r 12 | value := v 13 | end 14 | 15 | execute(state: STATE) 16 | do 17 | state.set_register(register, value.evaluate(state)) 18 | state.increment_pc 19 | end 20 | 21 | feature {} 22 | 23 | register: STRING 24 | value: VALUE 25 | 26 | end 27 | -------------------------------------------------------------------------------- /13_octave/input: -------------------------------------------------------------------------------- 1 | 0: 3 2 | 1: 2 3 | 2: 4 4 | 4: 6 5 | 6: 4 6 | 8: 6 7 | 10: 5 8 | 12: 6 9 | 14: 9 10 | 16: 6 11 | 18: 8 12 | 20: 8 13 | 22: 8 14 | 24: 8 15 | 26: 8 16 | 28: 8 17 | 30: 12 18 | 32: 14 19 | 34: 10 20 | 36: 12 21 | 38: 12 22 | 40: 10 23 | 42: 12 24 | 44: 12 25 | 46: 12 26 | 48: 12 27 | 50: 12 28 | 52: 14 29 | 54: 14 30 | 56: 12 31 | 62: 12 32 | 64: 14 33 | 66: 14 34 | 68: 14 35 | 70: 17 36 | 72: 14 37 | 74: 14 38 | 76: 14 39 | 82: 14 40 | 86: 18 41 | 88: 14 42 | 96: 14 43 | 98: 44 44 | -------------------------------------------------------------------------------- /18_eiffel/18a/add_instruction.e: -------------------------------------------------------------------------------- 1 | class ADD_INSTRUCTION 2 | inherit INSTRUCTION 3 | 4 | create {ANY} 5 | make 6 | 7 | feature {ANY} 8 | 9 | make(r: STRING; v: VALUE) 10 | do 11 | register := r 12 | value := v 13 | end 14 | 15 | execute(state: STATE) 16 | do 17 | state.set_register(register, state.get_register(register) + value.evaluate(state)) 18 | state.increment_pc 19 | end 20 | 21 | feature {} 22 | 23 | register: STRING 24 | value: VALUE 25 | 26 | end 27 | -------------------------------------------------------------------------------- /18_eiffel/18a/mod_instruction.e: -------------------------------------------------------------------------------- 1 | class MOD_INSTRUCTION 2 | inherit INSTRUCTION 3 | 4 | create {ANY} 5 | make 6 | 7 | feature {ANY} 8 | 9 | make(r: STRING; v: VALUE) 10 | do 11 | register := r 12 | value := v 13 | end 14 | 15 | execute(state: STATE) 16 | do 17 | state.set_register(register, state.get_register(register) \\ value.evaluate(state)) 18 | state.increment_pc 19 | end 20 | 21 | feature {} 22 | 23 | register: STRING 24 | value: VALUE 25 | 26 | end 27 | -------------------------------------------------------------------------------- /18_eiffel/18a/mul_instruction.e: -------------------------------------------------------------------------------- 1 | class MUL_INSTRUCTION 2 | inherit INSTRUCTION 3 | 4 | create {ANY} 5 | make 6 | 7 | feature {ANY} 8 | 9 | make(r: STRING; v: VALUE) 10 | do 11 | register := r 12 | value := v 13 | end 14 | 15 | execute(state: STATE) 16 | do 17 | state.set_register(register, state.get_register(register) * value.evaluate(state)) 18 | state.increment_pc 19 | end 20 | 21 | feature {} 22 | 23 | register: STRING 24 | value: VALUE 25 | 26 | end 27 | -------------------------------------------------------------------------------- /18_eiffel/18b/add_instruction.e: -------------------------------------------------------------------------------- 1 | class ADD_INSTRUCTION 2 | inherit INSTRUCTION 3 | 4 | create {ANY} 5 | make 6 | 7 | feature {ANY} 8 | 9 | make(r: STRING; v: VALUE) 10 | do 11 | register := r 12 | value := v 13 | end 14 | 15 | execute(state: STATE) 16 | do 17 | state.set_register(register, state.get_register(register) + value.evaluate(state)) 18 | state.increment_pc 19 | end 20 | 21 | feature {} 22 | 23 | register: STRING 24 | value: VALUE 25 | 26 | end 27 | -------------------------------------------------------------------------------- /18_eiffel/18b/mod_instruction.e: -------------------------------------------------------------------------------- 1 | class MOD_INSTRUCTION 2 | inherit INSTRUCTION 3 | 4 | create {ANY} 5 | make 6 | 7 | feature {ANY} 8 | 9 | make(r: STRING; v: VALUE) 10 | do 11 | register := r 12 | value := v 13 | end 14 | 15 | execute(state: STATE) 16 | do 17 | state.set_register(register, state.get_register(register) \\ value.evaluate(state)) 18 | state.increment_pc 19 | end 20 | 21 | feature {} 22 | 23 | register: STRING 24 | value: VALUE 25 | 26 | end 27 | -------------------------------------------------------------------------------- /18_eiffel/18b/mul_instruction.e: -------------------------------------------------------------------------------- 1 | class MUL_INSTRUCTION 2 | inherit INSTRUCTION 3 | 4 | create {ANY} 5 | make 6 | 7 | feature {ANY} 8 | 9 | make(r: STRING; v: VALUE) 10 | do 11 | register := r 12 | value := v 13 | end 14 | 15 | execute(state: STATE) 16 | do 17 | state.set_register(register, state.get_register(register) * value.evaluate(state)) 18 | state.increment_pc 19 | end 20 | 21 | feature {} 22 | 23 | register: STRING 24 | value: VALUE 25 | 26 | end 27 | -------------------------------------------------------------------------------- /18_eiffel/18a/rcv_instruction.e: -------------------------------------------------------------------------------- 1 | class RCV_INSTRUCTION 2 | inherit INSTRUCTION 3 | 4 | create {ANY} 5 | make 6 | 7 | feature {ANY} 8 | 9 | make(r: STRING) 10 | do 11 | register := r 12 | end 13 | 14 | execute(state: STATE) 15 | do 16 | if state.get_register(register) /= 0 then 17 | state.set_register(register, state.get_last_sound_frequency) 18 | state.set_done 19 | else 20 | state.increment_pc 21 | end 22 | end 23 | 24 | feature {} 25 | 26 | register: STRING 27 | 28 | end 29 | -------------------------------------------------------------------------------- /18_eiffel/input: -------------------------------------------------------------------------------- 1 | set i 31 2 | set a 1 3 | mul p 17 4 | jgz p p 5 | mul a 2 6 | add i -1 7 | jgz i -2 8 | add a -1 9 | set i 127 10 | set p 622 11 | mul p 8505 12 | mod p a 13 | mul p 129749 14 | add p 12345 15 | mod p a 16 | set b p 17 | mod b 10000 18 | snd b 19 | add i -1 20 | jgz i -9 21 | jgz a 3 22 | rcv b 23 | jgz b -1 24 | set f 0 25 | set i 126 26 | rcv a 27 | rcv b 28 | set p a 29 | mul p -1 30 | add p b 31 | jgz p 4 32 | snd a 33 | set a b 34 | jgz 1 3 35 | snd b 36 | set f 1 37 | add i -1 38 | jgz i -11 39 | snd a 40 | jgz f -16 41 | jgz a -19 42 | -------------------------------------------------------------------------------- /18_eiffel/18a/jgz_instruction.e: -------------------------------------------------------------------------------- 1 | class JGZ_INSTRUCTION 2 | inherit INSTRUCTION 3 | 4 | create {ANY} 5 | make 6 | 7 | feature {ANY} 8 | 9 | make(c: VALUE; o: VALUE) 10 | do 11 | condition := c 12 | offset := o 13 | end 14 | 15 | execute(state: STATE) 16 | do 17 | if condition.evaluate(state) > 0 then 18 | state.set_pc(state.get_pc + offset.evaluate(state)) 19 | else 20 | state.increment_pc 21 | end 22 | end 23 | 24 | feature {} 25 | 26 | condition: VALUE 27 | offset: VALUE 28 | 29 | end 30 | -------------------------------------------------------------------------------- /18_eiffel/18b/jgz_instruction.e: -------------------------------------------------------------------------------- 1 | class JGZ_INSTRUCTION 2 | inherit INSTRUCTION 3 | 4 | create {ANY} 5 | make 6 | 7 | feature {ANY} 8 | 9 | make(c: VALUE; o: VALUE) 10 | do 11 | condition := c 12 | offset := o 13 | end 14 | 15 | execute(state: STATE) 16 | do 17 | if condition.evaluate(state) > 0 then 18 | state.set_pc(state.get_pc + offset.evaluate(state)) 19 | else 20 | state.increment_pc 21 | end 22 | end 23 | 24 | feature {} 25 | 26 | condition: VALUE 27 | offset: VALUE 28 | 29 | end 30 | -------------------------------------------------------------------------------- /18_eiffel/18b/rcv_instruction.e: -------------------------------------------------------------------------------- 1 | class RCV_INSTRUCTION 2 | 3 | inherit 4 | INSTRUCTION 5 | redefine 6 | is_blocked 7 | end 8 | 9 | create {ANY} 10 | make 11 | 12 | feature {ANY} 13 | 14 | make(r: STRING) 15 | do 16 | register := r 17 | end 18 | 19 | is_blocked(state: STATE): BOOLEAN 20 | do 21 | Result := not state.can_receive 22 | end 23 | 24 | execute(state: STATE) 25 | do 26 | state.set_register(register, state.receive) 27 | state.increment_pc 28 | end 29 | 30 | feature {} 31 | 32 | register: STRING 33 | 34 | end 35 | -------------------------------------------------------------------------------- /24_java/input: -------------------------------------------------------------------------------- 1 | 42/37 2 | 28/28 3 | 29/25 4 | 45/8 5 | 35/23 6 | 49/20 7 | 44/4 8 | 15/33 9 | 14/19 10 | 31/44 11 | 39/14 12 | 25/17 13 | 34/34 14 | 38/42 15 | 8/42 16 | 15/28 17 | 0/7 18 | 49/12 19 | 18/36 20 | 45/45 21 | 28/7 22 | 30/43 23 | 23/41 24 | 0/35 25 | 18/9 26 | 3/31 27 | 20/31 28 | 10/40 29 | 0/22 30 | 1/23 31 | 20/47 32 | 38/36 33 | 15/8 34 | 34/32 35 | 30/30 36 | 30/44 37 | 19/28 38 | 46/15 39 | 34/50 40 | 40/20 41 | 27/39 42 | 3/14 43 | 43/45 44 | 50/42 45 | 1/33 46 | 6/39 47 | 46/44 48 | 22/35 49 | 15/20 50 | 43/31 51 | 23/23 52 | 19/27 53 | 47/15 54 | 43/43 55 | 25/36 56 | 26/38 57 | 1/10 58 | -------------------------------------------------------------------------------- /07_coffeescript/07a.coffee: -------------------------------------------------------------------------------- 1 | #!/usr/bin/coffee 2 | 3 | fs = require 'fs' 4 | 5 | input = fs.readFileSync('/dev/stdin').toString() 6 | lines = input.trim().split('\n') 7 | 8 | names = [] 9 | children = {} 10 | parents = {} 11 | weights = {} 12 | for line in lines 13 | match = /^(\S+) \((\d+)\)(?: -> (.*))?$/.exec(line) 14 | name = match[1] 15 | weight = match[2] 16 | childList = (match[3] || '').split(', ') 17 | names.push(name) 18 | children[name] = childList 19 | for child in childList 20 | parents[child] = name 21 | weights[name] = parseInt(weight) 22 | 23 | for name in names 24 | if !parents[name] 25 | console.log name 26 | -------------------------------------------------------------------------------- /25_kotlin/example: -------------------------------------------------------------------------------- 1 | Begin in state A. 2 | Perform a diagnostic checksum after 6 steps. 3 | 4 | In state A: 5 | If the current value is 0: 6 | - Write the value 1. 7 | - Move one slot to the right. 8 | - Continue with state B. 9 | If the current value is 1: 10 | - Write the value 0. 11 | - Move one slot to the left. 12 | - Continue with state B. 13 | 14 | In state B: 15 | If the current value is 0: 16 | - Write the value 1. 17 | - Move one slot to the left. 18 | - Continue with state A. 19 | If the current value is 1: 20 | - Write the value 1. 21 | - Move one slot to the right. 22 | - Continue with state A. 23 | -------------------------------------------------------------------------------- /04_awk/04b.awk: -------------------------------------------------------------------------------- 1 | #!/bin/awk -f 2 | 3 | BEGIN { count = 0 } 4 | { 5 | split("", seen) 6 | for (i = 1; i <= NF; i++) { 7 | word = $i 8 | 9 | split(word, chars, "") 10 | for (a = 1; a <= length(chars); a++) { 11 | for (b = a + 1; b <= length(chars); b++) { 12 | if (chars[a] > chars[b]) { 13 | tmp = chars[a] 14 | chars[a] = chars[b] 15 | chars[b] = tmp 16 | } 17 | } 18 | } 19 | 20 | sorted = "" 21 | for (a = 1; a <= length(chars); a++) { 22 | sorted = sorted chars[a] 23 | } 24 | 25 | if (seen[sorted]) { 26 | next 27 | } 28 | seen[sorted] = 1 29 | } 30 | count++ 31 | } 32 | END { print count } 33 | -------------------------------------------------------------------------------- /01_postgresql/01a.sql: -------------------------------------------------------------------------------- 1 | WITH 2 | digits AS ( 3 | SELECT 4 | to_number(digits.digit, '9') AS digit, (digits.index - 1) AS index 5 | FROM 6 | unnest(string_to_array(btrim(pg_read_file('input', 0, 999999), E' \r\n'), NULL)) 7 | WITH ORDINALITY AS digits(digit, index) 8 | ), 9 | num_digits AS ( 10 | SELECT 11 | COUNT(*) as num_digits 12 | FROM 13 | digits 14 | ), 15 | next_digits AS ( 16 | SELECT 17 | digit, (index - 1 + num_digits) % num_digits AS index 18 | FROM 19 | digits 20 | JOIN num_digits ON true 21 | ) 22 | SELECT 23 | SUM(digits.digit) AS answer 24 | FROM 25 | digits 26 | JOIN next_digits USING (index) 27 | WHERE 28 | digits.digit = next_digits.digit 29 | ; 30 | -------------------------------------------------------------------------------- /01_postgresql/01b.sql: -------------------------------------------------------------------------------- 1 | WITH 2 | digits AS ( 3 | SELECT 4 | to_number(digits.digit, '9') AS digit, (digits.index - 1) AS index 5 | FROM 6 | unnest(string_to_array(btrim(pg_read_file('input', 0, 999999), E' \r\n'), NULL)) 7 | WITH ORDINALITY AS digits(digit, index) 8 | ), 9 | num_digits AS ( 10 | SELECT 11 | COUNT(*) as num_digits 12 | FROM 13 | digits 14 | ), 15 | next_digits AS ( 16 | SELECT 17 | digit, (index + num_digits / 2) % num_digits AS index 18 | FROM 19 | digits 20 | JOIN num_digits ON true 21 | ) 22 | SELECT 23 | SUM(digits.digit) AS answer 24 | FROM 25 | digits 26 | JOIN next_digits USING (index) 27 | WHERE 28 | digits.digit = next_digits.digit 29 | ; 30 | -------------------------------------------------------------------------------- /22_elixir/input: -------------------------------------------------------------------------------- 1 | #..#...##.#.###.#.#.#...# 2 | .####....#..##.#.##....## 3 | ...#..#.#.#......##..#..# 4 | ##.####.#.##........#...# 5 | ##.#....##..#.####.###... 6 | #..#..###...#.#..##..###. 7 | .##.##..#.####.#.#.....## 8 | #....#......######...#... 9 | ..#.#.##.#..#...##.#.#### 10 | #.#..#.....#..####.#.#.## 11 | ...##.#..##.###.###...... 12 | ###..#.####.#..#####..#.. 13 | ...##.##.#.###.#..##.#.## 14 | .####.#.##.#####.##.##..# 15 | #.##.#...##.#.###.###..#. 16 | ..#.#..#..#..##..###...## 17 | ##.##.#..#.###....###..## 18 | .#...###..#####.#..#.#.## 19 | ....##..####.##...#..#.## 20 | #..#..###..#..###...#..## 21 | .##.#.###..####.#.#..##.# 22 | ..###.#....#...###...##.# 23 | .#...##.##.####...##.#### 24 | ###.#.#.####.##.###..#... 25 | #.#######.#######..##.#.# 26 | -------------------------------------------------------------------------------- /05_algol68/05a.alg: -------------------------------------------------------------------------------- 1 | main: ( 2 | REF INT count = LOC INT := 0; 3 | REF[]INT jumps = LOC[1:1092]INT; 4 | 5 | # Read input # 6 | PROC raise logical file end = (REF FILE f) BOOL: ( except logical file end ); 7 | on logical file end(stand in, raise logical file end); 8 | DO 9 | INT number; 10 | # Read line into line # 11 | read((number, new line)); 12 | # Store integer # 13 | count +:= 1; 14 | jumps[count] := number 15 | OD; 16 | except logical file end: 17 | SKIP; 18 | 19 | ( 20 | REF INT pc = LOC INT := 1; 21 | REF INT steps = LOC INT := 0; 22 | 23 | WHILE pc >= 1 & pc <= count DO 24 | # print((pc, new line)); # 25 | INT dest = pc + jumps[pc]; 26 | jumps[pc] +:= 1; 27 | pc := dest; 28 | steps +:= 1 29 | OD; 30 | 31 | # Print output # 32 | print((steps, new line)) 33 | ) 34 | ) 35 | -------------------------------------------------------------------------------- /12_dart/12a.dart: -------------------------------------------------------------------------------- 1 | #!/usr/bin/dart 2 | 3 | import 'dart:io'; 4 | 5 | dfs(Map> connections, int start, Set visited) { 6 | if (visited.contains(start)) { 7 | return; 8 | } 9 | visited.add(start); 10 | for (int next in connections[start]) { 11 | dfs(connections, next, visited); 12 | } 13 | } 14 | 15 | main() async { 16 | Map> connections = new Map(); 17 | while (true) { 18 | String line = stdin.readLineSync(); 19 | if (line == null) break; 20 | RegExp re = new RegExp(r'^(\d+) <-> (.*)$'); 21 | Match match = re.firstMatch(line); 22 | int id = int.parse(match.group(1)); 23 | List conns = match.group(2).split(', ').map(int.parse); 24 | connections[id] = conns; 25 | } 26 | 27 | Set reachable = new Set(); 28 | dfs(connections, 0, reachable); 29 | 30 | print(reachable.length); 31 | } 32 | -------------------------------------------------------------------------------- /09_julia/09a.jl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/julia -- 2 | 3 | input = readline() 4 | i = 0 5 | 6 | function eof() 7 | global input 8 | global i 9 | i > length(input) 10 | end 11 | 12 | function cur() 13 | global input 14 | global i 15 | input[i] 16 | end 17 | 18 | function next() 19 | global i 20 | i += 1 21 | while !eof() && cur() == '!' 22 | i += 2 23 | end 24 | end 25 | 26 | function readGarbage() 27 | next() # skip '<' 28 | while cur() != '>' 29 | next() 30 | end 31 | next() 32 | end 33 | 34 | function readGroup(depth) 35 | next() # skip '{' 36 | score = depth 37 | while cur() != '}' 38 | if cur() == '<' 39 | readGarbage() 40 | elseif cur() == '{' 41 | score += readGroup(depth + 1) 42 | end 43 | if cur() == ',' 44 | next() 45 | end 46 | end 47 | next() 48 | score 49 | end 50 | 51 | next() 52 | println(readGroup(1)) 53 | -------------------------------------------------------------------------------- /03_tcl/03a.tcl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/tclsh 2 | 3 | proc area {shell} { 4 | return [expr {(1 + 2*$shell) * (1 + 2*$shell)}] 5 | } 6 | 7 | set input [gets stdin] 8 | set shell 0 9 | set area 1 10 | while {$input > [area $shell]} { 11 | incr shell 12 | } 13 | set i [expr {$input - 1 - [area [expr {$shell - 1}]]}] 14 | set side_len [expr {2 * $shell}] 15 | set side [expr {$i / $side_len}] 16 | set i_in_side [expr {$i % $side_len}] 17 | if {$side == 0} { 18 | set x [expr {$shell}] 19 | set y [expr {-$shell + 1 + $i_in_side}] 20 | } elseif {$side == 1} { 21 | set x [expr {$shell - 1 - $i_in_side}] 22 | set y [expr {$shell}] 23 | } elseif {$side == 2} { 24 | set x [expr {-$shell}] 25 | set y [expr {$shell - 1 - $i_in_side}] 26 | } elseif {$side == 3} { 27 | set x [expr {-$shell + 1 + $i_in_side}] 28 | set y [expr {-$shell}] 29 | } else { 30 | puts "ERROR" 31 | } 32 | puts [expr {abs($x) + abs($y)}] 33 | -------------------------------------------------------------------------------- /09_julia/09b.jl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/julia -- 2 | 3 | input = readline() 4 | i = 0 5 | 6 | function eof() 7 | global input 8 | global i 9 | i > length(input) 10 | end 11 | 12 | function cur() 13 | global input 14 | global i 15 | input[i] 16 | end 17 | 18 | function next() 19 | global i 20 | i += 1 21 | while !eof() && cur() == '!' 22 | i += 2 23 | end 24 | end 25 | 26 | function readGarbage() 27 | next() # skip '<' 28 | score = 0 29 | while cur() != '>' 30 | score += 1 31 | next() 32 | end 33 | next() 34 | score 35 | end 36 | 37 | function readGroup(depth) 38 | next() # skip '{' 39 | score = 0 40 | while cur() != '}' 41 | if cur() == '<' 42 | score += readGarbage() 43 | elseif cur() == '{' 44 | score += readGroup(depth + 1) 45 | end 46 | if cur() == ',' 47 | next() 48 | end 49 | end 50 | next() 51 | score 52 | end 53 | 54 | next() 55 | println(readGroup(1)) 56 | -------------------------------------------------------------------------------- /05_algol68/05b.alg: -------------------------------------------------------------------------------- 1 | main: ( 2 | REF INT count = LOC INT := 0; 3 | REF[]INT jumps = LOC[1:1092]INT; 4 | 5 | # Read input # 6 | PROC raise logical file end = (REF FILE f) BOOL: ( except logical file end ); 7 | on logical file end(stand in, raise logical file end); 8 | DO 9 | INT number; 10 | # Read line into line # 11 | read((number, new line)); 12 | # Store integer # 13 | count +:= 1; 14 | jumps[count] := number 15 | OD; 16 | except logical file end: 17 | SKIP; 18 | 19 | ( 20 | REF INT pc = LOC INT := 1; 21 | REF INT steps = LOC INT := 0; 22 | 23 | WHILE pc >= 1 & pc <= count DO 24 | # print((pc, new line)); # 25 | INT dest = pc + jumps[pc]; 26 | IF jumps[pc] >= 3 THEN 27 | jumps[pc] -:= 1 28 | ELSE 29 | jumps[pc] +:= 1 30 | FI; 31 | pc := dest; 32 | steps +:= 1 33 | OD; 34 | 35 | # Print output # 36 | print((steps, new line)) 37 | ) 38 | ) 39 | -------------------------------------------------------------------------------- /12_dart/12b.dart: -------------------------------------------------------------------------------- 1 | #!/usr/bin/dart 2 | 3 | import 'dart:io'; 4 | 5 | dfs(Map> connections, int start, Set remaining) { 6 | if (!remaining.contains(start)) { 7 | return; 8 | } 9 | remaining.remove(start); 10 | for (int next in connections[start]) { 11 | dfs(connections, next, remaining); 12 | } 13 | } 14 | 15 | main() async { 16 | Map> connections = new Map(); 17 | while (true) { 18 | String line = stdin.readLineSync(); 19 | if (line == null) break; 20 | RegExp re = new RegExp(r'^(\d+) <-> (.*)$'); 21 | Match match = re.firstMatch(line); 22 | int id = int.parse(match.group(1)); 23 | List conns = match.group(2).split(', ').map(int.parse); 24 | connections[id] = conns; 25 | } 26 | 27 | Set remaining = new Set.from(connections.keys); 28 | int groups = 0; 29 | while (remaining.isNotEmpty) { 30 | groups++; 31 | dfs(connections, remaining.first, remaining); 32 | } 33 | 34 | print(groups); 35 | } 36 | -------------------------------------------------------------------------------- /24_java/README.md: -------------------------------------------------------------------------------- 1 | # [Day 24](http://adventofcode.com/2017/day/24) in Java 2 | 3 | I don't think the Java language needs an introduction. It's dull, but solid, 4 | and it gets the job done. 5 | 6 | As I didn't immediately see a way to do this efficiently (e.g. dynamic 7 | programming), I tried a simple recursive approach first. We store each 8 | component twice in a `HashMap` of `ArrayList`s, indexed on the number of ports 9 | on either side. The recursive algorithm simply flags a component as used, 10 | enters the recursion, then unflags it again when done. This is less efficient 11 | than removing the component outright, but saves the trouble of restoring it 12 | at the correct index. 13 | 14 | This worked fine, on the first try even. 15 | 16 | --- 17 | 18 | The second part isn't conceptually different, but we need to judge bridges 19 | based on a tuple of values now, rather than a single value. So I wrote a simple 20 | `Metrics` class to carry both strength and length around. 21 | 22 | This, too, worked fine on the first try. 23 | -------------------------------------------------------------------------------- /06_smalltalk/06a.st: -------------------------------------------------------------------------------- 1 | #!/usr/bin/gst -f 2 | 3 | state := ((stdin nextLine subStrings: ' ') collect: [:string | string asNumber]) asArray. 4 | banks := state size. 5 | 6 | seenStates := Set new. 7 | count := 0. 8 | 9 | [seenStates includes: state] whileFalse: [ 10 | count := count + 1. 11 | seenStates add: (state copy). 12 | 13 | "Find bank to redistribute" 14 | max := 0. 15 | redistributeIndex := 0. 16 | state keysAndValuesDo: [:i :blocks | 17 | (blocks > max) ifTrue: [ 18 | max := blocks. 19 | redistributeIndex := i. 20 | ]. 21 | ]. 22 | 23 | "Redistribute bank" 24 | redistributedBlocks := state at: redistributeIndex. 25 | state at: redistributeIndex put: 0. 26 | blocksForEachBank := redistributedBlocks // banks. 27 | remainingBlocks := redistributedBlocks \\ banks. 28 | state := (state collect: [:blocks | 29 | blocks + blocksForEachBank 30 | ]) asArray. 31 | 1 to: remainingBlocks do: [:i | 32 | index := ((redistributeIndex + i - 1) \\ banks) + 1. 33 | state at: index put: ((state at: index) + 1). 34 | ]. 35 | ]. 36 | 37 | count printNl. 38 | -------------------------------------------------------------------------------- /06_smalltalk/06b.st: -------------------------------------------------------------------------------- 1 | #!/usr/bin/gst -f 2 | 3 | state := ((stdin nextLine subStrings: ' ') collect: [:string | string asNumber]) asArray. 4 | banks := state size. 5 | 6 | seenStates := LookupTable new. 7 | count := 0. 8 | 9 | [seenStates includesKey: state] whileFalse: [ 10 | seenStates at: (state copy) put: count. 11 | count := count + 1. 12 | 13 | "Find bank to redistribute" 14 | max := 0. 15 | redistributeIndex := 0. 16 | state keysAndValuesDo: [:i :blocks | 17 | (blocks > max) ifTrue: [ 18 | max := blocks. 19 | redistributeIndex := i. 20 | ]. 21 | ]. 22 | 23 | "Redistribute bank" 24 | redistributedBlocks := state at: redistributeIndex. 25 | state at: redistributeIndex put: 0. 26 | blocksForEachBank := redistributedBlocks // banks. 27 | remainingBlocks := redistributedBlocks \\ banks. 28 | state := (state collect: [:blocks | 29 | blocks + blocksForEachBank 30 | ]) asArray. 31 | 1 to: remainingBlocks do: [:i | 32 | index := ((redistributeIndex + i - 1) \\ banks) + 1. 33 | state at: index put: ((state at: index) + 1). 34 | ]. 35 | ]. 36 | 37 | (count - (seenStates at: state)) printNl. 38 | -------------------------------------------------------------------------------- /02_forth/input: -------------------------------------------------------------------------------- 1 | 116 1470 2610 179 2161 2690 831 1824 2361 1050 2201 118 145 2275 2625 2333 2 | 976 220 1129 553 422 950 332 204 1247 1092 1091 159 174 182 984 713 3 | 84 78 773 62 808 83 1125 1110 1184 145 1277 982 338 1182 75 679 4 | 3413 3809 3525 2176 141 1045 2342 2183 157 3960 3084 2643 119 108 3366 2131 5 | 1312 205 343 616 300 1098 870 1008 1140 1178 90 146 980 202 190 774 6 | 4368 3905 3175 4532 3806 1579 4080 259 2542 221 4395 4464 208 3734 234 4225 7 | 741 993 1184 285 1062 372 111 118 63 843 325 132 854 105 956 961 8 | 85 79 84 2483 858 2209 2268 90 2233 1230 2533 322 338 68 2085 1267 9 | 2688 2022 112 130 1185 103 1847 3059 911 107 2066 1788 2687 2633 415 1353 10 | 76 169 141 58 161 66 65 225 60 152 62 64 156 199 80 56 11 | 220 884 1890 597 3312 593 4259 222 113 2244 3798 4757 216 1127 4400 178 12 | 653 369 216 132 276 102 265 889 987 236 239 807 1076 932 84 864 13 | 799 739 75 1537 82 228 69 1397 1396 1203 1587 63 313 1718 1375 469 14 | 1176 112 1407 136 1482 1534 1384 1202 604 851 190 284 1226 113 114 687 15 | 73 1620 81 1137 812 75 1326 1355 1545 1666 1356 1681 1732 85 128 902 16 | 571 547 160 237 256 30 496 592 385 576 183 692 192 387 647 233 17 | -------------------------------------------------------------------------------- /22_elixir/README.md: -------------------------------------------------------------------------------- 1 | # [Day 22](http://adventofcode.com/2017/day/22) in Elixir 2 | 3 | I don't know much about Elixir except that it runs on the Erlang virtual 4 | machine, and has a Ruby-like syntax. Time to learn! From a cursory look at the 5 | tutorial, the differences with Erlang are mostly syntactical, and the 6 | underlying model is the same. Conveniently, I already wrote some Erlang for a 7 | previous puzzle, so things are not entirely unfamiliar. 8 | 9 | To represent the current state of the grid, the obvious solution is to use a 2D 10 | array of booleans. However, we'd need to embiggen the array each time the 11 | pointer steps out of bounds, which is tedious. It's much easier to maintain a 12 | set of infected coordinates instead. 13 | 14 | As usual in functional languages, we need to keep the entire state in a value 15 | (a tuple in this case), and have a function that takes a state and computes the 16 | next one. Iterate this until done, and extract the answer. Simple. 17 | 18 | --- 19 | 20 | For the second part, a simple set is no longer enough. We now need a map of 21 | coordinates to state. The default state, clean, is represented as absence from 22 | the map. 23 | 24 | In conclusion: I was wrong, and Elixir is nothing like Ruby at all. It's just 25 | Erlang with a more pleasant syntax. Which is exactly what the world needed! 26 | -------------------------------------------------------------------------------- /03_tcl/README.md: -------------------------------------------------------------------------------- 1 | # [Day 3](http://adventofcode.com/2017/day/3) in TCL 2 | 3 | My first TCL code. But serious applications are (were?) being 4 | written in this language not so long ago, so how bad can it be? 5 | Either way it's just a bit of arithmetic, no fancy data 6 | structures like arrays required. 7 | 8 | Just browsing through the excellent 9 | [TCL tutorial](http://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial.html), 10 | the language reminds me a lot of bash. I hope it doesn't do the crazy 11 | whitespace stuff bash does, but it shouldn't matter for this puzzle. 12 | 13 | A naive solution would be to walk the spiral until we reach the target 14 | number, then check where we are. A less naive solution is to grow the 15 | field one square "shell" at a time, until it includes the input; then 16 | figure out where in the edge of the shell we are. An optimal solution would be 17 | not to enumerate the shells at all, but compute the right shell analytically 18 | with a floored square root. 19 | 20 | I chose the second solution, because it should be fast enough even if Part Two 21 | ups the ante, is easier to debug, and has less potential for off-by-one errors. 22 | 23 | --- 24 | 25 | For part two, actually the naive spiral walking solution would have been more 26 | useful. Time for a rewrite. The coordinate system from the first part came in 27 | useful after all. 28 | -------------------------------------------------------------------------------- /09_julia/README.md: -------------------------------------------------------------------------------- 1 | # [Day 9](http://adventofcode.com/2017/day/9) in Julia 2 | 3 | I was planning to use J today, but I can't find a readily installable package 4 | for Arch Linux for it. So I'll save that for some other day when I have more 5 | inclination to compile stuff from source. Instead, I'll use Julia. Also starts 6 | with a J. 7 | 8 | I don't know anything about Julia except that it is used for mathematical 9 | computing. Looking at the [website](https://julialang.org/), it seems friendly 10 | enough. Not that I'm going to be using any of its selling features here... 11 | 12 | Julia has a friendly REPL, with a built-in help function. That's really 13 | helpful! Its built-in `readline` function does exactly what I need for reading 14 | the input, no fuss. 15 | 16 | On to processing. Using a simple recursive parser, this should be pretty 17 | straightforward. Julia's syntax is familiar and unsurprising, somewhat of a mix 18 | between Ruby and Python. Arrays are indexed from 1, but I can live with that. 19 | One problem I encountered was that my parsing functions couldn't access the 20 | global variable `i` that I defined. I couldn't find a clear reason why, but 21 | fixed it by adding a `global` declaration for them. 22 | 23 | --- 24 | 25 | Part two was a fairly straightforward adjustment to which functions do the 26 | counting: `readGarbage` instead of `readGroup`. Skipping `!` pairs was 27 | conveniently already implemented. 28 | -------------------------------------------------------------------------------- /22_elixir/22a.exs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/elixir 2 | 3 | defmodule Aoc22a do 4 | 5 | def turn_left({dx, dy}) do 6 | {dy, -dx} 7 | end 8 | 9 | def turn_right({dx, dy}) do 10 | {-dy, dx} 11 | end 12 | 13 | def add({xa, ya}, {xb, yb}) do 14 | {xa + xb, ya + yb} 15 | end 16 | 17 | def step({pos, dir, infected, infections}) do 18 | if MapSet.member?(infected, pos) do 19 | dir = turn_right(dir) 20 | {add(pos, dir), dir, MapSet.delete(infected, pos), infections} 21 | else 22 | dir = turn_left(dir) 23 | {add(pos, dir), dir, MapSet.put(infected, pos), infections + 1} 24 | end 25 | end 26 | 27 | end 28 | 29 | input = String.split(IO.read(:stdio, :all), "\n") 30 | 31 | nx = String.length(hd(input)) 32 | ny = length(input) 33 | xs = -div(nx - 1, 2) .. div(nx - 1, 2) 34 | ys = -div(ny - 1, 2) .. div(ny - 1, 2) 35 | start_infected = Stream.filter( 36 | Stream.flat_map( 37 | Stream.zip(ys, input), 38 | fn({y, line}) -> 39 | Stream.map( 40 | Stream.zip(xs, String.codepoints(line)), 41 | fn({x, char}) -> if char == "#" do {x, y} else nil end end) end), 42 | fn(element) -> element != nil end) 43 | start_position = {0, 0} 44 | start_direction = {0, -1} 45 | start_infections_count = 0 46 | 47 | start_state = {start_position, start_direction, MapSet.new(start_infected), start_infections_count} 48 | path = Stream.iterate(start_state, fn(state) -> Aoc22a.step(state) end) 49 | 50 | {_, _, _, end_infections_count} = Enum.at(path, 10000) 51 | IO.puts(end_infections_count) 52 | -------------------------------------------------------------------------------- /03_tcl/03b.tcl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/tclsh 2 | 3 | set input [gets stdin] 4 | 5 | set value(0,0) 1 6 | 7 | proc get {x y} { 8 | global value 9 | if {[info exists value($x,$y)]} { 10 | return $value($x,$y) 11 | } else { 12 | return 0 13 | } 14 | } 15 | 16 | proc compute {x y} { 17 | global value 18 | set xm [expr {$x - 1}] 19 | set xp [expr {$x + 1}] 20 | set ym [expr {$y - 1}] 21 | set yp [expr {$y + 1}] 22 | set sum [expr { 23 | [get $xm $yp] + [get $x $yp] + [get $xp $yp] + 24 | [get $xm $y ] + [get $xp $y ] + 25 | [get $xm $ym] + [get $x $ym] + [get $xp $ym] 26 | }] 27 | set value($x,$y) $sum 28 | } 29 | 30 | set shell 1 31 | set side 0 32 | set side_len 2 33 | set i_in_side 0 34 | set x 1 35 | set y 0 36 | compute $x $y 37 | 38 | while {[get $x $y] < $input} { 39 | incr i_in_side 40 | if {$i_in_side >= $side_len} { 41 | incr side 42 | set i_in_side 0 43 | if {$side >= 4} { 44 | incr shell 45 | set side_len [expr {$side_len + 2}] 46 | set side 0 47 | } 48 | } 49 | if {$side == 0} { 50 | set x [expr {$shell}] 51 | set y [expr {-$shell + 1 + $i_in_side}] 52 | } elseif {$side == 1} { 53 | set x [expr {$shell - 1 - $i_in_side}] 54 | set y [expr {$shell}] 55 | } elseif {$side == 2} { 56 | set x [expr {-$shell}] 57 | set y [expr {$shell - 1 - $i_in_side}] 58 | } elseif {$side == 3} { 59 | set x [expr {-$shell + 1 + $i_in_side}] 60 | set y [expr {-$shell}] 61 | } else { 62 | puts "ERROR" 63 | } 64 | puts "$shell/$side/$i_in_side -> $x, $y" 65 | compute $x $y 66 | } 67 | puts [get $x $y] 68 | -------------------------------------------------------------------------------- /02_forth/02a.fs: -------------------------------------------------------------------------------- 1 | 128 constant max-line 2 | create line-buf max-line chars allot 3 | 4 | : read-number ( c-addr length -- c-addr' length' value ) 5 | 0. ( c-addr length value-d ) 6 | 2swap ( value-d c-addr length ) 7 | >number ( value-d c-addr' length' ) 8 | 1 - swap 1 + swap \ skip the tab 9 | 2swap ( c-addr' length' value-d ) 10 | d>s ( c-addr' length' value ) 11 | ; 12 | 13 | : compute-max-min ( max min value -- max' min' ) 14 | rot ( min value max ) 15 | over ( min value max value ) 16 | max ( min value max' ) 17 | -rot ( max' min value ) 18 | min ( max' min' ) 19 | ; 20 | 21 | : compute-max-min' { maxv minv value -- max' min' } 22 | maxv value max 23 | minv value min 24 | ; 25 | 26 | : line-max-min \ parses the line and computes the min and max ( c-addr length -- max min ) 27 | 0 99999999 2swap ( max min c-addr length ) 28 | begin 29 | dup 0 > 30 | while 31 | read-number ( max min c-addr length value ) 32 | 4 roll ( min c-addr length value max ) 33 | 4 roll ( c-addr length value max min ) 34 | rot ( c-addr length max min value ) 35 | compute-max-min ( c-addr length max min ) 36 | 2swap ( max min c-addr length ) 37 | repeat 38 | drop drop ( max min ) 39 | ; 40 | 41 | : main 42 | 0 ( acc ) 43 | begin 44 | \ read a line into line-buf ( -- length ) 45 | line-buf max-line stdin read-line throw 46 | while 47 | \ dup line-buf swap dump \ print current buffer 48 | line-buf swap line-max-min ( max min ) 49 | - ( acc difference ) 50 | + ( acc ) 51 | repeat 52 | drop ( acc length -- acc ) 53 | . 54 | ; 55 | main 56 | 57 | max-line negate chars allot 58 | -------------------------------------------------------------------------------- /20_nim/aoc20a.nim: -------------------------------------------------------------------------------- 1 | import pegs 2 | import sequtils 3 | import strutils 4 | 5 | type Vec3 = tuple[x: int, y: int, z: int] 6 | 7 | proc norm(p: Vec3): int = 8 | return abs(p.x) + abs(p.y) + abs(p.z) 9 | 10 | type Particle = tuple[p: Vec3, v: Vec3, a: Vec3] 11 | 12 | let assignmentPeg: Peg = peg""" 13 | assignment <- ws name ws '=' ws value ws ( ',' ws ) ? 14 | name <- { \w } + 15 | value <- '<' ws int ws ',' ws int ws ',' ws int ws '>' 16 | int <- { '-' ? \d + } 17 | ws <- \white * 18 | """ 19 | 20 | proc matchSubstr(s: string, c: Captures, i: int): string = 21 | return s.substr(c.bounds(i).first, c.bounds(i).last) 22 | var particles: seq[Particle] = @[] 23 | for line in lines(stdin): 24 | var particle: Particle 25 | var start: int = 0 26 | var length: int 27 | while true: 28 | var captures: Captures 29 | length = rawMatch(line, assignmentPeg, start, captures) 30 | if length < 0: 31 | break 32 | let name = line.matchSubstr(captures, 0) 33 | let vec: Vec3 = ( 34 | parseInt(line.matchSubstr(captures, 1)), 35 | parseInt(line.matchSubstr(captures, 2)), 36 | parseInt(line.matchSubstr(captures, 3))) 37 | case name 38 | of "p": 39 | particle.p = vec 40 | of "v": 41 | particle.v = vec 42 | of "a": 43 | particle.a = vec 44 | start += length 45 | particles &= particle 46 | 47 | var minAcceleration: int = high(int) 48 | var index: int = -1 49 | for i in countup(particles.low, particles.high): 50 | let acceleration = particles[i].a.norm() 51 | if acceleration < minAcceleration: 52 | minAcceleration = acceleration 53 | index = i 54 | echo($index) 55 | -------------------------------------------------------------------------------- /19_clojure/19b.clj: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clojure 2 | 3 | (require ['clojure.string :as 'string]) 4 | 5 | (defrecord Step [x y]) 6 | (defn turn-left [step] 7 | (->Step (- (:y step)) (:x step))) 8 | (defn turn-right [step] 9 | (->Step (:y step) (- (:x step)))) 10 | (defn steps-to-try [step] 11 | [step (turn-left step) (turn-right step)]) 12 | 13 | (defrecord Pos [x y]) 14 | (defn plus [pos step] (->Pos (+ (:x pos) (:x step)) (+ (:y pos) (:y step)))) 15 | 16 | (defn char-at [lines pos] 17 | (get (get lines (:y pos)) (:x pos))) 18 | (defn char-after-step [lines pos step] 19 | (char-at lines (plus pos step))) 20 | (defn can-walk-on [char] 21 | (not= char \space)) 22 | (defn can-walk-at [lines pos] 23 | (can-walk-on (char-at lines pos))) 24 | 25 | (defrecord State [pos step num-steps]) 26 | (defn find-start-state [lines] 27 | (->State (->Pos (string/index-of (get lines 0) "|") 0) (->Step 0 1) 1)) 28 | (defn find-next-step [lines cur-pos cur-step] 29 | (first (filter 30 | #(can-walk-at lines (plus cur-pos %)) 31 | (steps-to-try cur-step)))) 32 | (defn find-next-state [lines cur-state] 33 | (let [next-step (find-next-step lines (:pos cur-state) (:step cur-state))] 34 | (if (nil? next-step) 35 | nil 36 | (let [next-pos (plus (:pos cur-state) next-step)] 37 | (let [next-num-steps (+ (:num-steps cur-state) 1)] 38 | (->State next-pos next-step next-num-steps)))))) 39 | (defn walk [lines] 40 | (take-while some? (iterate #(find-next-state lines %) (find-start-state lines)))) 41 | 42 | (let [lines (string/split-lines (slurp *in*))] 43 | (let [end-state (last (walk lines))] 44 | (println (:num-steps end-state)))) 45 | -------------------------------------------------------------------------------- /19_clojure/README.md: -------------------------------------------------------------------------------- 1 | # [Day 19](http://adventofcode.com/2017/day/19) in Clojure 2 | 3 | I did use Scheme last year, which, like Clojure, is also a LISP derivative, but 4 | the [list of differences](https://clojure.org/reference/lisps) is long and 5 | deep, so I'm allowing Clojure as well now. Besides, I think it'll be fun. 6 | 7 | I'm building this up in very small steps, using a large number of small, pure 8 | functions. I hope this will help me get it right on the first try, because 9 | debugging Clojure does not seem like fun. If an exception is thrown, you get a 10 | line number which refers to the _top-level_ expression being evaluated at that 11 | moment (i.e. your "main function"), and a Java stack trace which bears no 12 | relationship whatsoever to your code. Seriously, they couldn't find a way to 13 | annotate the stack trace with the symbols from the Clojure source, rather than 14 | the Clojure interpreter?! (Incidentally, I remember very similar problems with 15 | Groovy. Perhaps the JVM isn't the ultimate answer to everything after all.) 16 | 17 | The algorithm is as follows: keep track of the current position and direction. 18 | Upon each step, check if the character ahead is walkable (i.e. not a space – 19 | thank you Eric for providing space-padded input!). If it is, walk there. If 20 | not, check the same for ninety-degree turns left and right, and pick the first 21 | walkable one. Note that we can completely ignore whether a character is a `|`, 22 | `+`, `-` or letter! 23 | 24 | Anyhow, after I got all the compilation and runtime errors ironed out, the 25 | program did give the correct output. 26 | 27 | --- 28 | 29 | The second part was a trivial modification. 30 | -------------------------------------------------------------------------------- /12_dart/README.md: -------------------------------------------------------------------------------- 1 | # [Day 12](http://adventofcode.com/2017/day/12) in Dart 2 | 3 | After a false start in Elm (which can't really be run in the console) and Pony 4 | (which makes input reading needlessly difficult), I lost patience and decided 5 | to go easy on myself today by using a language that surely, surely should do 6 | what I'm asking from it. Surely? 7 | 8 | Installation is simple thanks to an Arch Linux package (not even from AUR). 9 | Running is equally simple. Within minutes, I had a "hello world" program 10 | running. So far, this just looks like another curly-braces-and-semicolons C 11 | derivative, with elements of Java 8 and ES6. But unlike most 12 | compile-to-JavaScript languages, Dart also comes with its own VM and does not 13 | depend on NodeJS, as far as I can tell. Despite Dart being a statically typed 14 | language, the VM does not do static analysis, so if you want "compile-time" 15 | errors you need to run `dartanalyzer` yourself. 16 | 17 | The solution for this puzzle is pretty unexciting. I used a regex for parsing 18 | the line (probably overkill), a streamed thingy to break it up, and then a 19 | depth-first search to find the answer. 20 | 21 | --- 22 | 23 | The second part wasn't much harder. Just do DFS traversals from any node that 24 | hasn't been visited before, until none remain. 25 | 26 | Brief conclusion about the Dart language, after my 15 minutes of experience 27 | with it: thoroughly unsurprising and unexciting. Dart may have been the bee's 28 | knees before ES6, TypeScript, Flow and all the others happened, but now it's 29 | just one of many. But, for all that, it seems like a fine language, with good 30 | principles and great documentation. 31 | -------------------------------------------------------------------------------- /07_coffeescript/07b.coffee: -------------------------------------------------------------------------------- 1 | #!/usr/bin/coffee 2 | 3 | fs = require 'fs' 4 | 5 | input = fs.readFileSync('/dev/stdin').toString() 6 | lines = input.trim().split('\n') 7 | 8 | names = [] 9 | children = {} 10 | parents = {} 11 | weights = {} 12 | for line in lines 13 | match = /^(\S+) \((\d+)\)(?: -> (.*))?$/.exec(line) 14 | name = match[1] 15 | weight = match[2] 16 | childList = if match[3] then match[3].split(', ') else [] 17 | names.push(name) 18 | children[name] = childList 19 | for ch in childList 20 | parents[ch] = name 21 | weights[name] = parseInt(weight) 22 | 23 | root = null 24 | for name in names 25 | if !parents[name] 26 | root = name 27 | 28 | sumWeights = {} 29 | computeSumWeights = (name) -> 30 | sumWeight = weights[name] 31 | for child in children[name] 32 | computeSumWeights(child) 33 | sumWeight += sumWeights[child] 34 | sumWeights[name] = sumWeight 35 | computeSumWeights(root) 36 | 37 | isBalanced = (name) -> 38 | ch = children[name] 39 | if ch.length < 2 40 | true 41 | else 42 | expectedSumWeight = sumWeights[ch[0]] 43 | for c in ch 44 | if sumWeights[c] != expectedSumWeight 45 | return false 46 | true 47 | 48 | for name in names 49 | if !isBalanced(name) 50 | continue 51 | if !parents[name] 52 | continue 53 | siblings = children[parents[name]] 54 | if siblings.length < 3 55 | continue 56 | mySumWeight = sumWeights[name] 57 | differentSiblings = 0 58 | siblingsSumWeight = 0 59 | for sibling in siblings 60 | if sumWeights[sibling] != mySumWeight 61 | differentSiblings++ 62 | siblingsSumWeight = sumWeights[sibling] 63 | if differentSiblings >= 2 64 | console.log weights[name] + siblingsSumWeight - mySumWeight 65 | -------------------------------------------------------------------------------- /11_j/11a.ijs: -------------------------------------------------------------------------------- 1 | NB. Read stdin into a string, trimming off the trailing newline. 2 | NB. 'se,sw,se,sw,sw' 3 | NB. inputstr =: }: (1!:1) 3 4 | 5 | NB. Tokenize to get a table (?) of boxed strings (?). 6 | NB. See the "voyage of discovery": http://code.jsoftware.com/wiki/Vocabulary/eq#dyadic 7 | NB. ┌──┬─┬──┬─┬──┬─┬──┬─┬──┐ 8 | NB. │se│,│sw│,│se│,│sw│,│sw│ 9 | NB. └──┴─┴──┴─┴──┴─┴──┴─┴──┘ 10 | NB. tokens =: ;: inputstr 11 | 12 | NB. With the commas removed. 13 | NB. ┌──┬──┬──┬──┬──┐ 14 | NB. │se│sw│se│sw│sw│ 15 | NB. └──┴──┴──┴──┴──┘ 16 | NB. input =: ((<,',') ~: tokens) # tokens 17 | 18 | NB. Now all jammed together using a fork. Because I can. I think this could be 19 | NB. a hook as well, to get rid of the ]. 20 | input =: ((<, ',') & ~: # ]) ;: }: (1!:1) 3 21 | 22 | NB. Tabulate the steps. 23 | NB. ┌─┬──┬──┬──┬──┬─┐ 24 | NB. │n│nw│ne│sw│se│s│ 25 | NB. └─┴──┴──┴──┴──┴─┘ 26 | NB. ┌───┬────┬───┬────┬────┬────┐ 27 | NB. │0 1│_1 1│1 0│_1 0│1 _1│0 _1│ 28 | NB. └───┴────┴───┴────┴────┴────┘ 29 | NB. Single characters get rank 0 (atoms) while multiple characters get rank 1 30 | NB. (lists), which is why we need 1$ on the single-char names. 31 | stepnames =: (1$'n'); 'nw'; 'ne'; 'sw'; 'se'; (1$'s') 32 | stepsizes =: 0 1 ; _1 1; 1 0 ; _1 0; 1 _1; 0 _1 33 | 34 | NB. Look up the index of each step. 35 | NB. 4 3 4 3 3 36 | NB. I'm not sure why the 6 | is needed, but index 0 somehow comes back as 6. 37 | NB. stepindices =: stepnames i. input 38 | 39 | NB. Look up the steps themselves. 40 | NB. ┌────┬────┬────┬────┬────┐ 41 | NB. │1 _1│_1 0│1 _1│_1 0│_1 0│ 42 | NB. └────┴────┴────┴────┴────┘ 43 | NB. steps =: stepindices { stepsizes 44 | 45 | NB. Add up all the steps, after unboxing them. 46 | NB. position =: +/ > steps 47 | 48 | NB. And now all together! 49 | echo +/ > (stepnames i. input) { stepsizes 50 | -------------------------------------------------------------------------------- /25_kotlin/input: -------------------------------------------------------------------------------- 1 | Begin in state A. 2 | Perform a diagnostic checksum after 12629077 steps. 3 | 4 | In state A: 5 | If the current value is 0: 6 | - Write the value 1. 7 | - Move one slot to the right. 8 | - Continue with state B. 9 | If the current value is 1: 10 | - Write the value 0. 11 | - Move one slot to the left. 12 | - Continue with state B. 13 | 14 | In state B: 15 | If the current value is 0: 16 | - Write the value 0. 17 | - Move one slot to the right. 18 | - Continue with state C. 19 | If the current value is 1: 20 | - Write the value 1. 21 | - Move one slot to the left. 22 | - Continue with state B. 23 | 24 | In state C: 25 | If the current value is 0: 26 | - Write the value 1. 27 | - Move one slot to the right. 28 | - Continue with state D. 29 | If the current value is 1: 30 | - Write the value 0. 31 | - Move one slot to the left. 32 | - Continue with state A. 33 | 34 | In state D: 35 | If the current value is 0: 36 | - Write the value 1. 37 | - Move one slot to the left. 38 | - Continue with state E. 39 | If the current value is 1: 40 | - Write the value 1. 41 | - Move one slot to the left. 42 | - Continue with state F. 43 | 44 | In state E: 45 | If the current value is 0: 46 | - Write the value 1. 47 | - Move one slot to the left. 48 | - Continue with state A. 49 | If the current value is 1: 50 | - Write the value 0. 51 | - Move one slot to the left. 52 | - Continue with state D. 53 | 54 | In state F: 55 | If the current value is 0: 56 | - Write the value 1. 57 | - Move one slot to the right. 58 | - Continue with state A. 59 | If the current value is 1: 60 | - Write the value 1. 61 | - Move one slot to the left. 62 | - Continue with state E. 63 | -------------------------------------------------------------------------------- /19_clojure/19a.clj: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clojure 2 | 3 | (require ['clojure.string :as 'string]) 4 | 5 | (defrecord Step [x y]) 6 | (defn turn-left [step] 7 | (->Step (- (:y step)) (:x step))) 8 | (defn turn-right [step] 9 | (->Step (:y step) (- (:x step)))) 10 | (defn steps-to-try [step] 11 | [step (turn-left step) (turn-right step)]) 12 | 13 | (defrecord Pos [x y]) 14 | (defn plus [pos step] (->Pos (+ (:x pos) (:x step)) (+ (:y pos) (:y step)))) 15 | 16 | (defn char-at [lines pos] 17 | (get (get lines (:y pos)) (:x pos))) 18 | (defn char-after-step [lines pos step] 19 | (char-at lines (plus pos step))) 20 | (defn can-walk-on [char] 21 | (not= char \space)) 22 | (defn can-walk-at [lines pos] 23 | (can-walk-on (char-at lines pos))) 24 | 25 | (defrecord State [pos step letters]) 26 | (defn find-start-state [lines] 27 | (->State (->Pos (string/index-of (get lines 0) "|") 0) (->Step 0 1) [])) 28 | (defn find-next-step [lines cur-pos cur-step] 29 | (first (filter 30 | #(can-walk-at lines (plus cur-pos %)) 31 | (steps-to-try cur-step)))) 32 | (defn append-letters [letters char] 33 | (if (Character/isLetter char) 34 | (conj letters char) 35 | letters)) 36 | (defn find-next-state [lines cur-state] 37 | (let [next-step (find-next-step lines (:pos cur-state) (:step cur-state))] 38 | (if (nil? next-step) 39 | nil 40 | (let [next-pos (plus (:pos cur-state) next-step)] 41 | (let [next-letters (append-letters (:letters cur-state) (char-at lines next-pos))] 42 | (->State next-pos next-step next-letters)))))) 43 | (defn walk [lines] 44 | (take-while some? (iterate #(find-next-state lines %) (find-start-state lines)))) 45 | 46 | (let [lines (string/split-lines (slurp *in*))] 47 | (let [end-state (last (walk lines))] 48 | (println (string/join (:letters end-state))))) 49 | -------------------------------------------------------------------------------- /22_elixir/22b.exs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/elixir 2 | 3 | defmodule Aoc22a do 4 | 5 | def turn_left({dx, dy}) do 6 | {dy, -dx} 7 | end 8 | 9 | def turn_right({dx, dy}) do 10 | {-dy, dx} 11 | end 12 | 13 | def reverse({dx, dy}) do 14 | {-dx, -dy} 15 | end 16 | 17 | def add({xa, ya}, {xb, yb}) do 18 | {xa + xb, ya + yb} 19 | end 20 | 21 | def step({pos, dir, infected, infections}) do 22 | case Map.get(infected, pos, ".") do 23 | "." -> 24 | dir = turn_left(dir) 25 | {add(pos, dir), dir, Map.put(infected, pos, "W"), infections} 26 | "W" -> 27 | {add(pos, dir), dir, Map.put(infected, pos, "#"), infections + 1} 28 | "#" -> 29 | dir = turn_right(dir) 30 | {add(pos, dir), dir, Map.put(infected, pos, "F"), infections} 31 | "F" -> 32 | dir = reverse(dir) 33 | {add(pos, dir), dir, Map.delete(infected, pos), infections} 34 | end 35 | end 36 | 37 | end 38 | 39 | input = String.split(IO.read(:stdio, :all), "\n") 40 | 41 | nx = String.length(hd(input)) 42 | ny = length(input) 43 | xs = -div(nx - 1, 2) .. div(nx - 1, 2) 44 | ys = -div(ny - 1, 2) .. div(ny - 1, 2) 45 | start_infected = Stream.filter( 46 | Stream.flat_map( 47 | Stream.zip(ys, input), 48 | fn({y, line}) -> 49 | Stream.map( 50 | Stream.zip(xs, String.codepoints(line)), 51 | fn({x, char}) -> if char != "." do {{x, y}, char} else nil end end) end), 52 | fn(element) -> element != nil end) 53 | start_position = {0, 0} 54 | start_direction = {0, -1} 55 | start_infections_count = 0 56 | 57 | start_state = {start_position, start_direction, Map.new(start_infected), start_infections_count} 58 | path = Stream.iterate(start_state, fn(state) -> Aoc22a.step(state) end) 59 | 60 | {_, _, end_infected, end_infections_count} = Enum.at(path, 10000000) 61 | IO.puts(end_infections_count) 62 | -------------------------------------------------------------------------------- /18_eiffel/18a/state.e: -------------------------------------------------------------------------------- 1 | class STATE 2 | 3 | create {ANY} 4 | initial 5 | 6 | feature {ANY} 7 | 8 | initial(p: ARRAY[INSTRUCTION]) 9 | do 10 | program := p 11 | create registers 12 | end 13 | 14 | get_register(r: STRING): INTEGER_64 15 | do 16 | if registers.has(r) then 17 | Result := registers@r 18 | else 19 | Result := 0 20 | end 21 | end 22 | 23 | set_register(r: STRING; v: INTEGER_64) 24 | do 25 | registers.put(v, r) 26 | ensure 27 | register_is_set: get_register(r) = v 28 | end 29 | 30 | get_next_instruction: INSTRUCTION 31 | require 32 | not_done: not is_done 33 | do 34 | Result := program @ pc.to_integer_32 35 | end 36 | 37 | get_pc: INTEGER_64 38 | require 39 | not_done: not is_done 40 | do 41 | Result := pc 42 | end 43 | 44 | set_pc(ni: INTEGER_64) 45 | require 46 | not_done: not is_done 47 | do 48 | if ni.fit_integer_32 and program.valid_index(ni.to_integer_32) then 49 | pc := ni 50 | else 51 | set_done 52 | end 53 | end 54 | 55 | increment_pc 56 | require 57 | not_done: not is_done 58 | do 59 | set_pc(get_pc + 1) 60 | end 61 | 62 | get_last_sound_frequency: INTEGER_64 63 | do 64 | Result := last_sound_frequency 65 | end 66 | 67 | play_sound(f: INTEGER_64) 68 | require 69 | not_done: not is_done 70 | do 71 | last_sound_frequency := f 72 | end 73 | 74 | set_done 75 | do 76 | done := True 77 | end 78 | 79 | is_done: BOOLEAN 80 | do 81 | Result := done 82 | end 83 | 84 | feature {} 85 | 86 | program: ARRAY[INSTRUCTION] 87 | pc: INTEGER_64 88 | registers: HASHED_DICTIONARY[INTEGER_64, STRING] 89 | last_sound_frequency: INTEGER_64 90 | done: BOOLEAN 91 | 92 | end 93 | -------------------------------------------------------------------------------- /13_octave/README.md: -------------------------------------------------------------------------------- 1 | # [Day 13](http://adventofcode.com/2017/day/13) in Octave 2 | 3 | Some of my friends commented that they did today's puzzle in Matlab, so this 4 | must be a great day to use Matlab's open-source cousin, Octave. 5 | 6 | Reading input using `scanf` is delightfully simple, although it seems to read 7 | all numbers into a flat list rather than just one line. Fine with me! Reshaping 8 | that into two rows, and splitting them into separate matrices, is easy. 9 | 10 | From there on, it's a simple matter of computing each scanner's period (which 11 | is `2 * (depth - 1)`) and our arrival time at that scanner (which is simply 12 | `depth`). If arrival time modulo period equals zero, we are caught by that 13 | scanner. All these computations can nicely be done using matrix operations, no 14 | explicit loops needed. 15 | 16 | --- 17 | 18 | I had totally seen part two coming. (It was either this, or "compute the 19 | minimum severity that can be achieved".) Here, a simple brute force solution 20 | worked well on the example input, but takes a while to run on the real thing. 21 | Because I had other stuff to do, I just set it to run; it actually only took 22 | three minutes. 23 | 24 | After some pointers from friends who all got their brute-force results much 25 | quicker, and because I'm all caught up with AoC anyway, I tried some 26 | optimizations. All measurements below were done on my laptop (AMD A4-7210 at 27 | 1.8 GHz) using the `time` command. Each step below includes the ones before it. 28 | 29 | * Original: **3m19s**. 30 | * Using `~all(...)` instead of `sum(... == 0) == 0`: **3m7s**. 31 | * Caching the periods for all scanners: **2m54s**. 32 | * Using `bsxfun` instead of passing matrices to `mod` and `+` directly: **oh 33 | crap my train is arriving but it's been way more than three minutes**. 34 | * Using `--jit-compiler`: **0.5s**. But rather than giving me an answer, Octave 35 | gives me a segmentation fault. 36 | -------------------------------------------------------------------------------- /01_postgresql/README.md: -------------------------------------------------------------------------------- 1 | # [Day 1](http://adventofcode.com/2017/day/1) in PostgreSQL 2 | 3 | I've been doing some SQL recently, so this seems like a good exercise. I'm not 4 | sure if SQL is even Turing-complete, but it should be possible to crack this 5 | simple nut, before the puzzles get too complex. I'm using PostgreSQL 10.1 for 6 | this, and I'm not going to bother checking whether my solution is actually 7 | standard SQL – I'm sure it could be made so, if needed (which is never). 8 | 9 | First problem: how to read from stdin (as per my house rules)? There is 10 | [`pg_read_file`](https://www.postgresql.org/docs/8.2/static/functions-admin.html) 11 | (obviously nonstandard) but it reads from files only. Let's use `/dev/stdin` 12 | then, to make this even more platform-specific: 13 | 14 | SELECT pg_read_file('/dev/stdin', 0, 999999); 15 | 16 | But no: 17 | 18 | $ psql -f 01a.sql 19 | psql:01a.sql:1: ERROR: must be superuser to read files 20 | 21 | Okay, okay: 22 | 23 | $ psql -U postgres -f 01a.sql 24 | psql:01a.sql:1: ERROR: absolute path not allowed 25 | 26 | Turns out I should have read the docs better: 27 | 28 | > The functions shown in Table 9-49 provide native file access to files on the 29 | > machine hosting the server. Only files within the database cluster directory 30 | > and the log_directory may be accessed. 31 | 32 | Because the server is the process that actually reads the file, using stdin is 33 | going to be difficult, if not impossible. So I'll just copy the input file: 34 | 35 | $ sudo cp input /var/lib/postgres/data/ 36 | $ psql -U postgres -f 01a.sql 37 | ... output! 38 | 39 | After these hurdles, it was a fairly simple matter to split the input into a 40 | numbered table (using another PostgreSQLism, `WITH ORDINALITY`), shift it by 41 | one, join it, and sum the matches. 42 | 43 | --- 44 | 45 | On to part two, which was mercifully not very different and a one-line change. 46 | -------------------------------------------------------------------------------- /11_j/11b.ijs: -------------------------------------------------------------------------------- 1 | NB. Read stdin into a string, trimming off the trailing newline. 2 | NB. 'se,sw,se,sw,sw' 3 | inputstr =: }: (1!:1) 3 4 | 5 | NB. Tokenize to get a table (?) of boxed strings (?). 6 | NB. See the "voyage of discovery": http://code.jsoftware.com/wiki/Vocabulary/eq#dyadic 7 | NB. ┌──┬─┬──┬─┬──┬─┬──┬─┬──┐ 8 | NB. │se│,│sw│,│se│,│sw│,│sw│ 9 | NB. └──┴─┴──┴─┴──┴─┴──┴─┴──┘ 10 | tokens =: ;: inputstr 11 | 12 | NB. With the commas removed. 13 | NB. ┌──┬──┬──┬──┬──┐ 14 | NB. │se│sw│se│sw│sw│ 15 | NB. └──┴──┴──┴──┴──┘ 16 | input =: ((<,',') ~: tokens) # tokens 17 | 18 | NB. Tabulate the steps. 19 | NB. ┌─┬──┬──┬──┬──┬─┐ 20 | NB. │n│nw│ne│sw│se│s│ 21 | NB. └─┴──┴──┴──┴──┴─┘ 22 | NB. ┌───┬────┬───┬────┬────┬────┐ 23 | NB. │0 1│_1 1│1 0│_1 0│1 _1│0 _1│ 24 | NB. └───┴────┴───┴────┴────┴────┘ 25 | NB. Single characters get rank 0 (atoms) while multiple characters get rank 1 26 | NB. (lists), which is why we need 1$ on the single-char names. 27 | stepnames =: (1$'n'); 'nw'; 'ne'; 'sw'; 'se'; (1$'s') 28 | stepsizes =: 0 1 ; _1 1; 1 0 ; _1 0; 1 _1; 0 _1 29 | 30 | NB. Look up the index of each step. 31 | NB. 4 3 4 3 3 32 | NB. I'm not sure why the 6 | is needed, but index 0 somehow comes back as 6. 33 | stepindices =: stepnames i. input 34 | 35 | NB. Look up the steps themselves. 36 | NB. ┌────┬────┬────┬────┬────┐ 37 | NB. │1 _1│_1 0│1 _1│_1 0│_1 0│ 38 | NB. └────┴────┴────┴────┴────┘ 39 | steps =: stepindices { stepsizes 40 | 41 | NB. Add up all the steps cumulatively, after unboxing them. 42 | positions =: +/\ > steps 43 | 44 | NB. Mirror negative-x positions in the origin to make all x coordinates 45 | NB. nonnegative. 46 | rightpositions =: ((0 <: 0 }"1 positions) * positions) + ((-0 > 0 }"1 positions) * positions) 47 | 48 | NB. Now distance = max(x, -y, x+y). 49 | xs =: 0 }"1 rightpositions 50 | ys =: 1 }"1 rightpositions 51 | distances =: xs >. (-ys) >. (xs + ys) 52 | 53 | NB. Finally, print the maximum distance. 54 | echo >./ distances 55 | -------------------------------------------------------------------------------- /15_pony/15a/Main.pony: -------------------------------------------------------------------------------- 1 | use buffered = "buffered" 2 | use itertools = "itertools" 3 | 4 | 5 | interface Runner 6 | 7 | fun apply(startA: U64, startB: U64) 8 | 9 | 10 | class Parser is StdinNotify 11 | 12 | let _env: Env 13 | let _run: Runner 14 | 15 | let _reader: buffered.Reader = buffered.Reader.create() 16 | 17 | new create(env: Env, run: Runner) => 18 | _env = env 19 | _run = run 20 | 21 | fun ref apply(data: Array[U8] iso) => 22 | _reader.append(consume data) 23 | 24 | fun ref _parseLine(): U64 val ? => 25 | try 26 | let line = _reader.line()? 27 | let parts = line.split(" ") 28 | (let value, let _) = parts(4)?.read_int[U64]()? 29 | value 30 | else 31 | error 32 | end 33 | 34 | fun ref dispose() => 35 | try 36 | let startA = _parseLine()? 37 | let startB = _parseLine()? 38 | _run(startA, startB) 39 | end 40 | 41 | 42 | class Generator is Iterator[U64] 43 | 44 | let _factor: U64 45 | var _value: U64 46 | 47 | new create(factor: U64, start: U64) => 48 | _factor = factor 49 | _value = start 50 | 51 | fun ref next(): U64 => 52 | _value = (_value * _factor) % 2147483647 53 | _value 54 | 55 | fun tag has_next(): Bool => true 56 | 57 | 58 | actor Main 59 | 60 | let _env: Env 61 | 62 | new create(env: Env) => 63 | _env = env 64 | _env.input(recover Parser(env, this~judge()) end) 65 | 66 | be judge(startA: U64, startB: U64) => 67 | let generatorA = Generator.create(16807, startA) 68 | let generatorB = Generator.create(48271, startB) 69 | var count: U64 = 0 70 | var matches: U64 = 0 71 | for (valueA, valueB) in itertools.Iter[U64](generatorA).zip[U64](generatorB) do 72 | if (valueA and 0xffff) == (valueB and 0xffff) then 73 | matches = matches + 1 74 | end 75 | count = count + 1 76 | if count == 40_000_000 then 77 | break 78 | end 79 | end 80 | _env.out.print(matches.string()) 81 | -------------------------------------------------------------------------------- /20_nim/aoc20b.nim: -------------------------------------------------------------------------------- 1 | import math 2 | import pegs 3 | import sequtils 4 | import strutils 5 | import tables 6 | 7 | type Vec3 = tuple[x: int, y: int, z: int] 8 | 9 | proc `+`(a: Vec3, b: Vec3): Vec3 = 10 | return (a.x + b.x, a.y + b.y, a.z + b.z) 11 | 12 | type Particle = tuple[p: Vec3, v: Vec3, a: Vec3] 13 | 14 | proc timeStep(p: Particle): Particle = 15 | result.a = p.a 16 | result.v = p.v + result.a 17 | result.p = p.p + result.v 18 | 19 | let assignmentPeg: Peg = peg""" 20 | assignment <- ws name ws '=' ws value ws ( ',' ws ) ? 21 | name <- { \w } + 22 | value <- '<' ws int ws ',' ws int ws ',' ws int ws '>' 23 | int <- { '-' ? \d + } 24 | ws <- \white * 25 | """ 26 | 27 | proc matchSubstr(s: string, c: Captures, i: int): string = 28 | return s.substr(c.bounds(i).first, c.bounds(i).last) 29 | var particles: seq[Particle] = @[] 30 | for line in lines(stdin): 31 | var particle: Particle 32 | var start: int = 0 33 | var length: int 34 | while true: 35 | var captures: Captures 36 | length = rawMatch(line, assignmentPeg, start, captures) 37 | if length < 0: 38 | break 39 | let name = line.matchSubstr(captures, 0) 40 | let vec: Vec3 = ( 41 | parseInt(line.matchSubstr(captures, 1)), 42 | parseInt(line.matchSubstr(captures, 2)), 43 | parseInt(line.matchSubstr(captures, 3))) 44 | case name 45 | of "p": 46 | particle.p = vec 47 | of "v": 48 | particle.v = vec 49 | of "a": 50 | particle.a = vec 51 | start += length 52 | particles &= particle 53 | 54 | for i in countup(1, 1000): 55 | # Note: starting positions are unique, so no need to check for collisions 56 | # before we start. 57 | particles = particles.map(timeStep) 58 | var collided = repeat(false, particles.len()) 59 | var counts = initTable[Vec3, int](nextPowerOfTwo(particles.len())) 60 | for particle in particles: 61 | counts[particle.p] = counts.getOrDefault(particle.p) + 1 62 | particles.keepIf(proc(p: Particle): bool = counts[p.p] <= 1) 63 | echo($len(particles)) 64 | -------------------------------------------------------------------------------- /01_postgresql/input: -------------------------------------------------------------------------------- 1 | 516299281491169512719425276194596424291268712697155863651846937925928456958813624428156218468331423858422613471962165756423837756856519754524985759763747559711257977361228357678293572698839754444752898835313399815748562519958329927911861654784216355489319995566297499836295985943899373615223375271231128914745273184498915241488393761676799914385265459983923743146555465177886491979962465918888396664233693243983969412682561799628789569294374554575677368219724142536789649121758582991345537639888858113763738518511184439854223386868764189133964543721941169786274781775658991329331759679943342217578532643519615296424396487669451453728113114748217177826874953466435436129165295379157226345786756899935747336785161745487933721527239394118721517195849186676814232887413175587327214144876898248571248517121796766248817366614333915154796983612174281237846165129114988453188844745119798643314857871527757831265298846833327863781341559381238458322786192379487455671563757123534253463563421716138641611915686247343417126655317378639314168461345613427262786624689498485599942336813995725145169355942616672812792174556866436158375938988738721253664772584577384558696477546232189312287262439452141564522329987139692281984783513691857538335537553448919819545332125483128878925492334361562192621672993868479566688564752226111784486619789588318171745995253645886833872665447241245329935643883892447524286642296955354249478815116517315832179925494818748478164317669471654464867111924676961162162841232473474394739793968624974397916495667233337397241933765513777241916359166994384923869741468174653353541147616645393917694581811193977311981752554551499629219873391493426883886536219455848354426461562995284162323961773644581815633779762634745339565196798724847722781666948626231631632144371873154872575615636322965353254642186897127423352618879431499138418872356116624818733232445649188793318829748789349813295218673497291134164395739665667255443366383299669973689528188264386373591424149784473698487315316676637165317972648916116755224598519934598889627918883283534261513179931798591959489372165295 2 | -------------------------------------------------------------------------------- /10_erlang/aoc10a.erl: -------------------------------------------------------------------------------- 1 | -module(aoc10a). 2 | 3 | -export([start/0, element/1, dest_index/4]). 4 | 5 | dest_index(Marks, Index, Position, Length) -> 6 | if 7 | (Index - Position + Marks) rem Marks < Length -> 8 | (Position + Length - 1 - (Index - Position) + Marks) rem Marks; 9 | true -> 10 | Index 11 | end. 12 | 13 | element(Index) -> 14 | receive 15 | {pids, Pids} -> 16 | element(Pids, Index, Index, 0, 0) 17 | end. 18 | 19 | element(Pids, Index, Value, Position, Skip) -> 20 | Marks = length(Pids), 21 | % BUGGY! See README.md for details. 22 | io:format("Element ~w has value ~w (position = ~w, skip = ~w)~n", [Index, Value, Position, Skip]), 23 | receive 24 | {length, Length} -> 25 | DestIndex = dest_index(Marks, Index, Position, Length), 26 | io:format("Element ~w sending ~w to element dest_index(~w, ~w, ~w, ~w) = ~w~n", [Index, Value, Marks, Index, Position, Length, DestIndex]), 27 | lists:nth(DestIndex + 1, Pids) ! {value, Value}, 28 | receive 29 | {value, NewValue} -> 30 | NewPosition = (Position + Length + Skip) rem Marks, 31 | NewSkip = Skip + 1, 32 | element(Pids, Index, NewValue, NewPosition, NewSkip) 33 | end; 34 | {report, Pid} -> 35 | Pid ! {result, Value} 36 | end. 37 | 38 | get_result(Pid) -> 39 | Pid ! {report, self()}, 40 | receive 41 | {result, Value} -> 42 | Value 43 | end. 44 | 45 | start() -> 46 | Input = string:chomp(io:get_line("")), 47 | ParseInt = fun(String) -> {Int, _} = string:to_integer(String), Int end, 48 | Lengths = lists:map(ParseInt, string:tokens(Input, ",")), 49 | Marks = 256, 50 | Pids = lists:map(fun(Index) -> spawn(aoc10a, element, [Index]) end, lists:seq(0, Marks - 1)), 51 | lists:foreach(fun(Pid) -> Pid ! {pids, Pids} end, Pids), 52 | lists:foreach(fun(Length) -> lists:foreach(fun(Pid) -> Pid ! {length, Length} end, Pids) end, Lengths), 53 | Answer = get_result(lists:nth(1, Pids)) * get_result(lists:nth(2, Pids)), 54 | io:format("~w~n", [Answer]), 55 | halt(). 56 | -------------------------------------------------------------------------------- /15_pony/15b/Main.pony: -------------------------------------------------------------------------------- 1 | use buffered = "buffered" 2 | use itertools = "itertools" 3 | 4 | 5 | interface Runner 6 | 7 | fun apply(startA: U64, startB: U64) 8 | 9 | 10 | class Parser is StdinNotify 11 | 12 | let _env: Env 13 | let _run: Runner 14 | 15 | let _reader: buffered.Reader = buffered.Reader.create() 16 | 17 | new create(env: Env, run: Runner) => 18 | _env = env 19 | _run = run 20 | 21 | fun ref apply(data: Array[U8] iso) => 22 | _reader.append(consume data) 23 | 24 | fun ref _parseLine(): U64 val ? => 25 | try 26 | let line = _reader.line()? 27 | let parts = line.split(" ") 28 | (let value, let _) = parts(4)?.read_int[U64]()? 29 | value 30 | else 31 | error 32 | end 33 | 34 | fun ref dispose() => 35 | try 36 | let startA = _parseLine()? 37 | let startB = _parseLine()? 38 | _run(startA, startB) 39 | end 40 | 41 | 42 | class Generator is Iterator[U64] 43 | 44 | let _factor: U64 45 | let _modulo: U64 46 | var _value: U64 47 | 48 | new create(factor: U64, modulo: U64, start: U64) => 49 | _factor = factor 50 | _modulo = modulo 51 | _value = start 52 | 53 | fun ref next(): U64 => 54 | repeat 55 | _value = (_value * _factor) % 2147483647 56 | until (_value % _modulo) == 0 end 57 | _value 58 | 59 | fun tag has_next(): Bool => true 60 | 61 | 62 | actor Main 63 | 64 | let _env: Env 65 | 66 | new create(env: Env) => 67 | _env = env 68 | _env.input(recover Parser(env, this~judge()) end) 69 | 70 | be judge(startA: U64, startB: U64) => 71 | let generatorA = Generator.create(16807, 4, startA) 72 | let generatorB = Generator.create(48271, 8, startB) 73 | var count: U64 = 0 74 | var matches: U64 = 0 75 | for (valueA, valueB) in itertools.Iter[U64](generatorA).zip[U64](generatorB) do 76 | if (valueA and 0xffff) == (valueB and 0xffff) then 77 | matches = matches + 1 78 | end 79 | count = count + 1 80 | if count == 5_000_000 then 81 | break 82 | end 83 | end 84 | _env.out.print(matches.string()) 85 | -------------------------------------------------------------------------------- /24_java/Aoc24a.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.InputStreamReader; 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | class Aoc24a { 10 | 11 | private Map> components = new HashMap<>(); 12 | 13 | public Aoc24a() throws IOException { 14 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 15 | String line; 16 | while ((line = reader.readLine()) != null) { 17 | Component component = new Component(line); 18 | getList(component.a).add(component); 19 | getList(component.b).add(component); 20 | } 21 | } 22 | 23 | public void run() { 24 | int maxStrength = computeMaxStrength(0); 25 | System.out.println(maxStrength); 26 | } 27 | 28 | private int computeMaxStrength(int startNumPorts) { 29 | int maxStrength = 0; 30 | for (Component c : getList(startNumPorts)) { 31 | if (!c.used) { 32 | c.used = true; 33 | maxStrength = Math.max(maxStrength, c.strength + computeMaxStrength(c.otherSide(startNumPorts))); 34 | c.used = false; 35 | } 36 | } 37 | return maxStrength; 38 | } 39 | 40 | private List getList(int numPorts) { 41 | if (!components.containsKey(numPorts)) { 42 | components.put(numPorts, new ArrayList()); 43 | } 44 | return components.get(numPorts); 45 | } 46 | 47 | public static void main(String[] args) throws IOException { 48 | new Aoc24a().run(); 49 | } 50 | } 51 | 52 | class Component { 53 | 54 | public final int a; 55 | public final int b; 56 | public final int strength; 57 | 58 | public boolean used; 59 | 60 | public Component(String line) { 61 | String[] parts = line.split("/"); 62 | a = Integer.parseInt(parts[0]); 63 | b = Integer.parseInt(parts[1]); 64 | strength = a + b; 65 | } 66 | 67 | private Component(int a, int b) { 68 | this.a = a; 69 | this.b = b; 70 | strength = a + b; 71 | } 72 | 73 | public int otherSide(int numPorts) { 74 | return a == numPorts ? b : a; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /04_awk/README.md: -------------------------------------------------------------------------------- 1 | # [Day 4](http://adventofcode.com/2017/day/4) in awk 2 | 3 | I consider myself a bit of a shell wizard, but funnily enough, I've always 4 | sidestepped awk in favour of alternative line-processing utilities like `cut` 5 | and `sed`. Time to fill this gap in my knowledge! For reference, I'm using 6 | [this awk tutorial](http://www.grymoire.com/Unix/Awk.html). 7 | 8 | I started with the following base: 9 | 10 | count = 0 11 | { 12 | count++ 13 | } 14 | END { print count } 15 | 16 | The bit between curly braces is executed for each line. (Actually, for each 17 | line that matches the pattern in front of the braces, but there's no pattern 18 | here.) The block after `END` is executed at the end. So I expected this to 19 | output the number of lines in the file, but instead, it prints `1`. Why? There 20 | are no local variables in awk, so I would expect the increment to apply to the 21 | global `count` variable. But as it turns out, the initializer `count = 0` is 22 | run for each line as well. So that needs to go in the `BEGIN` block, and all is 23 | good. 24 | 25 | The next thing to note is that `$1`, `$2` etc. are not variables, like they are 26 | in bash and Perl. Actually, the `$` is a sort of function that takes one 27 | argument (a number) and spits out the field within the line. `NF` is set to the 28 | number of fields, so this makes it easy to handle a variable number of fields 29 | using a `for` loop. 30 | 31 | Then all I need is a hash table to track the words used in the current line. Of 32 | course awk provides: associative arrays are created and addressed using square 33 | brackets, `my_array[key]`. 34 | 35 | --- 36 | 37 | For part two, the trick is to sort the letters of each word before using it as 38 | the associative array index. POSIX awk does not have a sort function, so I'll 39 | just write my own bubble sort. In-line, because POSIX awk does not have 40 | user-defined functions either. It's worth noting here that arrays are 1-based. 41 | 42 | In short, I found ack to be a surprisingly practical language to work with, far 43 | more similar to C than I'd expected, and also more powerful than the 44 | alternatives I'd been using so far. A good tool to have in my toolbox! 45 | -------------------------------------------------------------------------------- /02_forth/02b.fs: -------------------------------------------------------------------------------- 1 | 128 constant max-line 2 | create line-buf max-line chars allot 3 | 4 | : read-number ( c-addr length -- c-addr' length' value ) 5 | 0. ( c-addr length value-d ) 6 | 2swap ( value-d c-addr length ) 7 | >number ( value-d c-addr' length' ) 8 | 1 - swap 1 + swap \ skip the tab 9 | 2swap ( c-addr' length' value-d ) 10 | d>s ( c-addr' length' value ) 11 | ; 12 | 13 | \ quotient is a/b or b/a if either divides cleanly, 0 otherwise 14 | : dividing ( a b -- quotient ) 15 | 2dup < if swap endif 16 | 2dup mod 0 = if 17 | / 18 | else 19 | 2drop 0 20 | endif 21 | ; 22 | 23 | : check-dividing ( values... count v -- values... count quotient ) 24 | \ iterator goes from count-1 down to 0 25 | over 1 - ( values... count v iter ) 26 | begin 27 | dup 0 >= 28 | while 29 | dup 3 + pick ( values... count v iter w ) 30 | 2 pick ( values... count v iter w v ) 31 | dividing ( values... count v iter quotient ) 32 | dup 0 > if 33 | nip nip ( values... count quotient) 34 | exit 35 | endif 36 | drop ( values... count v iter ) 37 | 1 - 38 | repeat 39 | 2drop 0 ( values... count 0 ) 40 | ; 41 | 42 | : dropn ( values... count -- ) 43 | begin 44 | dup 0 > 45 | while 46 | nip 47 | 1 - 48 | repeat 49 | drop 50 | ; 51 | 52 | : line-dividing ( c-addr length -- quotient ) 53 | 0 rot rot ( count c-addr length ) 54 | begin 55 | read-number ( values... count c-addr length v ) 56 | -rot ( values... count v c-addr length ) 57 | 2>r ( values... count v ) 58 | dup >r 59 | check-dividing ( values... count quotient ) 60 | dup 0 > if 61 | rdrop 2rdrop ( values... count quotient ) 62 | >r ( values... count ) 63 | dropn ( ) 64 | r> ( quotient ) 65 | exit 66 | else 67 | drop r> swap 1 + ( values... v count' ) 68 | 2r> ( values... count c-addr length ) 69 | endif 70 | again 71 | ; 72 | 73 | : main 74 | 0 ( acc ) 75 | begin 76 | \ read a line into line-buf ( -- length ) 77 | line-buf max-line stdin read-line throw 78 | while 79 | \ dup line-buf swap dump \ print current buffer 80 | line-buf swap line-dividing ( acc quotient ) 81 | + ( acc ) 82 | repeat 83 | drop ( acc length -- acc ) 84 | . 85 | ; 86 | main 87 | 88 | max-line negate chars allot 89 | -------------------------------------------------------------------------------- /08_tex/08b.tex: -------------------------------------------------------------------------------- 1 | \def\ensurereg#1{% 2 | \expandafter\ifx\csname reg#1\endcsname\relax 3 | \expandafter\xdef\csname reg#1\endcsname{0}% 4 | \fi 5 | } 6 | \def\getreg#1{\csname reg#1\endcsname} 7 | \def\increg#1#2{% 8 | \expandafter\count11=\getreg{#1}% 9 | \expandafter\advance\count11 by #2% 10 | \expandafter\xdef\csname reg#1\endcsname{\number\count11}% 11 | } 12 | 13 | % \getreg{abc} \increg{abc}{5} \getreg{abc} \increg{abc}{7} \getreg{abc} \increg{abc}{-1} \getreg{abc} \end 14 | 15 | \def\eq{==} 16 | \def\ne{!=} 17 | \def\gt{>} 18 | \def\ge{>=} 19 | \def\lt{<} 20 | \def\le{<=} 21 | \def\inc{inc} 22 | \def\dec{dec} 23 | \def\parseline#1 #2 #3 if #4 #5 #6 {% 24 | \tracingmacros=1 25 | {\tt #1 #2 #3 if #4 #5 #6}\par 26 | \ensurereg{#1} 27 | \ensurereg{#4} 28 | \edef\regval{\getreg{#1}} 29 | \edef\incdec{#2} 30 | \ifx\incdec\inc 31 | \edef\change{#3} 32 | \def\symbol{+} 33 | \else 34 | \edef\change{-#3} 35 | \def\symbol{-} 36 | \fi 37 | \edef\condregval{\getreg{#4}} 38 | \edef\operator{#5} 39 | \edef\testval{#6} 40 | \def\execute{% 41 | setting {\tt#1} from $\getreg{#1}$ 42 | to $\regval$ $\symbol$ $#3$ = 43 | \increg{#1}{\change}% 44 | $\getreg{#1}$.% 45 | \edef\newregval{\getreg{#1}} 46 | \ifnum\newregval>\maxvalue 47 | \xdef\maxvalue{\newregval} 48 | New maximum set to \maxvalue.\par 49 | \fi 50 | } 51 | \def\skip{skipping.} 52 | {\tt#4} = $\condregval$, 53 | \ifx\operator\eq\ifnum\condregval=\testval\execute\else\skip\fi\fi 54 | \ifx\operator\ne\ifnum\condregval=\testval\skip\else\execute\fi\fi 55 | \ifx\operator\gt\ifnum\condregval>\testval\execute\else\skip\fi\fi 56 | \ifx\operator\ge\ifnum\condregval<\testval\skip\else\execute\fi\fi 57 | \ifx\operator\lt\ifnum\condregval<\testval\execute\else\skip\fi\fi 58 | \ifx\operator\le\ifnum\condregval>\testval\skip\else\execute\fi\fi 59 | 60 | \par\vskip 0.5em 61 | } 62 | 63 | \gdef\maxvalue{0} 64 | 65 | \def\readlines{ 66 | \read1 to \curline 67 | \ifeof1 68 | END OF FILE 69 | \par\vskip 1em 70 | \let\next=\relax 71 | \else 72 | \expandafter\parseline\curline 73 | \let\next=\readlines 74 | \fi 75 | \next 76 | } 77 | \openin1=/dev/stdin 78 | \readlines 79 | \closein1 80 | 81 | Maximum is {\bf\maxvalue} 82 | 83 | \immediate\write17{\maxvalue} 84 | 85 | \end 86 | -------------------------------------------------------------------------------- /16_fsharp/16a.fs: -------------------------------------------------------------------------------- 1 | open System 2 | open System.Text.RegularExpressions 3 | 4 | module Aoc16a = 5 | 6 | type Program = char 7 | 8 | type State = array 9 | 10 | let stateFromString (s: string): State = s.ToCharArray() 11 | 12 | let stateToString (state: State): string = 13 | state |> Array.map Char.ToString |> String.concat "" 14 | 15 | let swap (i: int) (j: int) (arr: array<'a>): array<'a> = 16 | let tmp = Array.get arr i 17 | Array.set arr i (Array.get arr j) 18 | Array.set arr j tmp 19 | arr 20 | 21 | type Move = 22 | | Spin of count: int 23 | | Exchange of posA: int * posB: int 24 | | Partner of programA: Program * programB: Program 25 | 26 | let (|Match|_|) (regexp: string) (s: string) = 27 | let m = Regex.Match(s, regexp) 28 | if m.Success 29 | then Some(m.Groups |> Seq.cast |> Seq.map (fun g -> g.Value) |> Seq.toArray) 30 | else None 31 | 32 | let parseInt (s: string): int = s |> System.Int32.Parse 33 | 34 | let parseProgram (s: string): Program = s.[0] 35 | 36 | let parseMove (s: string): Move = 37 | match s with 38 | | Match "s(\d+)" groups -> Spin(groups.[1] |> parseInt) 39 | | Match "x(\d+)/(\d+)" groups -> Exchange((groups.[1] |> parseInt), (groups.[2] |> parseInt)) 40 | | Match "p(.)/(.)" groups -> Partner((groups.[1] |> parseProgram), (groups.[2] |> parseProgram)) 41 | | _ -> failwith "no match found" 42 | 43 | let parseDance (s: string): seq = s.Split(',') |> Array.map parseMove |> Array.toSeq 44 | 45 | let startState: State = 46 | stateFromString "abcdefghijklmnop" 47 | 48 | let makeMove (state: State) (move: Move) = 49 | match move with 50 | | Spin(count) -> 51 | Array.append 52 | (Array.sub state (state.Length - count) count) 53 | (Array.sub state 0 (state.Length - count)) 54 | | Exchange(i, j) -> 55 | Array.copy state |> swap i j 56 | | Partner(a, b) -> 57 | let i = Array.IndexOf(state, a) 58 | let j = Array.IndexOf(state, b) 59 | Array.copy state |> swap i j 60 | 61 | let dance (start: State) (dance: seq): State = 62 | Seq.fold makeMove start dance 63 | 64 | [] 65 | let main argv = 66 | Console.ReadLine() |> parseDance |> dance startState |> stateToString |> printf "%s\n" 67 | 0 68 | -------------------------------------------------------------------------------- /23_php/23a.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | 1]; 5 | $instructions = []; 6 | $pc = 0; 7 | $mul_count = 0; 8 | 9 | $get_register = function($register) use (&$registers) { 10 | if (array_key_exists($register, $registers)) { 11 | return $registers[$register]; 12 | } else { 13 | return 0; 14 | } 15 | }; 16 | 17 | $set_register = function($register, $value) use (&$registers) { 18 | $registers[$register] = $value; 19 | }; 20 | 21 | $constant_value = function($value) { 22 | return $value; 23 | }; 24 | 25 | $set = function($register, $value_func) use (&$pc, $set_register) { 26 | $set_register($register, $value_func()); 27 | $pc++; 28 | }; 29 | 30 | $sub = function($register, $value_func) use (&$pc, $get_register, $set_register) { 31 | $set_register($register, $get_register($register) - $value_func()); 32 | $pc++; 33 | }; 34 | 35 | $mul = function($register, $value_func) use (&$pc, &$mul_count, $get_register, $set_register) { 36 | $set_register($register, $get_register($register) * $value_func()); 37 | $pc++; 38 | $mul_count++; 39 | }; 40 | 41 | $jnz = function($condition_func, $offset_func) use (&$pc) { 42 | if ($condition_func() != 0) { 43 | $pc += $offset_func(); 44 | } else { 45 | $pc++; 46 | } 47 | }; 48 | 49 | function parse_value($value) { 50 | global $get_register; 51 | global $constant_value; 52 | if (is_numeric($value)) { 53 | return bind($constant_value, intval($value)); 54 | } else { 55 | return bind($get_register, $value); 56 | } 57 | } 58 | 59 | function bind($closure, ...$arguments) { 60 | return function() use ($closure, $arguments) { 61 | return $closure(...$arguments); 62 | }; 63 | } 64 | 65 | while ($line = fgets(STDIN)) { 66 | $parts = explode(" ", rtrim($line)); 67 | $instruction = null; 68 | switch ($parts[0]) { 69 | case "set": 70 | $instruction = bind($set, $parts[1], parse_value($parts[2])); 71 | break; 72 | case "sub": 73 | $instruction = bind($sub, $parts[1], parse_value($parts[2])); 74 | break; 75 | case "mul": 76 | $instruction = bind($mul, $parts[1], parse_value($parts[2])); 77 | break; 78 | case "jnz": 79 | $instruction = bind($jnz, parse_value($parts[1]), parse_value($parts[2])); 80 | break; 81 | } 82 | $instructions[] = $instruction; 83 | } 84 | 85 | while (array_key_exists($pc, $instructions)) { 86 | $instructions[$pc](); 87 | print_r($registers); 88 | } 89 | echo $mul_count, "\n"; 90 | 91 | ?> 92 | -------------------------------------------------------------------------------- /06_smalltalk/README.md: -------------------------------------------------------------------------------- 1 | # [Day 6](http://adventofcode.com/2017/day/6) in Smalltalk 2 | 3 | Another first for me, Smalltalk is one of those languages that influenced 4 | modern-day languages (most notably Java) but doesn't seem to be widely used 5 | anymore, although it's still 6 | [very well loved](https://insights.stackoverflow.com/survey/2017#technology-most-loved-dreaded-and-wanted-languages) 7 | even today, so I suppose I'm in for something good. 8 | 9 | I'm going to be using GNU Smalltalk-80. The 10 | [tutorial](https://www.gnu.org/software/smalltalk/manual/html_node/Tutorial.html#Tutorial) 11 | is excellent. There are many more implementations, like Squeak, Pharo and 12 | Smalltalk/X, but GNU Smalltalk might be the least modern and therefore the most 13 | historically interesting. No idea, really. 14 | 15 | Conceptually, Smalltalk indeed strongly resembles Java, with a 16 | single-inheritance class hierarchy. The most obvious difference is syntax: 17 | where in Java, you would write `object.method(argument)`, in Smalltalk this is 18 | `object method: argument` (actually, that's more like named arguments, but the 19 | names of the arguments also indicate the method which gets called). Also, 20 | statements are (very sensibly!) terminated with a `.` rather than a `;`. Unlike 21 | Java, everything is an object (yay!); there are no primitive types which sit 22 | outside the class hierarchy. 23 | 24 | The core language is so minimalistic that basic control structures like `if` 25 | and `while` are missing. These are instead implemented as methods on booleans 26 | or blocks (anonymous functions), reminiscent of Ruby. (Indeed, Ruby seems to 27 | have borrowed a lot from Smalltalk.) For example: 28 | 29 | 0 to: 99 do: [ :i | (99 - i) print. ' bottles of beer on the wall' printNl. ] 30 | isRaining ifTrue: [ bringUmbrella ] ifFalse: [ wearShorts ] 31 | 32 | This block concept allows for higher-order functions, which result in very 33 | compact and readable code. I'm beginning to see why people like this language. 34 | 35 | On to solving the problem. It's a simple matter of simulating, and tracking the 36 | states we've seen. Smalltalk's `Set` class accepts any type as members, 37 | including arrays, and comparisons are done properly (by value, not reference), 38 | making life easy. As long as you remember that elements are actually _added_ by 39 | reference, so you'd better not modify them afterwards. Which took me 40 | embarrassingly long to realise. 41 | 42 | --- 43 | 44 | For the second part, we need to track when a state was first encountered. A 45 | `LookupTable` is perfect for this. 46 | -------------------------------------------------------------------------------- /25_kotlin/25a.kts: -------------------------------------------------------------------------------- 1 | class Transition(var writeValue: Int = 0, var moveDirection: Int = 0, var nextState: String = "") 2 | 3 | class State(val transitions: MutableMap = mutableMapOf()) 4 | 5 | val states = mutableMapOf() 6 | var currentState = "" 7 | val tape = mutableMapOf() 8 | var currentPos = 0 9 | var steps = 0 10 | 11 | class MatchSequence(private val line: CharSequence) { 12 | var matched = false 13 | private set 14 | infix fun String.then(fn: (MatchResult.Destructured) -> Unit) { 15 | if (!matched) { 16 | Regex(this).matchEntire(line)?.also { 17 | fn(it.destructured) 18 | matched = true 19 | } 20 | } 21 | } 22 | } 23 | 24 | fun CharSequence.matchRegexes(fn: MatchSequence.() -> Unit) { 25 | if (!MatchSequence(this).apply(fn).matched) { 26 | throw RuntimeException("Line ${this} did not match any regex") 27 | } 28 | } 29 | 30 | var state: State? = null 31 | var transition: Transition? = null 32 | System.`in`.bufferedReader().lines().map(String::trim).filter(String::isNotEmpty).forEach { line -> 33 | line.matchRegexes { 34 | """Begin in state (.*)\.""" then { (startState) -> 35 | currentState = startState 36 | } 37 | """Perform a diagnostic checksum after (\d+) steps\.""" then { (stepsStr) -> 38 | steps = stepsStr.toInt() 39 | } 40 | """In state (.*):""" then { (stateName) -> 41 | state = State() 42 | states[stateName] = state!! 43 | } 44 | """If the current value is (\d+):""" then { (currentValueStr) -> 45 | transition = Transition() 46 | state!!.transitions[currentValueStr.toInt()] = transition!! 47 | } 48 | """- Write the value (\d+)\.""" then { (writeValueStr) -> 49 | transition!!.writeValue = writeValueStr.toInt() 50 | } 51 | """- Move one slot to the (.*)\.""" then { (directionStr) -> 52 | when (directionStr) { 53 | "left" -> transition!!.moveDirection = -1 54 | "right" -> transition!!.moveDirection = 1 55 | } 56 | } 57 | """- Continue with state (.*)\.""" then { (nextState) -> 58 | transition!!.nextState = nextState 59 | } 60 | } 61 | } 62 | 63 | fun Transition.run() { 64 | tape[currentPos] = writeValue 65 | currentPos += moveDirection 66 | currentState = nextState 67 | } 68 | 69 | for (i in 0..steps) { 70 | states[currentState]!!.transitions[tape[currentPos] ?: 0]!!.run() 71 | } 72 | 73 | println(tape.values.count { it == 1 }) -------------------------------------------------------------------------------- /05_algol68/README.md: -------------------------------------------------------------------------------- 1 | # [Day 5](http://adventofcode.com/2017/day/5) in ALGOL 68 2 | 3 | Previously, COBOL was the oldest programming language I had used, and that 4 | wasn't an experience worth repeating. But today I'm going to go one year 5 | further back, to 1958, and write this jump table thing in ALGOL. ALGOL 68, to be 6 | precise, in the [Algol 68 Genie](https://jmvdveer.home.xs4all.nl/algol.html) 7 | implementation. It seems that the ALGOL language has been very influential on 8 | modern-day languages, so maybe it will be somewhat familiar. 9 | 10 | _Update: a redditor 11 | [informed](https://www.reddit.com/r/adventofcode/comments/7hngbn/2017_day_5_solutions/dr2x3vo/) 12 | me that ALGOL 68 is nothing like the original, so I've updated the naming here 13 | accordingly. 14 | 15 | Because machines back in the day had wildly different character sets, ALGOL 16 | programs can look syntactically different depending on the flavour 17 | ("stropping") they use to indicate keywords. Possibilities include: 18 | 19 | * quoted: `'for'`, 20 | * dotted: `.FOR`, 21 | * uppercase: `FOR`, 22 | * bold: **`for`** (I'm not sure if this is used in machines or just on paper). 23 | 24 | I'm using uppercase style ("upper stropping") here. 25 | 26 | ALGOL reminds me a bit of Pascal: semicolons to separate, rather than 27 | terminate, statements (boo!), and `:=` for assignment and `=` for equality 28 | testing (yay!). The data types remind me of C, with prefixes like `long long 29 | real`; and there is even a lovely shorthand `+:=` operator. Array types 30 | ("multiples") are declared like `[]INT`, which has come back into style with 31 | Go. And, unlike much newer languages like C, ALGOL is memory-safe. And every 32 | sequence of statements ("units") is itself an expression, a concept which has 33 | found its way into languages like Ruby. 34 | 35 | But some things are new and interesting. Identifiers may contain spaces, which 36 | I imagine can make programs easy to read but hard to parse. Rather than being 37 | entirely strongly or weakly typed like most languages nowadays, things in ALGOL 38 | can be softly, weakly, meekly, firmly and strongly typed, depending on context. 39 | I'm not sure what that means but I love it already. What we would today call 40 | "types" are referred to as "modes". All values are constant; in order to have a 41 | "variable", you need to declare a _reference_ to a constant (because references 42 | themselves can be mutated). 43 | Reading decimal numbers from stdin is 44 | [shown on Rosetta Code](https://rosettacode.org/wiki/Input/Output_for_Pairs_of_Numbers#ALGOL_68) 45 | and not actually difficult (unlike COBOL). 46 | 47 | --- 48 | 49 | Part two was a straightforward change, although the program took about 25 50 | seconds to execute the 30 million steps needed. 51 | -------------------------------------------------------------------------------- /18_eiffel/18b/state.e: -------------------------------------------------------------------------------- 1 | class STATE 2 | 3 | create {ANY} 4 | initial 5 | 6 | feature {ANY} 7 | 8 | initial(p: ARRAY[INSTRUCTION]; index: INTEGER_64) 9 | do 10 | program := p 11 | create queue.make 12 | create registers 13 | set_register("p", index) 14 | end 15 | 16 | set_peer(p: STATE) 17 | do 18 | peer := p 19 | end 20 | 21 | get_register(r: STRING): INTEGER_64 22 | do 23 | if registers.has(r) then 24 | Result := registers@r 25 | else 26 | Result := 0 27 | end 28 | end 29 | 30 | set_register(r: STRING; v: INTEGER_64) 31 | do 32 | registers.put(v, r) 33 | ensure 34 | register_is_set: get_register(r) = v 35 | end 36 | 37 | get_pc: INTEGER_64 38 | require 39 | not_done: not is_done 40 | do 41 | Result := pc 42 | end 43 | 44 | set_pc(ni: INTEGER_64) 45 | require 46 | not_done: not is_done 47 | do 48 | pc := ni 49 | end 50 | 51 | increment_pc 52 | require 53 | not_done: not is_done 54 | do 55 | set_pc(get_pc + 1) 56 | end 57 | 58 | send(v: INTEGER_64) 59 | require 60 | has_peer: peer /= Void 61 | do 62 | peer.enqueue(v) 63 | send_count := send_count + 1 64 | end 65 | 66 | can_receive: BOOLEAN 67 | do 68 | Result := not queue.is_empty 69 | end 70 | 71 | receive: INTEGER_64 72 | require 73 | can_receive: can_receive 74 | do 75 | Result := queue.first 76 | queue.remove 77 | end 78 | 79 | is_blocked: BOOLEAN 80 | do 81 | if is_done then 82 | Result := True 83 | else 84 | Result := get_next_instruction.is_blocked(Current) 85 | end 86 | end 87 | 88 | maybe_execute 89 | do 90 | if not is_blocked then 91 | get_next_instruction.execute(Current) 92 | end 93 | end 94 | 95 | is_done: BOOLEAN 96 | do 97 | if not pc.fit_integer_32 then 98 | Result := False 99 | else 100 | Result := not program.valid_index(pc.to_integer_32) 101 | end 102 | end 103 | 104 | get_send_count: INTEGER 105 | do 106 | Result := send_count 107 | end 108 | 109 | feature {STATE} 110 | 111 | enqueue(v: INTEGER_64) 112 | do 113 | queue.add(v) 114 | end 115 | 116 | feature {} 117 | 118 | program: ARRAY[INSTRUCTION] 119 | peer: STATE 120 | 121 | pc: INTEGER_64 122 | registers: HASHED_DICTIONARY[INTEGER_64, STRING] 123 | queue: QUEUE[INTEGER_64] 124 | send_count: INTEGER 125 | 126 | get_next_instruction: INSTRUCTION 127 | require 128 | not_done: not is_done 129 | do 130 | Result := program @ pc.to_integer_32 131 | end 132 | 133 | end 134 | -------------------------------------------------------------------------------- /14_cuda/14a.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int const MARKS = 256; 7 | int const ROWS = 128; 8 | 9 | __global__ 10 | void knotHash(unsigned char const *input, int inputSize, int *usedSquares) { 11 | int row = blockIdx.x * blockDim.x + threadIdx.x; 12 | 13 | unsigned char lengths[64]; 14 | int numLengths = 0; 15 | for (int i = 0; i < inputSize; i++) { 16 | lengths[numLengths++] = input[i]; 17 | } 18 | lengths[numLengths++] = '-'; 19 | if (row >= 100) { 20 | lengths[numLengths++] = '0' + (row / 100); 21 | } 22 | if (row >= 10) { 23 | lengths[numLengths++] = '0' + ((row % 100) / 10); 24 | } 25 | lengths[numLengths++] = '0' + (row % 10); 26 | unsigned char const APPEND[] = { 17, 31, 73, 47, 23 }; 27 | int const APPEND_SIZE = sizeof(APPEND) / sizeof(unsigned char); 28 | for (int i = 0; i < APPEND_SIZE; i++) { 29 | lengths[numLengths++] = APPEND[i]; 30 | } 31 | 32 | int items[MARKS]; 33 | for (int i = 0; i < MARKS; i++) { 34 | items[i] = i; 35 | } 36 | 37 | int start = 0; 38 | int skip = 0; 39 | for (int round = 0; round < 64; round++) { 40 | for (int i = 0; i < numLengths; i++) { 41 | int length = lengths[i]; 42 | for (int j = 0; j < length / 2; j++) { 43 | int a = (start + j) % MARKS; 44 | int b = (start + length - 1 - j) % MARKS; 45 | unsigned char t = items[a]; 46 | items[a] = items[b]; 47 | items[b] = t; 48 | } 49 | start = (start + length + skip) % 256; 50 | skip = (skip + 1) % 256; 51 | } 52 | } 53 | 54 | unsigned char hash[16]; 55 | for (int i = 0; i < 16; i++) { 56 | unsigned char xored = 0; 57 | for (int j = 0; j < 16; j++) { 58 | xored ^= items[16 * i + j]; 59 | } 60 | hash[i] = xored; 61 | } 62 | 63 | int bitCount = 0; 64 | for (int i = 0; i < 16; i++) { 65 | for (int j = 1; j < 0x100; j <<= 1) { 66 | if (hash[i] & j) { 67 | bitCount++; 68 | } 69 | } 70 | } 71 | usedSquares[row] = bitCount; 72 | } 73 | 74 | int main(void) { 75 | std::string inputStr; 76 | std::getline(std::cin, inputStr); 77 | int n = inputStr.size(); 78 | 79 | unsigned char *input; 80 | cudaMallocManaged(&input, n); 81 | memcpy(input, inputStr.data(), n); 82 | 83 | int *usedSquares; 84 | cudaMallocManaged(&usedSquares, ROWS * sizeof(int)); 85 | 86 | knotHash<<<1, ROWS>>>(input, n, usedSquares); 87 | 88 | cudaDeviceSynchronize(); 89 | 90 | int totalUsedSquares = 0; 91 | for (int i = 0; i < ROWS; i++) { 92 | totalUsedSquares += usedSquares[i]; 93 | } 94 | std::cout << totalUsedSquares << '\n'; 95 | 96 | cudaFree(input); 97 | cudaFree(usedSquares); 98 | 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /18_eiffel/18a/main.e: -------------------------------------------------------------------------------- 1 | class MAIN 2 | 3 | create {ANY} 4 | make 5 | 6 | feature {ANY} 7 | 8 | make 9 | local 10 | program: ARRAY[INSTRUCTION] 11 | state: STATE 12 | do 13 | program := parse_input 14 | from 15 | create state.initial(program) 16 | until 17 | state.is_done 18 | loop 19 | state.get_next_instruction.execute(state) 20 | end 21 | print(state.get_last_sound_frequency.to_string + "%N") 22 | end 23 | 24 | feature {} 25 | 26 | parse_input: ARRAY[INSTRUCTION] 27 | local 28 | program: ARRAY[INSTRUCTION] 29 | do 30 | create program.make(0, -1) 31 | from 32 | std_input.read_line 33 | until std_input.end_of_input 34 | loop 35 | program.add_last(parse_instruction(std_input.last_string)) 36 | std_input.read_line 37 | end 38 | Result := program 39 | ensure 40 | no_void_instructions: not Result.fast_has(Void) 41 | end 42 | 43 | parse_instruction(str: STRING): INSTRUCTION 44 | local 45 | parts: ARRAY[STRING] 46 | do 47 | parts := str.split 48 | inspect parts @ 1 49 | when "snd" then 50 | create {SND_INSTRUCTION} Result.make(parse_value(parts @ 2)) 51 | when "set" then 52 | create {SET_INSTRUCTION} Result.make(parts @ 2, parse_value(parts @ 3)) 53 | when "add" then 54 | create {ADD_INSTRUCTION} Result.make(parts @ 2, parse_value(parts @ 3)) 55 | when "mul" then 56 | create {MUL_INSTRUCTION} Result.make(parts @ 2, parse_value(parts @ 3)) 57 | when "mod" then 58 | create {MOD_INSTRUCTION} Result.make(parts @ 2, parse_value(parts @ 3)) 59 | when "rcv" then 60 | create {RCV_INSTRUCTION} Result.make(parts @ 2) 61 | when "jgz" then 62 | create {JGZ_INSTRUCTION} Result.make(parse_value(parts @ 2), parse_value(parts @ 3)) 63 | end 64 | ensure 65 | instruction_parsed: Result /= Void 66 | end 67 | 68 | parse_value(str: STRING): VALUE 69 | do 70 | if str.is_integer then 71 | create {CONSTANT_VALUE} Result.make(str.to_integer) 72 | else 73 | create {REGISTER_VALUE} Result.make(str) 74 | end 75 | ensure 76 | value_parsed: Result /= Void 77 | end 78 | 79 | end 80 | 81 | -- class SND 82 | -- inherit INSTRUCTION 83 | -- end 84 | -- 85 | -- class SET 86 | -- inherit INSTRUCTION 87 | -- end 88 | -- 89 | -- class ADD 90 | -- inherit INSTRUCTION 91 | -- end 92 | -- 93 | -- class MUL 94 | -- inherit INSTRUCTION 95 | -- end 96 | -- 97 | -- class MOD 98 | -- inherit INSTRUCTION 99 | -- end 100 | -- 101 | -- class RCV 102 | -- inherit INSTRUCTION 103 | -- end 104 | -- 105 | -- class JGZ 106 | -- inherit INSTRUCTION 107 | -- end 108 | -------------------------------------------------------------------------------- /07_coffeescript/README.md: -------------------------------------------------------------------------------- 1 | # [Day 7](http://adventofcode.com/2017/day/7) in CoffeeScript 2 | 3 | I don't have a lot of time today, so I decided to solve today's puzzle in a 4 | language I actually knew once: CoffeeScript. 5 | 6 | The first question is simple: which name occurs on the left, but not on the 7 | right? But I'm going to be building the complete graph structure, because I 8 | have a feeling I might need it in the second part. 9 | 10 | One reason I chose a JavaScript derivative is because input parsing is 11 | nontrivial here, and JavaScript has surprisingly decent regexp support. So that 12 | was easy. 13 | 14 | --- 15 | 16 | It turns out I was right about the second half; now we need to actually 17 | traverse the graph. We can do this from the root down (or up, if you follow the 18 | puzzle text or have ever seen a real tree). First we do a pass to compute and 19 | store all the total weights. 20 | 21 | One fact that isn't shown in the example is this: an imbalance propagates 22 | downwards through the tree. After all, if node N has the wrong weight, this 23 | means the _entire_ tower would be in balance if we changed N's weight. That 24 | means that the parent of node N will also see an imbalance. So one would think 25 | that we can do a top-down pass: start at the root, and follow imbalances until 26 | we can't find one anymore. However, the problem is: if there are only two 27 | nodes, which one do we follow? Answer: the one that is itself imbalanced. 28 | That's a fair amount of code to get right, and I had a feeling there should be 29 | a simpler way. 30 | 31 | This thinking led me to an observation: the level of the tree at which the 32 | imbalance manifests, is always one level _above_ the place we are interested 33 | in. So we can take a non-recursive approach: test each node locally, checking 34 | whether it's balanced _and_ its total weight matches that of its siblings. If 35 | it has fewer than two siblings, we don't bother: if we spot an imbalance 36 | between just two siblings, we cannot tell which is to blame, so the input would 37 | be ambiguous. 38 | 39 | This should all work fine, but I tried this and several other approaches and 40 | they all gave the wrong answer (multiple answers, in fact). Eventually I 41 | tracked it down to my `sumWeights` map containing the wrong values. This turned 42 | out to be caused by a CoffeeScript quirk (I'm beginning to remember why I don't 43 | like this language): a recursive function was doing `for child in children`, 44 | but a variable `child` also existed in an outer scope. In plain JavaScript, I'd 45 | have declared `var child` (or `let child`) inside the function, but in 46 | CoffeeScript it silently picked up the global variable. Then the recursive 47 | calls stomped all over it. After working around this by renaming one of the 48 | variables (this really is the only way...) I finally got the right answer. 49 | 50 | Spooky action at a distance. Bah. 51 | -------------------------------------------------------------------------------- /10_erlang/aoc10b.erl: -------------------------------------------------------------------------------- 1 | -module(aoc10b). 2 | 3 | -export([start/0, element/1, dest_index/4]). 4 | 5 | dest_index(Marks, Index, Position, Length) -> 6 | if 7 | (Index - Position + Marks) rem Marks < Length -> 8 | (Position + Length - 1 - (Index - Position) + Marks) rem Marks; 9 | true -> 10 | Index 11 | end. 12 | 13 | element(Index) -> 14 | receive 15 | {pids, Pids} -> 16 | element(Pids, Index, Index, 0, 0) 17 | end. 18 | 19 | element(Pids, Index, Value, Position, Skip) -> 20 | Marks = length(Pids), 21 | %io:format("Element ~w has value ~w (position = ~w, skip = ~w)~n", [Index, Value, Position, Skip]), 22 | receive 23 | {length, Length, MainPid} -> 24 | DestIndex = dest_index(Marks, Index, Position, Length), 25 | %io:format("Element ~w sending ~w to element dest_index(~w, ~w, ~w, ~w) = ~w~n", [Index, Value, Marks, Index, Position, Length, DestIndex]), 26 | lists:nth(DestIndex + 1, Pids) ! {value, Value}, 27 | receive 28 | {value, NewValue} -> 29 | NewPosition = (Position + Length + Skip) rem Marks, 30 | NewSkip = Skip + 1, 31 | MainPid ! done, 32 | element(Pids, Index, NewValue, NewPosition, NewSkip) 33 | end; 34 | {report, Pid} -> 35 | Pid ! {result, Index, Value} 36 | end. 37 | 38 | broadcast(Pids, Message) -> 39 | %io:format("~w~n", [Length]), 40 | lists:foreach(fun(Pid) -> Pid ! Message end, Pids). 41 | 42 | wait_done(0) -> 43 | true; 44 | wait_done(N) -> 45 | receive 46 | done -> 47 | wait_done(N - 1) 48 | end. 49 | 50 | get_results([], Acc) -> 51 | lists:map(fun({_, Value}) -> Value end, lists:sort(Acc)); 52 | get_results([Pid | Pids], Acc) -> 53 | Pid ! {report, self()}, 54 | receive 55 | {result, Index, Value} -> 56 | get_results(Pids, [{Index, Value} | Acc]) 57 | end. 58 | 59 | segmentize(_, []) -> 60 | []; 61 | segmentize(N, List) -> 62 | {Head, Tail} = lists:split(N, List), 63 | [ Head | segmentize(N, Tail) ]. 64 | 65 | start() -> 66 | Input = string:chomp(io:get_line("")), 67 | Lengths = lists:flatten(lists:duplicate(64, lists:append(Input, [17, 31, 73, 47, 23]))), 68 | %io:format("~w~n", [Lengths]), 69 | Marks = 256, 70 | Pids = lists:map(fun(Index) -> spawn(aoc10b, element, [Index]) end, lists:seq(0, Marks - 1)), 71 | broadcast(Pids, {pids, Pids}), 72 | lists:foreach(fun(Length) -> 73 | broadcast(Pids, {length, Length, self()}), 74 | wait_done(Marks) 75 | end, 76 | Lengths), 77 | broadcast(Pids, {report, self()}), 78 | Results = get_results(Pids, []), 79 | Segments = segmentize(16, Results), 80 | Numbers = lists:map(fun(Segment) -> lists:foldl(fun(A, B) -> A bxor B end, 0, Segment) end, Segments), 81 | Hexes = lists:flatten(lists:map(fun(Number) -> io_lib:format("~2.16.0b", [Number]) end, Numbers)), 82 | io:format("~s~n", [Hexes]), 83 | halt(). 84 | -------------------------------------------------------------------------------- /24_java/Aoc24b.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.InputStreamReader; 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | class Aoc24b { 10 | 11 | private Map> components = new HashMap<>(); 12 | 13 | public Aoc24b() throws IOException { 14 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 15 | String line; 16 | while ((line = reader.readLine()) != null) { 17 | Component component = new Component(line); 18 | getList(component.a).add(component); 19 | getList(component.b).add(component); 20 | } 21 | } 22 | 23 | public void run() { 24 | Metrics max = computeMax(0); 25 | System.out.println(max.strength); 26 | } 27 | 28 | private Metrics computeMax(int startNumPorts) { 29 | Metrics max = new Metrics(); 30 | for (Component c : getList(startNumPorts)) { 31 | if (!c.used) { 32 | c.used = true; 33 | max = Metrics.max(max, computeMax(c.otherSide(startNumPorts)).add(1, c.strength)); 34 | c.used = false; 35 | } 36 | } 37 | return max; 38 | } 39 | 40 | private List getList(int numPorts) { 41 | if (!components.containsKey(numPorts)) { 42 | components.put(numPorts, new ArrayList()); 43 | } 44 | return components.get(numPorts); 45 | } 46 | 47 | public static void main(String[] args) throws IOException { 48 | new Aoc24b().run(); 49 | } 50 | } 51 | 52 | class Metrics { 53 | public final int length; 54 | public final int strength; 55 | 56 | public Metrics() { 57 | length = 0; 58 | strength = 0; 59 | } 60 | 61 | public Metrics(int length, int strength) { 62 | this.length = length; 63 | this.strength = strength; 64 | } 65 | 66 | public Metrics add(int length, int strength) { 67 | return new Metrics(this.length + length, this.strength + strength); 68 | } 69 | 70 | public static Metrics max(Metrics a, Metrics b) { 71 | if (a.length > b.length) return a; 72 | if (b.length > a.length) return b; 73 | if (a.strength > b.strength) return a; 74 | if (b.strength > a.strength) return b; 75 | return a; 76 | } 77 | } 78 | 79 | class Component { 80 | 81 | public final int a; 82 | public final int b; 83 | public final int strength; 84 | 85 | public boolean used; 86 | 87 | public Component(String line) { 88 | String[] parts = line.split("/"); 89 | a = Integer.parseInt(parts[0]); 90 | b = Integer.parseInt(parts[1]); 91 | strength = a + b; 92 | } 93 | 94 | private Component(int a, int b) { 95 | this.a = a; 96 | this.b = b; 97 | strength = a + b; 98 | } 99 | 100 | public int otherSide(int numPorts) { 101 | return a == numPorts ? b : a; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /21_icon/21a.icn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/icon 2 | 3 | procedure dump(parts) 4 | every write(!parts) 5 | end 6 | 7 | procedure split(str) 8 | local parts 9 | parts := list() 10 | str ? while put(parts, tab(many('.#'))) do tab(upto('.#')) 11 | return parts 12 | end 13 | 14 | procedure join(parts) 15 | local str 16 | str := "" 17 | every str ||:= "/" || !parts 18 | str := str[2:0] 19 | return str 20 | end 21 | 22 | procedure flip(in) 23 | local out 24 | out := list() 25 | every put(out, reverse(!in)) 26 | return out 27 | end 28 | 29 | procedure rotate(in) 30 | local out 31 | local line 32 | local i 33 | local j 34 | out := list() 35 | every i := 1 to *in do { 36 | line := "" 37 | every j := 1 to *in do { 38 | line ||:= in[j][-i] 39 | } 40 | put(out, line) 41 | } 42 | return out 43 | end 44 | 45 | procedure readInput() 46 | local line 47 | local arrow 48 | local input 49 | local output 50 | local codebook 51 | codebook := table() 52 | while line := read() do { 53 | arrow := find(" => ", line) 54 | input := split(line[1:arrow]) 55 | output := split(line[arrow + 4:0]) 56 | codebook[join(input)] := output 57 | codebook[join(rotate(input))] := output 58 | codebook[join(rotate(rotate(input)))] := output 59 | codebook[join(rotate(rotate(rotate(input))))] := output 60 | codebook[join(flip(input))] := output 61 | codebook[join(flip(rotate(input)))] := output 62 | codebook[join(flip(rotate(rotate(input))))] := output 63 | codebook[join(flip(rotate(rotate(rotate(input)))))] := output 64 | } 65 | return codebook 66 | end 67 | 68 | procedure slice(in, row, col, size) 69 | local out 70 | local i 71 | out := list() 72 | every i := row to row + size - 1 do { 73 | put(out, in[i][col+:size]) 74 | } 75 | return out 76 | end 77 | 78 | procedure replace(in, codebook) 79 | local out 80 | local step 81 | local n 82 | local i 83 | local j 84 | local k 85 | local block 86 | local replacement 87 | n := *in 88 | step := if (n % 2) = 0 then 2 else 3 89 | out := list() 90 | every i := 1 to n by step do { 91 | every j := 1 to step + 1 do put(out, "") 92 | every j := 1 to n by step do { 93 | block := slice(in, i, j, step) 94 | replacement := codebook[join(block)] 95 | every k := 1 to *replacement do { 96 | out[-k] ||:= replacement[-k] 97 | } 98 | } 99 | } 100 | return out 101 | end 102 | 103 | procedure count(in) 104 | local cnt 105 | cnt := 0 106 | every cnt +:= ((!!in == "#") & 1) 107 | return cnt 108 | end 109 | 110 | procedure main() 111 | local codebook 112 | local grid 113 | local size 114 | 115 | codebook := readInput() 116 | 117 | grid := [ 118 | ".#.", 119 | "..#", 120 | "###" 121 | ] 122 | every 1 to 5 do grid := replace(grid, codebook) 123 | write(count(grid)) 124 | end 125 | -------------------------------------------------------------------------------- /21_icon/21b.icn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/icon 2 | 3 | procedure dump(parts) 4 | every write(!parts) 5 | end 6 | 7 | procedure split(str) 8 | local parts 9 | parts := list() 10 | str ? while put(parts, tab(many('.#'))) do tab(upto('.#')) 11 | return parts 12 | end 13 | 14 | procedure join(parts) 15 | local str 16 | str := "" 17 | every str ||:= "/" || !parts 18 | str := str[2:0] 19 | return str 20 | end 21 | 22 | procedure flip(in) 23 | local out 24 | out := list() 25 | every put(out, reverse(!in)) 26 | return out 27 | end 28 | 29 | procedure rotate(in) 30 | local out 31 | local line 32 | local i 33 | local j 34 | out := list() 35 | every i := 1 to *in do { 36 | line := "" 37 | every j := 1 to *in do { 38 | line ||:= in[j][-i] 39 | } 40 | put(out, line) 41 | } 42 | return out 43 | end 44 | 45 | procedure readInput() 46 | local line 47 | local arrow 48 | local input 49 | local output 50 | local codebook 51 | codebook := table() 52 | while line := read() do { 53 | arrow := find(" => ", line) 54 | input := split(line[1:arrow]) 55 | output := split(line[arrow + 4:0]) 56 | codebook[join(input)] := output 57 | codebook[join(rotate(input))] := output 58 | codebook[join(rotate(rotate(input)))] := output 59 | codebook[join(rotate(rotate(rotate(input))))] := output 60 | codebook[join(flip(input))] := output 61 | codebook[join(flip(rotate(input)))] := output 62 | codebook[join(flip(rotate(rotate(input))))] := output 63 | codebook[join(flip(rotate(rotate(rotate(input)))))] := output 64 | } 65 | return codebook 66 | end 67 | 68 | procedure slice(in, row, col, size) 69 | local out 70 | local i 71 | out := list() 72 | every i := row to row + size - 1 do { 73 | put(out, in[i][col+:size]) 74 | } 75 | return out 76 | end 77 | 78 | procedure replace(in, codebook) 79 | local out 80 | local step 81 | local n 82 | local i 83 | local j 84 | local k 85 | local block 86 | local replacement 87 | n := *in 88 | step := if (n % 2) = 0 then 2 else 3 89 | out := list() 90 | every i := 1 to n by step do { 91 | every j := 1 to step + 1 do put(out, "") 92 | every j := 1 to n by step do { 93 | block := slice(in, i, j, step) 94 | replacement := codebook[join(block)] 95 | every k := 1 to *replacement do { 96 | out[-k] ||:= replacement[-k] 97 | } 98 | } 99 | } 100 | return out 101 | end 102 | 103 | procedure count(in) 104 | local cnt 105 | cnt := 0 106 | every cnt +:= ((!!in == "#") & 1) 107 | return cnt 108 | end 109 | 110 | procedure main() 111 | local codebook 112 | local grid 113 | local size 114 | 115 | codebook := readInput() 116 | 117 | grid := [ 118 | ".#.", 119 | "..#", 120 | "###" 121 | ] 122 | every 1 to 18 do grid := replace(grid, codebook) 123 | write(count(grid)) 124 | end 125 | -------------------------------------------------------------------------------- /16_fsharp/16b.fs: -------------------------------------------------------------------------------- 1 | open System 2 | open System.Text.RegularExpressions 3 | 4 | module Aoc16a = 5 | 6 | type Program = char 7 | 8 | type State = array 9 | 10 | let stateFromString (s: string): State = s.ToCharArray() 11 | 12 | let stateToString (state: State): string = 13 | state |> Array.map Char.ToString |> String.concat "" 14 | 15 | let swap (i: int) (j: int) (arr: array<'a>): array<'a> = 16 | let tmp = Array.get arr i 17 | Array.set arr i (Array.get arr j) 18 | Array.set arr j tmp 19 | arr 20 | 21 | type Move = 22 | | Spin of count: int 23 | | Exchange of posA: int * posB: int 24 | | Partner of programA: Program * programB: Program 25 | 26 | type Dance = seq 27 | 28 | let (|Match|_|) (regexp: string) (s: string) = 29 | let m = Regex.Match(s, regexp) 30 | if m.Success 31 | then Some(m.Groups |> Seq.cast |> Seq.map (fun g -> g.Value) |> Seq.toArray) 32 | else None 33 | 34 | let parseInt (s: string): int = s |> System.Int32.Parse 35 | 36 | let parseProgram (s: string): Program = s.[0] 37 | 38 | let parseMove (s: string): Move = 39 | match s with 40 | | Match "s(\d+)" groups -> Spin(groups.[1] |> parseInt) 41 | | Match "x(\d+)/(\d+)" groups -> Exchange((groups.[1] |> parseInt), (groups.[2] |> parseInt)) 42 | | Match "p(.)/(.)" groups -> Partner((groups.[1] |> parseProgram), (groups.[2] |> parseProgram)) 43 | | _ -> failwith "no match found" 44 | 45 | let parseDance (s: string): Dance = s.Split(',') |> Array.map parseMove |> Array.toSeq 46 | 47 | let startState: State = 48 | stateFromString "abcdefghijklmnop" 49 | 50 | let makeMove (state: State) (move: Move) = 51 | match move with 52 | | Spin(count) -> 53 | Array.append 54 | (Array.sub state (state.Length - count) count) 55 | (Array.sub state 0 (state.Length - count)) 56 | | Exchange(i, j) -> 57 | Array.copy state |> swap i j 58 | | Partner(a, b) -> 59 | let i = Array.IndexOf(state, a) 60 | let j = Array.IndexOf(state, b) 61 | Array.copy state |> swap i j 62 | 63 | let danceOnce (start: State) (dance: Dance): State = 64 | Seq.fold makeMove start dance 65 | 66 | let danceForever (start: State) (dance: Dance): seq = 67 | seq { 68 | yield start; 69 | yield! Seq.unfold (fun s -> Some(danceOnce s dance, danceOnce s dance)) start 70 | } 71 | 72 | let cycleLength (start: State) (dance: Dance): int = 73 | danceForever start dance |> Seq.tail |> Seq.takeWhile ((<>) start) |> Seq.length |> (+) 1 74 | 75 | let danceManyTimes (start: State) (count: int) (dance: Dance): State = 76 | let reducedCount = count % (cycleLength start dance) 77 | danceForever start dance |> Seq.item reducedCount 78 | 79 | [] 80 | let main argv = 81 | Console.ReadLine() |> parseDance |> danceManyTimes startState 1000000000 |> stateToString |> printf "%s\n" 82 | 0 83 | -------------------------------------------------------------------------------- /08_tex/08a.tex: -------------------------------------------------------------------------------- 1 | \gdef\registers{} 2 | \def\ensurereg#1{% 3 | \expandafter\ifx\csname reg#1\endcsname\relax 4 | \edef\oldreg{\registers} 5 | \xdef\registers{\oldreg#1,} 6 | \expandafter\xdef\csname reg#1\endcsname{0}% 7 | \fi 8 | } 9 | \def\getreg#1{\csname reg#1\endcsname} 10 | \def\increg#1#2{% 11 | \expandafter\count11=\getreg{#1}% 12 | \expandafter\advance\count11 by #2% 13 | \expandafter\xdef\csname reg#1\endcsname{\number\count11}% 14 | } 15 | 16 | % \getreg{abc} \increg{abc}{5} \getreg{abc} \increg{abc}{7} \getreg{abc} \increg{abc}{-1} \getreg{abc} \end 17 | 18 | \def\eq{==} 19 | \def\ne{!=} 20 | \def\gt{>} 21 | \def\ge{>=} 22 | \def\lt{<} 23 | \def\le{<=} 24 | \def\inc{inc} 25 | \def\dec{dec} 26 | \def\parseline#1 #2 #3 if #4 #5 #6 {% 27 | \tracingmacros=1 28 | {\tt #1 #2 #3 if #4 #5 #6}\par 29 | \ensurereg{#1} 30 | \ensurereg{#4} 31 | \edef\regval{\getreg{#1}} 32 | \edef\incdec{#2} 33 | \ifx\incdec\inc 34 | \edef\change{#3} 35 | \def\symbol{+} 36 | \else 37 | \edef\change{-#3} 38 | \def\symbol{-} 39 | \fi 40 | \edef\condregval{\getreg{#4}} 41 | \edef\operator{#5} 42 | \edef\testval{#6} 43 | \def\execute{% 44 | setting {\tt#1} from $\getreg{#1}$ 45 | to $\regval$ $\symbol$ $#3$ = 46 | \increg{#1}{\change}% 47 | $\getreg{#1}$.% 48 | } 49 | \def\skip{skipping.} 50 | {\tt#4} = $\condregval$, 51 | \ifx\operator\eq\ifnum\condregval=\testval\execute\else\skip\fi\fi 52 | \ifx\operator\ne\ifnum\condregval=\testval\skip\else\execute\fi\fi 53 | \ifx\operator\gt\ifnum\condregval>\testval\execute\else\skip\fi\fi 54 | \ifx\operator\ge\ifnum\condregval<\testval\skip\else\execute\fi\fi 55 | \ifx\operator\lt\ifnum\condregval<\testval\execute\else\skip\fi\fi 56 | \ifx\operator\le\ifnum\condregval>\testval\skip\else\execute\fi\fi 57 | 58 | \par\vskip 0.5em 59 | } 60 | 61 | \def\readlines{ 62 | \read1 to \curline 63 | \ifeof1 64 | END OF FILE 65 | \par\vskip 1em 66 | \let\next=\relax 67 | \else 68 | \expandafter\parseline\curline 69 | \let\next=\readlines 70 | \fi 71 | \next 72 | } 73 | \openin1=/dev/stdin 74 | \readlines 75 | \closein1 76 | 77 | Registers:\par 78 | \registers\par 79 | End registers.\par\vskip 1em 80 | 81 | \gdef\maxvalue{0} 82 | \def\endmarker{!} 83 | \def\findmax#1,{ 84 | \def\register{#1} 85 | \ifx\register\endmarker 86 | END OF LIST 87 | \let\next=\relax 88 | \else 89 | \edef\value{\csname reg#1\endcsname} 90 | Checking {\tt#1} $= \value{}$ against \maxvalue: 91 | \ifnum\value>\maxvalue 92 | \xdef\maxvalue{\value}% 93 | new maximum! 94 | \else 95 | not greater than \maxvalue. 96 | \fi 97 | \let\next=\findmax 98 | \fi 99 | \par 100 | \next 101 | } 102 | \edef\oldreg{\registers} 103 | \xdef\registers{\oldreg\endmarker,} 104 | \expandafter\findmax\registers 105 | Maximum is {\bf\maxvalue} 106 | 107 | \immediate\write17{\maxvalue} 108 | 109 | \end 110 | -------------------------------------------------------------------------------- /18_eiffel/18b/main.e: -------------------------------------------------------------------------------- 1 | class MAIN 2 | 3 | create {ANY} 4 | make 5 | 6 | feature {ANY} 7 | 8 | make 9 | local 10 | program: ARRAY[INSTRUCTION] 11 | states: ARRAY[STATE] 12 | do 13 | program := parse_input 14 | from 15 | create states.make(0, 1) 16 | states.put(create {STATE}.initial(program, 0), 0) 17 | states.put(create {STATE}.initial(program, 1), 1) 18 | (states @ 0).set_peer(states @ 1) 19 | (states @ 1).set_peer(states @ 0) 20 | until 21 | states.for_all(agent {STATE}.is_blocked) 22 | loop 23 | states.for_each(agent {STATE}.maybe_execute) 24 | end 25 | print((states @ 1).get_send_count.to_string + "%N") 26 | end 27 | 28 | feature {} 29 | 30 | parse_input: ARRAY[INSTRUCTION] 31 | local 32 | program: ARRAY[INSTRUCTION] 33 | do 34 | create program.make(0, -1) 35 | from 36 | std_input.read_line 37 | until std_input.end_of_input 38 | loop 39 | program.add_last(parse_instruction(std_input.last_string)) 40 | std_input.read_line 41 | end 42 | Result := program 43 | ensure 44 | no_void_instructions: not Result.fast_has(Void) 45 | end 46 | 47 | parse_instruction(str: STRING): INSTRUCTION 48 | local 49 | parts: ARRAY[STRING] 50 | do 51 | parts := str.split 52 | inspect parts @ 1 53 | when "snd" then 54 | create {SND_INSTRUCTION} Result.make(parse_value(parts @ 2)) 55 | when "set" then 56 | create {SET_INSTRUCTION} Result.make(parts @ 2, parse_value(parts @ 3)) 57 | when "add" then 58 | create {ADD_INSTRUCTION} Result.make(parts @ 2, parse_value(parts @ 3)) 59 | when "mul" then 60 | create {MUL_INSTRUCTION} Result.make(parts @ 2, parse_value(parts @ 3)) 61 | when "mod" then 62 | create {MOD_INSTRUCTION} Result.make(parts @ 2, parse_value(parts @ 3)) 63 | when "rcv" then 64 | create {RCV_INSTRUCTION} Result.make(parts @ 2) 65 | when "jgz" then 66 | create {JGZ_INSTRUCTION} Result.make(parse_value(parts @ 2), parse_value(parts @ 3)) 67 | end 68 | ensure 69 | instruction_parsed: Result /= Void 70 | end 71 | 72 | parse_value(str: STRING): VALUE 73 | do 74 | if str.is_integer then 75 | create {CONSTANT_VALUE} Result.make(str.to_integer) 76 | else 77 | create {REGISTER_VALUE} Result.make(str) 78 | end 79 | ensure 80 | value_parsed: Result /= Void 81 | end 82 | 83 | end 84 | 85 | -- class SND 86 | -- inherit INSTRUCTION 87 | -- end 88 | -- 89 | -- class SET 90 | -- inherit INSTRUCTION 91 | -- end 92 | -- 93 | -- class ADD 94 | -- inherit INSTRUCTION 95 | -- end 96 | -- 97 | -- class MUL 98 | -- inherit INSTRUCTION 99 | -- end 100 | -- 101 | -- class MOD 102 | -- inherit INSTRUCTION 103 | -- end 104 | -- 105 | -- class RCV 106 | -- inherit INSTRUCTION 107 | -- end 108 | -- 109 | -- class JGZ 110 | -- inherit INSTRUCTION 111 | -- end 112 | -------------------------------------------------------------------------------- /08_tex/README.md: -------------------------------------------------------------------------------- 1 | # [Day 8](http://adventofcode.com/2017/day/8) in TeX 2 | 3 | Linear execution, just a few conditionals and variables, straightforward input 4 | format, no complex logic... this sounds like a perfect job for TeX! Heck, it 5 | even has "registers". 6 | 7 | And TeX can read from stdin just fine, if you just `\openin1=/dev/stdin`. This 8 | will let you read the file line by line using `\read1 to \currentline`. Using a 9 | recursive macro `\readlines` containing an `\ifeof1` check, it's surprisingly 10 | straightforward to read until end-of-file. For kicks, or actually because 11 | Knuth's TeXbook told me to, I did this in a tail-recursive way, so it should be 12 | efficient even for large inputs. 13 | 14 | To parse each line I defined a macro `\parseline` like this: 15 | 16 | \def\parseline#1 #2 #3 if #4 #5 #6 {...} 17 | 18 | Normally you'd write `#1#2#3...` all without spaces, but the general definition 19 | is actually more versatile, and works a lot like pattern matching. We have to 20 | invoke it like this: 21 | 22 | \expandafter\parseline\currentline 23 | 24 | The `\expandafter` will expand `\currentline` to the actual line's contents 25 | before proceeding with the expansion of `\parseline`. The effect is that 26 | `\parseline` sees the arguments in the format it expects, and all is great. 27 | 28 | The next step was to store register values somehow. TeX has registers, but they 29 | are numbered, not named. But we can pull another trick: using `\csname foobar 30 | \endcsname`, an arbitrary sequence of characters can be turned into a `\foobar` 31 | token. This lets us define macros `\regfoobar` which expand to that register's 32 | current value. Easy peasy. 33 | 34 | Actually, the harder part was to get all conditionals right. Somehow, `\ifx` 35 | (which tests two... things for... some form of equality) would never do what I 36 | wanted or expected. But eventually I beat it into submission with lots of 37 | auxiliary macros. 38 | 39 | The final step was to enumerate all registers and extract the maximum. For 40 | this, I first needed to have a list of all of them. Easier said than done, 41 | because appending to a macro without hitting infinite recursion is not trivial, 42 | but eventually I got it to work. I now had a `\registers` macro that expanded 43 | to all register names, each terminated by a `,` character. 44 | 45 | To iterate over that list, I used another tail-recursive macro with the 46 | following signature: 47 | 48 | \def\findmax#1,{...} 49 | 50 | Note the comma? Actually TeX macros can do a sort of primitive pattern 51 | matching. This one will gobble up all tokens until it finds a comma, and then 52 | expand. As the final token in its expansion, it will produce a `\findmax` 53 | token, which is ready to consume the next sequence of tokens. Nifty, eh? 54 | 55 | The rest of it was some more simple arithmetic. As a bonus, you receive a 56 | nicely typeset `.pdf` file with debug output! 57 | 58 | --- 59 | 60 | I was terrified that the second part would have me do something 61 | near-impossible, so I was happily surprised that all I needed to do was keep a 62 | running maximum. It was actually even easier than the first part! 63 | -------------------------------------------------------------------------------- /10_erlang/README.md: -------------------------------------------------------------------------------- 1 | # [Day 10](http://adventofcode.com/2017/day/10) in Erlang 2 | 3 | After doing tomorrow's puzzle in J, going back to even a somewhat quirky 4 | language like Erlang feels like a breath of fresh air. I've never actually 5 | written any Erlang before, but I like its notion of processes and its approach 6 | to robustness. It reminds me a bit of Go, although of course Erlang was first. 7 | 8 | In fact, I always thought Erlang was a procedural language, but it seems like I 9 | was mistaken: it has much more in common with functional languages than with 10 | procedural ones. For instance, there's powerful pattern matching, most looping 11 | is done using recursion or higher-order functions, and variables can't be 12 | reassigned. 13 | 14 | To remain true to the Erlang spirit, I'm going to implement this puzzle as a 15 | bunch of numbered processes, where each process holds one item of the list. 16 | They do all the work by passing messages between them. 17 | 18 | After reading the input (straightforward for a change), we create all the 19 | processes. Any process needs to be able to send its value to any other process, 20 | so we pass the entire list of pids to all processes. They store this as a 21 | function argument, as is common in functional languages. 22 | 23 | Then the fun part: for input value, we send it to all processes. The process 24 | inspects the value, and if it's in the range to be swapped, it sends its 25 | current value to the appropriate destination process. If it's not in the range 26 | to be swapped, it just sends it to itself (we could probably skip this). Next, 27 | the process receives its new value from itself or one of its colleagues. If it 28 | receives a special `report` message (sent from the main process), it responds 29 | with its current value and exits. 30 | 31 | The main process, after sending out all the input, simply sends `report` to 32 | processes 1 and 2 (lists are 1-based in Erlang), waits for two answers, 33 | multiplies them and prints the result. 34 | 35 | This all worked and gave me the right answer! Once, as I found out. Apparently 36 | I messed something up, and it's actually nondeterministic – but it works 37 | "reliably" when I leave my debug printing in. Good enough, because bedtime is 38 | drawing near. 39 | 40 | --- 41 | 42 | For the second part, it seems I can conveniently treat an Erlang string as a 43 | list of numbers. Doing the additional rounds is easy, except it's too slow with 44 | my debug printing, so I have to fix this synchronization bug after all... 45 | Eventually it dawned on me: these processes are _not_ in lockstep; for example, 46 | if a process only sends to itself, it can get ahead of the others. And if 47 | it then hits a point where it does send a value to another process, the other 48 | process might be receiving its values out of order... and boom. 49 | 50 | As a fix, I'll have all processes report "done" to the main process before we 51 | send out the next round of input. This increases the number of messages by 50%, 52 | but we weren't exactly going for efficiency here anyway. This worked well. 53 | 54 | The final hash computation will be done on the main process. As the last step, 55 | it asks every worker for its value, and sticks them all into a list. Then it's 56 | a simple matter of sequential code to find the answer. 57 | -------------------------------------------------------------------------------- /14_cuda/README.md: -------------------------------------------------------------------------------- 1 | # [Day 14](http://adventofcode.com/2017/day/14) in CUDA 2 | 3 | Okay, I'll admit, calling NVIDIA's GPGPU programming technology CUDA a 4 | "language" is a bit of a stretch. Actually it's just C++ with some extensions. 5 | But because it does enforce a different programming model upon you, I'm going 6 | to count it anyway. Besides, I allowed OpenCL last year, which is mostly just 7 | C. 8 | 9 | Since a GPU program can't run on its own, I also need some C++ glue code, but 10 | the bulk of the algorithm will be running on the GPU. This means no use of the 11 | C++ or C standard libaries – in particular, no `std::string` or `sprintf`. 12 | 13 | On to the puzzle. My knot hash implementation is stuck somewhere in an Erlang 14 | message box, so I'll have to reimplement it. Then it's a simple matter of 15 | running it over the 256 rows and collecting the output. In glorious parallel 16 | code, of course. 17 | 18 | As soon as it compiled, it actually worked and gave the right answer. This made 19 | me happy, because debugging GPU code can be unfun. NVIDIA has some supposedly 20 | great tooling, but it would have taken some time to set it up and figure it all 21 | out (if it works on Linux at all). 22 | 23 | --- 24 | 25 | Now on to part two: a connected components analysis. How could this efficiently 26 | be done in parallel on a GPU? It's not embarrassingly parallel, because one 27 | square somewhere can affect connectedness of components that span the entire 28 | grid. So we could do some bottom-up approach, starting with 1×1 squares, 29 | linking them into 2×2, then 4×4, and so on. The drawback is that this is tricky 30 | to get right. We could also work along scanlines: first do a horizontal pass, 31 | then a vertical one, and repeat until everything is linked up. This is 32 | sensitive to pathological cases where we'd need O(_n_) iterations (where _n_ is 33 | the number of pixels) before convergence happens. 34 | 35 | It turns out that CCL (connected components labeling) on the GPU is a 36 | nontrivial problem, and several 37 | [papers](http://hpcg.purdue.edu/bbenes/papers/Stava2011CCL.pdf) 38 | [have](http://citeseerx.ist.psu.edu/showciting?cid=477854) 39 | [been](https://www.researchgate.net/publication/224257745_Computing_Strongly_Connected_Components_in_Parallel_on_CUDA) 40 | [written](http://cstar.iiit.ac.in/~kkishore/conn_c.pdf) about it. 41 | 42 | I'm not inclined to put a lot of effort into this right now, so I'll just do 43 | the entire thing in a single CUDA kernel invocation. And because I'm lazy, I'll 44 | just do the whole thing using flood fills. The advantage over established 45 | algorithms is that there's no need to relabel all components afterwards; I can 46 | just assign incremental labels and the total count pops right out at the end. 47 | Lame but effective. 48 | 49 | However, I ran into a problem: 50 | 51 | ptxas warning : Stack size for entry function '_Z24labelConnectedComponentsPiS_' cannot be statically determined 52 | Bus error (core dumped) 53 | 54 | I think this is due to recursion, and might be CUDA-speak for "stack overflow". 55 | But even giving it 10 MB of stack (using 56 | `cudaDeviceSetLimit(cudaLimitStackSize, 10 * (1 << 20));`) did not solve it. 57 | Alright, alright, I'll use a queue instead of recursion. A nice ring buffer, 58 | to salvage at least _some_ of my dignity. 59 | -------------------------------------------------------------------------------- /25_kotlin/README.md: -------------------------------------------------------------------------------- 1 | # [Day 25](http://adventofcode.com/2017/day/25) in Kotlin 2 | 3 | I saved Kotlin for last, because it's my favourite language, and perfectly 4 | suited for these kinds of puzzles. I'm going to use some Kotlin-specific 5 | features to make the code look Really Nice™. 6 | 7 | First, to parse the input. The easiest way to do this is iterate over the 8 | lines, and apply a chain of regular expression matches to each line. But this 9 | could get tedious, as we'd have to repeat it for all 7 types of input line: 10 | 11 | val match = Regexp("""...""").matchEntire(line) 12 | if (match != null) { 13 | ... 14 | continue 15 | } 16 | 17 | We could make this snippet a bit more compact using `?.` and `apply`: 18 | 19 | Regexp("""...""").matchEntire(line)?.apply { 20 | ... 21 | continue 22 | } 23 | 24 | The `?.` operator checks if its left side is `null`, and only invokes the 25 | right-hand side if it's not. The `apply` extension method simply runs the 26 | block, setting the `this` value inside the block to the thing it's being called 27 | on (in this case, the `MatchResult` object). 28 | 29 | The trouble with this is that the `continue` statement is no longer allowed 30 | inside the block. If we assume that one and exactly one regex matches, that's 31 | not a problem (apart from a slight performance hit). But I like to write my 32 | code in such a way that it complains loudly if I messed it up, so I want it to 33 | throw an exception if nothing matched. 34 | 35 | Eventually I came up with this function: 36 | 37 | fun CharSequence.ifMatches(regex: String, fn: (MatchResult.Destructured) -> Unit): Boolean { 38 | val result = Regex(regex).matchEntire(this) 39 | if (result != null) { 40 | fn(result.destructured) 41 | return true 42 | } 43 | return false 44 | } 45 | 46 | It's an extension function on `CharSequence`, so we can call this on `String`s. 47 | We pass it the regular expression (uncompiled, for convenience). If it matches, 48 | it invokes the given function, passing it the `destructured` match object. 49 | Because of lambda argument destructuring, we can now give a name to each 50 | capture group: 51 | 52 | line.ifMatches("""Say (.*) (\d+) times""") { (word, repetitions) -> ... } 53 | 54 | Finally, the `ifMatches` function returns `true` if it matched, so we can use 55 | the short-circuit behaviour of the `||` operator to chain these together and 56 | finally throw an exception (because `throw` is also an expression in Kotlin): 57 | 58 | line.apply { 59 | ifMatches("""...""") { (foo) -> ... } || 60 | ifMatches("""...""") { (bar) -> ... } || 61 | throw RuntimeException("Could not parse line ${this}") 62 | } 63 | 64 | With some more machinery behind the scenes (see the final code for details), 65 | this can be made to look even nicer: 66 | 67 | line.matchRegexes { 68 | """...""" then { (foo) -> ... } 69 | """...""" then { (bar) -> ... } 70 | } 71 | 72 | This is one of the main things I love about Kotlin: you can make up your own 73 | DSL within the (quite flexible) Kotlin syntax, and there's usually a way to 74 | make it work. Such tricks are often pulled in Ruby too, but in Kotlin it 75 | requires far fewer hacks and is entirely verified at compile time to be type 76 | safe. 77 | 78 | --- 79 | 80 | The second part, like last year, wasn't. 81 | -------------------------------------------------------------------------------- /16_fsharp/README.md: -------------------------------------------------------------------------------- 1 | # [Day 16](http://adventofcode.com/2017/day/16) in F# 2 | 3 | This puzzle is practically begging to be solved in a functional language, using 4 | algebraic data types, pattern matching and folds. Fortunately, we still have 5 | one: F#, Microsoft's functional .NET language. The compiler is open source and 6 | runs on Mono, and can be installed on Arch Linux through the AUR. 7 | 8 | F# is an interesting take on the "functional language" concept, because it's 9 | not nearly as pure as other functional languages like Haskell or Elm. Nor could 10 | it be, because it uses the same underlying .NET library as C# does. I found the 11 | [F# Tour](https://docs.microsoft.com/en-us/dotnet/fsharp/tour) to be a useful 12 | resource in my quest. The 13 | [F# Cheat Sheet](http://dungpa.github.io/fsharp-cheatsheet/) is a bit terser, 14 | but equally useful. 15 | 16 | I'm not entirely sure how function call syntax works in F# with multiple 17 | arguments; sometimes separating them by spaces works, sometimes it doesn't. It 18 | seems to be more idiomatic to chain function calls using the `|>` operator, a 19 | bit like a `|` pipeline in the shell. It actually feels fairly natural. (Later 20 | realization: probably functions are called with spaces, methods are called with 21 | parentheses and commas.) 22 | 23 | --- 24 | 25 | Second part! I had a feeling this was coming. Of course simulating 1000000000 26 | dances is not great, so we need to be clever. Surely, after a certain number of 27 | dances, the elements will end up in their initial state again, so we can cut 28 | that loop short? The good news is, with 16 programs we can only have 16! = 29 | 20922789888000 different orderings... wait, that's not good news at all. My 30 | next idea was to see where elements ended up after one full dance, which 31 | reduces the entire dance to just one permutation... but this won't work either, 32 | because the `Partner` operation depends on the identity of elements, not on 33 | their position. 34 | 35 | But! `Spin` and `Exchange` move elements irrespective of their identity, and 36 | `Partner` moves elements irrespective of their position. So the two are 37 | completely independent of each other, and actually commutative. So we can, 38 | conceptually, perform all `Partner` operations first, and then all `Spin` and 39 | `Exchange` operations, and we'll end up with the same result. And because a 40 | `Partner` operation is just a permutation (of identities), and 41 | `Spin`/`Exchange` are also just permutations (of positions), both must 42 | eventually end up back where they started – within just a few dozen repetitions 43 | or so. So within (a few dozen)² repetitions, we should find ourselves back in 44 | the situation where we started. Despite the huge number of 16! _potential_ 45 | orderings, only a very small amount can actually be reached by repeatedly 46 | applying the same two orthogonal permutations. 47 | 48 | In conclusion: I'm sure F# is a productive and practical language, but being a 49 | bit of a C#/OCaml hybrid, it lacks the elegance of other functional languages. 50 | Sometimes you invoke things as a function (`func object`), sometimes as a 51 | method (`object.func`). Most values are mutable. There are different types of 52 | sequences (`array`, `list`, `seq`) but no generic ways to process them (you 53 | need to pick `Array.map`, `List.map` or `Seq.map`). And so on. 54 | 55 | To compensate for this brief rant, I'll leave you with 56 | [this amazing song by Tim Minchin](https://www.youtube.com/watch?v=5Ju8Wxmrk3s). 57 | -------------------------------------------------------------------------------- /02_forth/README.md: -------------------------------------------------------------------------------- 1 | # [Day 2](http://adventofcode.com/2017/day/2) in Forth 2 | 3 | [Forth](https://en.wikipedia.org/wiki/Forth_(programming_language)) is a 4 | concatenative (stack-based) language developed in the 70s. I've never used such 5 | a language before, so this will be interesting. For implementation, I'm using 6 | GNU Gforth. 7 | 8 | References I used: 9 | 10 | * The [Gforth manual](http://www.complang.tuwien.ac.at/forth/gforth/Docs-html/). 11 | It takes some getting used to, but most of what you need to know is there. 12 | * [Rosetta Code](http://rosettacode.org/wiki/Forth). Brilliant. Not sure if I 13 | knew about this site last year... 14 | 15 | As is often the case, figuring out how to read the input is a relatively big 16 | hurdle. It needs memory allocation that is not on the "regular" stack, nor on 17 | the return stack, but on yet another stack (although equivalents of `malloc` 18 | and `free` also exist). A fixed size buffer will do for now, and we have a 19 | sequence of bytes in memory and the length on the stack. Such strings are 20 | commonly represented as pairs `c-addr u` on the stack, where `c-addr` is the 21 | address of the first element and `u` is the length. 22 | 23 | Then I tackled the overall structure. There should be a function ("word" in 24 | Forth parlance, although word is broader) to compute the min and max of a line. 25 | To make it easier for the caller, these will be returned in the order `max min` 26 | (top of the stack is always on the right in this notation), so we can just 27 | write `-` to find the difference and finally `+` to add it to the accumulator, 28 | which should be on the stack before. In pseudo-Forth; the bit in parentheses is 29 | always a comment that shows the current top of the stack: 30 | 31 | 0 ( acc ) 32 | \ for line in lines 33 | read-line ( acc line-length ) 34 | parse-line ( acc max min ) 35 | - ( acc max-min ) 36 | + ( acc ) 37 | \ endfor 38 | 39 | While writing and debugging this, `.s` is a godsend: you can plop it anywhere, 40 | and it simply prints the current stack. Printf debugging at its finest. Also 41 | `dump` is handy for dumping the contents of a memory buffer, both in hex and in 42 | ASCII. 43 | 44 | For parsing, `>number` seems like the right word. Complication: it uses a 45 | "double" (two stack cells) for input and output, which together represent one 46 | large integer. `s>d` and `d>s` can be used to convert back and forth between 47 | singles and doubles, but mainly the trick is to keep all stack operations 48 | straight. Doubles are the reason that several stack operations come in a `2` 49 | variant, like `2swap` and `2tuck`. 50 | 51 | Finally, the most Forthy bit I wrote was the `compute-max-min` word, which 52 | updates the current maximum and minimum: 53 | 54 | : compute-max-min ( max min value -- max' min' ) 55 | 56 | It's full of lovely stack manipulations. For fun, I also wrote a much more 57 | readable two-line version using "locals" (which pop cells from the stack and 58 | binds them to local names). 59 | 60 | --- 61 | 62 | On to part two, which requires a structurally different program... we now need 63 | to keep all numbers in memory and try to divide each pair. I'm sure this can be 64 | done with another memory buffer, but for kicks, I'll try to manage the array 65 | entirely on the stack. 66 | 67 | With some shunting values onto the return stack temporarily to get them out of 68 | the way, and judicious use of the `pick` word which lets you dig deeply into 69 | the stack, this only took an hour or two. Usage of locals could have cleaned 70 | thing up a bit, but it's not standard Forth and I wanted the classical 71 | experience. It seems I got it. 72 | -------------------------------------------------------------------------------- /18_eiffel/README.md: -------------------------------------------------------------------------------- 1 | # [Day 18](http://adventofcode.com/2017/day/18) in Eiffel 2 | 3 | Eiffel is an object-oriented language whose key selling point is Design by 4 | Contract. Every method you write can have explicitly stated preconditions and 5 | postconditions, and you can have class invariants and loop (in)variants as 6 | well. Presumably all these are checked at runtime, but I'm not sure. 7 | 8 | Several Eiffel implementations exist. For my purposes, Liberty Eiffel seems to 9 | be the most suitable: not too old and crusty, and available for Linux. I had to 10 | compile from source, because (unlike the Eiffel Studio IDE) no Arch Linux 11 | package exists even in the AUR. The home-grown compilation script took a long 12 | time, because it only uses one CPU core. And then I accidentally ran the 13 | compile script again, and it removed all the output. Maybe I'll finish my code 14 | in the meantime… 15 | 16 | To get to know the language, I had expected an article like "The Tour of 17 | Eiffel" to exist. And there is 18 | [something](http://wiki.liberty-eiffel.org/index.php/Tutorial_tour) that's 19 | almost it, but entirely not it. Missed opportunity. So I'll have to make do 20 | with lesser named tutorials like 21 | [this one](https://www.eiffel.org/doc/eiffel/An%20Eiffel%20Tutorial%20%28ET%29), 22 | and my trusty companion 23 | [Rosetta Code](http://rosettacode.org/wiki/Category:Eiffel). 24 | 25 | I have some time today, so rather than writing this as if it were C, I'll 26 | embrace the Eiffel way and try for some idiomatic code. We'll have an abstract, 27 | ahem, `deferred` class `INSTRUCTION` (Eiffel is case insensitive, but class 28 | names are traditionally in all-caps), which is inherited from by the different 29 | instruction types. Each instruction type implements a method `execute` which 30 | takes a `STATE` object and possibly modifies it. (Were this a functional 31 | language, I'd opt for returning a new `STATE`, but here it'll just be more 32 | boilerplate.) 33 | 34 | While writing this, one thing that stood out to me time and time again was the 35 | quality of the Liberty Eiffel compiler errors. They are rather verbose, but are 36 | very clear and helpful. It sets a great example for other compiler writers to 37 | follow. An example: 38 | 39 | ****** Fatal Error: Cannot pass `program.upper' which is of type INTEGER_32 40 | into formal type INSTRUCTION. 41 | 42 | The source lines involved by the message are the following: 43 | 44 | Line 34 column 29 in AOC18A (aoc18a.e): 45 | program.put(program.upper, parse_instruction(std_input.last_string)) 46 | ^ 47 | Line 195 column 18 in ARRAY (eiffel/src/lib/storage/collection/array.e): 48 | put (element: like item; i: INTEGER) 49 | ^ 50 | 51 | The stack traces coming out of contract failures at runtime are equally useful: 52 | not only do they show you the stack itself, but they also print the values of 53 | all local variables in each stack frame. This is often enough to pinpoint the 54 | bug without needing to break out a debugger. 55 | 56 | I'm using the design-by-contract features to handle parse errors too. I'm not 57 | sure whether or not this is idiomatic, but it is convenient. From a cursory 58 | glance, it seems that return codes or setting class fields would be more 59 | idiomatic. 60 | 61 | Once I got everything to compile, the program crashed with an overflow. 62 | Changing all registers to be `INTEGER_64` fixed this and resulted in the right 63 | answer. 64 | 65 | --- 66 | 67 | On to the second part! `snd` and `rcv`… how could I possibly not have seen this 68 | coming?! Anyway, I'll implement it as follows: keep two parallel `STATE`s, each 69 | with a feature (function) `is_blocked`. We just execute instructions on 70 | non-blocked `STATE`s until both are blocked, and then we're done. 71 | 72 | This all worked fine, but took over a minute to run. I'm not sure that's 73 | because Eiffel is slow, or because the input contains a long-running program. 74 | Either way, the answer was correct, so yay! 75 | -------------------------------------------------------------------------------- /21_icon/input: -------------------------------------------------------------------------------- 1 | ../.. => #.#/##./..# 2 | #./.. => ###/.##/..# 3 | ##/.. => ..#/.#./##. 4 | .#/#. => ###/.##/### 5 | ##/#. => ###/#.#/.## 6 | ##/## => #.#/..#/.#. 7 | .../.../... => ..../.#../##.#/#.#. 8 | #../.../... => .##./#.../.##./#..# 9 | .#./.../... => ...#/.#.#/###./##.# 10 | ##./.../... => #.##/..#./.#.#/..## 11 | #.#/.../... => ..#./.#../.#.#/###. 12 | ###/.../... => #.#./.#../.#../.... 13 | .#./#../... => ..#./##../.###/###. 14 | ##./#../... => ..#./###./#.#./#.#. 15 | ..#/#../... => ..##/###./.#.#/#... 16 | #.#/#../... => #.../...#/.#.#/#... 17 | .##/#../... => ###./####/.###/#.## 18 | ###/#../... => #.../#.##/#.../.#.# 19 | .../.#./... => .##./#.#./#..#/..#. 20 | #../.#./... => #.../##.#/#.#./.##. 21 | .#./.#./... => ##../.###/####/.... 22 | ##./.#./... => #.#./..../###./.#.# 23 | #.#/.#./... => ..../..../#.##/.##. 24 | ###/.#./... => ####/#.##/.###/#.#. 25 | .#./##./... => ####/#..#/#.##/.##. 26 | ##./##./... => .#.#/#.##/####/.### 27 | ..#/##./... => .##./...#/.#.#/..#. 28 | #.#/##./... => #..#/...#/.#../.##. 29 | .##/##./... => ##../#..#/##../..## 30 | ###/##./... => ..##/..../#.../..## 31 | .../#.#/... => ###./#.../##.#/.#.# 32 | #../#.#/... => ..#./...#/#..#/#.## 33 | .#./#.#/... => ##../..#./##../###. 34 | ##./#.#/... => .#.#/#.#./####/.##. 35 | #.#/#.#/... => .##./.##./#.##/#..# 36 | ###/#.#/... => #..#/.##./..#./##.. 37 | .../###/... => ###./#..#/.###/#.## 38 | #../###/... => #.../#..#/####/##.. 39 | .#./###/... => ###./.##./#..#/.### 40 | ##./###/... => #..#/##../.##./#.#. 41 | #.#/###/... => ..#./...#/#.../...# 42 | ###/###/... => ...#/##../...#/#.## 43 | ..#/.../#.. => ##.#/.#.#/.##./###. 44 | #.#/.../#.. => ###./#..#/.#.#/#.## 45 | .##/.../#.. => ...#/.#.#/.###/###. 46 | ###/.../#.. => .#../...#/..#./.#.. 47 | .##/#../#.. => .#../...#/.##./..#. 48 | ###/#../#.. => .###/##.#/#.##/.### 49 | ..#/.#./#.. => ##.#/##../##../#... 50 | #.#/.#./#.. => #.../.###/#.#./#... 51 | .##/.#./#.. => ###./#.##/###./#### 52 | ###/.#./#.. => .#../..##/##.#/##.# 53 | .##/##./#.. => ##.#/##../.##./...# 54 | ###/##./#.. => .#.#/.#../####/.##. 55 | #../..#/#.. => ..##/###./...#/##.. 56 | .#./..#/#.. => .#../...#/.#../..## 57 | ##./..#/#.. => ###./..##/###./.##. 58 | #.#/..#/#.. => ####/.#.#/...#/..## 59 | .##/..#/#.. => #..#/.#../#.##/#### 60 | ###/..#/#.. => .#../#.##/#.##/.#.. 61 | #../#.#/#.. => ..#./#.##/.#../.##. 62 | .#./#.#/#.. => ##../#.../#.#./###. 63 | ##./#.#/#.. => #..#/.##./####/.#.. 64 | ..#/#.#/#.. => ##.#/..#./..#./.#.# 65 | #.#/#.#/#.. => .#../..#./..#./..## 66 | .##/#.#/#.. => ##../#.##/#.#./#.## 67 | ###/#.#/#.. => ##.#/..##/##../##.# 68 | #../.##/#.. => .###/####/#.##/..## 69 | .#./.##/#.. => #.#./.##./###./#.## 70 | ##./.##/#.. => ..#./#..#/####/...# 71 | #.#/.##/#.. => ####/.#.#/##../##.# 72 | .##/.##/#.. => #.#./#..#/.#.#/.##. 73 | ###/.##/#.. => .#../.##./.##./.### 74 | #../###/#.. => #..#/###./##.#/##.. 75 | .#./###/#.. => #.#./#..#/..#./#..# 76 | ##./###/#.. => ..../##.#/####/...# 77 | ..#/###/#.. => ..../#.../##../#..# 78 | #.#/###/#.. => ..#./.#../..../##.# 79 | .##/###/#.. => #..#/###./##.#/.### 80 | ###/###/#.. => #.../.##./#.##/.##. 81 | .#./#.#/.#. => ...#/#.../.#../##.# 82 | ##./#.#/.#. => .#.#/#.#./.#../#.## 83 | #.#/#.#/.#. => #.##/.##./###./.... 84 | ###/#.#/.#. => ##../#..#/#.../.### 85 | .#./###/.#. => ###./#.../.#../#..# 86 | ##./###/.#. => ##../##../#.../#... 87 | #.#/###/.#. => ##../.#.#/#.##/#.#. 88 | ###/###/.#. => #.##/##.#/#.#./#... 89 | #.#/..#/##. => ..../..#./####/..## 90 | ###/..#/##. => #.../...#/#.#./#.#. 91 | .##/#.#/##. => ..##/###./.##./#... 92 | ###/#.#/##. => .#../###./##.#/...# 93 | #.#/.##/##. => .###/##../.###/..#. 94 | ###/.##/##. => .#.#/##.#/.##./.### 95 | .##/###/##. => ..#./.#.#/.#../#..# 96 | ###/###/##. => ###./#..#/####/...# 97 | #.#/.../#.# => .#.#/.#../.#.#/#... 98 | ###/.../#.# => #..#/##../.#../...# 99 | ###/#../#.# => ..../.#../#.../..## 100 | #.#/.#./#.# => #.#./####/.#.#/.##. 101 | ###/.#./#.# => ..#./####/#..#/..## 102 | ###/##./#.# => .##./.#../#.##/.#.# 103 | #.#/#.#/#.# => ##../..##/##.#/#.#. 104 | ###/#.#/#.# => .##./#..#/#..#/.#.# 105 | #.#/###/#.# => ..#./.###/#.##/#.## 106 | ###/###/#.# => ###./###./.#.#/###. 107 | ###/#.#/### => #.##/..##/#..#/...# 108 | ###/###/### => ...#/.#../##.#/.##. 109 | -------------------------------------------------------------------------------- /21_icon/README.md: -------------------------------------------------------------------------------- 1 | # [Day 21](http://adventofcode.com/2017/day/21) in Icon 2 | 3 | I'm saving my strongest languages for the last (and presumably most difficult) 4 | puzzles, so today I'll have to make do with a lesser-known language called 5 | Icon. According to the website, "Icon is a high-level, general-purpose 6 | programming language with novel features including string scanning and 7 | goal-directed evaluation." I don't expect any novelties, but it sounds like a 8 | fine tool for the job. 9 | 10 | The job at hand is to simply follow the rules and mechanically match the 11 | patterns. But first, as usual, we need to read our input. And while reading the 12 | patterns, I'm also going to store the flipped and rotated versions, to make 13 | lookup simpler and more efficient. 14 | 15 | Reading a line from stdin is simple: the `read()` function does exactly this. 16 | Parsing it is done with `find()`. We store the mapping in a "table", which is 17 | just a hashtable, dictionary, hashmap, or whatever you want to call it. 18 | Incidentally, this isn't the only place where Icon reminds me of Lua. Which of 19 | these languages came first, or whether one influenced the other, I don't know. 20 | 21 | I don't suppose we can use arbitrary types for keys, so I'll just use the 22 | slash-separated strings directly instead. I'll also use strings to represent 23 | each grid row, because they seem to be easy to work with. 24 | 25 | An interesting and unusual characteristic of Icon is its "goal-directed 26 | evaluation". What does that mean? First, you need to know that many expressions 27 | in Icon actually produce a _generator_, rather than a single value. The 28 | expression evaluation mechanism then tries successive values from each 29 | generator until a successful value for the entire expression is produced. For 30 | example, `x | 5` is a generator that first produces the value of `x` and then 31 | the value `5`, so `if y < (x | 5) then ...` only runs the `then` part if `y < 32 | x` _or_ `y < 5`. If no success value can be found, the entire expression is 33 | said to "fail". The expression evaluator actually uses a backtracking algorithm 34 | to make this all work! [Here](https://www2.cs.arizona.edu/icon/intro.htm) is a 35 | great introduction to it. 36 | 37 | As an illustrative example of this style of programming, here's how I'm 38 | splitting a string like `"##./#../..."` into a list of strings: 39 | 40 | parts := list() 41 | str ? while put(parts, tab(many('.#'))) do tab(upto('.#')) 42 | 43 | The first part, `str ?`, means to use `str` as the "subject" of the rest of the 44 | expression; many string operations then work implicitly with it. `many('.#')` 45 | scans the subject while its characters match those in the input set, and 46 | generates the index after this prefix. `tab` then sets this index as the new 47 | starting point, and returns the substring, which is `put` onto the list. Then 48 | the part after `do` is executed, which is used to skip the `/`. This repeats 49 | until `many` fails to find a matching character at the current index in the 50 | current subject. 51 | 52 | Similar to `while`, there's also `every`, which evaluates all possible values 53 | of an expression (essentially, the Cartesian product of all generators in the 54 | expression). We can use this to join a list back into a string: 55 | 56 | str := "" 57 | every str ||:= "/" || !parts 58 | str := str[2:0] 59 | 60 | Here, `!parts` generates all elements of the list `parts` one by one. `||` is 61 | the string concatenation operator. At the end, we have to trim off the leading 62 | `/`. I'm sure there's a better way to do all this, but hey, it works. 63 | 64 | A consequence of this, plus the common "everything is an expression" paradigm, 65 | means that – as far as I understand – procedure calls will silently be skipped 66 | if one of their arguments fails. I'm not sure I like that. It makes programs 67 | very hard to reason about, and hard to debug. Even your debug prints can fail 68 | in this way! 69 | 70 | --- 71 | 72 | Part two was a mercifully simple change, and even my crude string concatenation 73 | based algorithms produced the right answer in seconds. 74 | 75 | In retrospect, Icon turned out to be much more interesting than I had expected. 76 | I still don't think it's a shining example of great language design, but its 77 | expression evaluation strategy is certainly powerful, and unlike anything I've 78 | ever seen before (except maybe Prolog). 79 | 80 | By the way, did you notice that the initial pattern is the 81 | [glider](https://en.wikipedia.org/wiki/Glider_(Conway's_Life))? 82 | -------------------------------------------------------------------------------- /14_cuda/14b.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int const MARKS = 256; 7 | int const ROWS = 128; 8 | int const COLS = 128; 9 | 10 | __global__ 11 | void knotHash(unsigned char const *input, int inputSize, int *grid) { 12 | int row = blockIdx.x * blockDim.x + threadIdx.x; 13 | 14 | unsigned char lengths[64]; 15 | int numLengths = 0; 16 | for (int i = 0; i < inputSize; i++) { 17 | lengths[numLengths++] = input[i]; 18 | } 19 | lengths[numLengths++] = '-'; 20 | if (row >= 100) { 21 | lengths[numLengths++] = '0' + (row / 100); 22 | } 23 | if (row >= 10) { 24 | lengths[numLengths++] = '0' + ((row % 100) / 10); 25 | } 26 | lengths[numLengths++] = '0' + (row % 10); 27 | unsigned char const APPEND[] = { 17, 31, 73, 47, 23 }; 28 | int const APPEND_SIZE = sizeof(APPEND) / sizeof(unsigned char); 29 | for (int i = 0; i < APPEND_SIZE; i++) { 30 | lengths[numLengths++] = APPEND[i]; 31 | } 32 | 33 | int items[MARKS]; 34 | for (int i = 0; i < MARKS; i++) { 35 | items[i] = i; 36 | } 37 | 38 | int start = 0; 39 | int skip = 0; 40 | for (int round = 0; round < 64; round++) { 41 | for (int i = 0; i < numLengths; i++) { 42 | int length = lengths[i]; 43 | for (int j = 0; j < length / 2; j++) { 44 | int a = (start + j) % MARKS; 45 | int b = (start + length - 1 - j) % MARKS; 46 | unsigned char t = items[a]; 47 | items[a] = items[b]; 48 | items[b] = t; 49 | } 50 | start = (start + length + skip) % 256; 51 | skip = (skip + 1) % 256; 52 | } 53 | } 54 | 55 | unsigned char hash[16]; 56 | for (int i = 0; i < 16; i++) { 57 | unsigned char xored = 0; 58 | for (int j = 0; j < 16; j++) { 59 | xored ^= items[16 * i + j]; 60 | } 61 | hash[i] = xored; 62 | } 63 | 64 | for (int i = 0; i < COLS; i++) { 65 | int index = COLS * row + i; 66 | grid[index] = (hash[i / 8] & (1 << (7 - (i % 8)))) ? -1 : 0; 67 | } 68 | } 69 | 70 | class Fifo { 71 | static int const SIZE = ROWS * COLS; 72 | int elements[SIZE]; 73 | int head; 74 | int tail; 75 | 76 | public: 77 | __device__ 78 | Fifo() { 79 | head = 0; 80 | tail = 0; 81 | } 82 | 83 | __device__ 84 | void add(int element) { 85 | elements[tail] = element; 86 | tail = (tail + 1) % SIZE; 87 | } 88 | 89 | __device__ 90 | int remove() { 91 | int element = elements[head]; 92 | head = (head + 1) % SIZE; 93 | return element; 94 | } 95 | 96 | __device__ 97 | bool isEmpty() const { 98 | return head == tail; 99 | } 100 | }; 101 | 102 | __device__ 103 | int idx(int i, int j) { 104 | return i * COLS + j; 105 | } 106 | 107 | __device__ 108 | void floodFill(int *grid, int i, int j, int label) { 109 | Fifo queue; 110 | queue.add(idx(i, j)); 111 | while (!queue.isEmpty()) { 112 | int index = queue.remove(); 113 | if (grid[index] >= 0) { 114 | continue; 115 | } 116 | grid[index] = label; 117 | int i = index / COLS; 118 | int j = index % COLS; 119 | if (i > 0) queue.add(idx(i - 1, j)); 120 | if (i < ROWS - 1) queue.add(idx(i + 1, j)); 121 | if (j > 0) queue.add(idx(i, j - 1)); 122 | if (j < COLS - 1) queue.add(idx(i, j + 1)); 123 | } 124 | } 125 | 126 | __global__ 127 | void labelConnectedComponents(int *grid, int *numConnectedComponents) { 128 | int numLabels = 0; 129 | for (int i = 0; i < ROWS; i++) { 130 | for (int j = 0; j < COLS; j++) { 131 | if (grid[i * COLS + j] < 0) { 132 | numLabels++; 133 | floodFill(grid, i, j, numLabels); 134 | } 135 | } 136 | } 137 | *numConnectedComponents = numLabels; 138 | } 139 | 140 | int main(void) { 141 | std::string inputStr; 142 | std::getline(std::cin, inputStr); 143 | int n = inputStr.size(); 144 | 145 | unsigned char *input; 146 | cudaMallocManaged(&input, n); 147 | memcpy(input, inputStr.data(), n); 148 | 149 | int *grid; 150 | cudaMallocManaged(&grid, ROWS * COLS * sizeof(int)); 151 | 152 | int *numConnectedComponents; 153 | cudaMallocManaged(&numConnectedComponents, sizeof(int)); 154 | 155 | cudaDeviceSetLimit(cudaLimitStackSize, 10 * (1 << 20)); 156 | 157 | knotHash<<<1, ROWS>>>(input, n, grid); 158 | cudaDeviceSynchronize(); // Not sure if needed to prevent both kernels running in parallel. 159 | labelConnectedComponents<<<1, 1>>>(grid, numConnectedComponents); 160 | cudaDeviceSynchronize(); 161 | 162 | std::cout << *numConnectedComponents << '\n'; 163 | 164 | cudaFree(input); 165 | cudaFree(grid); 166 | cudaFree(numConnectedComponents); 167 | 168 | return 0; 169 | } 170 | 171 | -------------------------------------------------------------------------------- /15_pony/README.md: -------------------------------------------------------------------------------- 1 | # [Day 15](http://adventofcode.com/2017/day/15) in Pony 2 | 3 | This looks similar to a linear congruential generator, but instead of using 4 | addition, it uses multiplication. It hasn't escaped me that 2147483647 is 5 | 231 - 1, and is a (Mersenne) prime. Neither 16807 nor 8921 is prime, 6 | although the two numbers are comprime. But number theory isn't my strong suit, 7 | so I don't know if these facts are at all helpful. From a quick check, the 8 | matches appears to be scattered fairly randomly. So I guess brute force is what 9 | we ought to be doing; and for 40 million numbers, I'd better pick a language 10 | that isn't _overly_ slow. 11 | 12 | "Pony is an open-source, object-oriented, actor-model, capabilities-secure, 13 | high-performance programming language." OK, I don't need half of that, but 14 | let's see how it compares to Python (PyPy solved this in 3 seconds). I tried 15 | Pony on an earlier puzzle but got stuck on reading input; if needed, I can 16 | cheat here and hardcode the two numbers. 17 | 18 | Getting the Pony compiler up and running was easy thanks to some kind Arch 19 | Linux user who made an AUR package that just installs from the official `.rpm`. 20 | ["Hello world"](https://tutorial.ponylang.org/getting-started/hello-world.html) 21 | ran nicely. 22 | 23 | On to reading input. 24 | [This gist](https://gist.github.com/jemc/dff14f448faea7a922e4) has an example 25 | of how to copy from stdin to stdout. It's... interesting. I suppose this is 26 | async I/O? And what's with `be`, `fun tag`, `fun ref`, `consume` and `^`? I 27 | suppose Pony is different, so I should learn some more about this language 28 | first! 29 | 30 | Pony has the somewhat novel notion of 31 | [reference capabilities](https://tutorial.ponylang.org/capabilities/reference-capabilities.html). 32 | That means, a reference to an object isn't just a reference, but it also 33 | records what the reference's receiver can do with it. Think of it as `const` 34 | references in C++, but on steroids. Besides being immutable (`val`), references 35 | can also be read-only (`box`), mutable (`ref`), isolated (`iso`) meaning this 36 | is the only reference in existence to the object, transition (`trn`) meaning 37 | this is the only writable reference in existence to the object, and tag (`tag`) 38 | meaning you can do nothing except storing and comparing references. 39 | 40 | So this clears up some of the mysterious syntax: 41 | 42 | * `fun ref` means the function can only be called on mutable references. In 43 | C++ parlance, this would be a non-const function. 44 | * `fun tag` means the actor itself isn't even accessed; it's a bit similar to a 45 | static method in other languages, although it does have a `this` pointer. 46 | * `consume` is like `std::move` in C++, except with the compile-time guarantee 47 | that you can't accidentally access it again later. 48 | * `^` is a suffix that indicates an ephemeral type, which is a type for an 49 | anonymous value. 50 | * `be` indicates a behaviour, which is an asynchronous function. Think of it 51 | like `async function` in ES6, except you can't return a `Promise` that the 52 | caller can wait on. So maybe it's more like message passing with an implicit 53 | message loop per object. 54 | 55 | Synchronization is done on a per-actor basis. It's as if in Java you made all 56 | methods `synchronized`, except of course Java would run them synchronously, 57 | rather than in the background. I'm not sure I like this notion of having actors 58 | be both the "unit of sequentiality" (monitor in Java) and the "unit of data" 59 | (object in Java), but that's how it works. Then again, you don't need to use 60 | actors as "unit of data"; Pony has regular classes too. 61 | 62 | `env.stdin` is an actor, so it runs in its own "green thread". To read from it, 63 | we call it as a function, passing it an object that implements the 64 | `StdinNotify` interface, which receives the data in chunks of unspecified size 65 | through its `apply` method (which seems to be the overloaded function call 66 | operator). At EOF, `dispose` is called. 67 | 68 | To process this chunked data, the 69 | [`buffered.Reader`](https://stdlib.ponylang.org/buffered-Reader/) class is 70 | helpful. Error handling is of course mandatory; the `try ... end` construct 71 | looks similar to `try`/`catch`, but error handling is actually more like 72 | `Maybe` monads or nullable types, and consequently indicated by a `?` suffix on 73 | the type name and also on any method calls that might call `error`. 74 | 75 | For the actual algorithm, my initial thought was to have two `Generator` actor 76 | and a `Judge` actor, and have them communicate using behaviours, just to try 77 | out these concurrency features. One problem with this is that behaviours are 78 | fire-and-forget; to get values back from a generator, you either need to pass 79 | it a callback (which makes zip'ing the two iterators difficult) or pass it a 80 | promise (which is syntactically awkward). Besides, even if Pony is as fast as 81 | it claims to be, all this message passing is going to be a lot more costly than 82 | the 3-second loop we saw using PyPy. So I settled for iterators instead, which 83 | have good support in Pony too. 84 | 85 | After I got all my reference capabilities straight, the Pony program gave the 86 | right answer in 3 seconds – _including_ compile time. Actual running time is 87 | only 0.7 seconds. Nice! 88 | 89 | --- 90 | 91 | The second part isn't much more difficult. Just change a few numbers, add a 92 | modulo check, and Bob's your pony. 93 | 94 | As said, I tried Pony a few days ago and got really frustrated because of the 95 | difficulty reading stdin. But once you get into the right mindset, Pony's 96 | actually a pretty fun language with a lot of static guarantees, great compiler 97 | messages, reasonable documentation and a very good tutorial. It does really 98 | seem like a solid competitor to Rust, Nim, Go et al. to become the 99 | close-to-the-metal yet safe parallel programming language of the future. 100 | -------------------------------------------------------------------------------- /11_j/README.md: -------------------------------------------------------------------------------- 1 | # [Day 11](http://adventofcode.com/2017/day/10) in J 2 | 3 | I tried to do yesterday's puzzle in J, but it took me a long time to get to 4 | grips with this rather different language, so didn't have time to complete it. 5 | I think it's faster to do today's in J instead, and then do yesterday's in 6 | another language. If you want to see my J attempt for Day 10, dive into the Git 7 | history. 8 | 9 | So, today is the day I try the 10 | [J programming language](https://en.wikipedia.org/wiki/J_%28programming_language%29). 11 | It was designed by the creator of APL, so I would expect to see many of the 12 | same ideas (whichever those are), but with the advantage of not having to buy a 13 | [dedicated keyboard](https://duckduckgo.com/?q=apl+keyboard&t=ffab&iax=images&ia=images). 14 | I'm seeing lots of people using J for code golf, resulting in extremely terse 15 | code [like this](https://codegolf.stackexchange.com/a/148196/3319): 16 | 17 | 0=10|1#.(8$3 1)*] 18 | 19 | These people seem to use some online interpreter, but I'm going to stick with 20 | my own rules and read from stdin. So I grabbed the J source code (GPLv3) from 21 | https://github.com/jsoftware/jsource and looked at `overview.txt`. It has some 22 | instructions on building and installing this stuff. It's a ridiculous build 23 | system with homegrown shell scripts and files that need to be copied to 24 | specific locations in your home directory, but OK, I'll bear with it. 25 | 26 | After sourcing the environment shell script `~/jvars.sh`, I find that `$j64` 27 | (yes, a variable... why not a shell alias or simply a `PATH` update?!) gives me 28 | a kind of REPL. Although it refuses to do `--help` or `-h`, it seems I can pass 29 | it a filename on the command line, and it'll execute that script. Subsequently, 30 | it also decides to go ahead and parse and execute stdin. Fun times. 31 | 32 | On to actually learning a bit of the language. There is a 33 | [cheat sheet](http://code.jsoftware.com/wiki/NuVoc) containing all the magical 34 | symbols, but it's a bit cryptic. Worth noting is that operators ("verbs" in 35 | J-speak) can be used either monadic (with one argument) or dyadic (with two 36 | arguments), and they may do completely different things. Monadic verbs are 37 | written prefix, dyadic ones infix, and general order of execution is from right 38 | to left: 39 | 40 | 2*3+1 41 | 8 42 | 43 | It's worth noting that J is not a functional programming language, in the sense 44 | that verbs are not values that can be passed around. The distinction between 45 | verbs and nouns (values) is hard-wired into the parser. There are higher-order 46 | verbs, "adverbs", which modify verbs — I have to admit the terminology is 47 | growing on me – but I'm not sure how far their power reaches. 48 | 49 | I'm gathering some building blocks first. 50 | [`@:`](http://code.jsoftware.com/wiki/Vocabulary/atco) performs function 51 | composition. A space between two verbs is a 52 | [hook](http://code.jsoftware.com/wiki/Vocabulary/hook) instead which I don't 53 | quite follow yet, but might be useful. Two spaces, as in `f g h`, is a 54 | [fork](http://code.jsoftware.com/wiki/Vocabulary/fork), where `(f g h) x` is 55 | equivalent to `(f x) g (h x)`. These might help me write this program in a 56 | nice, compact, point-free, entirely inscrutable style. 57 | 58 | The [`/`](http://code.jsoftware.com/wiki/Vocabulary/slash) adverb ("insert") is 59 | used to fold a dyad over a list. For example, `/+` would be "sum". 60 | 61 | [`>:`](http://code.jsoftware.com/wiki/Vocabulary/gtco) is increment: 62 | 63 | >: 5 64 | 6 65 | 66 | Finally, on the topic of reading input, I stumbled upon 67 | [J for C programmers](http://www.jsoftware.com/help/jforc/input_and_output.htm#_Toc191734418) 68 | which pointed me in the right direction; finally, I found out about 69 | [`1!:1 3`](http://code.jsoftware.com/wiki/Vocabulary/Foreigns#m1) which will 70 | read from stdin. 71 | 72 | I'm going to stop describing all operators from here on; look at the source and 73 | the NuVoc table if you want to follow along in detail. The essence is: read the 74 | input into a string, tokenize it into words, and throw out the commas. Then 75 | turn each directional string into a "step" array like `1 0`, sum up all the 76 | steps to find the position, then apply some simple arithmetic to find the 77 | distance. 78 | 79 | My coordinate system has the y axis running to the north, and the x axis to the 80 | northeast. On an infinite grid, this is much more convenient than a "staggered" 81 | coordinate system. For more on hex grid coordinate systems, see 82 | [Amit Patel's excellent blog](https://www.redblobgames.com/grids/hexagons/#coordinates). 83 | To find the distance between two hex cells, consider the number of x steps (nw, 84 | ne, sw, se) first. This is always exactly equal to the x distance, because a y 85 | step doesn't affect the x distance, and it never makes sense to take both 86 | positive and negative x steps (a single y step would be shorter). But we have a 87 | choice with these x steps: northish or southish? We try to get as close to the 88 | target as possible, and then continue with y steps. I worked that bit out with 89 | pen and paper though. 90 | 91 | --- 92 | 93 | Onwards to the second part! Computing the entire path is a one-character 94 | change: `\` combined with `/` will compute cumulative sums. Then I just need to 95 | write that verb that I was too lazy to make before, to take an `x y` position 96 | and output the distance, depending on the "hexant" the position is in. 97 | 98 | Conclusion: the J language is bonkers. Its main selling point is "array 99 | processing", but one can do parallel operations on arrays in many other 100 | languages with less cryptic syntax, like Python with numpy, or Matlab/Octave. 101 | Also, its name is virtually ungoogleable, so you're stuck with browsing the 102 | wiki until you find what you need. Search on the wiki is useless too, unless 103 | you know the J terminology for what you want, but this is almost always 104 | different from other languages' terminology. However, although I wouldn't use J 105 | for anything practical, it did provide an interesting and mind-warping exercise. 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Polyglot Advent of Code 2017 2 | 3 | [Advent of Code 2017](http://adventofcode.com/2017) is a programming contest, 4 | in which a new puzzle comes online every day from 1 until 25 December. These 5 | are my solutions to the puzzles. Like 6 | [last year](https://github.com/ttencate/aoc2016), I solved each day's puzzle in 7 | a different programming language. As an additional additional challenge, I 8 | didn't use any language I used last year! 9 | 10 | A [retrospective](RETROSPECTIVE.md) is now available. Moreover, each solution 11 | subdirectory contains a `README.md` with my thoughts on that day's language. 12 | 13 | ## Languages 14 | 15 | I used only "mainstream" programming languages, not esoteric or toy 16 | languages, because this was hard enough as is. However, I did include some 17 | older languages in the list out of historic interest. Plus, a free (as in beer) 18 | implementation for Linux had to exist. 19 | 20 | Languages I could still have used, in alphabetical order: 21 | 22 | * Ada 23 | * ALGOL 60 24 | * APL 25 | * BLISS 26 | * Boo 27 | * CMake 28 | * COMAL 29 | * Common Lisp 30 | * Céu 31 | * Ceylon 32 | * Crystal 33 | * [Dyon](https://github.com/PistonDevelopers/dyon) 34 | * Elm (browser only) 35 | * GDscript 36 | * GLSL 37 | * [Gluon](https://github.com/gluon-lang/gluon) 38 | * Idris 39 | * Infinifactory 40 | * Inform7 41 | * Io 42 | * Labview 43 | * [Little](http://www.little-lang.org/) 44 | * Logtalk 45 | * M4 46 | * Make 47 | * Modula-3 48 | * Moonscript 49 | * MUMPS 50 | * Nemerle 51 | * Pharo 52 | * PL/I 53 | * Postscript 54 | * Prolog 55 | * Raku 56 | * REBOL 57 | * Self 58 | * SNOBOL 59 | * Wren 60 | 61 | Used, in alphabetical order (this year's entries in bold): 62 | 63 | * **ALGOL 68** ([2017, day 5](https://github.com/ttencate/aoc2017/tree/master/05_algol68)) 64 | * Assembly, any architecture (x86, [2016, day 7](https://github.com/ttencate/aoc2016/tree/master/07_x86_assembly)) 65 | * **awk** ([2017, day 4](https://github.com/ttencate/aoc2017/tree/master/04_awk)) 66 | * **BASIC** (BBC BASIC, [2017, day 17](https://github.com/ttencate/aoc2017/tree/master/17_bbcbasic)) 67 | * C ([2016, day 23](https://github.com/ttencate/aoc2016/tree/master/23_c)) 68 | * C++ ([2016, day 24](https://github.com/ttencate/aoc2016/tree/master/24_cpp)) 69 | * C# ([2016, day 17](https://github.com/ttencate/aoc2016/tree/master/17_cs)) 70 | * **Clojure** ([2017, day 19](https://github.com/ttencate/aoc2017/tree/master/19_clojure)) 71 | * COBOL ([2016, day 1](https://github.com/ttencate/aoc2016/tree/master/01_cobol)) 72 | * **CUDA** ([2017, day 14](https://github.com/ttencate/aoc2017/tree/master/14_cuda)) 73 | * **CoffeeScript** ([2017, day 7](https://github.com/ttencate/aoc2017/tree/master/07_coffeescript)) 74 | * D ([2016, day 11](https://github.com/ttencate/aoc2016/tree/master/11_d)) 75 | * **Dart** ([2017, day 12](https://github.com/ttencate/aoc2017/tree/master/12_dart)) 76 | * **Eiffel** ([2017, day 18](https://github.com/ttencate/aoc2017/tree/master/18_eiffel)) 77 | * **Elixir** ([2017, day 22](https://github.com/ttencate/aoc2017/tree/master/22_elixir)) 78 | * **Erlang** ([2017, day 10](https://github.com/ttencate/aoc2017/tree/master/10_erlang)) 79 | * **F#** ([2017, day 16](https://github.com/ttencate/aoc2017/tree/master/10_fsharp)) 80 | * **Forth** ([2016, day 2](https://github.com/ttencate/aoc2017/tree/master/02_forth)) 81 | * Fortran 90 ([2016, day 8](https://github.com/ttencate/aoc2016/tree/master/08_fortran)) 82 | * Go ([2016, day 25](https://github.com/ttencate/aoc2016/tree/master/25_go)) 83 | * Groovy ([2016, day 13](https://github.com/ttencate/aoc2016/tree/master/13_groovy)) 84 | * Haskell ([2016, day 16](https://github.com/ttencate/aoc2016/tree/master/16_haskell)) 85 | * Haxe ([2016, day 22](https://github.com/ttencate/aoc2016/tree/master/22_haxe)) 86 | * **Icon** ([2017, day 21](https://github.com/ttencate/aoc2017/tree/master/21_icon)) 87 | * JavaScript ([2016, day 20](https://github.com/ttencate/aoc2016/tree/master/20_javascript)) 88 | * **J** ([2017, day 11](https://github.com/ttencate/aoc2017/tree/master/11_j)) 89 | * **Java** ([2017, day 24](https://github.com/ttencate/aoc2017/tree/master/24_java)) 90 | * **Julia** ([2017, day 9](https://github.com/ttencate/aoc2017/tree/master/09_julia)) 91 | * **Kotlin** ([2017, day 25](https://github.com/ttencate/aoc2017/tree/master/25_kotlin)) 92 | * Lua ([2016, day 18](https://github.com/ttencate/aoc2016/tree/master/18_lua)) 93 | * **Nim** ([2017, day 20](https://github.com/ttencate/aoc2017/tree/master/20_nim)) 94 | * OCaml ([2016, day 12](https://github.com/ttencate/aoc2016/tree/master/12_ocaml)) 95 | * **Octave** ([2017, day 13](https://github.com/ttencate/aoc2017/tree/master/13_octave)) 96 | * OpenCL ([2016, day 14](https://github.com/ttencate/aoc2016/tree/master/14_opencl)) 97 | * Pascal ([2016, day 10](https://github.com/ttencate/aoc2016/tree/master/10_pascal)) 98 | * Pen and paper (and a calculator) ([2016, day 15](https://github.com/ttencate/aoc2016/tree/master/15_pen_and_paper)) 99 | * Perl ([2016, day 21](https://github.com/ttencate/aoc2016/tree/master/21_perl)) 100 | * **PHP** ([2017, day 23](https://github.com/ttencate/aoc2017/tree/master/23_php)) 101 | * **Pony** ([2017, day 15](https://github.com/ttencate/aoc2017/tree/master/15_pony)) 102 | * **PostgreSQL** ([2017, day 1](https://github.com/ttencate/aoc2017/tree/master/01_postgresql)) 103 | * Python ([2016, day 5](https://github.com/ttencate/aoc2016/tree/master/05_python)) 104 | * R ([2016, day 6](https://github.com/ttencate/aoc2016/tree/master/06_r)) 105 | * Ruby ([2016, day 19](https://github.com/ttencate/aoc2016/tree/master/19_ruby)) 106 | * Rust ([2016, day 9](https://github.com/ttencate/aoc2016/tree/master/09_rust)) 107 | * Scala ([2016, day 3](https://github.com/ttencate/aoc2016/tree/master/03_scala)) 108 | * Scheme ([2016, day 2](https://github.com/ttencate/aoc2016/tree/master/02_scheme)) 109 | * Sh (bash and common shell utilities) ([2016, day 4](https://github.com/ttencate/aoc2016/tree/master/04_bash)) 110 | * **Smalltalk** ([2017, day 6](https://github.com/ttencate/aoc2017/tree/master/06_smalltalk)) 111 | * **TCL** ([2017, day 3](https://github.com/ttencate/aoc2017/tree/master/03_tcl)) 112 | * **TeX** ([2017, day 8](https://github.com/ttencate/aoc2017/tree/master/08_tex)) 113 | 114 | ## Rules 115 | 116 | * Input should be read from stdin if at all possible. Otherwise, read from a 117 | file named `input`. 118 | * The input file must be read and parsed exactly as provided. No modifications 119 | are allowed. 120 | -------------------------------------------------------------------------------- /23_php/README.md: -------------------------------------------------------------------------------- 1 | # [Day 23](http://adventofcode.com/2017/day/23) in PHP 2 | 3 | Finally, we've reached the point where I can use languages I actually know! Or, 4 | in PHP's case, knew. Around version 3 or so is where I left it. But I'm sure 5 | one can still write horrible code in it, which is exactly what I need. 6 | 7 | Apparently PHP has advanced since then, so I'm going to try storing the 8 | instructions not as strings, nor as classes, but as functions bound to their 9 | arguments. 10 | 11 | This turns out to be harder than needed. Functions and anonymous functions 12 | ("closures") are not the same thing. To bind a regular function to some 13 | arguments, you have to wrap it into an anonymous function and tediously repeat 14 | all arguments. To get around this, I declared all functions I wanted to bind as 15 | anonymous straight away. This also involved changing all call sites, because 16 | you have to write `$my_func()` instead of `my_func()`. Also, closures don't 17 | close over their outer scope automatically; you have to declare `use ($foo)` if 18 | you want to use `$foo` from an outer scope. If you also want to modify it, you 19 | have to write `use (&$foo)`, otherwise assignments to `$foo` from within the 20 | function stay within the function, and you get hilarious bugs like the program 21 | counter not incrementing (true story). This is similar to C++ lambdas, except 22 | worse because you can't say "just use everything by default". Finally, there is 23 | no `bind()` function, just more anonymous functions! So I wrote that one 24 | myself. 25 | 26 | --- 27 | 28 | The puzzle description for the second part strongly suggests that brute force 29 | is hopeless. I set it to run anyway, while I figure out what this assembly code 30 | actually does. It's a relief not to have to work with this PHP language for a 31 | bit. 32 | 33 | First, let's simplify assignments and arithmetic, and turn `jnz` instructions 34 | into `if`s and `do` loops: 35 | 36 | set b 65 | b = 65 37 | set c b | c = b 38 | jnz a 2 | if (a != 0) { 39 | jnz 1 5 | 40 | mul b 100 | b = b * 100 + 100000 41 | sub b -100000 | 42 | set c b | c = b + 17000 43 | sub c -17000 | 44 | | } 45 | | do { 46 | set f 1 | f = 1; 47 | set d 2 | d = 2; 48 | | do { 49 | set e 2 | e = 2; 50 | | do { 51 | set g d | g = d * e - b; 52 | mul g e | 53 | sub g b | 54 | jnz g 2 | if (g == 0) { 55 | set f 0 | f = 0; 56 | | } 57 | sub e -1 | e++; 58 | set g e | g = e - b; 59 | sub g b | 60 | jnz g -8 | } while (g != 0); 61 | sub d -1 | d--; 62 | set g d | g = d - b; 63 | sub g b | 64 | jnz g -13 | } while (g != 0); 65 | jnz f 2 | if (f == 0) { 66 | sub h -1 | h++; 67 | | } 68 | set g b | g = b - c; 69 | sub g c | 70 | jnz g 2 | if (g == 0) { 71 | jnz 1 3 | break; 72 | | } 73 | sub b -17 | b += 17; 74 | jnz 1 -23 | } while (true); 75 | | print(h); 76 | 77 | Then simplify the arithmetic some more by inlining all the expressions 78 | involving the temporary variable `g`, and turn `do` loops into C-style `for` 79 | loops (under the assumption that each loop will run at least once): 80 | 81 | b = 65 | 82 | c = b | 83 | if (a != 0) { | 84 | b = b * 100 + 100000 | 85 | c = b + 17000 | 86 | } | 87 | do { | for (b = 106500; b <= 123500; b += 17) { 88 | f = 1; | f = true; 89 | d = 2; | 90 | do { | for (d = 2; d < b; d++) { 91 | e = 2; | 92 | do { | for (e = 2; e < b; e++) { 93 | g = d * e - b; | 94 | if (g == 0) { | if (d * e == b) { 95 | f = 0; | f = false; 96 | } | } 97 | e++; | 98 | g = e - b; | 99 | } while (g != 0); | } 100 | d++; | 101 | g = d - b; | 102 | } while (g != 0); | } 103 | if (f == 0) { | if (!f) { 104 | h++; | h++; 105 | } | } 106 | g = b - c; | 107 | if (g == 0) { | 108 | break; | 109 | } | 110 | b += 17; | 111 | } while (true); | } 112 | print(h); | print(h); 113 | 114 | So we're left with a pretty short and simple program: 115 | 116 | for (b = 106500; b <= 123500; b += 17) { 117 | f = true; 118 | for (d = 2; d < b; d++) { 119 | for (e = 2; e < b; e++) { 120 | if (d * e == b) { 121 | f = false; 122 | } 123 | } 124 | } 125 | if (!f) { 126 | h++; 127 | } 128 | } 129 | print(h); 130 | 131 | So what does this do? For each value `b` in the range 106500 to 123500 with a 132 | step size of 17, it checks by brute force whether any pair of numbers `d` and 133 | `e` exist whose product form `b`. If this is the case, `h` is incremented. So 134 | it's a super inefficient way to count the number of non-prime numbers in the 135 | given range! 136 | 137 | With the debug flag `a` set to 0, the range was simply reduced to the single 138 | number 65, and indeed `h` contains 1 after running the code in this way (65 is 139 | not prime). As an additional confirmation, the number of multiplications was 140 | 3969, which is 63×63 (there are 63 numbers in the range of 2 up to and 141 | excluding 65). 142 | 143 | So now we just do the same in PHP directly, except more efficiently: instead of 144 | the double loop over `d` and `e`, we use only one loop, which ends at `sqrt(b)` 145 | instead of `b`. Moreover, it breaks as soon as a divisor is found. With just 146 | 1000 numbers to test for primality, there is no need to optimize further. And 147 | after fixing the unavoidable off-by-one error, it produces the right answer. 148 | -------------------------------------------------------------------------------- /20_nim/README.md: -------------------------------------------------------------------------------- 1 | # [Day 20](http://adventofcode.com/2017/day/20) in Nim 2 | 3 | Looks like we have another open-ended simulation on our hands today. With 1000 4 | particles, I'll pick a statically typed, compiled language today: Nim. Nim 5 | (formerly Nimrod) is supposed to be syntactically similar to Python, and well 6 | liked by its users, so I hope this will be interesting. 7 | 8 | First question: can we solve this analytically, to avoid simulating _n_ 9 | particles over _k_ time steps? Not easily, I think. The trajectory of each 10 | particle is a parabola, so it's easy to express it in closed form, and easy to 11 | find its minimum. But the distance to the origin (even in 1D) is more 12 | complicated, because we're using the Manhattan distance rather than Euclidean 13 | distance. Even in 1D, this means the distance can have three different minima 14 | (one from the top of the parabola, and two from its zero crossings), and in 15 | general these won't correspond to minima in 3D. Furthermore, the minima might 16 | occur between two time steps, but we're only looking at integers here. 17 | 18 | Second question: when do we stop? Were I competing for the leaderboards, I'd 19 | just set it to print minimum distances and wait until the value stops changing, 20 | but let's try for something more correct. We can't simply stop after each 21 | particle has passed a minimum distance to the origin. A particle can come close 22 | to the origin, zoom around, and come back even closer. But one guarantee we 23 | have is this: if position, velocity and acceleration all have the same sign (in 24 | each dimension separately), it will only get further away. So this is the point 25 | where we can kill that particle. 26 | 27 | So that's the plan. Let's write some code. Installing the Nim compiler is easy; 28 | there's an Arch Linux package. Reading input is literally the first example in 29 | the [tutorial](https://nim-lang.org/docs/tut1.html), convenient! Seems I need 30 | to handle an `EOFException` to deal with the end of the file; a bit Pythonic 31 | indeed. But wait, isn't there an iterator construct as well? Why, yes: 32 | `for line in lines(stdin)` works perfectly. 33 | 34 | Nim doesn't have methods that "belong" to a class; everything is a free 35 | function, although `obj.method(args...)` can be used as syntactic sugar for 36 | `method(obj, args...)`. This means you get "extension methods" for free, so, 37 | for instance, the `regexp` module can define a method `match(String, RegExp)` 38 | "on `String` objects". Information hiding is done on the module level, not the 39 | class level, just like in Go. Polymorphism is achieved by declaring your free 40 | functions as `method` instead of `proc`, which makes overload selection happen 41 | at runtime instead of compile time. 42 | 43 | [Multiple dispatch](https://en.wikipedia.org/wiki/Multiple_dispatch) is 44 | sneakily also supported in this way – a feature that's missing from (nearly?) 45 | all mainstream languages. One example where this is useful is if you're 46 | computing collisions between different types of shape (circle, rectangle, 47 | line), so you need a different algorithm depending on the type of _both_ 48 | shapes. Multiple dispatch is not often needed, but when it is, it's a pain to 49 | have to work around it. 50 | 51 | On with the puzzle. For parsing each line, I could just split it on commas, or 52 | use a regular expression, but let's try something fancier instead: Nim's 53 | built-in [`pegs` module](https://nim-lang.org/docs/pegs.html) for parsing 54 | expression grammars. It lets you parse things by writing an EBNF-like grammar 55 | for them. Pretty cool! Sadly, you don't get a real AST out of it, just a flat 56 | array of matched capture groups, limited to 20 entries. So for parsing a full 57 | programming language, this may be a bit limited. 58 | 59 | After parsing, we just need to do the simulation and keep track of the global 60 | minimum, eliminating particles as soon as they can no longer get any closer to 61 | the origin. You might have guessed by now that I'm a fan of functional 62 | programming style, so some `map` and `filter` operations from the `sequtils` 63 | module come in handy here. 64 | 65 | At this point, I reread the assignment, and discover that I've been 66 | misinterpreting it all along: the question is not which particle comes closest 67 | to the origin ever, but which particle is the closest to the origin in the 68 | limit situation `t → ∞`. Oops. In that case: it must be the one with the 69 | smallest acceleration, measured by the L1 norm (Manhattan distance). And this 70 | is much easier. 71 | 72 | --- 73 | 74 | On to the second part: collision detection! I didn't see that coming. Now we 75 | _do_ need to simulate, I suppose. Or do we…? Collisions are not based on 76 | Manhattan distance, but on exact equality. So we _can_ solve this analytically, 77 | by finding the collisions between each pair of particles in an O(_n_²) fashion. 78 | As _n_ = 1000, this isn't too bad. 79 | 80 | The position of particle `p` at time `t` is given by: 81 | 82 | pos(p, t) = p.p + p.v × t + p.a × t² 83 | 84 | Particles `p` and `q` collide if and only if `pos(p, t) = pos(q, t)` for some 85 | `t ≥ 0`. (I'm sure this latter condition is going to trip many people up if 86 | they also count collisions in the past.) 87 | 88 | Because this is an equality whose left and right side are both 3D vectors, we 89 | need to find a `t` for which all three of them match. We can just find the (up 90 | to two) `t`s for which this holds, per coordinate, using the 91 | [quadratic formula](https://en.wikipedia.org/wiki/Quadratic_formula), 92 | and checking that the answer is an integer. Having found the candidate times 93 | for each coordinate individually, we do a set intersection on them. If the set 94 | is nonempty, we've found a `t` at which a collision happens. 95 | 96 | Finally, we need to be careful not to remove colliding pairs entirely, because 97 | they might both collide with a third particle. 98 | 99 | After getting this to compile, it crashed with an `OverflowError`. Turns out 100 | (of course) that the difference in acceleration can sometimes be zero, resulting 101 | in division by zero. In this degenerate case, we need to do an intersection of 102 | two linear equations, rather than quadratic. Similarly, if the initial velocity 103 | difference is also zero, we just test if the positions are equal. This is the 104 | ultimate degenerate case: both particles have an identical `x` or `y` or `z` 105 | coordinate _at all times_. I represented this as a `nil` sequence, to 106 | distinguish it from an empty sequence. 107 | 108 | While debugging this (because of course it's too complicated), I realised that 109 | I was overlooking something else. When two particles are determined to collide, 110 | we must also check whether both of them even still existed at that time! This 111 | isn't impossible, but would involve storing all potential collisions in a list, 112 | sort it by time, then run through it. 113 | 114 | From my (admittedly buggy) debug output, I can see that times are rarely 115 | greater than a few hundred or so. So a simulation is entirely feasible. And 116 | that's what I ended up doing. Using a hash table for collision checks means we 117 | don't have an O(_n_²) collision detection, but an O(_n_) one. And because the 118 | right answer appears after 39 time steps already, we have done 1000 × 39 119 | operations instead of 1000 × 1000, so it's actually pretty fast too. 120 | -------------------------------------------------------------------------------- /RETROSPECTIVE.md: -------------------------------------------------------------------------------- 1 | # Polyglot Advent of Code 2017: a retrospective 2 | 3 | It's over! I finished! 25 programming puzzles in 25 different languages. And if 4 | you include last year's: 50 programming puzzles in 50 different languages. 5 | 6 | First off, a **huge** thank-you to Eric for making and running Advent of Code. 7 | Once more, it's been a blast. If you liked it even half as much as I did, 8 | consider [giving this guy some money](http://adventofcode.com/2017/support). He 9 | deserves it. 10 | 11 | I'm not going to write something about every day, because I already did that. 12 | See the `README.md` files in the individual subdirectories in 13 | [my repo](https://github.com/ttencate/aoc2017). Rather, I'll just pick the most 14 | interesting experiences here. 15 | 16 | Remember the rule I used: input must be parsed as-is from stdin if possible, 17 | from a file named `input` otherwise. 18 | 19 | ### [Day 1: PostgreSQL](https://github.com/ttencate/aoc2017/tree/master/01_postgresql) 20 | 21 | The main issue: making an SQL database server read from a file (stdin was a 22 | non-starter). Turns out, PostgreSQL can actually do this! As long as you read 23 | the docs closely, and put the input file in just the right location (root 24 | privileges required). 25 | 26 | ### [Day 2: Forth](https://github.com/ttencate/aoc2017/tree/master/02_forth) 27 | 28 | Forth is a stack-based language from the early '70s. Apparently it's still in 29 | use in the space industry (of all places), and the MS-DOS game Starflight 30 | (recommended!) was also written in Forth. 31 | 32 | The stack-based paradigm isn't very popular anymore, and with good reason: code 33 | is much easier to read if variables have names, rather than being anonymous 34 | stack entries. Yet, Forth made for a good challenge and forced me to think 35 | differently. Incidentally, I'm working on a programming game involving a 36 | stack-based language, so it was good to have tried a "real" one. 37 | 38 | ### [Day 8: TeX](https://github.com/ttencate/aoc2017/tree/master/08_tex) 39 | 40 | Suggested by 41 | [a redditor](https://www.reddit.com/r/adventofcode/comments/7gu2ze/2017_25_more_languages_polyglot_aoc2017_a/dqnhek4/), 42 | TeX is Donald Knuth's late '70s typesetting language, which underlies the more 43 | well-known LaTeX. Despite being a typesetting language, not a "real" 44 | programming language, it is actually Turing complete thanks to the availability 45 | of conditionals and recursive macros. What's more, it can read files (including 46 | `/dev/stdin`), and has some limited form of pattern matching for macro 47 | arguments, which made it possible to process the input file entirely by the 48 | rules. 49 | 50 | And the best part: as a side effect, I got a PDF file with a beautifully 51 | typeset debug log. 52 | 53 | ### [Day 9: Julia](https://github.com/ttencate/aoc2017/tree/master/09_julia) 54 | 55 | Julia is a high-level language for numerical computing. My limited experience 56 | with it on this one day has been very promising: a good self-documenting 57 | library, good documentation, simple syntax. It's sort of a mix between Python 58 | and Lua, with a few drops of Ruby thrown in. I don't know if it can oust Python 59 | (and numpy) from this domain, but it's certainly a good contender. 60 | 61 | ### [Day 10: Erlang](https://github.com/ttencate/aoc2017/tree/master/10_erlang) and [Day 22: Elixir](https://github.com/ttencate/aoc2017/tree/master/22_elixir) 62 | 63 | Erlang's main selling point is its magical highly concurrent runtime, where 64 | programs run as independent "processes" that communicate via message passing. 65 | The Go language has taken some inspiration from this, but hasn't taken it to 66 | the extremes that Erlang has. For instance, if a process crashes, you can 67 | arrange for it to be automatically restarted. And even neater: processes can be 68 | hot-swapped to allow for updates without having to restart the entire VM. 69 | 70 | The main drawback of Erlang is its unusual, and plain inconvenient, syntax. 71 | For example, bindings are separated by `,`, function definitions by `.`, and 72 | overloads of the same function by `;`. As a consequence, whenever you change 73 | something, you have to sort out your punctuation again. 74 | 75 | And this is where Elixir comes in. It's basically Erlang, but with more modern 76 | and convenient syntax. Throw in a more consistent standard library, and you 77 | have a great language based on a powerful platform, which I wouldn't mind doing 78 | actual work in. 79 | 80 | ### [Day 11: J](https://github.com/ttencate/aoc2017/tree/master/11_j) 81 | 82 | J is an oddball language. Rather than using English words to define its 83 | standard library functions, it's entirely based around infix (binary) and 84 | prefix (unary) operators, and there are 85 | [tons](http://code.jsoftware.com/wiki/NuVoc) of them. Just to give you a taste, 86 | here's how I read and parsed the input: 87 | 88 | input =: ((<, ',') & ~: # ]) ;: }: (1!:1) 3 89 | 90 | Most operators have both an infix and a prefix version, and they do different 91 | things. Some operators take another operator as argument, and modify its 92 | behaviour. It's functional programming, condensed beyond the point of 93 | usefulness. J is fantastic for code golf, or for impressing your programmer 94 | friends, but not for much else. 95 | 96 | ### [Day 15: Pony](https://github.com/ttencate/aoc2017/tree/master/15_pony) and [Day 20: Nim](https://github.com/ttencate/aoc2017/tree/master/20_nim) 97 | 98 | I'm lumping these together because Pony and Nim are both among those languages 99 | (others being Rust and Go) that aim to be "a better C": statically typed, 100 | compiled to machine code, high performance, memory safe, type safe, all other 101 | kinds of safe. 102 | 103 | Of the two, Pony is the most innovative: with its different types of reference 104 | capabilities, it becomes possible to have an ironclad compile-time guarantee 105 | that your concurrent program is free of race conditions and deadlocks. Nim is a 106 | bit more conservative, with a Python-like syntax and no particularly 107 | interesting concurrency mechanisms, but this does make Nim a bit easier to get 108 | into. I found both very pleasant to work with. 109 | 110 | ### [Day 23: PHP](https://github.com/ttencate/aoc2017/tree/master/23_php) 111 | 112 | Saved for one of the last puzzles, because _surely_ a language that so many 113 | people are making so many products in can't be that bad? Think again. PHP has 114 | only been "improved" over the years by bolting on more features, but none of 115 | the existing problems have been fixed. Despite the existence of anonymous 116 | functions, functional-style programming is still a pain. Basic functionality is 117 | missing. And all the world is still in a single namespace. Even AoC creator 118 | Eric Wastl [agrees that PHP is sad](http://phpsadness.com/). 119 | 120 | ## Conclusion 121 | 122 | These were just the highlights. If you want to see whether your favourite 123 | language was featured this year (or last), head over to 124 | [the repo](https://github.com/ttencate/aoc2017) to see the full code with 125 | commentary. 126 | 127 | So what's in store for next year? Another fresh set of 25 languages? Unlikely. 128 | I finished last year with a lot of languages I hadn't used yet, but this year 129 | only 9 are left. I would have to dive into the _really_ obscure and esoteric 130 | stuff to make it work yet another time, and I probably won't have time for 131 | that. Maybe I'll just compete for the leaderboard instead – having to learn 132 | about 16 new languages from scratch, I didn't stand a chance in 2017. Or maybe 133 | I'll up the ante in some other way. Who knows? 134 | 135 | Happy holidays everybody! 136 | -------------------------------------------------------------------------------- /17_bbcbasic/README.md: -------------------------------------------------------------------------------- 1 | # [Day 17](http://adventofcode.com/2017/day/17) in BBC BASIC 2 | 3 | I'm going to do something a bit special today. My first ever computer was the 4 | BBC Micro (Model B), powered by BBC BASIC. The machine had 32 kB of RAM and ran 5 | at 2 MHz. Which, I figured, should be enough to get through today's puzzle! 6 | Part one, at least… 7 | 8 | ![Screenshot of BeebEm](screenshot_beeb.png) 9 | 10 | Unfortunately, my Beeb died on me a few years ago, so I'm going to be using an 11 | emulator today. I installed the Linux port of BeebEm and tried to type `*.` (an 12 | abbreviation for `*CAT`) to see if there was an emulated disk in the drive. But 13 | apparently, BeebEm for Unix doesn't map the keyboard as nicely as the Windows 14 | version, so it came out as `(.`. Eventually I found the `*` character under the 15 | `'` key (which, to complicate matters further, is actually the `Q` key on my 16 | keyboard because it's set to dvorak layout). 17 | 18 | As a language reference, I'm using 19 | [bbcbasic.co.uk](http://www.bbcbasic.co.uk/). It's actually a Windows 20 | reimplementation of the language, but it should be close enough. I'm also using 21 | [Rosetta Code](http://rosettacode.org/wiki/Category:BBC_BASIC). 22 | 23 | I do try to stick to the rules, so I should either read the input from "stdin" 24 | (the keyboard, because no input redirection exists on this OS), or from a file 25 | named `input`. Typing the input (359) for each test run is both tedious and 26 | overly simple, so I'll read from a file instead. But it must be a plain-text 27 | ASCII file. I created it like this: 28 | 29 | >F%=OPENOUT("input") 30 | >PRINT#F%,"359" 31 | >CLOSE#F% 32 | >*TYPE"input" 33 | 953 34 | 35 | Huh? Well, it seems strings are stored backwards in a file, but reading them 36 | back works fine: 37 | 38 | >F%=OPENIN("input") 39 | >INPUT#F%,A$ 40 | >CLOSE#F% 41 | >PRINTA$ 42 | 359 43 | 44 | There might be an invisible length byte preceding it as well. To write a string 45 | verbatim, followed by a newline (ASCII 10), we need `BPUT#` instead: 46 | 47 | >F%=OPENOUT("input") 48 | >BPUT#F%,"359" 49 | 50 | Type mismatch 51 | >_ 52 | 53 | Hmm, seems that BBC BASIC for Windows isn't quite the same as the real thing 54 | after all. 55 | [Here](http://central.kaserver5.org/Kasoft/Typeset/BBC/Contents.html) is what 56 | seems to be a transcript of the official BBC Micro User Guide, which should be 57 | the canonical reference, and it makes no mention of `BGET#` supporting strings. 58 | So let's do it by hand: 59 | 60 | >BPUT#F%,51 61 | >BPUT#F%,53 62 | >BPUT#F%,57 63 | >BPUT#F%,10 64 | 65 | Next up: write code to read this file and parse the bytes back into an integer. 66 | Notes at the end of the line are just for this README and not part of the 67 | program, of course. 68 | 69 | >10F%=OPENIN("input") Open the input file. 70 | >20I%=0 Our step number I% (for "input") starts at zero. 71 | >30B%=BGET#F% Read a byte into B%. 72 | >40IFB%=10THENGOTO70 If it's a newline, break out of the loop. 73 | >50I%=I%*10+(B%-48) Shift all digits left (*10) and add the last digit. 74 | >60GOTO30 And repeat. 75 | >70CLOSE#F% Close the file. 76 | >RUN 77 | >PRINTI% 78 | 359 79 | 80 | By prefixing a statement with a line number, it becomes part of your program 81 | instead of being executed immediately. Note how spaces are entirely optional. 82 | Also note the use of `GOTO`: the `break` statement doesn't exist here. Integer 83 | variables can be suffixed with `%`, which makes them a bit faster and 84 | memory-efficient. 85 | 86 | On to the actual puzzle. Arrays need to be dimensioned up front, so we just 87 | create one that's big enough and keep track of how many elements are used. 88 | Arrays are zero-indexed, but they include the upper bound, so `DIMB%(2017)` 89 | actually gives us a 2018-element array, just as we need. To find the next 90 | index, of course we use the `MOD` operator instead of stepping 359 times. 91 | 92 | 80DIMB%(2017) B% is the circular buffer. 93 | 90A%=0 A% is our current index. 94 | 100FORS%=1TO2017 For each number in the range 1...2017: 95 | 110A%=((A%+I%)MODS%)+1 Find the next insertion point. 96 | 120FORJ%=S%TOA%+1STEP-1 \ 97 | 130B%(J%)=B%(J%-1) | Shift all subsequent elements up. 98 | 140NEXTJ% / 99 | 150B%(A%)=S% Put the next element into place. 100 | 160NEXTS% And repeat. 101 | 170PRINTB%((A%+1)MODS%) Finally, print the answer. 102 | 103 | This being a quadratic-time algorithm due to the slow insertions, it would have 104 | taken a while to run on a real Beeb. Fortunately, I can crank up the emulator 105 | speed to 100×; I don't know if it achieves that, but it did make the program 106 | finish in under a minute and produce the right answer: 107 | 108 | ![Screenshot of BeebEm with solution for 17a](screenshot_17a.png) 109 | 110 | I wonder if a linked list would be more efficient: on the one hand, insertions 111 | are constant-time, but on the other hand, we'd need to traverse half the list 112 | on average to find the next insertion point. The big-O doesn't care, but the 113 | machine might. Cache efficiency wasn't really a thing yet, and maybe memory 114 | reads are faster than writes so the read-heavy linked list implementation comes 115 | out ahead of the write-heavy? 116 | 117 | --- 118 | 119 | Second part: yikes! There's no way we're going to fit 50 million items in 32 120 | kB. So I'm going to have to get clever. It's easy enough to keep track of the 121 | place where 0 ends up – in fact, insertions always happen after it, so 0 will 122 | remain at position 0. So all we need to do is track which value comes after it 123 | during each of the 50000000 steps, and hope that they can be simulated fast 124 | enough. For reference: the array-based implementation probably did about 2000 × 125 | 2000 × ½ × ½ = 1 million operations, but each operation was more complex. 126 | 127 | Can we improve even further? After all, most insertions won't happen anywhere 128 | near 0. Can we predict when the current pointer will land at 0, and only handle 129 | those cases? Not easily, I think: the denominator of the modulo operation 130 | changes each step. But we do know that at most times, the buffer is going to be 131 | much bigger than 359, so we can skip a good number of iterations by jumping 132 | ahead by the largest possible integer multiple of 359. This should make the 133 | algorithm fast enough even for this early '80s machine. 134 | 135 | 80A%=0 A% is again our current index. 136 | 90S%=1 S% is the next value to insert. 137 | 100B%=0 B% is the value after 0 in the buffer. 138 | 110REPEAT Start the loop. 139 | 120C%=(S%-A%)DIV(I%+1) C% indicates how many times we can step ahead 140 | before we get close to the end. (The end shifts 141 | in the meantime, so this could happen more than 142 | once in a row.) 143 | 130IFC%>0:A%=A%+C%*(I%+1):S%=S%+C%:GOTO180 144 | If we can skip ahead: do it, and proceed with the 145 | next loop iteration. 146 | 140A%+(A%+I%)MODS% We couldn't skip ahead, so wrap around. 147 | 150IFA%=0:B%=S% If we insert after the 0: save this value. 148 | 160A%=A%+1 Increment current index. 149 | 170S%=S%+1 Increment value to be inserted. 150 | 180UNTILS%>50000000 Loop until we've inserted 50000000 values. 151 | 190PRINTB% Print the answer! 152 | 153 | In fact, it was more than fast enough: it ran even faster than the first 154 | part, _and_ produced the right answer. Victory! 155 | 156 | ![Screenshot of BeebEm with solution for 17b](screenshot_17b.png) 157 | --------------------------------------------------------------------------------