## DEV Community is a community of 697,485 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

# Discussion on: Advent of Code 2020 Solution Megathread - Day 12: Rain Risk

Derk-Jan Karrenbeld

There are (at least) two ways to "model" the solution: polar coordinates or vector/gonio math. Here is a Ruby OOP solution using the latter:

``````require 'benchmark'

class Ship

def initialize
self.facing = 90
self.position = Position.new(0, 0)
self.waypoint = Waypoint.new
end

def sail!(instruction)

case instruction.action
when 'N'
position.translate!(0, -instruction.value)
when 'S'
position.translate!(0,  instruction.value)
when 'E'
position.translate!( instruction.value, 0)
when 'W'
position.translate!(-instruction.value, 0)
when 'L'
self.facing = (self.facing - instruction.value) % 360
when 'R'
self.facing = (self.facing + instruction.value) % 360
when 'F'
direction = %w[N E S W][facing / 90]
sail! instruction.with_action(direction)
# input only contains 90 180 270 so no need for
# SOS Castoa (gonio).

end
end

def sail_with_waypoint!(instruction)
if %w[N S E W L R].include?(instruction.action)
waypoint.sail! instruction
return
end

dx, dy = waypoint.position.difference(Position.new(0, 0))
self.position.translate!(dx * instruction.value, dy * instruction.value)
end

private

attr_accessor :facing, :waypoint
attr_writer :position
end

class Waypoint

def initialize
self.position = Position.new(10, -1)
end

def sail!(instruction)
case instruction.action
when 'N'
position.translate!(0, -instruction.value)
when 'S'
position.translate!(0,  instruction.value)
when 'E'
position.translate!( instruction.value, 0)
when 'W'
position.translate!(-instruction.value, 0)
when 'L'
theta = -(instruction.value / 180.to_f * Math::PI)

self.position = Position.new(
(position.x * Math.cos(theta) - position.y * Math.sin(theta)).round,
(position.x * Math.sin(theta) + position.y * Math.cos(theta)).round
)
when 'R'
theta = instruction.value / 180.to_f * Math::PI

self.position = Position.new(
(position.x * Math.cos(theta) - position.y * Math.sin(theta)).round,
(position.x * Math.sin(theta) + position.y * Math.cos(theta)).round
)
end
end

private

attr_accessor :facing
attr_writer :position
end

class Instruction

def initialize(action, value)
self.action = action
self.value = value.to_i
end

def with_action(new_action)
Instruction.new(new_action, value)
end

private

attr_writer :action, :value
end

class Position

def initialize(x, y)
self.x = x
self.y = y
end

def translate!(x, y)
self.x += x
self.y += y

self
end

def distance(other)
difference(other).map(&:abs).sum
end

def difference(other)
[x - other.x, y - other.y]
end

private

attr_writer :x, :y
end

def self.from_lines(lines)
lines.map do |line|
line = line.chomp
Instruction.new(line[0], line[1..])
end
end
end

def track(ship)
original = ship.position.dup
yield
original.distance(ship.position)
end

source = 'input.txt'

verbose = false

Benchmark.bmbm do |x|
x.report(:part_1) do
ship = Ship.new

distance = track(ship) do
instructions.each do |instruction|
ship.sail! instruction
puts "[ahoy] #{instruction.action} by #{instruction.value}: #{ship.position.x}, #{ship.position.y}" if verbose
end
end

puts distance
end

x.report(:part_2) do
ship = Ship.new

distance = track(ship) do
instructions.each do |instruction|
ship.sail_with_waypoint! instruction
puts "[ahoy] #{instruction.action} by #{instruction.value}: #{ship.position.x}, #{ship.position.y}" if verbose
end
end
puts distance
end
end
``````