require_relative 'util' # --- Day 1: No Time for a Taxicab --- # Santa's sleigh uses a very high-precision clock to guide its # movements, and the clock's oscillator is regulated by # stars. Unfortunately, the stars have been stolen... by the Easter # Bunny. To save Christmas, Santa needs you to retrieve all fifty # stars by December 25th. # Collect stars by solving puzzles. Two puzzles will be made available # on each day in the advent calendar; the second puzzle is unlocked # when you complete the first. Each puzzle grants one star. Good luck! # You're airdropped near Easter Bunny Headquarters in a city # somewhere. "Near", unfortunately, is as close as you can get - the # instructions on the Easter Bunny Recruiting Document the Elves # intercepted start here, and nobody had time to work them out # further. # The Document indicates that you should start at the given # coordinates (where you just landed) and face North. Then, follow the # provided sequence: either turn left (L) or right (R) 90 degrees, # then walk forward the given number of blocks, ending at a new # intersection. # There's no time to follow such ridiculous instructions on foot, # though, so you take a moment and work out the destination. Given # that you can only walk on the street grid of the city, how far is # the shortest path to the destination? # For example: # - Following `R2, L3` leaves you 2 blocks East and 3 blocks North, or # 5 blocks away. # - `R2, R2, R2` leaves you 2 blocks due South of your starting # position, which is 2 blocks away. # - `R5, L5, R5, R3` leaves you 12 blocks away. # How many blocks away is Easter Bunny HQ? input = File.open('01.txt') { |f| f.read.chomp } DIRECTIONS = { north: [0, -1], east: [1, 0], south: [0, 1], west: [-1, 0] } class TaxiGrid def initialize @direction = :north @x = 0 @y = 0 end def turn_right case @direction when :north then @direction = :east when :east then @direction = :south when :south then @direction = :west when :west then @direction = :north end end def turn_left case @direction when :north then @direction = :west when :east then @direction = :north when :south then @direction = :east when :west then @direction = :south end end def walk(n) xo, yo = DIRECTIONS[@direction] n.times do @x += xo @y += yo end end def distance(x = @x, y = @y) x.abs + y.abs end end def easy(input) grid = TaxiGrid.new input.split(', ').each do |instruction| direction = instruction[0] steps = instruction[1..-1].to_i direction == 'L' ? grid.turn_left : grid.turn_right grid.walk(steps) end grid.distance end assert(easy('R2, L3') == 5) assert(easy('R2, R2, R2') == 2) assert(easy('R5, L5, R5, R3') == 12) puts "easy(input): #{easy(input)}" # --- Part Two --- # Then, you notice the instructions continue on the back of the # Recruiting Document. Easter Bunny HQ is actually at the first # location you visit twice. # For example, if your instructions are `R8, R4, R4, R8`, the first # location you visit twice is 4 blocks away, due East. # How many blocks away is the first location you visit twice? class VisitedTaxiGrid < TaxiGrid def initialize super @visited = Hash.new { |h, k| h[k] = 0 } end def walk(n) xo, yo = DIRECTIONS[@direction] n.times do @x += xo @y += yo @visited[[@x, @y]] += 1 end end def already_visited @visited.select { |_, v| v > 1 }.keys[0] end end def hard(input) grid = VisitedTaxiGrid.new input.split(', ').each do |instruction| direction = instruction[0] steps = instruction[1..-1].to_i direction == 'L' ? grid.turn_left : grid.turn_right grid.walk(steps) end grid.distance(*grid.already_visited) end assert(hard('R8, R4, R4, R8') == 4) puts "hard(input): #{hard(input)}"