2024-08-27|閱讀時間 ‧ 約 23 分鐘

🕹回憶殺 python實現 華容道(數字版)可線上玩

raw-image

華容道(數字版)的遊戲描述


初始給定一個1~15數字隨機打散的4x4遊戲版

使用者透過移動空格來重新排列數字
最後數字排成1~15,而且空格剛好在右下角的時候,遊戲結束。


華容道(數字版)的遊戲場景


遊戲開始時,電腦為玩家建立一個4x4的板子,隨機填充0~15的數字。

數字0代表空格


玩家每回合可以移動空格一次,藉此重新排列板子上的數字


最後數字依序排成1~15,而且空格0剛好在右下角的時候,遊戲結束



上層思考邏輯與框架


建立遊戲場景,為玩家建立一個4x4的板子,隨機填充0~15的數字。

數字0代表空格


每回合都先顯示當下的板子狀態給玩家看。

玩家每回合可以選擇一個和空格相鄰的數字,接著把空格移動到對應的位置,
藉此重新排列板子上的數字


接著檢查所有數字是否已經依序排成1~15,而且空格0剛好在右下角?

如果是,則遊戲結束

如果不是,則遊戲繼續。


中層功能元件分析


1.怎麼建立4x4的遊戲版,並且填充隨機數?


透過list()建構子 和 range()先建立長度為16的list,依序填入0~15的數字。


並且使用python內建的random.shuffle()方法,

進行隨機採樣,打散所有數字的位置。


最後,透過List comprehension生成4x4的遊戲版,上面帶著隨機生成的0~15的數字。

class FifteenPuzzle:
def __init__(self):
# 生成4x4的遊戲版
self.board = self.create_board()

# 紀錄空格所在的位置
self.empty_tile = self.find_empty()

def find_empty(self):
# 找空格所在的位置
for y in range(4):
for x in range(4):
if self.board[y][x] == 0:
return y, x

return -1, -1

def create_board(self):
# 生成4x4的遊戲版
tiles = list(range(0, 16))
random.shuffle(tiles)
return [tiles[i:i+4] for i in range(0, 16, 4)]

2.怎麼顯示4x4的遊戲版當下的遊戲狀態?


用雙層迴圈去做輸出排版,印出4x4遊戲版當下的內容。


  def display_board(self):

for row in self.board:
print(' '.join(f'{tile:2}' for tile in row))
print()
return

3.怎麼移動空格(空格用特殊數字0來代表)?


先找出使用者輸入的數字,
接著把空格(用特殊數字0來代表)移動到使用者指定的數字所在的位置上。


  def move_tile(self, tile):

y, x = self.empty_tile
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
nx, ny = x + dx, y + dy
if 0 <= nx < 4 and 0 <= ny < 4 and self.board[ny][nx] == tile:

# 移動空格
self.board[y][x], self.board[ny][nx] = self.board[ny][nx], self.board[y][x]

# 紀錄空格在移動後的新位置
self.empty_tile = (ny, nx)
return True
return False

4.怎麼判斷遊戲是否已經結束?


每回合檢查所有數字是否已經依序排成1~15,而且空格剛好在右下角?

如果是,則遊戲結束

如果不是,則遊戲繼續。


  def is_solved(self):

# 完全排好的狀態
answer = [list(range(i, i+4)) for i in range(1, 16, 4)]
answer[-1][-1] = 0

'''
[
[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[ 13, 14, 15, 0]
]
'''

# 檢查是否已經排好所有數字
return self.board == answer

5.如何建立遊戲的回合循環機制?


每回合先呼叫 is_solved()檢查是否遊戲結束

如果遊戲還沒結束,就進入while本體
等待使用者輸入數字,透過移動空格去重新排列遊戲版上的數字。



def play():

game = FifteenPuzzle()
while not game.is_solved():
game.display_board()
try:

tile = int(input("Enter the number to move (-1 to quit): "))
if tile == -1:
break

if not game.move_tile(tile):
print("Invalid move!")

except ValueError:
print("Please enter a valid number.")

print("Congratulations! You solved the puzzle!")

底層的完整實作: 華榮道(數字版)

import random

class FifteenPuzzle:
def __init__(self):
self.board = self.create_board()
self.empty_tile = self.find_empty()

def find_empty(self):
for y in range(4):
for x in range(4):
if self.board[y][x] == 0:
return y, x

return -1, -1

def create_board(self):

tiles = list(range(0, 16))
random.shuffle(tiles)
return [tiles[i:i+4] for i in range(0, 16, 4)]


def display_board(self):

for row in self.board:
print(' '.join(f'{tile:2}' for tile in row))
print()
return


def move_tile(self, tile):
y, x = self.empty_tile
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
nx, ny = x + dx, y + dy
if 0 <= nx < 4 and 0 <= ny < 4 and self.board[ny][nx] == tile:
self.board[y][x], self.board[ny][nx] = self.board[ny][nx], self.board[y][x]
self.empty_tile = (ny, nx)
return True
return False


def is_solved(self):
answer = [list(range(i, i+4)) for i in range(1, 16, 4)]
answer[-1][-1] = 0
'''
[
[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[ 13, 14, 15, 0]
]
'''
return self.board == answer


def play():

game = FifteenPuzzle()
while not game.is_solved():
game.display_board()
try:

tile = int(input("Enter the number to move (-1 to quit): "))
if tile == -1:
break

if not game.move_tile(tile):
print("Invalid move!")

except ValueError:
print("Please enter a valid number.")

print("Congratulations! You solved the puzzle!")

if __name__ == "__main__":
play()

試玩畫面


點這行 線上執行 與 試玩(在新的頁面按Run開始玩)


延伸思考:


如果對程式或者演算法有興趣的同學,

可以試著觀察華榮道移動數字的規律,

開發更強的解題演算法嘗試用最少的移動步數去重新排好所有的數字

很有趣喔~

分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.