changeset 22:ad73a2ff3d06

Implement Day 15 part 1 and all of Day 16 Hash implementation was trivial, but I skipped the rest for now. Day 16 part 1 approach works unchanged for part 2! Just needed to run repeatedly with different inputs.
author IBBoard <dev@ibboard.co.uk>
date Sat, 16 Dec 2023 14:42:06 +0000
parents 46fb65f2cb94
children f34254b67082
files day15.rb day15.txt day16.rb day16.txt day16b.rb
diffstat 5 files changed, 248 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/day15.rb	Sat Dec 16 14:42:06 2023 +0000
@@ -0,0 +1,11 @@
+#! /usr/bin/env ruby
+
+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]
+
+puts File.open(file, "r").each_line(chomp: true).first.split(",").map {|str| str.each_char.map(&:ord).reduce(0) {|sum, c| puts ((sum + c) * 17) % 256;  ((sum + c) * 17) % 256 }}.reduce(:+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/day15.txt	Sat Dec 16 14:42:06 2023 +0000
@@ -0,0 +1,27 @@
+--- Day 15: Lens Library ---
+
+Implement a custom hashing algorithm:
+    Determine the ASCII code for the current character of the string.
+    Increase the current value by the ASCII code you just determined.
+    Set the current value to itself multiplied by 17.
+    Set the current value to the remainder of dividing itself by 256.
+
+Values are comma-separated. For example:
+
+rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7
+
+Gives 11 individual steps; the result of running the HASH algorithm on each of the steps is as follows:
+
+    rn=1 becomes 30.
+    cm- becomes 253.
+    qp=3 becomes 97.
+    cm=2 becomes 47.
+    qp- becomes 14.
+    pc=4 becomes 180.
+    ot=9 becomes 9.
+    ab=5 becomes 197.
+    pc- becomes 48.
+    pc=6 becomes 214.
+    ot=7 becomes 231.
+
+Then sum the hashes.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/day16.rb	Sat Dec 16 14:42:06 2023 +0000
@@ -0,0 +1,77 @@
+#! /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]
+
+Laser = Struct.new(:x, :y, :direction)
+
+mirror_map = {
+	"." => {
+		right: [:right],
+		up: [:up],
+		left: [:left],
+		down: [:down]
+	},
+	"/" => {
+		right: [:up],
+		up: [:right],
+		down: [:left],
+		left: [:down]
+	},
+	"\\" => {
+		right: [:down],
+		down: [:right],
+		up: [:left],
+		left: [:up]
+	},
+	"-" => {
+		right: [:right],
+		left: [:left],
+		down: [:left, :right],
+		up: [:left, :right]
+	},
+	"|" => {
+		right: [:up, :down],
+		left: [:up, :down],
+		down: [:down],
+		up: [:up]
+	}
+}
+
+def next_positions(puzzle, mirror_map, laser)
+	x_prime, y_prime = case laser.direction
+		when :right then [laser.x + 1, laser.y]
+		when :down then [laser.x, laser.y + 1]
+		when :left then [laser.x - 1, laser.y]
+		when :up then [laser.x, laser.y - 1]
+	end
+	if x_prime < 0 or x_prime >= puzzle[0].length or y_prime < 0 or y_prime >= puzzle.length
+		[]
+	else
+		new_space = puzzle[y_prime][x_prime]
+		# puts "#{laser.direction} to #{x_prime}, #{y_prime} (#{new_space})"
+		mirror_map[new_space][laser.direction].map {|dir| Laser.new(x_prime, y_prime, dir)}
+	end
+end
+
+puzzle = File.open(file, "r").each_line(chomp: true).map {|line| line.each_char.to_a}
+lasers = next_positions(puzzle, mirror_map, Laser.new(-1, 0, :right))
+energised = Array.new(puzzle.length) {Array.new(puzzle[0].length)}
+energised [0][0] = 1
+previous_lasers = Set.new
+
+while lasers != []
+#	puts "#{lasers}"
+	lasers = lasers.flat_map do |laser|
+		next_positions(puzzle, mirror_map, laser).filter {|new_laser| !previous_lasers.include? new_laser}.each {|new_laser| energised[new_laser.y][new_laser.x] = 1; previous_lasers << new_laser}
+	end
+end
+
+puts energised.flatten.map(&:to_i).reduce(:+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/day16.txt	Sat Dec 16 14:42:06 2023 +0000
@@ -0,0 +1,49 @@
+--- Day 16: The Floor Will Be Lava ---
+
+A laser/mirror problem - "/" and "\" are mirrors that reflect 90°.
+"-" and "|" are splitters - transparent in the same orientation,
+but TWO beams go at 90° if hit against the flat edge.
+"." is empty space.
+
+The laser starts in the top-right.
+
+For an example map:
+
+.|...\....
+|.-.\.....
+.....|-...
+........|.
+..........
+.........\
+..../.\\..
+.-.-/..|..
+.|....-|.\
+..//.|....
+
+The laser goes:
+
+>|<<<\....
+|v-.\^....
+.v...|->>>
+.v...v^.|.
+.v...v^...
+.v...v^..\
+.v../2\\..
+<->-/vv|..
+.|<<<2-|.\
+.v//.|.v..
+
+Every space that the laser goes through is "energised", which gives:
+
+######....
+.#...#....
+.#...#####
+.#...##...
+.#...##...
+.#...##...
+.#..####..
+########..
+.#######..
+.#...#.#..
+
+The count of energised spaces is 46.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/day16b.rb	Sat Dec 16 14:42:06 2023 +0000
@@ -0,0 +1,84 @@
+#! /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]
+
+Laser = Struct.new(:x, :y, :direction)
+
+mirror_map = {
+	"." => {
+		right: [:right],
+		up: [:up],
+		left: [:left],
+		down: [:down]
+	},
+	"/" => {
+		right: [:up],
+		up: [:right],
+		down: [:left],
+		left: [:down]
+	},
+	"\\" => {
+		right: [:down],
+		down: [:right],
+		up: [:left],
+		left: [:up]
+	},
+	"-" => {
+		right: [:right],
+		left: [:left],
+		down: [:left, :right],
+		up: [:left, :right]
+	},
+	"|" => {
+		right: [:up, :down],
+		left: [:up, :down],
+		down: [:down],
+		up: [:up]
+	}
+}
+
+def next_positions(puzzle, mirror_map, laser)
+	x_prime, y_prime = case laser.direction
+		when :right then [laser.x + 1, laser.y]
+		when :down then [laser.x, laser.y + 1]
+		when :left then [laser.x - 1, laser.y]
+		when :up then [laser.x, laser.y - 1]
+	end
+	if x_prime < 0 or x_prime >= puzzle[0].length or y_prime < 0 or y_prime >= puzzle.length
+		[]
+	else
+		new_space = puzzle[y_prime][x_prime]
+		# puts "#{laser.direction} to #{x_prime}, #{y_prime} (#{new_space})"
+		mirror_map[new_space][laser.direction].map {|dir| Laser.new(x_prime, y_prime, dir)}
+	end
+end
+
+puzzle = File.open(file, "r").each_line(chomp: true).map {|line| line.each_char.to_a}
+puzzle_width = puzzle[0].length
+puzzle_height = puzzle.length
+start_points = ((0..puzzle_height).map{|y| [Laser.new(-1, y, :right), Laser.new(puzzle_width, y, :left)]} + (0..puzzle_width).map{|x| [Laser.new(x, -1, :down), Laser.new(x, puzzle_height, :up)]}).flatten
+
+max_energised = start_points.reduce(0) do |highest, start_point|
+	energised = Array.new(puzzle.length) {Array.new(puzzle[0].length)}
+	previous_lasers = Set.new
+	lasers = [start_point]
+	
+	while lasers != []
+	#	puts "#{lasers}"
+		lasers = lasers.flat_map do |laser|
+			next_positions(puzzle, mirror_map, laser).filter {|new_laser| !previous_lasers.include? new_laser}.each {|new_laser| energised[new_laser.y][new_laser.x] = 1; previous_lasers << new_laser}
+		end
+	end
+
+	energised_count = energised.flatten.map(&:to_i).reduce(:+)
+	[highest, energised_count].max
+end
+puts max_energised
\ No newline at end of file