Day 20 k-d tree
This commit is contained in:
parent
99d3aafa27
commit
ae355f1a54
2 changed files with 132 additions and 22 deletions
141
days/day20.st
141
days/day20.st
|
|
@ -1,11 +1,12 @@
|
||||||
Object subclass: Racetrack [
|
Object subclass: Racetrack [
|
||||||
| grid distToEnd start end trackLen allCheats |
|
| grid distToEnd start end trackLen allCheats tree |
|
||||||
rows: r
|
rows: r
|
||||||
[ grid := Grid new rows: r.
|
[ grid := Grid new rows: r.
|
||||||
self initTrack ]
|
self initTrack ]
|
||||||
|
|
||||||
initTrack
|
initTrack
|
||||||
[ | pos dir step |
|
[ | pos dir step posnList |
|
||||||
|
"Find the start, end, and count the path tiles..."
|
||||||
trackLen := 1.
|
trackLen := 1.
|
||||||
grid allPosnsDo: [
|
grid allPosnsDo: [
|
||||||
:p | | c | c := grid at: p.
|
:p | | c | c := grid at: p.
|
||||||
|
|
@ -19,6 +20,7 @@ Object subclass: Racetrack [
|
||||||
height: grid height
|
height: grid height
|
||||||
initWith: [:p | nil].
|
initWith: [:p | nil].
|
||||||
|
|
||||||
|
"Trace the path, obtaining the distances to the end..."
|
||||||
pos := start.
|
pos := start.
|
||||||
dir := {Posn up. Posn down. Posn left. Posn right} findFirstElt: [
|
dir := {Posn up. Posn down. Posn left. Posn right} findFirstElt: [
|
||||||
:dir | self isEmpty: start + dir ].
|
:dir | self isEmpty: start + dir ].
|
||||||
|
|
@ -31,34 +33,43 @@ Object subclass: Racetrack [
|
||||||
step := step + 1.
|
step := step + 1.
|
||||||
].
|
].
|
||||||
distToEnd at: end put: 0.
|
distToEnd at: end put: 0.
|
||||||
]
|
|
||||||
|
"Build a k-d tree to answer range queries
|
||||||
|
over the {distToEnd . x . y} dimensions."
|
||||||
|
posnList := OrderedCollection new.
|
||||||
|
distToEnd allPosnsDo: [
|
||||||
|
:p | (distToEnd at: p) ifNotNil: [
|
||||||
|
:dist | posnList add: {dist. p x. p y}]].
|
||||||
|
tree := KDTree fromPoints: posnList. ]
|
||||||
|
|
||||||
isEmpty: pos [ ^(grid at: pos) = $. ]
|
isEmpty: pos [ ^(grid at: pos) = $. ]
|
||||||
|
|
||||||
countCheatsWithTime: time
|
countCheatsWithTime: time
|
||||||
[ | total |
|
[ | total |
|
||||||
total := 0.
|
total := 0.
|
||||||
"allCheats := Bag new."
|
|
||||||
grid allPosnsDo: [
|
grid allPosnsDo: [
|
||||||
:pos | total := total + (self countCheatsFrom: pos withTime: time) ].
|
:pos | total := total + (self countCheatsFrom: pos withTime: time) ].
|
||||||
"allCheats printNl."
|
|
||||||
^total ]
|
^total ]
|
||||||
|
|
||||||
countCheatsFrom: pos withTime: time
|
countCheatsFrom: pos withTime: time
|
||||||
[ | dist |
|
[ | dist count |
|
||||||
dist := distToEnd at: pos.
|
dist := distToEnd at: pos.
|
||||||
dist ifNil: [^0].
|
dist ifNil: [^0].
|
||||||
"dist < 100 ifTrue: [^0]."
|
dist < 102 ifTrue: [^0].
|
||||||
^self rangeCount: [
|
count := 0.
|
||||||
|
self rangeDo: [
|
||||||
:dpos :ddist |
|
:dpos :ddist |
|
||||||
self validCheatFrom: pos dist: dist
|
(self validCheatFrom: pos dist: dist
|
||||||
to: dpos dist: ddist
|
to: dpos dist: ddist
|
||||||
withTime: time
|
withTime: time) ifTrue: [
|
||||||
]
|
count := count + 1 ]]
|
||||||
inManhDistance: time
|
inManhDistance: time
|
||||||
from: pos
|
from: pos
|
||||||
maxDist: dist - 100. ]
|
maxDist: dist - 102.
|
||||||
|
^count ]
|
||||||
|
|
||||||
|
"Answers whether POS -> DPOS is a cheat that indeed only takes
|
||||||
|
TIME ps, and saves more than 100 ps."
|
||||||
validCheatFrom: pos dist: dist
|
validCheatFrom: pos dist: dist
|
||||||
to: dpos dist: ddist
|
to: dpos dist: ddist
|
||||||
withTime: time
|
withTime: time
|
||||||
|
|
@ -67,15 +78,16 @@ Object subclass: Racetrack [
|
||||||
(dpos y - pos y) abs.
|
(dpos y - pos y) abs.
|
||||||
ctime > time ifTrue: [^false].
|
ctime > time ifTrue: [^false].
|
||||||
saved := dist - ddist - ctime.
|
saved := dist - ddist - ctime.
|
||||||
"saved > 0 ifTrue: [allCheats add: saved]."
|
|
||||||
^saved >= 100 ]
|
^saved >= 100 ]
|
||||||
|
|
||||||
rangeCount: block
|
"Call BLOCK with DPOS and DDIST for each position within
|
||||||
|
DISTMH of POS, which is at most MSCORE away from the end.
|
||||||
|
BLOCK may be called with other positions."
|
||||||
|
OLDrangeDo: block
|
||||||
inManhDistance: distmh
|
inManhDistance: distmh
|
||||||
from: pos
|
from: pos
|
||||||
maxDist: mscore
|
maxDist: mscore
|
||||||
[ | ddist dpos count px py doit |
|
[ | ddist dpos px py |
|
||||||
count := 0.
|
|
||||||
dpos := Posn new.
|
dpos := Posn new.
|
||||||
px := pos x. py := pos y.
|
px := pos x. py := pos y.
|
||||||
distmh negated to: distmh do: [
|
distmh negated to: distmh do: [
|
||||||
|
|
@ -85,9 +97,94 @@ Object subclass: Racetrack [
|
||||||
dpos x: px + dx; y: py + dy.
|
dpos x: px + dx; y: py + dy.
|
||||||
ddist := distToEnd at: dpos.
|
ddist := distToEnd at: dpos.
|
||||||
ddist ifNotNil: [
|
ddist ifNotNil: [
|
||||||
(block value: dpos value: ddist)
|
block value: dpos value: ddist ]]]] ]
|
||||||
ifTrue: [ count := count + 1 ]]]]].
|
|
||||||
^count ]
|
"Optimisation of the above that uses the k-d tree to answer range queries."
|
||||||
|
rangeDo: block
|
||||||
|
inManhDistance: distmh
|
||||||
|
from: pos
|
||||||
|
maxDist: mscore
|
||||||
|
[ | min max dpos |
|
||||||
|
min := {0. pos x - distmh. pos y - distmh}.
|
||||||
|
max := {mscore. pos x + distmh. pos y + distmh}.
|
||||||
|
dpos := Posn new.
|
||||||
|
tree from: min to: max do: [
|
||||||
|
:ddist :dstx :dsty |
|
||||||
|
dpos x: dstx y: dsty.
|
||||||
|
block value: dpos value: ddist ] asSpreader ]
|
||||||
|
]
|
||||||
|
|
||||||
|
Object subclass: KDTree [
|
||||||
|
from: min to: max do: block
|
||||||
|
[ self subclassResponsibility ]
|
||||||
|
]
|
||||||
|
KDTree class extend [
|
||||||
|
fromPoints: points
|
||||||
|
[ ^self fromShuffledPoints: (Random new shuffle: points asArray) ]
|
||||||
|
|
||||||
|
fromShuffledPoints: points
|
||||||
|
[ ^self fromShuffledPoints: points axis: 1 ]
|
||||||
|
|
||||||
|
leafMaxSize [ ^5 ]
|
||||||
|
fromShuffledPoints: points axis: ax
|
||||||
|
[ | med splits dims nextAx value |
|
||||||
|
points size <= self leafMaxSize ifTrue: [^KDLeaf new points: points asArray].
|
||||||
|
med := self guessMedian: points axis: ax.
|
||||||
|
dims := med size.
|
||||||
|
nextAx := ax \\ dims + 1.
|
||||||
|
value := med at: ax.
|
||||||
|
splits := self splitPoints: points axis: ax on: value.
|
||||||
|
^KDInner new
|
||||||
|
left: (self fromShuffledPoints: (splits at: 1) axis: nextAx)
|
||||||
|
right: (self fromShuffledPoints: (splits at: 2) axis: nextAx)
|
||||||
|
axis: ax
|
||||||
|
value: value ]
|
||||||
|
|
||||||
|
splitPoints: points axis: ax on: val
|
||||||
|
[ | l r fairLeft |
|
||||||
|
l := OrderedCollection new.
|
||||||
|
r := OrderedCollection new.
|
||||||
|
fairLeft := true.
|
||||||
|
points do: [
|
||||||
|
:point | | v onLeft |
|
||||||
|
v := point at: ax.
|
||||||
|
onLeft := (v < val) or: [
|
||||||
|
v = val and: [fairLeft := fairLeft not. fairLeft]].
|
||||||
|
(onLeft ifTrue: [l] ifFalse: [r]) add: point ].
|
||||||
|
^{l . r} ]
|
||||||
|
|
||||||
|
medianGuessSize [ ^5 ]
|
||||||
|
guessMedian: points axis: ax
|
||||||
|
[ | arr |
|
||||||
|
arr := 1 to: (points size min: self medianGuessSize)
|
||||||
|
collect: [:i | points at: i].
|
||||||
|
arr sort: [:l :r | (l at: ax) <= (r at: ax)].
|
||||||
|
^arr at: arr size // 2 + 1 ]
|
||||||
|
]
|
||||||
|
|
||||||
|
KDTree subclass: KDLeaf [
|
||||||
|
| points |
|
||||||
|
points: n [points := n. "self class leafSize: points size"]
|
||||||
|
from: min to: max do: block [ points do: block ]
|
||||||
|
|
||||||
|
printOn: st [ st << 'lf' << points ]
|
||||||
|
]
|
||||||
|
KDLeaf class extend [
|
||||||
|
| leafSizes |
|
||||||
|
leafSizes [ ^leafSizes ifNil: [leafSizes := Bag new] ]
|
||||||
|
leafSize: sz [ self leafSizes add: sz ]
|
||||||
|
]
|
||||||
|
|
||||||
|
KDTree subclass: KDInner [
|
||||||
|
| left right axis value |
|
||||||
|
left: l right: r axis: ax value: v
|
||||||
|
[left:=l. right:=r. axis:=ax. value:=v]
|
||||||
|
from: min to: max do: block
|
||||||
|
[ (min at: axis) <= value ifTrue: [ left from: min to: max do: block ].
|
||||||
|
(max at: axis) >= value ifTrue: [ right from: min to: max do: block ] ]
|
||||||
|
|
||||||
|
printOn: st [ st << '{' << left << ' <= [' << axis << '] '
|
||||||
|
<< value << ' <= ' << right << '}' ]
|
||||||
]
|
]
|
||||||
|
|
||||||
AOC input: [ Racetrack new rows: stdin toLines asArray ];
|
AOC input: [ Racetrack new rows: stdin toLines asArray ];
|
||||||
|
|
|
||||||
|
|
@ -218,6 +218,7 @@ Object extend [ chain [ ^MessageChain new pipelineValue: self ] ]
|
||||||
|
|
||||||
Object subclass: Posn [
|
Object subclass: Posn [
|
||||||
| x y | x [^x] y [^y] x: n [x:=n] y: n [y:=n]
|
| x y | x [^x] y [^y] x: n [x:=n] y: n [y:=n]
|
||||||
|
x: nx y: ny [x := nx. y := ny]
|
||||||
|
|
||||||
+ o [ ^Posn x: x + o x y: y + o y ]
|
+ o [ ^Posn x: x + o x y: y + o y ]
|
||||||
+= o [ self x: x + o x; y: y + o y. ]
|
+= o [ self x: x + o x; y: y + o y. ]
|
||||||
|
|
@ -384,3 +385,15 @@ Integer extend [
|
||||||
^{s0 . t0}
|
^{s0 . t0}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
Random extend [
|
||||||
|
shuffle: coll
|
||||||
|
[ 1 to: coll size do: [
|
||||||
|
:i | | j tmp |
|
||||||
|
j := self between: 1 and: coll size.
|
||||||
|
tmp := coll at: i.
|
||||||
|
coll at: i put: (coll at: j).
|
||||||
|
coll at: j put: tmp.
|
||||||
|
].
|
||||||
|
^coll ]
|
||||||
|
]
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue