Code for the Advent of Code event
aoc
advent-of-code
1#!/usr/bin/env ruby
2# frozen_string_literal: true
3
4input = $stdin.readline
5
6moves = input.scan(/([RL])(\d+)/).map { |d, a| [d.downcase.to_sym, a.to_i] }
7
8class Point
9 MOVEMENT = {
10 north: ->(steps) { [0, steps] },
11 east: ->(steps) { [steps, 0] },
12 south: ->(steps) { [0, -1 * steps] },
13 west: ->(steps) { [-1 * steps, 0] }
14 }.freeze
15
16 attr_reader :x, :y, :twice
17
18 def initialize
19 @x = 0
20 @y = 0
21 @orientation = 0
22 @visited = []
23 end
24
25 def orientation
26 MOVEMENT.keys[@orientation]
27 end
28
29 def turn(direction)
30 @orientation += direction == :r ? 1 : MOVEMENT.keys.size - 1
31 @orientation %= MOVEMENT.keys.size
32 end
33
34 def move(steps)
35 dx, dy = MOVEMENT[orientation].call(steps)
36 # puts "#{dx}, #{dy}"
37 current = [@x, @y]
38 target = [@x + dx, @y + dy]
39
40 iterate_horizontal current[0], target[0] if current[1] == target[1]
41 iterate_vertical current[1], target[1] if current[0] == target[0]
42
43 @x, @y = target
44 end
45
46 def iterate_horizontal(current, target)
47 # Exclude our current position from checks
48 current += (current < target ? 1 : -1)
49 # Swap values to make range work if we're walking to the left
50 current, target = target, current if current > target
51 (current..target).each do |x|
52 if @visited.include? [x, @y]
53 @twice ||= [x, @y]
54 else
55 @visited << [x, @y]
56 end
57 end
58 end
59
60 def iterate_vertical(current, target)
61 # Exclude our current position from checks
62 current += (current < target ? 1 : -1)
63 # Swap values to make range work if we're walking down
64 current, target = target, current if current > target
65 (current..target).each do |y|
66 if @visited.include? [@x, y]
67 @twice ||= [@x, y]
68 else
69 @visited << [@x, y]
70 end
71 end
72 end
73
74 def shortest_path
75 @x.abs + @y.abs
76 end
77
78 def real_shortest
79 @twice.map(&:abs).inject(&:+)
80 end
81end
82
83destination = Point.new
84
85moves.each do |move|
86 # puts "Move: #{move[0]} then #{move[1]} steps"
87 destination.turn move[0]
88 destination.move move[1]
89end
90
91puts "Target position: (#{destination.x}, #{destination.y})"
92
93puts "(1) Shortest path is #{destination.shortest_path} steps long"
94
95puts "(2) The REAL shortest path is #{destination.real_shortest} steps long"