view day14b.rb @ 24:19481b061461

Implement tilting and cycling for Day 14 part 2 Lots of false starts trying to iterate. Eventually looked for "back in same position" to spot a loop. Then took longer to spot that "same position" isn't necessarily "start position" and loop can be offset!
author IBBoard <dev@ibboard.co.uk>
date Sat, 16 Dec 2023 20:39:02 +0000
parents
children
line wrap: on
line source

#! /usr/bin/env ruby

require 'set'

if ARGV.length != 1
        abort("Incorrect arguments - needs input file")
elsif not File.exist? (ARGV[0])
	abort("File #{ARGV[0]} did not exist")
end

file = ARGV[0]

platform = File.open(file, "r").each_line(chomp: true).map {|row| row.each_char.to_a}.to_a.transpose()

COL_SIZE = platform[0].length
puts "Column size: #{COL_SIZE}"

def cycle(platform)
	[:north, :west, :south, :east].reduce(platform) do |platform, iter|
#		puts "Push #{iter}"
		result = platform.map {|col| col.chunk_while {|i, j| i != "#"}.to_a}.map do |col|
			col.flat_map do |chunk|
				num_movable_objects = chunk.count("O")
				new_chunk = Array.new(num_movable_objects, "O")
				new_chunk.concat(Array.new(chunk.length - num_movable_objects, "."))
				new_chunk[-1] = "#" if chunk[-1] == "#"
				new_chunk
			end
		end
		case iter
		when :north
			# Pushed it North, now we need to go West
#			puts "#{result.transpose.map{|col| col.join('')}.join("\n")}\n\n"
			result.transpose
		when :west
			# Pushed it West, now we need to go South
#			puts "#{result.map{|col| col.join('')}.join("\n")}\n\n"
			result.transpose.map(&:reverse)
		when :south
			# Pushed it South, now we need to go East
#			puts "#{result.map(&:reverse).transpose.map{|col| col.join('')}.join("\n")}\n\n"
			result.transpose.map(&:reverse)
		when :east
			# Pushed it East, now we need to get back to North alignment
#			puts "#{result.map(&:reverse).transpose.map(&:reverse).transpose.map{|col| col.join('')}.join("\n")}\n\n"
			result.map(&:reverse).transpose.map(&:reverse)
		end
	end
end

previous_states = []
max_cycle_length = 1000000000
cycle_start = nil
cycle_length = max_cycle_length
(1..max_cycle_length).each do |iter|
#	puts "Cycle #{iter}"
	platform = cycle(platform)
	if iter % 1000 == 0
		puts iter
	end
	cycle_start = previous_states.index platform
	if !cycle_start.nil?
		cycle_start += 1  # Compensate for zero-indexing in array
		cycle_length = iter - cycle_start
#		puts "#{iter} - #{cycle_start} + 1 = #{cycle_length}"
		break
	end
	previous_states << platform
	puts "\n\n"
end
# Already did the first cycle of the loop, so skip it
#puts "#{(max_cycle_length - cycle_start)} % #{cycle_length} = #{((max_cycle_length - cycle_start) % cycle_length)} extras"
((max_cycle_length - cycle_start) % cycle_length).times {|n| platform = cycle(platform)}
total_load = platform.reduce(0) do |weight, col|
	col_weight = col.each_with_index.reduce(0) do |accum, cell|
		cell_contents, cell_pos = cell
		accum + (cell_contents == "O" ? (COL_SIZE - cell_pos) : 0)
	end
#	puts "#{col} = #{col_weight}"
	weight + col_weight
end
puts total_load