diff --git a/day23/part1.lua b/day23/part1.lua new file mode 100755 index 0000000..b64b055 --- /dev/null +++ b/day23/part1.lua @@ -0,0 +1,153 @@ +#!/usr/bin/env luajit +require("utils") + +local targets = { A=3, B=5, C=7, D=9 } +local costs = { A=1, B=10, C=100, D=1000 } + +local f = io.open(arg[3]) +local line = f:read("*l") -- ##### +line = f:read("*l") -- #...# +local hall = {} +local length = #line - 2 +line = f:read("*l") -- ##A#B## +local rooms = {} +local depth = 1 +local ncorrect = 0 +while line do + local l = 0 + for char in chars(line) do + if char ~= "#" and char ~= " " then + if rooms[l] == nil then rooms[l] = {} end + table.insert(rooms[l], char) + if targets[char] == l then ncorrect = ncorrect + 1 end + end + l = l + 1 + end + line = f:read("*l") -- ##A#B## + depth = depth + 1 +end +depth = depth - 2 + +function printit() + for l = 1, length do io.write(hall[l] or ".") end + io.write("\n") + for d = 1, depth do + for l = 1, length do + if rooms[l] then + io.write(rooms[l][d] or ".") + else + io.write(" ") + end + end + io.write("\n") + end +end + +printit() + +function addtoroom(rooms, room, char) + local room = rooms[room] + for d = depth, 1, -1 do + if room[d] == nil then + room[d] = char + return + end + end +end + +function removefromroom(rooms, i) + local room = rooms[i] + if room[1] == nil then + if room[2] == nil then + return + else + local c = room[2] + room[2] = nil + return c, 2, targets[c] == i + end + else + local c = room[1] + room[1] = nil + return c, 1, targets[c] == i and targets[room[2]] == i + end +end + +function pathtoroom(l, c, hall, rooms) + local t = targets[c] + if rooms[t][1] ~= nil then return end + for p = l + sign(t - l), t, sign(t - l) do + if hall[p] ~= nil then return end + end + local room = rooms[t] + if room[2] == nil and room[1] == nil then + return t, math.abs(t - l) + 2 + elseif room[2] == c and room[1] == nil then + return t, math.abs(t - l) + 1 + end +end + +local nhall = 0 +local minscore = math.huge +-- global: length, depth, hall, rooms, ncorrect, nhall, minscore +-- returns: score +function backtrack(step, score) + -- print(step) + -- printit() + if step > 16 then + elseif ncorrect == 8 and score < minscore then + minscore = score + print(minscore) + elseif score > minscore then + else + -- move into rooms + for l = 1, length do + if hall[l] ~= nil then + local c = hall[l] + local room, distance = pathtoroom(l, c, hall, rooms) + if room then + hall[l] = nil + addtoroom(rooms, room, c) + ncorrect = ncorrect + 1 + backtrack(step + 1, score + distance * costs[c]) + ncorrect = ncorrect - 1 + removefromroom(rooms, room) + hall[l] = c + end + end + end + -- remove from rooms + for l, room in pairs(rooms) do + local c, distance, complete = removefromroom(rooms, l) + if c ~= nil then + if not complete then + if targets[c] == l then ncorrect = ncorrect - 1 end + -- left + local t = l - 1 + while t >= 1 and hall[t] == nil do + if rooms[t] == nil then + hall[t] = c + backtrack(step + 1, score + (distance + l - t) * costs[c]) + hall[t] = nil + end + t = t - 1 + end + -- right + local t = l + 1 + while t <= length and hall[t] == nil do + if rooms[t] == nil then + hall[t] = c + backtrack(step + 1, score + (distance + t - l) * costs[c]) + hall[t] = nil + end + t = t + 1 + end + if targets[c] == l then ncorrect = ncorrect + 1 end + end + addtoroom(rooms, l, c) + end + end + end +end + +backtrack(0, 0) +print(minscore) diff --git a/day23/part2.lua b/day23/part2.lua new file mode 100755 index 0000000..cef4253 --- /dev/null +++ b/day23/part2.lua @@ -0,0 +1,157 @@ +#!/usr/bin/env luajit +require("utils") + +local targets = { A=3, B=5, C=7, D=9 } +local costs = { A=1, B=10, C=100, D=1000 } + +local f = io.open(arg[3]) +local line = f:read("*l") -- ##### +line = f:read("*l") -- #...# +local hall = {} +local length = #line - 2 +line = f:read("*l") -- ##A#B## +local rooms = {} +local depth = 1 +local ncorrect = 0 +while line do + local l = 0 + for char in chars(line) do + if char ~= "#" and char ~= " " then + if rooms[l] == nil then rooms[l] = {} end + table.insert(rooms[l], char) + if targets[char] == l then ncorrect = ncorrect + 1 end + end + l = l + 1 + end + line = f:read("*l") -- ##A#B## + depth = depth + 1 +end + +table.insert(rooms[3], 2, "D") +table.insert(rooms[3], 2, "D") +table.insert(rooms[5], 2, "B"); ncorrect = ncorrect + 1 -- this line +table.insert(rooms[5], 2, "C") +table.insert(rooms[7], 2, "A") +table.insert(rooms[7], 2, "B") +table.insert(rooms[9], 2, "C") +table.insert(rooms[9], 2, "A") + +function printit() + for l = 1, length do io.write(hall[l] or ".") end + io.write("\n") + for d = 1, depth do + for l = 1, length do + if rooms[l] then + io.write(rooms[l][d] or ".") + else + io.write(" ") + end + end + io.write("\n") + end +end + +printit() + +function addtoroom(rooms, room, char) + local room = rooms[room] + for d = depth, 1, -1 do + if room[d] == nil then + room[d] = char + return + end + end +end + +function removefromroom(rooms, i) + local room = rooms[i] + local c, dst, complete = nil, nil, true + for d = depth, 1, -1 do + if room[d] ~= nil then + c, dst = room[d], d + if targets[c] ~= i then complete = false end + end + end + if dst then room[dst] = nil end + return c, dst, complete +end + +function pathtoroom(l, c, hall, rooms) + local t = targets[c] + if rooms[t][1] ~= nil then return end + for p = l + sign(t - l), t, sign(t - l) do + if hall[p] ~= nil then return end + end + local room = rooms[t] + for d = depth, 1, -1 do + if room[d] == nil then + return t, math.abs(t - l) + d + elseif room[d] ~= c then + return + end + end +end + +local nhall = 0 +local minscore = math.huge +-- global: length, depth, hall, rooms, ncorrect, nhall, minscore +-- returns: score +function backtrack(step, score) + if step > 32 then + elseif ncorrect == 16 and score < minscore then + minscore = score + print(minscore) + elseif score > minscore then + else + -- move into rooms + for l = 1, length do + if hall[l] ~= nil then + local c = hall[l] + local room, distance = pathtoroom(l, c, hall, rooms) + if room then + hall[l] = nil + addtoroom(rooms, room, c) + ncorrect = ncorrect + 1 + backtrack(step + 1, score + distance * costs[c]) + ncorrect = ncorrect - 1 + removefromroom(rooms, room) + hall[l] = c + end + end + end + -- remove from rooms + for l, room in pairs(rooms) do + local c, distance, complete = removefromroom(rooms, l) + if c ~= nil then + if not complete then + if targets[c] == l then ncorrect = ncorrect - 1 end + -- left + local t = l - 1 + while t >= 1 and hall[t] == nil do + if rooms[t] == nil then + hall[t] = c + backtrack(step + 1, score + (distance + l - t) * costs[c]) + hall[t] = nil + end + t = t - 1 + end + -- right + local t = l + 1 + while t <= length and hall[t] == nil do + if rooms[t] == nil then + hall[t] = c + backtrack(step + 1, score + (distance + t - l) * costs[c]) + hall[t] = nil + end + t = t + 1 + end + if targets[c] == l then ncorrect = ncorrect + 1 end + end + addtoroom(rooms, l, c) + end + end + end +end + +backtrack(0, 0) +print(minscore)