day 25 optimize and improve heuristics
[aoc_eblake.git] / 2023 / day14.m4
blobd0b5eb8f2b218bc121391552863ac0f023a032bd
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
8 ')m4exit(1)')
10 # Using the following macros:
11 # p: 1-D coordinate while parsing grid, 1-based (implicit square rock border),
12 #  p = x+y*maxx
13 # maxx, maxy: size of grid (maxx includes newline)
14 # [nwse]P: P,d collector spot to use when rock at P needs to move in
15 #  direction d, only populated for grid spots that were . or O at beginning
16 # cP[nwse]: number of rocks in collector spot for direction
17 # q[nwse]: queue of collector spots with rocks in direction
18 # n: number of round rocks
19 # lW_E: spin at which west/east load pair was last seen
20 # lS: load after spin S
22 define(`input', translit(include(defn(`file')), `.#'nl, `123'))
23 define(`maxx', incr(index(defn(`input'), 3)))
24 define(`maxy', len(translit(defn(`input'), `O12')))
25 define(`p', maxx)define(`n', 0)
26 define(`collect', `ifdef(`c$1$2', `define(`c$1$2', incr(c$1$2))',
27   `define(`c$1$2', 1)pushdef(`q$2', `$1')')')
28 define(`propn', `ifdef(`n$1', `defn(`n$1')', ``$2,`n''')')
29 define(`propw', `ifdef(`w$1', `defn(`w$1')', ``$2,`w''')')
30 define(`props', `ifdef(`n$1', `define(`s$1', `$2,`s'')$0(eval($1-''maxx``),
31   $2)')')
32 define(`prope', `ifdef(`w$1', `define(`e$1', `$2,`e'')$0(decr($1), $2)')')
33 define(`do0', `define(`p', incr($1))')
34 define(`doO', `do1($1)collect(n$1)define(`n', incr(n))')
35 define(`do1', `define(`n$1', propn(eval($1-'maxx`), $1))define(`w$1',
36   propw(decr($1), $1))do0($1)')
37 define(`do2', `props(eval($1-'maxx`), eval($1-'maxx`))prope(decr($1),
38   decr($1))do0($1)')
39 define(`do3', `prope(decr($1), decr($1))do0($1)')
41 ifdef(`__gnu__', `
42   patsubst(defn(`input'), `.', `do\&(p)')
43 ',`
44   define(`chew', `ifelse($1, 1, `do$2(p)', `$0(eval($1/2), substr(
45     `$2', 0, eval($1/2)))$0(eval($1-$1/2), substr(`$2', eval($1/2)))')')
46   chew(len(defn(`input')), defn(`input'))
49 # Part 1 falls out of the first spin round. Part 2 requires running
50 # spins until a cycle is detected, and extrapolating that out to the limit.
51 define(`prep', `props($1, $1)')
52 forloop_arg(eval(p-maxx), decr(p), `prep')
53 define(`_placen', `collect(w$1)ifelse($2, 1, `', `$0(eval($1+''maxx``),
54   decr($2))')')
55 define(`_placew', `-$1/'maxx`collect(s$1)ifelse($2, 1, `', `$0(incr($1),
56   decr($2))')')
57 define(`_places', `collect(e$1)ifelse($2, 1, `', `$0(eval($1-''maxx``),
58   decr($2))')')
59 define(`_placee', `-$1/'maxx`collect(n$1)ifelse($2, 1, `', `$0(decr($1),
60   decr($2))')')
61 define(`_place', `$0$1($2, c$2$1)popdef(`c$2$1')')
62 define(`place', `ifdef(`q$1', `_$0(`$1', q$1)popdef(`q$1')$0(`$1')')')
63 define(`_spin', `ifelse(eval($1%20), 0, `output(1, `...$1')')ifdef(`l$2_$3',
64   `ifelse(eval($1-l$2_$3), defn(`cycle'), `-', `define(`cycle',
65   eval($1-l$2_$3))')', `popdef(`cycle')')define(`l$2_$3', $1)define(`l$1', $3)')
66 pushdef(`_spin', `define(`part1', $2)popdef(`$0')')
67 define(`spin', `ifelse(_$0($1, eval('n*incr(maxy)`place(`n')place(`w')),
68   eval('n*incr(maxy)`place(`s')place(`e'))), `-', `defn(`l'eval($1-cycle+(
69   1000000000-$1)%cycle))', `$0(incr($1))')')
70 define(`part2', spin(1))
72 divert`'part1
73 part2