diff --git a/days/day14.st b/days/day14.st new file mode 100644 index 0000000..531336e --- /dev/null +++ b/days/day14.st @@ -0,0 +1,108 @@ +Object subclass: Robot [ + | pos vel | + parse: line + [ line scanf: 'p=%d,%d v=%d,%d' with: [ + :px :py :vx :vy | + pos := Posn x: px y: py. + vel := Posn x: vx y: vy ]] + + printOn: st [ st << '{p=' << pos << ' v=' << vel << '}' ] + + posAt: time in: br + [ ^Posn + x: (pos x + (vel x * (time \\ br w))) \\ br w + y: (pos y + (vel y * (time \\ br h))) \\ br h ] +] + +Array extend [ + incAt: pos [ self at: pos put: 1 + (self at: pos) ] +] + +Posn subclass: Bathroom [ + | midx midy | + w [^x] h [^y] + + x: n [super x: n. midx := x // 2] + y: n [super y: n. midy := y // 2] + + countQuadrants: posns + [ | qs | + qs := Array new: 4 withAll: 0. + posns do: [ :p | self putPosn: p toQuad: qs ]. + ^qs inject: 1 into: [:l :r | l * r] ] + + putPosn: p toQuad: qs + [ | top bot | + top := p y < midy. + bot := p y > midy. + p x < midx ifTrue: + [ top ifTrue: [^qs incAt: 1]. + bot ifTrue: [^qs incAt: 2]. + ^nil ]. + p x > midx ifTrue: + [ top ifTrue: [^qs incAt: 3]. + bot ifTrue: [^qs incAt: 4]. + ^nil ] ] + + findMaxOf: robots inPeriod: p withTrue: filter + [ | best bestTime | + best := -1. + 1 to: p do: [ + :time | | cnt | + cnt := robots count: [:it | filter value: (it posAt: time in: self)]. + cnt > best ifTrue: [best := cnt. bestTime := time] ]. + ^bestTime ] + + plot: robots at: time + [ | grid orig | + grid := 1 to: self h collect: [:i | String new: self w withAll: $.]. + grid := Grid new rows: grid. + orig := Posn x: 1 y: 1. + robots do: [:r | grid at: orig + (r posAt: time in: self) put: $#]. + stdout << 'At ' << time << ':'; nl; << grid; nl; nl. ] + + betweenPercentile: min and: max by: getKey + [ | mine minV maxV | + mine := getKey value: self. + minV := mine * min. + maxV := mine * max. + ^[ :it | | its | its := getKey value: it. + minV <= its and: [its <= maxV] ] ] + + inMiddleBy: getKey + [ ^self betweenPercentile: 0.4 and: 0.6 by: getKey ] + + findMaxRobots: robots inMiddleBy: getKey + [ ^self findMaxOf: robots + inPeriod: (getKey value: self) + withTrue: (self inMiddleBy: getKey) ] + + findTreeTimeOf: robots + [ | bestx besty | + "The tree is a little pictogram in the center of the image -- we + find the offset with which the robots condense here on both the + X and Y axes, then use CRT to compute where these coincide." + bestx := self findMaxRobots: robots inMiddleBy: [:it | it x]. + besty := self findMaxRobots: robots inMiddleBy: [:it | it y]. + + (self w gcd: self h) = 1 ifFalse: [self error: 'Not coprime']. + ^(self w egcd: self h) letArrayInBlock: + [ :cx :cy | + (bestx * cy * self h) + + (besty * cx * self w) + \\ (self w * self h) ] ] +] + +Bathroom class extend [ + width: w height: h [^self x: w y: h] +] + +theBathroom := Bathroom width: 101 height: 103. + +AOC input: [ stdin toLines asArray collect: [ :it | Robot new parse: it ] ]; + part1: [ :robots | | posns | + posns := robots collect: [:it | it posAt: 100 in: theBathroom]. + theBathroom countQuadrants: posns ]; + part2: [ :robots | theBathroom findTreeTimeOf: robots ]; + result: [ :robots :part | part value: robots. ]; + finish. diff --git a/days/utils.st b/days/utils.st index 0e81104..6ed35a1 100644 --- a/days/utils.st +++ b/days/utils.st @@ -74,11 +74,15 @@ Object subclass: ScannerState [ finish [ ^oc asArray ] parseIntOn: stream - [ | d | - d := 0. + [ | d sf c | + d := 0. sf := 1. c := stream peek. + (c = $+) | (c = $-) ifTrue: [ + stream next. + c = $- ifTrue: [sf := -1] + ]. stream whilePeek: [ :c | c isDigit ] do: [ :c | d := d * 10 + c digitValue ]. - oc add: d ] + oc add: (sf * d) ] parseStringOn: stream [ | s | s := OrderedCollection new. @@ -224,7 +228,7 @@ Object subclass: Posn [ printOn: s [ s << '<' << x << ', ' << y << '>'. ] ] Posn class extend [ - x: x y: y [ ^Posn new x: x; y: y ] + x: x y: y [ ^self new x: x; y: y ] up: n [ ^Posn x: 0 y: n negated ] down: n [ ^Posn x: 0 y: n ] left: n [ ^Posn x: n negated y: 0 ] @@ -272,7 +276,9 @@ Array extend [ asPosn [ ^Posn x: (self at: 1) y: (self at: 2) ] ] Iterable extend [ asPosns [ ^self collect: [:it | it asPosn] ] ] Object subclass: Grid [ - | rows | rows: n [rows:=n] + | rows | + rows: n [rows:=n] + rows [^rows] at: pos [ ^(rows at: pos y ifAbsent: [^nil]) at: pos x ifAbsent: [nil] ] at: pos put: elt @@ -319,9 +325,12 @@ Dictionary extend [ ] Integer extend [ - ceilingDiv: denom [ ^((self - 1) // denom) + 1 ] + ceilingDiv: denom [ + "Answer (self / denom) ceiling." + ^((self - 1) // denom) + 1 + ] - "The original implementation was incorrect. Here is the correct code:" + "Original code was incorrect:" ceilingLog: radix [ "Answer (self log: radix) ceiling. Optimized to answer an integer." @@ -343,4 +352,22 @@ Integer extend [ answer := answer + 1]. ^answer ] + + egcd: o [ + "Answer {x . y} such that (self * x) + (o * y) = (self gcd: o)." + | q r0 r s0 s t0 t tmp | + r0 := self. r := o. + s0 := 1. s := 0. + t0 := 0. t := 1. + + [r ~= 0] whileTrue: [ + q := r0 // r. + + tmp := r. r := r0 - (q * r). r0 := tmp. + tmp := s. s := s0 - (q * s). s0 := tmp. + tmp := t. t := t0 - (q * t). t0 := tmp. + ]. + + ^{s0 . t0} + ] ]