今天用Python Class來做一個大家都玩過的遊戲,踩地雷,在做這個專案之前我也覺得很難,主要是沒看到甚麼比較清楚的教學,但實際上只要我們先將遊戲拆成很多個不同的區段來處理就會簡單許多,這也是程式中很重要的概念,即分治法,把大問題先分成一個一個小問題來看,這樣就會簡單上許多了。
思考
先來看踩地雷的遊戲過程是甚麼吧!
- 遊戲版面由一個矩形方格組成,其中隨機分佈地雷。
- 玩家需要揭開它們,每個方塊上的數字代表周圍8個方塊中地雷的數量。
- 如果玩家揭開了一個地雷,遊戲就會結束。
由上述可知,我們的流程大概可以想成:
創建矩形board→需要二維陣列→用迴圈把board建好→隨機分布地雷→用random→用迴圈檢查附近有幾個地雷→得到每格的數字→印出供遊玩的board→玩家輸入要挖哪裡→完成🥰
「組」出遊戲
import
1 | import random |
board class
init
1
2
3
4
5
6
7
8
9
10
11def __init__(self, size, num_bombs):
#size and num_bombs
self.width = size[0]
self.height = size[1]
self.size = self.width * self.height
self.num_bombs = num_bombs
#create_board
self.board = self.create_board()
self.set_block_values()
#save blocks which have dug
self.dug = set()board & bombs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23def create_board(self):
#create empty board
board = [[None for c in range(self.width)] for r in range(self.height)]
"""
board =
[None, None, ..., None],
[None, None, ..., None],
[None, None, ..., None],
[None, None, ..., None]
"""
for _ in range(self.num_bombs):
location = random.randint(0, self.size -1)
row = location // self.height#y
col = location % self.width#x
board[row][col] = '💣'
"""
#this is fine also
row = random.randint(0, self.height - 1)
col = random.randint(0, self.width - 1)
board[row][col] = '💣'
"""
return boardblock
number
1
2
3
4
5
6def set_block_values(self):
for r in range(self.height):
for c in range(self.width):
if self.board[r][c] == '💣':#bomb
continue
self.board[r][c] = self.calculate_nearby_bombs(r, c)nearby bombs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15def calculate_nearby_bombs(self, row, col):
#make sure not to go out of bounds
"""
[None -> None -> None],
[None -> block(continue) -> None],
[None -> None -> None],
"""
num_nearby_bombs = 0
for r in range(max(0, row-1), min(self.height-1, row+1) + 1):
for c in range(max(0, col-1), min(self.width-1, col+1) + 1):
if (r==row and c==col):
continue#don't count the spot itself as a nearby bomb
elif self.board[r][c] == '💣':
num_nearby_bombs += 1
return num_nearby_bombsdig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15def dig(self, row, col):
self.dug.add((row, col))#add to set
if self.board[row][col] == '💣':#bomb
return False
elif self.board[row][col] > 0:#have bombs nearby, don't dig outside
return True
#if self.board[row][col] == 0:
for r in range(max(0, row-1), min(self.height-1, row+1) + 1):
for c in range(max(0, col-1), min(self.width-1, col+1) + 1):
if (r, c) in self.dug:
continue #checked
self.dig(r, c)#recursion
return True
game play
visible board
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19def show(self):
visible_board = [[None for c in range(self.width)] for r in range(self.height)]
for row in range(self.height):
for col in range(self.width):
if (row, col) in self.dug:
tmp = number_to_emoji(self.board[row][col]) if self.board[row][col] != '💣' else '💣'
visible_board[row][col] = tmp
else:
visible_board[row][col] = "🧱"
print(visible_board[row][col],end=" ")
print("")
def reveal(self):
for row in range(self.height):
for col in range(self.width):
tmp = number_to_emoji(self.board[row][col]) if self.board[row][col] != '💣' else '💣'
self.board[row][col] = tmp
print(self.board[row][col],end=" ")
print("")play function流程(輸入)
- 創建棋盤
- show出棋盤並請使用者輸入要挖的格子
- 輸入
- If挖到炸彈,遊戲結束
- If沒有,遞迴挖到周邊格子的周圍都有至少一顆地雷
- 重複2~5直到格子被挖完,顯示遊戲結果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55def play(size, num_bombs):
#Step 1. create board
board = Board(size, num_bombs)
is_safe = True
while len(board.dug) < board.size - board.num_bombs:
#Step 2. print boards
board.show()
print("")
#Step 3. Input
tmp = input("Where would you like to dig?(col:x) (row:y):")#from 1
if tmp == "reveal":
board.reveal()
break
else:
col, row = map(int, tmp.split())
col, row = col -1, row -1#from 0
#check if invalid input
if row < 0 or row >= board.height or col < 0 or col >= board.width:
print("Invalid location. Please try again.")
continue
#Step 4. & 5. check result of dug
is_safe = board.dig(row, col)
if not is_safe:
break #game over
time.sleep(0.5)
time.sleep(1)#wait for game over screen show up
#Step 6. win or lose
if is_safe:
print("CONGRATULATIONS! YOU WIN!!!")
time.sleep(1)
tmp = input("again?(Y/N):")
if tmp == "Y":
os.system('cls')
size, num_bombs= title_screen()
play(size, num_bombs)
else:
print("byebye ^ ^")
else:
print("SORRY, GAME OVER :(")
#reveal complete board
board.reveal()
time.sleep(2)
tmp = input("again?(Y/N):")
if tmp == "Y":
os.system('cls')
size, num_bombs= title_screen()
play(size, num_bombs)
else:
print("byebye ^ ^")title screen(difficulty settings)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33def title_screen():
while(True):
difficulty = int(input("Please Choose the Difficulty(enter number)\n1 Beginner\n2 Intermediate\n3 Expert\n4 Custom\nmode:"))
match(difficulty):
case 1:
size = (8, 8)
num_bombs = 10
case 2:
size = (16, 16)
num_bombs = 40
case 3:
size = (16, 30)
num_bombs = 99
case 4:
while(True):
w,h = map(int, input("Please Enter Width and Height for the board(up to 80, 80):").split())
if w > 35 or w < 0 or h > 35 or h < 0:
print("Width and Height of map should be between 0 and 35. Please Try Again.")
continue
else:
num_bombs = int(input("Please Enter The Number of Bombs:"))
if num_bombs > 300 or num_bombs < 0:
print("Number of bombs should be between 0 and 300. Please Try Again.")
continue
break
size = (w, h)
case _:
print("Invalid Input")
time.sleep(1.5)
os.system('cls')
continue
break
return size, num_bombs
最後兩個function就能玩啦!
1 | size, num_bombs= title_screen() |
結語
文章頗長,感謝您的觀看!完整程式碼我會放在這裡,另外,文章中的遊戲只能在終端運行,並且輸入必須用座標來寫也是略為麻煩,早就被介面化的應用程式麻痺的我們怎麼可能忍的了呢!所以下一篇文章,就要用Pygame做出互動介面,還請您敬請期待!
About this Post
此文章由 IHCT 所撰寫,版權聲明:CC BY-NC 4.0.