You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
96 lines
2.1 KiB
Python
96 lines
2.1 KiB
Python
#!/usr/bin/python
|
|
# vim:set fileencoding=utf-8:
|
|
|
|
"""
|
|
A backtracking Knight's Tour solver.
|
|
"""
|
|
|
|
from __future__ import division, print_function
|
|
import numpy as np
|
|
import random
|
|
import sys
|
|
|
|
|
|
visual = True
|
|
|
|
|
|
def clear_board(n):
|
|
ANSI_CPL = "\033[%dF" # CPL = Cursor Previous Line
|
|
ANSI_EL = "\033[2K" # EL = Erase Line
|
|
print(n*(ANSI_CPL % 1 + ANSI_EL), end='')
|
|
|
|
|
|
def print_board(board, clear=False):
|
|
if clear:
|
|
n = board.shape[0]
|
|
clear_board(n)
|
|
|
|
print(board)
|
|
|
|
|
|
def next_moves(position, n):
|
|
offsets = [(r, c) for r in range(-2, 2+1) for c in range(-2, 2+1)
|
|
if abs(r*c) == 2]
|
|
|
|
moves = [tuple(np.add(position, offset)) for offset in offsets]
|
|
moves = [move for move in moves
|
|
if all(coord in range(0, n) for coord in move)]
|
|
|
|
return moves
|
|
|
|
|
|
def possible_next_moves(position, n, board):
|
|
return [move for move in next_moves(position, n) if board[move] == 0]
|
|
|
|
|
|
def ordered_possible_next_moves(position, n, board):
|
|
# Warnsdorf's Rule
|
|
moves = sorted([(len(possible_next_moves(move, n, board)), move)
|
|
for move in possible_next_moves(position, n, board)])
|
|
return [move[1] for move in moves]
|
|
|
|
|
|
def set_next_knight(board, position, k):
|
|
n = board.shape[0]
|
|
|
|
assert board[position] == 0
|
|
board[position] = k
|
|
|
|
if visual and random.random() < 0.001:
|
|
print_board(board, clear=True)
|
|
|
|
if 0 not in board:
|
|
# Solved
|
|
return board
|
|
|
|
for move in ordered_possible_next_moves(position, n, board):
|
|
|
|
ret = set_next_knight(board, move, k+1)
|
|
if ret is not None:
|
|
board = ret
|
|
return board
|
|
|
|
# dead end ⇒ backtrack
|
|
board[position] = 0
|
|
return None
|
|
|
|
|
|
def main():
|
|
np.set_printoptions(linewidth=999) # if n>19 or so
|
|
|
|
print(__doc__)
|
|
|
|
for n in range(1, 31+1):
|
|
print('\nn = {}\n'.format(n))
|
|
board = np.zeros((n, n), dtype=np.int)
|
|
print_board(board)
|
|
board = set_next_knight(board, (0, 0), 1)
|
|
if board is not None:
|
|
print_board(board, clear=True)
|
|
else:
|
|
clear_board(n)
|
|
print('No solution')
|
|
|
|
|
|
main()
|