馬踏棋盤
馬踏棋盤(騎士周遊)是4399的一個小遊戲,遊戲規則很簡單:
玩過象棋嗎?這款和國內外的象棋都不一樣,裏面只有一隻馬在跳,但是跳過的格子都會出現數字來圍困你,所以記得不要被這些數字圍住,否則遊戲就結束了。來挑戰一下新玩法吧。
雖然遊戲規則很簡單,但是如果想要通關的話,卻是很有難度的,下面我們通過算法來尋找通關方法。
回溯算法
回溯算法的思想其實很簡單:我們在尋找解決方案,當遇到有多種選擇方案時,我們先選擇一個方案進行嘗試,如果走不通,則返回選擇另外的方案進行嘗試。
對應到我們的遊戲中,則:如下圖,我們所處現在的位置有8種格子可走,我們可以先選擇編號爲0的格子嘗試走下去,如果無法通關,我們可以重新返回到當前位置,嘗試走編號爲1的格式,仍然不行再返回選擇2…
但是,當我們走了格子0後,我們又會有多種選擇,這其實是一個遞歸過程了。
代碼實現
import numpy as np
class KnightTour:
"""
馬踏棋盤(騎士周遊)小遊戲,以"日"的走法將整個棋盤走滿,並且不能重複走
"""
def __init__(self, size):
self.X = size
self.Y = size
# 棋盤的二維數組,用於記錄每一步所走的位置
self.chessboard = np.zeros([self.X, self.Y], dtype=np.int32)
# 用於記錄每個位置是否已經走過
self.visited = np.full([self.X, self.Y], False, dtype=bool)
# 是否完成遊戲的標記
self.finished = False
# 用於存儲所有解法
self.all_solution = []
def solution(self, x, y, get_all=False):
"""
獲取馬踏棋盤的走法
:param x: 出發位置的橫座標,從0開始
:param y: 出發位置的縱座標
:param get_all: 是否獲取所有走法
:return:
"""
if get_all:
self.tour_all(x, y, 1)
print('總共有 %d 種解法' % (len(self.all_solution)))
for i in range(len(self.all_solution)):
print('第%d種解法:' % (i + 1))
print(self.all_solution[i])
else:
self.tour(x, y, 1)
print(self.chessboard)
def tour(self, x, y, step):
"""
通過遞歸和回溯的方法尋找解法,找到一種解決即停止
:param x: 當前位置的橫座標
:param y: 當前位置的縱座標
:param step: 當前的步數
:return:
"""
self.visited[x][y] = True
self.chessboard[x][y] = step
nexts = self.get_next(x, y)
# 通過貪心算法進行優化,按照下一步的所有可走位置的下一步的可走位置數量進行升序排序
# 這樣先進行遞歸的數量會比較少
nexts.sort(key=lambda x: len(self.get_next(x[0], x[1])))
for p in nexts:
if not self.visited[p[0]][p[1]]:
self.tour(p[0], p[1], step+1)
if (step < self.X * self.Y) & (not self.finished): # 如果已經無路可走但仍未結束,或者處於回溯過程中
self.chessboard[x][y] = 0
self.visited[x][y] = False
else: # 已經完成遊戲
self.finished = True
def tour_all(self, x, y, step):
"""
通過遞歸和回溯的方法尋找所有解法
:param x: 當前位置的橫座標
:param y: 當前位置的縱座標
:param step: 當前的步數
:return:
"""
self.visited[x][y] = True
self.chessboard[x][y] = step
nexts = self.get_next(x, y)
# 通過貪心算法進行優化,按照下一步的所有可走位置的下一步的可走位置數量進行升序排序
# 這樣先進行遞歸的數量會比較少
# nexts.sort(key=lambda x: len(self.get_next(x[0], x[1])))
for p in nexts:
if not self.visited[p[0]][p[1]]:
self.tour_all(p[0], p[1], step+1)
# 已經按照規則將棋盤走滿,獲得一種解法
if step == self.X * self.Y:
# print("====")
# print(self.chessboard)
self.all_solution.append(self.chessboard.copy())
# 開始回溯,尋找其他解法
self.chessboard[x][y] = 0
self.visited[x][y] = False
def get_next(self, x, y):
"""
返回當前位置的下一步所有可走位置集合
:return:
"""
res = []
if (x - 1 >= 0) & (y - 2 >= 0):
res.append([x - 1, y - 2])
if (x - 2 >= 0) & (y - 1 >= 0):
res.append([x - 2, y - 1])
if (x + 1 < self.X) & (y - 2 >= 0):
res.append([x + 1, y - 2])
if (x + 2 < self.X) & (y - 1 >= 0):
res.append([x + 2, y - 1])
if (x - 2 >= 0) & (y + 1 < self.Y):
res.append([x - 2, y + 1])
if (x - 1 >= 0) & (y + 2 < self.Y):
res.append([x - 1, y + 2])
if (x + 2 < self.X) & (y + 1 < self.Y):
res.append([x + 2, y + 1])
if (x + 1 < self.X) & (y + 2 < self.Y):
res.append([x + 1, y + 2])
return res
if __name__ == '__main__':
game = KnightTour(5)
game.solution(0, 0, True)
歡迎關注同名公衆號:“我就算餓死也不做程序員”。
交個朋友,一起交流,一起學習,一起進步。