summaryrefslogtreecommitdiff
path: root/24
diff options
context:
space:
mode:
authorAlex Pickering <alex@cogarr.net>2025-02-07 12:49:48 -0600
committerAlex Pickering <alex@cogarr.net>2025-02-07 12:49:48 -0600
commit3555be54c2abb8d5ece008a60dbdfbde0ffbddd7 (patch)
tree278876284d07118ecdea5c48cb6453f3122887f0 /24
downloadadvent_of_code_2022-3555be54c2abb8d5ece008a60dbdfbde0ffbddd7.tar.gz
advent_of_code_2022-3555be54c2abb8d5ece008a60dbdfbde0ffbddd7.tar.bz2
advent_of_code_2022-3555be54c2abb8d5ece008a60dbdfbde0ffbddd7.zip
inital commitHEADmaster
Diffstat (limited to '24')
-rw-r--r--24/1.lua196
-rw-r--r--24/1_1.lua192
-rw-r--r--24/2.lua289
-rwxr-xr-x24/ext.lua62
-rw-r--r--24/input.txt27
-rw-r--r--24/sample.txt7
-rw-r--r--24/sample2.txt6
-rw-r--r--24/scratch7
8 files changed, 786 insertions, 0 deletions
diff --git a/24/1.lua b/24/1.lua
new file mode 100644
index 0000000..3d7dfa0
--- /dev/null
+++ b/24/1.lua
@@ -0,0 +1,196 @@
+require "ext"
+local directions = {
+ right = {0,1},
+ down = {1,0},
+ left = {0,-1},
+ up = {-1,0},
+ wait = {0,0},
+}
+local tiles = {
+ wall = {rep="#"},
+ lbliz = {rep="<",move=directions.left},
+ rbliz = {rep=">",move=directions.right},
+ ubliz = {rep="^",move=directions.up},
+ dbliz = {rep="v",move=directions.down},
+ empty = {rep="."}
+}
+local map = {}
+local row,col = 0,0
+local start,finish
+local function place(map,row,col,ent)
+ map[row] = map[row] or {}
+ map[row][col] = map[row][col] or {}
+ table.insert(map[row][col],ent)
+end
+for line in io.lines() do
+ map[#map+1] = {}
+ row = row + 1
+ col = 0
+ for char in line:gmatch("(.)") do
+ col = col + 1
+ if char == "." then
+ if row == 1 then
+ start = col
+ else
+ finish = col
+ end
+ end
+ for name,tile in pairs(tiles) do
+ if char == tile.rep and name ~= "empty" then
+ place(map,row,col,{r=row,c=col,t=tile})
+ break
+ end
+ end
+ end
+end
+local w,h = col,row
+local function print_puzzle(map)
+ for row = 1,h do
+ for col = 1,w do
+ if map[row] and map[row][col] then
+ local elist = map[row][col]
+ if #elist == 1 then
+ io.write(elist[1].t.rep)
+ else
+ io.write(#elist)
+ end
+ else
+ io.write(".")
+ end
+ end
+ io.write("\n")
+ end
+end
+
+local h,w = #map, #map[1]
+local min_walk = math.abs(finish - start) + h
+
+print_puzzle(map)
+
+local function step_map(map)
+ local newmap = {}
+ for row = 1,h do
+ for col = 1,w do
+ if map[row] and map[row][col] then
+ for _, ent in pairs(map[row][col]) do
+ if ent.t.move then
+ local dir = ent.t.move
+ ent.r = ent.r + dir[1]
+ ent.c = ent.c + dir[2]
+ --loop
+ if ent.r == 1 then
+ ent.r = h-1
+ elseif ent.r == h then
+ ent.r = 2
+ elseif ent.c == 1 then
+ ent.c = w-1
+ elseif ent.c == w then
+ ent.c = 2
+ end
+ place(newmap,ent.r,ent.c,ent)
+ else
+ place(newmap,ent.r,ent.c,ent)
+ end
+ end
+ end
+ end
+ end
+ return newmap
+end
+local function could_move(map,row,col)
+ local ret = (
+ row >= 1 and
+ col > 1 and
+ row <= h and
+ col < w and
+ map[row] and
+ map[row][col] == nil
+ )
+ --print(ret)
+ return ret
+end
+
+--map doesn't change, no matter what we do, we can store the blizzard simulation
+local map_mem, mapat = {}, {[0] = map}
+setmetatable(mapat,{__index=function(self,key)
+ if map_mem[key] then return map_mem[key] end
+ map_mem[key] = step_map(mapat[key-1])
+ return map_mem[key]
+end})
+
+local state = {
+ location = {1,start},
+ minute = 0,
+ speculative_goal = min_walk
+}
+local function copy_state(state)
+ return {
+ location = {state.location[1],state.location[2]},
+ minute = state.minute,
+ speculative_goal = state.speculative_goal
+ }
+end
+local spec_tbl = {
+ [directions.right] = -1,
+ [directions.down] = -1,
+ [directions.left] = 2,
+ [directions.up] = 2,
+ [directions.wait] = 0
+}
+local branches = {state}
+local min_path = 147
+local nbranches = 0
+while #branches > 0 do
+ --print(#branches)
+ local tbranch = table.remove(branches)
+ nbranches = nbranches + 1
+ if nbranches % 100000 == 0 then
+ print("Analyzed",nbranches,"branches",#branches,"active")
+ end
+ if tbranch.minute > min_path then
+ goto nextbranch
+ end
+ if tbranch.speculative_goal > min_path then
+ goto nextbranch
+ end
+ local nloc = tbranch.location
+ if nloc[1] == h and nloc[2] == finish then
+ print("Found a working branch:",tbranch)
+ print("Branches remaining:",#branches)
+ if tbranch.minute < min_path then
+ min_path = tbranch.minute
+ end
+ goto nextbranch
+ end
+ local ctime = tbranch.minute
+ local nmap = mapat[ctime + 1]
+ --print_puzzle(nmap)
+ for name, direction in pairs(directions) do
+ local nrow, ncol = nloc[1] + direction[1], nloc[2] + direction[2]
+ if could_move(nmap,nrow,ncol) then
+ if nrow == h then
+ assert(ncol == finish)
+ end
+ local sc = copy_state(tbranch)
+ sc.location = {nrow,ncol}
+ sc.speculative_goal = sc.speculative_goal + spec_tbl[direction]
+ if sc.speculative_goal < min_path and sc.minute < min_path then
+ sc.minute = sc.minute + 1
+ table.insert(branches,sc)
+ end
+ end
+ end
+ --[[
+ --It's actually faster if we don't do this, and all states fit into one memory page
+ --Once in a while, sort the branhces to prioritize promising paths
+ if nbranches % 100000 == 0 then
+ table.sort(branches,function(a,b)
+ return (a.minute + a.speculative_goal) > (b.minute + b.speculative_goal)
+ end)
+ end
+ ]]
+ ::nextbranch::
+end
+print(nbranches)
+print("Out of branches")
+print(min_path)
diff --git a/24/1_1.lua b/24/1_1.lua
new file mode 100644
index 0000000..586835a
--- /dev/null
+++ b/24/1_1.lua
@@ -0,0 +1,192 @@
+require "ext"
+local directions = {
+ right = {0,1},
+ down = {1,0},
+ left = {0,-1},
+ up = {-1,0},
+ wait = {0,0},
+}
+local tiles = {
+ wall = {rep="#"},
+ lbliz = {rep="<",move=directions.left},
+ rbliz = {rep=">",move=directions.right},
+ ubliz = {rep="^",move=directions.up},
+ dbliz = {rep="v",move=directions.down},
+ empty = {rep="."}
+}
+local map = {}
+local row,col = 0,0
+local start,finish
+local function place(map,row,col,ent)
+ map[row] = map[row] or {}
+ map[row][col] = map[row][col] or {}
+ table.insert(map[row][col],ent)
+end
+for line in io.lines() do
+ map[#map+1] = {}
+ row = row + 1
+ col = 0
+ for char in line:gmatch("(.)") do
+ col = col + 1
+ if char == "." then
+ if row == 1 then
+ start = col
+ else
+ finish = col
+ end
+ end
+ for name,tile in pairs(tiles) do
+ if char == tile.rep and name ~= "empty" then
+ place(map,row,col,{r=row,c=col,t=tile})
+ break
+ end
+ end
+ end
+end
+local w,h = col,row
+local function print_puzzle(map,flood)
+ for row = 1,h do
+ for col = 1,w do
+ if map[row] and map[row][col] then
+ local elist = map[row][col]
+ if #elist == 1 then
+ io.write(elist[1].t.rep .. " ")
+ else
+ io.write("* ")
+ end
+ else
+ if flood and flood[row] and flood[row][col] then
+ io.write(string.format("%-2d",flood[row][col]))
+ else
+ io.write(" ")
+ end
+ end
+ end
+ io.write("\n")
+ end
+end
+
+local h,w = #map, #map[1]
+local min_walk = math.abs(finish - start) + h
+
+print_puzzle(map)
+
+local function step_map(map)
+ local newmap = {}
+ for row = 1,h do
+ for col = 1,w do
+ if map[row] and map[row][col] then
+ for _, ent in pairs(map[row][col]) do
+ if ent.t.move then
+ local dir = ent.t.move
+ ent.r = ent.r + dir[1]
+ ent.c = ent.c + dir[2]
+ --loop
+ if ent.r == 1 then
+ ent.r = h-1
+ elseif ent.r == h then
+ ent.r = 2
+ elseif ent.c == 1 then
+ ent.c = w-1
+ elseif ent.c == w then
+ ent.c = 2
+ end
+ place(newmap,ent.r,ent.c,ent)
+ else
+ place(newmap,ent.r,ent.c,ent)
+ end
+ end
+ end
+ end
+ end
+ return newmap
+end
+local function could_move(map,row,col)
+ local ret = (
+ row >= 1 and
+ col > 1 and
+ row <= h and
+ col < w and
+ (map[row] == nil or
+ map[row][col] == nil or
+ map[row][col].t ~= tiles.wall)
+ )
+ --print(ret)
+ return ret
+end
+
+--map doesn't change, no matter what we do, we can store the blizzard simulation
+local map_mem, mapat = {}, {[0] = map}
+setmetatable(mapat,{__index=function(self,key)
+ if map_mem[key] then return map_mem[key] end
+ map_mem[key] = step_map(mapat[key-1])
+ return map_mem[key]
+end})
+
+local state = {
+ location = {1,start},
+ minute = 0,
+ speculative_goal = min_walk
+}
+local function copy_state(state)
+ return {
+ location = {state.location[1],state.location[2]},
+ minute = state.minute,
+ speculative_goal = state.speculative_goal
+ }
+end
+local spec_tbl = {
+ [directions.right] = -1,
+ [directions.down] = -1,
+ [directions.left] = 2,
+ [directions.up] = 2,
+ [directions.wait] = 0
+}
+local flood = {}
+for row = 1,h do
+ flood[row] = {}
+end
+flood[1][2] = 0
+local min = 0
+while flood[h][w-1] == nil do
+ min = min + 1
+ print("Min",min)
+ local nmap = mapat[min-1]
+ local toins = {}
+ --[[
+ print("Before flooding 1:")
+ print_puzzle(nmap,flood)
+ ]]
+ --flood outward
+ for rn,row in pairs(flood) do
+ for cn,col in pairs(row) do
+ if flood[rn][cn] then
+ for name, direction in pairs(directions) do
+ --print("Flooding",rn,cn,name)
+ local trow = rn + direction[1]
+ local tcol = cn + direction[2]
+ if could_move(nmap,trow,tcol) and flood[trow][tcol] == nil then
+ table.insert(toins,{trow,tcol})
+ end
+ end
+ end
+ end
+ end
+ for _,v in pairs(toins) do
+ flood[v[1]][v[2]] = min
+ end
+ --[[
+ print("After flooding 1:")
+ print_puzzle(nmap,flood)
+ ]]
+ nmap = mapat[min]
+ --clean up where blizzards pass
+ for rn,row in pairs(nmap) do
+ for cn, col in pairs(row) do
+ flood[rn][cn] = nil
+ end
+ end
+ print("After cleaning:")
+ print_puzzle(nmap,flood)
+end
+print(flood[h][w-1])
diff --git a/24/2.lua b/24/2.lua
new file mode 100644
index 0000000..6243cf0
--- /dev/null
+++ b/24/2.lua
@@ -0,0 +1,289 @@
+require "ext"
+local directions = {
+ right = {0,1},
+ down = {1,0},
+ left = {0,-1},
+ up = {-1,0},
+ wait = {0,0},
+}
+local tiles = {
+ wall = {rep="#"},
+ lbliz = {rep="<",move=directions.left},
+ rbliz = {rep=">",move=directions.right},
+ ubliz = {rep="^",move=directions.up},
+ dbliz = {rep="v",move=directions.down},
+ empty = {rep="."}
+}
+local map = {}
+local row,col = 0,0
+local start,finish
+local function place(map,row,col,ent)
+ map[row] = map[row] or {}
+ map[row][col] = map[row][col] or {}
+ table.insert(map[row][col],ent)
+end
+for line in io.lines() do
+ map[#map+1] = {}
+ row = row + 1
+ col = 0
+ for char in line:gmatch("(.)") do
+ col = col + 1
+ if char == "." then
+ if row == 1 then
+ start = col
+ else
+ finish = col
+ end
+ end
+ for name,tile in pairs(tiles) do
+ if char == tile.rep and name ~= "empty" then
+ place(map,row,col,{r=row,c=col,t=tile})
+ break
+ end
+ end
+ end
+end
+local w,h = col,row
+local function print_puzzle(map,flood)
+ for row = 1,h do
+ for col = 1,w do
+ if map[row] and map[row][col] then
+ local elist = map[row][col]
+ if #elist == 1 then
+ io.write(elist[1].t.rep .. " ")
+ else
+ io.write("* ")
+ end
+ else
+ if flood and flood[row] and flood[row][col] then
+ io.write(string.format("%-2d",flood[row][col]))
+ else
+ io.write(" ")
+ end
+ end
+ end
+ io.write("\n")
+ end
+end
+
+local h,w = #map, #map[1]
+local min_walk = math.abs(finish - start) + h
+
+print_puzzle(map)
+
+local function step_map(map)
+ local newmap = {}
+ for row = 1,h do
+ for col = 1,w do
+ if map[row] and map[row][col] then
+ for _, ent in pairs(map[row][col]) do
+ if ent.t.move then
+ local dir = ent.t.move
+ ent.r = ent.r + dir[1]
+ ent.c = ent.c + dir[2]
+ --loop
+ if ent.r == 1 then
+ ent.r = h-1
+ elseif ent.r == h then
+ ent.r = 2
+ elseif ent.c == 1 then
+ ent.c = w-1
+ elseif ent.c == w then
+ ent.c = 2
+ end
+ place(newmap,ent.r,ent.c,ent)
+ else
+ place(newmap,ent.r,ent.c,ent)
+ end
+ end
+ end
+ end
+ end
+ return newmap
+end
+local function could_move(map,row,col)
+ local ret = (
+ row >= 1 and
+ col > 1 and
+ row <= h and
+ col < w and
+ (map[row] == nil or
+ map[row][col] == nil or
+ map[row][col].t ~= tiles.wall)
+ )
+ --print(ret)
+ return ret
+end
+
+--map doesn't change, no matter what we do, we can store the blizzard simulation
+local map_mem, mapat = {}, {[0] = map}
+setmetatable(mapat,{__index=function(self,key)
+ if map_mem[key] then return map_mem[key] end
+ map_mem[key] = step_map(mapat[key-1])
+ return map_mem[key]
+end})
+
+local state = {
+ location = {1,start},
+ minute = 0,
+ speculative_goal = min_walk
+}
+local function copy_state(state)
+ return {
+ location = {state.location[1],state.location[2]},
+ minute = state.minute,
+ speculative_goal = state.speculative_goal
+ }
+end
+local spec_tbl = {
+ [directions.right] = -1,
+ [directions.down] = -1,
+ [directions.left] = 2,
+ [directions.up] = 2,
+ [directions.wait] = 0
+}
+
+local flood = {}
+for row = 1,h do
+ flood[row] = {}
+end
+flood[1][2] = 0
+local min = 0
+while flood[h][w-1] == nil do
+ min = min + 1
+ print("Min",min)
+ local nmap = mapat[min-1]
+ local toins = {}
+ --[[
+ print("Before flooding 1:")
+ print_puzzle(nmap,flood)
+ ]]
+ --flood outward
+ for rn,row in pairs(flood) do
+ for cn,col in pairs(row) do
+ if flood[rn][cn] then
+ for name, direction in pairs(directions) do
+ --print("Flooding",rn,cn,name)
+ local trow = rn + direction[1]
+ local tcol = cn + direction[2]
+ if could_move(nmap,trow,tcol) and flood[trow][tcol] == nil then
+ table.insert(toins,{trow,tcol})
+ end
+ end
+ end
+ end
+ end
+ for _,v in pairs(toins) do
+ flood[v[1]][v[2]] = min
+ end
+ --[[
+ print("After flooding 1:")
+ print_puzzle(nmap,flood)
+ ]]
+ nmap = mapat[min]
+ --clean up where blizzards pass
+ for rn,row in pairs(nmap) do
+ for cn, col in pairs(row) do
+ flood[rn][cn] = nil
+ end
+ end
+ --[[
+ print("After cleaning:")
+ print_puzzle(nmap,flood)
+ ]]
+end
+print("First goal reached after:",min)
+flood = {}
+for row = 1,h do
+ flood[row] = {}
+end
+flood[h][w-1] = min
+while flood[1][2] == nil do
+ min = min + 1
+ print("Min",min)
+ local nmap = mapat[min-1]
+ local toins = {}
+ --[[
+ print("Before flooding 1:")
+ print_puzzle(nmap,flood)
+ ]]
+ --flood outward
+ for rn,row in pairs(flood) do
+ for cn,col in pairs(row) do
+ if flood[rn][cn] then
+ for name, direction in pairs(directions) do
+ --print("Flooding",rn,cn,name)
+ local trow = rn + direction[1]
+ local tcol = cn + direction[2]
+ if could_move(nmap,trow,tcol) and flood[trow][tcol] == nil then
+ table.insert(toins,{trow,tcol})
+ end
+ end
+ end
+ end
+ end
+ for _,v in pairs(toins) do
+ flood[v[1]][v[2]] = min
+ end
+ --[[
+ print("After flooding 1:")
+ print_puzzle(nmap,flood)
+ ]]
+ nmap = mapat[min]
+ --clean up where blizzards pass
+ for rn,row in pairs(nmap) do
+ for cn, col in pairs(row) do
+ flood[rn][cn] = nil
+ end
+ end
+ print("After cleaning:")
+ print_puzzle(nmap,flood)
+end
+print("back to start reached after:",min)
+flood = {}
+for row = 1,h do
+ flood[row] = {}
+end
+flood[1][2] = min
+while flood[h][w-1] == nil do
+ min = min + 1
+ print("Min",min)
+ local nmap = mapat[min-1]
+ local toins = {}
+ --[[
+ print("Before flooding 1:")
+ print_puzzle(nmap,flood)
+ ]]
+ --flood outward
+ for rn,row in pairs(flood) do
+ for cn,col in pairs(row) do
+ if flood[rn][cn] then
+ for name, direction in pairs(directions) do
+ --print("Flooding",rn,cn,name)
+ local trow = rn + direction[1]
+ local tcol = cn + direction[2]
+ if could_move(nmap,trow,tcol) and flood[trow][tcol] == nil then
+ table.insert(toins,{trow,tcol})
+ end
+ end
+ end
+ end
+ end
+ for _,v in pairs(toins) do
+ flood[v[1]][v[2]] = min
+ end
+ --[[
+ print("After flooding 1:")
+ print_puzzle(nmap,flood)
+ ]]
+ nmap = mapat[min]
+ --clean up where blizzards pass
+ for rn,row in pairs(nmap) do
+ for cn, col in pairs(row) do
+ flood[rn][cn] = nil
+ end
+ end
+ print("After cleaning:")
+ print_puzzle(nmap,flood)
+end
+print("Back to goal reached after",min)
diff --git a/24/ext.lua b/24/ext.lua
new file mode 100755
index 0000000..c1eb1cc
--- /dev/null
+++ b/24/ext.lua
@@ -0,0 +1,62 @@
+-- Override tostring to display more info about the table
+local old_tostring = tostring
+local numtabs = 0
+local printed_tables = {}
+
+local function tostring_helper(el)
+ assert(type(el) == "table", "Tried to call helper with something that was not a table, it was a " .. type(el))
+ local mt = getmetatable(el)
+ if mt and mt.__tostring then
+ return mt.__tostring(el)
+ elseif printed_tables[el] == true then
+ return old_tostring(el)
+ else
+ printed_tables[el] = true
+ numtabs = numtabs + 1
+ local strbuilder = setmetatable({"{"},{__index = table})
+ for k,v in pairs(el) do
+ local key,value
+ if type(k) == "table" then
+ key = tostring_helper(k)
+ else
+ key = old_tostring(k)
+ end
+ if type(v) == "table" then
+ value = tostring_helper(v)
+ else
+ value = old_tostring(v)
+ end
+ strbuilder:insert(string.format("%s%s : %s", string.rep("\t",numtabs), key, value))
+ end
+ strbuilder:insert(string.rep("\t",numtabs - 1) .. "}")
+ numtabs = numtabs - 1
+ return strbuilder:concat("\n")
+ end
+
+end
+function tostring(el)
+ printed_tables = {}
+ if type(el) == "table" then
+ return tostring_helper(el)
+ end
+ return old_tostring(el)
+end
+
+-- Functions to save my hands
+function printf(fmt, ...)
+ print(string.format(fmt,...))
+end
+function errorf(fmt, ...)
+ --Our error isn't actually in this function, it's 1 above us (1) = 2
+ error(string.format(fmt,...),2)
+end
+function assertf(bool, fmt, ...)
+ assert(type(fmt) == "string", "Assertf arg #2 was \"" .. type(fmt) .. "\", expected string")
+ if not bool then
+ args = {fmt}
+ for k,v in ipairs({...}) do
+ table.insert(args,tostring(v))
+ end
+ error(string.format(unpack(args)),2)
+ end
+end
diff --git a/24/input.txt b/24/input.txt
new file mode 100644
index 0000000..285925a
--- /dev/null
+++ b/24/input.txt
@@ -0,0 +1,27 @@
+#.########################################################################################################################
+#<>v^<vv<v<><.^v><<.>v><<^^<v..<>.v^>^v<^><vv>.>^v>^>>v^v>><^><><<^><^>.^>^<vv>v>^>>^<^v.^<^v<.><.v><<.^v^.^<v^v<<vv><<v>#
+#<>.vv^^vv<^v.^><^v<>>.<<<.^<^<>v<v<<>v>^vv..<<.^>^>>>.<>>^.^<^<<<^<>vv>.<<><^>.^>.vv^^v..v^<v^<vv.^.>v<^<^.<>^^v>^>v^<^>#
+#<<>v>v>v.^<><^><>^>.v<<>.^v.v<<v<^^v>v<^^<<>^>vv<vvv>^v^.<>>^^^^<><vvv<v^>^<vv.><..><^>^<<^><^.v>>^>>>v>v>>vv^v<<.<v^<^>#
+#<^.vv><^>^^>v<.>^vvv>v^v>><v>><v<><^.>v^v<v<<<>v.^>>^>v>^.>><<<v.^^^v^.v>^^vv^<<.v^<^v.v<<>><><vv.vv<^..<vv^.^<>>>.<<^^>#
+#<^<v<.>vvvvvv^>>.^v>.<<v^^^><^<^^<^>v><>>><>>vv^v><^>v>>v.^>>>v^v^^^.^^>^v<.v^>^^v<vv.<.<^v>vv^v>^v>>v<^^>>^^<><^>v<v>.>#
+#<vvvv<<^<v^vvv<.^<vv>.>v<v><<^v>^^<^v<^>vv^.<^vvv<^>^^vv^.v.^^..><<>^.>...^<<^>^^<^vvv>.^<>>^.v^v.^<>v^<vvvv>^.<>v>><v<>#
+#>.^>.vv^<^^<v><><<>>>vv>.<><^^<>>v><<>>v^vv.>^.>v^^<^<^.>>v<<<vv<<>>.<^<><.<<^v^><>.<>^<<.^>^v<>^^>><<>vv.v<>v^^>>>>>v><#
+#<.^<>.>.^>v><.^<>>>^^<^v^>^<^.>^^^v.<>vv><>v^v^v^^<^>^<<.v^>^v.<>.<^.>v><>>.<<v<^>>v>>>.^<><v<vv.<^^>^v<<<^^.<v<^>v>v^<<#
+#><^>.<<<^..>>>^>.>^<vv.<.v^><>>>^<.v<^^^><>v.>.v^v<^^v><^.^.><^^<>^^v<>><^<.v>><>>>>.>^v<^v^<<^v>>.v<^<>vv<>v>vv.^<<<v.<#
+#>.^^<<><v.>^^>>.^>^v>^>..>>>^v^<v^^^>v>>v>>v.^<>vv><>.<>vvvvv.vvv..><>v.vv<^v^v>^>^vv^<v<.^>>.>v^><<v>^v^>>.>^v><<^^^<v<#
+#.^<>>.v<>vv<vvv^^>vv><<.^>><^.v>>^v<.v^<^v^.<vvv^^>^^>^>>>.^.>vvv>>^^^^<^^v>^>v^<v><v^<><><<^v<<<><><<v^.vv><^vv^v<<vv<>#
+#<..vvv>><.v<^><v.v^^^v>^^v<<^v<>^><^v>^vvv^<^v>>^>>^<v>^<<^v^v.^vv>^><^v<^v^v>vv>^<^^^v>^^^<.>^v.v^<^>>^>^^^^^^vv<.v^^><#
+#<v><<v>>^v<>>v^^v<^v<<><<^><^<<^^>.<.^v>^>>v<>^><.^<<<.>^^v^vvv><<^>>^^<^^<><v.^v<<^..v^.<v>>^v.>^<^^v<<vvv^v<^<<<<<v>^>#
+#<v<<v^<^<^^v^vv<<<.>v<<><<^v^.^>><.v<^<v<^<<<<.<v^^vvv^>^^^>.><<><><<<^>v.>>>vv<.<v.><>^v^>^^^^.>v^<>.v>^.<v>v<^.v>vv<>>#
+#>v<v..<^v^<<<>><>>^<^v>>v>v^>vv^v<^.vv^vv>v<<<vv.v^.>^v><<v<vv>>^<.>vvv...v<^><>>.^v>v<^^>v^v<<>v>.^>><<<><<v^<v^.^v^>v>#
+#<<>^v>v>v<<<vvvv^^>v^..>..>^<>^>>><><<><v><^vv.>^v>v^v<<<vv><<.>><<<<<.<<<>^><v><vv^^v>^>>>><.^.^^>v<^<^>>.^>>^v.<>^>v>>#
+#>v<<<>^^>..^>>v.v^<><^>^^<<vv<<>>^<<>v^.<^><.v>>vv^v<v<^^v..>>^^^>^v>^v^^v^>^.<^>>>v^<><v>^.<>>^^<vv>>>^><^>>.<<>>v>vv<>#
+#<^^^..>>><>v>v>.>^.<.vv.vv<>v^vv<<^^>>v<^^<vv><^>><v>>>^^^><^^<.<^^>v>^^><v<vv^>vvv<^<..^>>>^v^v^^v>^^^.<.>v<^v><><>^^.>#
+#>^^>^^^^.<<.v^.><v<^^.<<<.><<><>.<<<^^><.>>..<>v^^^>^v<^>>.>v><^^vvv^>>^<v>v><^<>^<>>^^v>^.v>v<<>>>^v^vv<<<^..<>^><>>^>.#
+#.^<<<^^vv<.>v<.<>.>v.^..^^.>.vv.v>^>v.^.v<>v>>^^<^<^^^>>^>vvv.<^>><^>><^^>^>..^><.<v>.<^>^^v^v.>v<v^^><<>>vv^vv><v<>v^<>#
+#<vvv><<>v<^<^.>..^<.<v^v^v>.^<^vv^v>v<vvv<>><>vv.vvv<^><^^>><>vv>^^^><>^<^^><^>.>v<>^.vv<v<<v^^>v<v.^^^><>^<v^<>.>.^^v^>#
+#>v.^>..^>v>v>^^<^v>..>..^<.^..>>v<>.<>.<v>>>vv^<<>>>v<v^v^^vv<^vvvv<^v<^v<^^>>v>v><v><<v><v><^v<v>^>^^<>v.v<<^<.<><<<>.>#
+#>>v><..^^^.<v<^^<v<><<<<<^^^<>^^v^.>vv<v><<>>^^^<v^vv^v^<>...>^.<v><<^^v^..><>vv>v^>^v>.vv<v<^.<<<v^<^v^<<v^.<.^<.>v^<><#
+#>^v<..<>>v^v>v>v^v^<^<^^<..><v>^^>><<><>.^v.v<^>^<v><.^vv^.>v^>^v>^^v^>v<v^...><<<>><>>>^<vv<<^^^.<^vv^^^.v<vv<<<>>^><^<#
+#>><<v<>>>vv>>^^^v^^^>^>><v<>><^vv><>>>^.v^>^>^vv<>>..^v.^^^vvvv<><^><^v>vv<<^vv>^>v..<><.>^^^v.v>vvvv>>>><<vv<>.<v^<vv>>#
+########################################################################################################################.#
diff --git a/24/sample.txt b/24/sample.txt
new file mode 100644
index 0000000..158014b
--- /dev/null
+++ b/24/sample.txt
@@ -0,0 +1,7 @@
+#.#####
+#.....#
+#>....#
+#.....#
+#...v.#
+#.....#
+#####.#
diff --git a/24/sample2.txt b/24/sample2.txt
new file mode 100644
index 0000000..685dc4f
--- /dev/null
+++ b/24/sample2.txt
@@ -0,0 +1,6 @@
+#.######
+#>>.<^<#
+#.<..<<#
+#>v.><>#
+#<^v^^>#
+######.#
diff --git a/24/scratch b/24/scratch
new file mode 100644
index 0000000..a2f43da
--- /dev/null
+++ b/24/scratch
@@ -0,0 +1,7 @@
+146 - too low
+322
+330
+345
+346 - too high
+358
+381