85 lines
2.1 KiB
Lua
85 lines
2.1 KiB
Lua
-- Calculates shortest path of Knight from (1, 1) to (x, y).
|
|
-- Prints matrix of shortest paths for all cells.
|
|
-- Knight can't leave the rectangle from (1, 1) to (x, y).
|
|
|
|
-- See https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
|
|
-- See http://stackoverflow.com/questions/2339101
|
|
|
|
-- Usage:
|
|
-- $ lua knight_dijkstra.lua X Y
|
|
|
|
local binaryheap = require 'binaryheap'
|
|
|
|
local ROWS = tonumber(arg[2]) or 8
|
|
local COLS = tonumber(arg[1]) or 8
|
|
|
|
local unvisited = binaryheap.minUnique()
|
|
|
|
local function Cell(x, y)
|
|
return x .. '_' .. y
|
|
end
|
|
|
|
local function Coordinates(cell)
|
|
local x, y = cell:match('(%d+)_(%d+)')
|
|
return x, y
|
|
end
|
|
|
|
local function neighbours(cell)
|
|
local x, y = Coordinates(cell)
|
|
local gen = coroutine.wrap(function()
|
|
coroutine.yield(x - 1, y - 2)
|
|
coroutine.yield(x - 1, y + 2)
|
|
coroutine.yield(x + 1, y - 2)
|
|
coroutine.yield(x + 1, y + 2)
|
|
coroutine.yield(x - 2, y - 1)
|
|
coroutine.yield(x - 2, y + 1)
|
|
coroutine.yield(x + 2, y - 1)
|
|
coroutine.yield(x + 2, y + 1)
|
|
end)
|
|
return coroutine.wrap(function()
|
|
for xx, yy in gen do
|
|
if 1 <= xx and xx <= COLS and
|
|
1 <= yy and yy <= ROWS then
|
|
coroutine.yield(Cell(xx, yy))
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
for y = 1, ROWS do
|
|
for x = 1, COLS do
|
|
local cell = Cell(x, y)
|
|
unvisited:insert(math.huge, cell)
|
|
end
|
|
end
|
|
unvisited:update('1_1', 0)
|
|
|
|
local final_distance = {}
|
|
|
|
while unvisited:peek() do
|
|
local current_distance, current = unvisited:peek()
|
|
assert(not final_distance[current])
|
|
final_distance[current] = current_distance
|
|
unvisited:remove(current)
|
|
-- update neighbours
|
|
local new_distance = current_distance + 1
|
|
for neighbour in neighbours(current) do
|
|
if unvisited.reverse[neighbour] then
|
|
local pos = unvisited.reverse[neighbour]
|
|
local distance = unvisited.value[pos]
|
|
if distance > new_distance then
|
|
unvisited:update(neighbour, new_distance)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
for y = 1, ROWS do
|
|
local row = {}
|
|
for x = 1, COLS do
|
|
local cell = Cell(x, y)
|
|
local distance = final_distance[cell]
|
|
table.insert(row, distance)
|
|
end
|
|
print(table.concat(row, ' '))
|
|
end
|