Skip to content

Instantly share code, notes, and snippets.

@kieran
Created December 4, 2017 05:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kieran/989310802e6d1ae7789ffdcc46560583 to your computer and use it in GitHub Desktop.
Save kieran/989310802e6d1ae7789ffdcc46560583 to your computer and use it in GitHub Desktop.
Advent of Code - day 3
###
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