From 99d3aafa271b70676cb8c2ab2f332919424787f7 Mon Sep 17 00:00:00 2001 From: eutro Date: Fri, 20 Dec 2024 10:49:19 +0000 Subject: [PATCH] Day 20 --- days/day20.st | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++ days/utils.st | 6 ++++ 2 files changed, 102 insertions(+) create mode 100644 days/day20.st diff --git a/days/day20.st b/days/day20.st new file mode 100644 index 0000000..931cd0f --- /dev/null +++ b/days/day20.st @@ -0,0 +1,96 @@ +Object subclass: Racetrack [ + | grid distToEnd start end trackLen allCheats | + rows: r + [ grid := Grid new rows: r. + self initTrack ] + + initTrack + [ | pos dir step | + trackLen := 1. + grid allPosnsDo: [ + :p | | c | c := grid at: p. + (c = $S or: [c = $E]) ifTrue: [ + grid at: p put: $.. + c = $S ifTrue: [start := p]. + c = $E ifTrue: [end := p]. + ]. + c = $. ifTrue: [ trackLen := trackLen + 1 ]]. + distToEnd := Grid width: grid width + height: grid height + initWith: [:p | nil]. + + pos := start. + dir := {Posn up. Posn down. Posn left. Posn right} findFirstElt: [ + :dir | self isEmpty: start + dir ]. + step := 0. + [ pos = end ] whileFalse: [ + distToEnd at: pos put: trackLen - step. + dir := {dir. dir rotateCw. dir rotateCcw} findFirstElt: [ + :dir | self isEmpty: pos + dir ]. + pos := pos + dir. + step := step + 1. + ]. + distToEnd at: end put: 0. + ] + + isEmpty: pos [ ^(grid at: pos) = $. ] + + countCheatsWithTime: time + [ | total | + total := 0. + "allCheats := Bag new." + grid allPosnsDo: [ + :pos | total := total + (self countCheatsFrom: pos withTime: time) ]. + "allCheats printNl." + ^total ] + + countCheatsFrom: pos withTime: time + [ | dist | + dist := distToEnd at: pos. + dist ifNil: [^0]. + "dist < 100 ifTrue: [^0]." + ^self rangeCount: [ + :dpos :ddist | + self validCheatFrom: pos dist: dist + to: dpos dist: ddist + withTime: time + ] + inManhDistance: time + from: pos + maxDist: dist - 100. ] + + validCheatFrom: pos dist: dist + to: dpos dist: ddist + withTime: time + [ | ctime saved | + ctime := (dpos x - pos x) abs + + (dpos y - pos y) abs. + ctime > time ifTrue: [^false]. + saved := dist - ddist - ctime. + "saved > 0 ifTrue: [allCheats add: saved]." + ^saved >= 100 ] + + rangeCount: block + inManhDistance: distmh + from: pos + maxDist: mscore + [ | ddist dpos count px py doit | + count := 0. + dpos := Posn new. + px := pos x. py := pos y. + distmh negated to: distmh do: [ + :dx | distmh negated to: distmh do: [ + :dy | + (dx abs + dy abs) <= distmh ifTrue: [ + dpos x: px + dx; y: py + dy. + ddist := distToEnd at: dpos. + ddist ifNotNil: [ + (block value: dpos value: ddist) + ifTrue: [ count := count + 1 ]]]]]. + ^count ] +] + +AOC input: [ Racetrack new rows: stdin toLines asArray ]; + part1: 2; part2: 20; + result: [ :track :time | track countCheatsWithTime: time ]; + finish. diff --git a/days/utils.st b/days/utils.st index b3e850b..b3f9f3d 100644 --- a/days/utils.st +++ b/days/utils.st @@ -156,6 +156,12 @@ Iterable extend [ intoCollection: acc [ self do: [:x | acc add: x]. ^acc ] ] +SequenceableCollection extend [ + findFirstElt: block [ ^self at: (self findFirst: block) ] + findFirstElt: block ifAbsent: abs + [ ^self at: (self findFirst: block) ifAbsent: abs ] +] + Collection extend [ transposed [ ^(self size <= 0) ifTrue: