Droga skoczka szachowego

https://en.wikipedia.org/wiki/Knight%27s_tour

WPROWADZENIE

Problem polega na obejściu całej szachownicy w taki sposób, że każde pole będzie odwiedzone dokładnie raz. Możliwe są różne punkty startowe na szachownicy. Dla skoczka istnieje potencjalnie osiem kierunków ruchu do następnej pozycji.

Warto zauważyć, że problem drogi skoczka szachowego jest specjalnym przypadkiem NP-trudnego problemu znajdowania ścieżki lub cyklu Hamiltona w grafie nieskierowanym. Okazuje się, problem znalezienia drogi skoczka szachowego jest taką instancją ogólnego problemu, która ma rozwiązanie wielomianowe. Istnieją algorytmy działające w czasie liniowym względem liczby pól szachownicy O(n^2) [n to bok planszy kwadratowej n × n], które znajdują rozwiązanie problemu.

Otwarta ścieżka Hamiltona istnieje dla każdego n większego lub równego 5. Zamknięta ścieżka Hamiltona (cykl) istnieje dla każdego n parzystego większego lub równego 6. Algorytmy znajdujące rozwiązanie zwykle wykorzystują metodę dziel i zwyciężaj, gdzie rozwiązania dla małych planszy są zawczasu przygotowane.

Problem znajdowania zamkniętej ścieżki skoczka może być rozwiązany przy pomocy sztucznej sieci neuronowej (Takefuji, Lee, 1992).


# Problem drogi skoczka na kwadratowej szachownicy o boku N.
# Współrzędne planszy x i y mają zakres od 0 do N-1.

def rysuj():
    for i in range(N):
        print(" ".join("{0:2d}".format(plansza[i,j]) for j in range(N)))

def dopuszczalny(x, y):
    return 0 <= x < N and 0 <= y < N and plansza[x, y] == 0

def zapisz(krok, x, y):
    plansza[x, y] = krok   # zapis ruchu

def wymaz(x, y):
    plansza[x, y] = 0   # pole nieodwiedzone

def probuj(krok, x, y):
    # krok - nr kolejnego kroku do zrobienia
    # x, y - pozycja startowa skoczka
    # Funkcja zwraca bool (czy udany ruch).
    udany = False
    kandydat = 0          # numery od 0 do RUCHY_SKOCZKA-1
    while (not udany) and (kandydat < RUCHY_SKOCZKA):
        u = x + delta_x[kandydat]         # wybieramy kandydata 
        v = y + delta_y[kandydat]
        if dopuszczalny(u, v):
            zapisz(krok, u, v)
            if krok < N * N:         # warunek końca rekurencji
                udany = probuj(krok+1, u, v)
                if not udany:
                    wymaz(u, v)
            else:
                udany = True
        kandydat += 1
    return udany

N = 5  # bok szachownicy
RUCHY_SKOCZKA = 8

# Inicjalizacja pustej planszy.
plansza = {}
for i in range(N):
    for j in range(N):
        plansza[i, j] = 0

# . 2 . 1 .   kolejne możliwe ruchy skoczka
# 3 . . . 0
# . . x . .
# 4 . . . 7
# . 5 . 6 .

delta_x = [2,1,-1,-2,-2,-1,1,2]         # różnica współrzędnej x
delta_y = [1,2,2,1,-1,-2,-2,-1]         # różnica współrzędnej y
(x0, y0) = (2, 2)                       # pole startowe skoczka

zapisz(1, x0, y0)

if probuj(2, x0, y0):
    print("Mamy rozwiązanie")
    rysuj()
else:
    print("Nie istnieje rozwiązanie")

Przykładowe rozwiązania problemu dla planszy 5 × 5.


 1  6 15 10 21  Start (0, 0).
14  9 20  5 16
19  2  7 22 11
 8 13 24 17  4
25 18  3 12 23

Start (0, 1) - brak rozwiązań.

25 14  1  8 19  Start (0, 2).
 4  9 18 13  2
15 24  3 20  7
10  5 22 17 12
23 16 11  6 21

23 10 15  2 25  Start (1, 1).
16  1 24  9 14
11 22  5 18  3
 6 17 20 13  8
21 12  7  4 19

Start (1, 2) - brak rozwiązań.

23 10 15  4 25  Start (2, 2).
16  5 24  9 14
11 22  1 18  3
 6 17 20 13  8
21 12  7  2 19