Code for the Advent of Code event
aoc advent-of-code
at rust 119 lines 3.0 kB view raw
1#!/usr/bin/env ruby 2# frozen_string_literal: true 3 4require 'matrix' 5require 'pry' 6 7DEBUG = false 8 9def debug(msg = nil, &block) 10 return unless DEBUG 11 warn msg if msg 12 block.call if block_given? 13end 14 15class Vector 16 def x = self[0] 17 def y = self[1] 18 def z = self[2] 19 20 def x=(v) self[0] = v end 21 def y=(v) self[1] = v end 22 def z=(v) self[2] = v end 23 24 def to_s = "(#{x}, #{y}, #{z})" 25 def inspect = to_s 26end 27 28V = Vector 29 30TRANSFORMS = [ 31 ->(p) { V[p.z, p.y, -p.x] }, 32 ->(p) { V[-p.y, p.z, -p.x] }, 33 ->(p) { V[-p.z, -p.y, -p.x] }, 34 ->(p) { V[p.y, -p.z, -p.x] }, 35 ->(p) { V[p.z, p.x, p.y] }, 36 ->(p) { V[-p.x, p.z, p.y] }, 37 ->(p) { V[-p.z, -p.x, p.y] }, 38 ->(p) { V[p.x, -p.z, p.y] }, 39 ->(p) { V[p.y, p.x, -p.z] }, 40 ->(p) { V[-p.x, p.y, -p.z] }, 41 ->(p) { V[-p.y, -p.x, -p.z] }, 42 ->(p) { V[p.x, -p.y, -p.z] }, 43 ->(p) { V[p.y, p.z, p.x] }, 44 ->(p) { V[-p.z, p.y, p.x] }, 45 ->(p) { V[-p.y, -p.z, p.x] }, 46 ->(p) { V[p.z, -p.y, p.x] }, 47 ->(p) { V[p.x, p.z, -p.y] }, 48 ->(p) { V[-p.z, p.x, -p.y] }, 49 ->(p) { V[-p.x, -p.z, -p.y] }, 50 ->(p) { V[p.z, -p.x, -p.y] }, 51 ->(p) { V[p.x, p.y, p.z] }, 52 ->(p) { V[-p.y, p.x, p.z] }, 53 ->(p) { V[-p.x, -p.y, p.z] }, 54 ->(p) { V[p.y, -p.x, p.z] } 55].freeze 56 57def rotate(map) 58 TRANSFORMS.map { |t| map.map { |p| t[p] } } 59end 60 61def manhattan(a, b) 62 (a.x - b.x).abs + (a.y - b.y).abs + (a.z - b.z).abs 63end 64 65maps = ARGF.read.strip.split("\n\n").map { |s| s.lines[1..].map { Vector[*_1.split(',').map(&:to_i)] } } 66maps_dict = maps.each_with_index.to_a.to_h(&:reverse) 67 68current = maps_dict[0] 69current_i = 0 70solved = { 0 => current } 71scanner_positions = [Vector[0, 0, 0]] 72 73loop do 74 unsolved_is = maps_dict.keys - solved.keys 75 debug "Solved indices: #{solved.keys}" 76 debug "Unsolved indices: #{unsolved_is}" 77 debug "Trying to find a match for #{current_i}" 78 break if solved.size == maps_dict.size 79 solution = nil 80 solution_i = nil 81 solution_score = 0 82 unsolved_is.each do |i| 83 candidate = maps_dict[i] 84 rotations = rotate candidate 85 rotations.each do |rotation| 86 diffs = current.product(rotation).map { _1 - _2 } 87 diffs.each do |diff| 88 translation = rotation.map { _1 + diff } 89 common_count = (current & translation).size 90 if common_count >= 12 && common_count > solution_score 91 debug "Scanner #{i} matches with score #{common_count}" 92 solution = translation 93 solution_i = i 94 solution_score = common_count 95 scanner_positions << diff 96 break 97 end 98 break if solution 99 end 100 break if solution 101 end 102 break if solution 103 end 104 if solution 105 debug "Best match for #{current_i} is #{solution_i} (score: #{solution_score})" 106 solution = solution.union current 107 solved[solution_i] = solution 108 current = solution 109 current_i = solution_i 110 else 111 abort 'Failed to find a match' 112 end 113 debug '' 114end 115 116puts solved.values.flatten.uniq.size 117puts scanner_positions.combination(2).map { manhattan(_1, _2) }.max 118 119# binding.pry