add knight's tour

master
neingeist 10 years ago
parent 7ee2c4ad8f
commit 26f5113e68

@ -0,0 +1,95 @@
#!/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()
Loading…
Cancel
Save