width = nil map = {} for line in io.lines(arg[1]) do if not width then width = #line end for char in line:gmatch(".") do table.insert(map, char) end end function reachable_keys(map, pos_y, pos_x, keys, dist) local cell = map[width * pos_y + pos_x] if cell == "@" then cell = "." end if cell == "." then map[width * pos_y + pos_x] = "#" reachable_keys(map, pos_y + 1, pos_x, keys, dist + 1) reachable_keys(map, pos_y - 1, pos_x, keys, dist + 1) reachable_keys(map, pos_y, pos_x + 1, keys, dist + 1) reachable_keys(map, pos_y, pos_x - 1, keys, dist + 1) elseif string.byte(cell) >= 97 and string.byte(cell) <= 122 then table.insert(keys, {key=cell, y=pos_y, x=pos_x, dist=dist}) end return keys end function find_paths(map, pos_y, pos_x) local keys = reachable_keys({table.unpack(map)}, pos_y, pos_x, {}, 0) local paths = {} for _, key in ipairs(keys) do --print(key.key, key.dist) local open_map = {table.unpack(map)} open_map[width * pos_y + pos_x] = "." open_map[width * key.y + key.x] = "@" for i, x in ipairs(open_map) do if x:lower() == key.key then open_map[i] = "." break end end local path = find_paths(open_map, key.y, key.x) if #path == 0 then table.insert(paths, {path={key.key}, dist=key.dist}) else for _, p in ipairs(path) do table.insert(p.path, key.key) p.dist = p.dist + key.dist table.insert(paths, p) end end end return paths end pos_y = nil pos_x = nil for i, x in ipairs(map) do if x == "@" then pos_y = i // width pos_x = i % width break end end paths = find_paths(map, pos_y, pos_x) smallest = nil for _, path in ipairs(paths) do if not smallest or smallest.dist > path.dist then smallest = path end end print(table.concat(smallest.path, ","), smallest.dist)