Created
December 4, 2017 05:12
-
-
Save kieran/989310802e6d1ae7789ffdcc46560583 to your computer and use it in GitHub Desktop.
Advent of Code - day 3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### | |
Example square: | |
17 16 15 14 13 | |
18 5 4 3 12 | |
19 6 1 2 11 | |
20 7 8 9 10 | |
21 22 23---> ... | |
### | |
class Ring | |
constructor: (@width=1)-> | |
# ensure we only construct well | |
# structured rings (odd widths) | |
console.assert @width % 2 is 1 | |
# find the ring that contains this number | |
@forNumber: (num)-> | |
# find the width of the largest square | |
# which contains the number | |
width = Math.ceil Math.sqrt num | |
# if that width is even, we need to expand | |
# the width by 1 to make it odd | |
# since the inner-most square is 1x1 | |
width = width + 1 if width % 2 is 0 | |
# return a new Ring object for that square | |
new Ring width | |
# find the ring that contains this number, | |
# then find the manhattan distance from the | |
# number to the center of the ring | |
@distanceFor: (num)-> | |
@forNumber(num).distanceFor(num) | |
### | |
Basic ring information | |
### | |
area: -> | |
Math.pow @width, 2 | |
# the number of cells in the outer | |
# circumference of this ring | |
circ: -> | |
return 1 if @width is 1 | |
(@width - 1) * 4 | |
# the min value of this ring, | |
min: -> | |
@area() - @circ() + 1 | |
# the max value is equal to | |
# the area of this ring | |
max: -> | |
@area() | |
# the numeric range of the ring | |
range: -> | |
[@min()...@max()] | |
# next ring out - maybe? | |
outer: -> | |
new Ring(@width+2) | |
### | |
Determine offset of a number within the ring | |
### | |
maxX: -> | |
Math.floor @width / 2 | |
minX: -> | |
0 - @maxX() | |
maxY: -> | |
Math.floor @width / 2 | |
minY: -> | |
0 - @maxY() | |
# calculate the first x and y coordinates | |
firstCoords: -> | |
x = @maxX() | |
y = @minY() + 1 | |
[x, y] | |
# | |
# Searches around the ring in insertion | |
# order for the correct number, returning | |
# the coords of the first match | |
# | |
coordsFor: (num)-> | |
[x, y] = @firstCoords() | |
# start the cursor at the min, | |
# which is the value at firstCoords | |
current = @min() | |
# and return immediately if | |
# this is the target number | |
return [x,y] if current is num | |
# move up until max Y | |
while y < @maxY() | |
y += 1 | |
current += 1 | |
return [x,y] if current is num | |
# move left until min X | |
while x > @minX() | |
x -= 1 | |
current += 1 | |
return [x,y] if current is num | |
# move down until min Y | |
while y > @minY() | |
y -= 1 | |
current += 1 | |
return [x,y] if current is num | |
# move right until max X | |
while x < @maxX() | |
x += 1 | |
current += 1 | |
return [x,y] if current is num | |
throw "¿WTF num #{num} not found?" | |
distanceFor: (num)-> | |
@coordsFor(num).map(Math.abs).reduce (memo, val)-> memo + val | |
# actual question: | |
alert Ring.distanceFor(361527) | |
### | |
Tests | |
### | |
assert = console.assert | |
# Ring.forNumber | |
sizes = | |
1: 1 | |
9: 3 | |
10: 5 | |
25: 5 | |
26: 7 | |
for num, width of sizes | |
assert Ring.forNumber(num).width is width, "Expected ring for #{num} to be #{width}" | |
# ring.firstCoords | |
assert (new Ring(3)).firstCoords().toString() is [1,0].toString() | |
assert (new Ring(5)).firstCoords().toString() is [2,-1].toString() | |
assert (new Ring(7)).firstCoords().toString() is [3,-2].toString() | |
# ring.coordsFor num | |
assert (new Ring(3)).coordsFor(3).toString() is [1,1].toString() | |
assert (new Ring(3)).coordsFor(9).toString() is [1,-1].toString() | |
assert (new Ring(5)).coordsFor(22).toString() is [-1,-2].toString() | |
# ring.distanceFor num | |
assert (new Ring(3)).distanceFor(3) is 2 | |
assert (new Ring(3)).distanceFor(9) is 2 | |
assert (new Ring(5)).distanceFor(22) is 3 | |
# Ring.distanceFor num | |
assert Ring.distanceFor(3) is 2 | |
assert Ring.distanceFor(9) is 2 | |
assert Ring.distanceFor(22) is 3 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment