1 divert(-1)dnl -*- m4 -*-
2 # Usage: m4 [-Dfile=day14.input] day14.m4
3 # Optionally use -Dlimit=N to stop part 2 at N spins
4 # Optionally use -Dverbose=1 to see some progress
6 include(`common.m4')ifelse(common(14), `ok', `',
7 `errprint(`Missing common initialization
10 # Using the following macros:
11 # x, y: coordinates while parsing grid, 1-based (implicit square rock border)
12 # maxx, maxy: size of grid
13 # [nwse]X_Y: X,Y,d collector spot to use when rock at X,Y needs to move in
14 # direction d, only populated for grid spots that were . or O at beginning
15 # cX_Y[nwse]: number of rocks in collector spot for direction
16 # q[nwse]: queue of collector spots with rocks in direction
17 # n: number of round rocks
18 # lW_E: spin at which west/east load pair was last seen
19 # lL: load after spin S
21 define(`input', translit(include(defn(`file')), `.#'nl, `123'))
22 define(`x', 1)define(`y', 1)define(`n', 0)
23 define(`maxx', index(defn(`input'), 3))
24 define(`maxy', len(translit(defn(`input'), `O12')))
25 ifelse(maxx, maxy, `', `fatal(`input must be square')')
26 define(`collect', `ifdef(`c$1_$2$3', `define(`c$1_$2$3', incr(c$1_$2$3))',
27 `define(`c$1_$2$3', 1)pushdef(`q$3', `$1,$2')')')
28 define(`propn', `ifdef(`n$2_$1', `defn(`n$2_$1')', ``$2,$3,`n''')')
29 define(`propw', `ifdef(`w$1_$3', `defn(`w$1_$3')', ``$2,$3,`w''')')
30 define(`props', `ifdef(`n$2_$1', `define(`s$2_$1', `$2,$3,`s'')$0(decr($1), $2,
32 define(`prope', `ifdef(`w$1_$3', `define(`e$1_$3', `$2,$3,`e'')$0(decr($1), $2,
34 define(`do0', `define(`x', incr($1))')
35 define(`doO', `do1($@)collect(n$1_$2)define(`n', incr(n))')
36 define(`do1', `define(`n$1_$2', propn(decr($2), $@))define(`w$1_$2',
37 propw(decr($1), $@))do0($@)')
38 define(`do2', `props(decr($2), $1, decr($2))prope(decr($1), decr($1),
40 define(`do3', `prope(decr($1), decr($1), $2)define(`x', 1)define(`y',
44 patsubst(defn(`input'), `.', `do\&(x, y)')
46 define(`chew', `ifelse($1, 1, `do$2(x, y)', `$0(eval($1/2), substr(
47 `$2', 0, eval($1/2)))$0(eval($1-$1/2), substr(`$2', eval($1/2)))')')
48 chew(len(defn(`input')), defn(`input'))
51 # Part 1 falls out of the first spin round. Part 2 requires running
52 # spins until a cycle is detected, and extrapolating that out to the limit.
53 define(`prep', `props($2, $1, $2)')
54 forloop(1, maxx, `prep(', `, 'maxy`)')
55 define(`_placen', `collect(w$1_$2)ifelse($3, 1, `', `$0($1, incr($2),
57 define(`_placew', `-$2collect(s$1_$2)ifelse($3, 1, `', `$0(incr($1), $2,
59 define(`_places', `collect(e$1_$2)ifelse($3, 1, `', `$0($1, decr($2),
61 define(`_placee', `-$2collect(n$1_$2)ifelse($3, 1, `', `$0(decr($1), $2,
63 define(`_place', `$0$1($2, $3, c$2_$3$1)popdef(`c$2_$3$1')')
64 define(`place', `ifdef(`q$1', `_$0(`$1', q$1)popdef(`q$1')$0($@)')')
65 define(`_spin', `ifelse(eval($1%20), 0, `output(1, `...$1')')ifdef(`l$2_$3',
66 `ifelse(eval($1-l$2_$3), defn(`cycle'), `-', `define(`cycle',
67 eval($1-l$2_$3))')', `popdef(`cycle')')define(`l$2_$3', $1)define(`l$1', $3)')
68 pushdef(`_spin', `define(`part1', $2)popdef(`$0')')
69 define(`spin', `ifelse(_$0($1, eval('n*incr(maxy)`place(`n')place(`w')),
70 eval('n*incr(maxy)`place(`s')place(`e'))), `-', `defn(`l'eval($1-cycle+(
71 1000000000-$1)%cycle))', `$0(incr($1))')')
72 define(`part2', spin(1))