Current section

Files

Jump to
inplace lib examples knight_tour_functional.ex
Raw

lib/examples/knight_tour_functional.ex

defmodule KnightTour.Functional do
@board_size 7
@all_moves [
{-2, -1}, {-1, -2}, {1, -2}, {2, -1},
{2, 1}, {1, 2}, {-1, 2}, {-2, 1}
]
def solve(start \\ {0, 0}) do
board = MapSet.new()
tour = [start]
case backtrack(tour, board, 1) do
{:ok, path} -> Enum.reverse(path)
:no_solution -> :no_solution
end
end
defp backtrack([{x, y} | _rest] = tour, board, step) when step == @board_size * @board_size do
if MapSet.member?(board, {x, y}) do
{:ok, tour}
else
{:ok, [{x, y} | tour]}
end
end
defp backtrack([{x, y} | _rest] = tour, board, step) do
next_moves = valid_moves({x, y}, board)
Enum.find_value(next_moves, :no_solution, fn next ->
new_board = MapSet.put(board, {x, y})
new_tour = [next | tour]
case backtrack(new_tour, new_board, step + 1) do
{:ok, path} -> {:ok, path}
:no_solution -> nil
end
end)
end
defp valid_moves({x, y}, board) do
for {dx, dy} <- @all_moves do
{x + dx, y + dy}
end
|> Enum.filter(&(in_bounds(&1) and not MapSet.member?(board, &1)))
end
defp in_bounds({x, y}) do
x in 0..(@board_size - 1) and y in 0..(@board_size - 1)
end
end