Python 实现黑白棋
Python實現黑白棋
- 題目要求
- 電腦的策略
- 游戲結束的條件
- 解題思路
- 關鍵邏輯
- 關鍵函數
- Init_board:
- printBoard:
- computer_move:
- human_move:
- check_board:
- check_legal_move:
- gameover:
- saveinfo:
- main
- 后記
- 程序可能的優化方向
題目要求
電腦的策略
對每個可能的落子位置,都進行嘗試,計算該位置的“分值” (可以翻轉的對手棋子數量),分值越高則在該位置落子越有利。計算每個可能位置的分值,選擇最大值位置落子。可能有2個或多個棋盤格有相同的分值。這種情況下,選擇行字母最小的棋盤格。如果兩個棋盤格分值相同且在同一行,則選擇列字母較小的棋盤格
游戲結束的條件
1)整個棋盤滿了;
2)一方的棋子已經被對方吃光;
3)兩名玩家都 沒有可以落子的棋盤格;
4)一方落子在非法位置。
前 3 種情況以棋子數目來計算勝負,棋子多的一方獲 勝;第 4 種情況判定對方獲勝。
解題思路
關鍵邏輯
1)不論是電腦下棋還是人類下棋,都需要先判斷是否有字可落
2)人類下棋之前,要判斷落子是否合法
3)在判斷落子是否合法的時候,要注意棋盤的邊界
因為python的列表存在下標為-1的情況,故要注意下標的下界,另一方面,因為黑白棋要考慮盡頭那個棋子是否為同色的情況,所以要注意當前搜索棋子和同方向的下一個棋子(潛在的同色可能性)是否越界。
舉例:4*4棋盤的“黑 白 白 白” ,如果不考慮下一個棋子的越界情況的話,會出現從黑開始繞一個圈回來又查找到黑從而把三個白棋翻轉的錯誤情況
4)其實翻轉、檢測落子是否合法,計算落子的分值用的都是同一套循環邏輯,所以只寫一個然后基本復制黏貼就好了
5)用多次sort進行不同權重的比較
6)游戲結束的幾種情況要分情況討論
關鍵函數
Init_board:
讀入棋盤大小 n(n 為偶數,且 4≤n≤26),按照要求初始化棋盤。程序使用如下字 符表示每個棋盤格的狀態:
. – 未被占用 X – 被黑棋占用 O – 被白棋占用
printBoard:
輸出棋盤。例如:4×4 棋盤的初始狀態如下:
a b c d
a . . . .
b . O X .
c . X O .
d . . . .
computer_move:
計算機下棋。落子位置表示為“行列字母”的格式,如:“ba”代表棋子 落在行 b 列 a。
def computer_move(row, col, color): #電腦行棋和棋子翻轉global boardif (check != 0): #check中存儲的是該棋子的價值(翻轉的棋子數),不為0則合法board[row][col] = color #向規定地點放上棋子direction = ((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1))for i in range(8):z = 1 #對8個方向進行搜索while (0<= row + (z+1) * direction[i][0] < l) and (0<= col + (z+1) * direction[i][1] < l) and (board[row + z * direction[i][0]][col + z * direction[i][1]] == -color):#搜索進行的條件,搜索的下一個位置沒有超過系統邊界(因為盡頭是一個相同的棋子所以要多查找一步),當前位置的棋子是一個異色的棋子。z += 1 #這里涉及到一個問題就是python列表值是存在-1值的,所以要規定列表搜索的下界只能到0,不然本質上會查找到列表最后一個(下標為-1)的值if board[row + z * direction[i][0]][col + z * direction[i][1]] == color: #如果查找到了盡頭的那個同色棋子for j in range(z):board[row + j * direction[i][0]][col + j * direction[i][1]] = color #那就把中間的棋子全部置為同色并退出這次搜索breakhuman_move:
用戶下棋
def human_move(row, col, color): #人類行棋和棋子翻轉,與電腦行棋類似global boardif (check != 0):board[row][col] = colordirection = ((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1))for i in range(8):z = 1while (0<=row + (z+1) * direction[i][0] < l) and (0 <= col + (z+1) * direction[i][1] < l) and (board[row + z * direction[i][0]][col + z * direction[i][1]] == -color):z += 1if board[row + z * direction[i][0]][col + z * direction[i][1]] == color:for j in range(z):board[row + j * direction[i][0]][col + j * direction[i][1]] = colorbreakcheck_board:
檢測游戲是否結束
def check_board(): #檢測本局游戲是否結束a1 = 1 #用來標記棋盤是否下滿a21 = 1 #檢測棋盤中是否還有黑棋a22 = 1 #檢測棋盤中是否還有白棋a4 = 0 #檢測當前落子位置是否合法for i in range(l):for j in range(l): #遍歷整個棋盤if (board[i][j] == 0): #如果存在空位a1 = 0elif (board[i][j] == 1): #如果存在黑棋a21 = 0else: #如果存在白棋a22 = 0if not (check): # check為0則不存在翻轉,落子合法a4 = 1if (a1 or (a21 and a22) or (not (xp or op)) or a4):if (a1 or (a21 and a22) or (not (xp or op))): # xp和op代表了雙方是否存在落子位置if (not (xp or op)): #如雙雙置0,則證明雙方都無落子位置print("Both player have no vaild move!")gameover('score') #這些都是積分計算勝負關系else: #如果a4出現了,則落子非法,計算勝負關系gameover('illegal')check_legal_move:
檢測顏色為 color 的棋子落在棋盤格(row, col) 上是否合法
def check_legal_move(row, col, color): #檢測落子位置是否合法direction = ((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1))score = 0for i in range(8): #思路與落子的思路類似,但不同的是這里當搜索到最后的同色棋子時,進行加和操作而非翻面操作 zscore = 1while (0 <= (row + (zscore+1) * direction[i][0]) < l) and (0 <= (col + (zscore+1) * direction[i][1]) < l) and ((board[row + zscore * direction[i][0]][col + zscore * direction[i][1]]) == -color):if (board[row + (zscore + 1) * direction[i][0]][col + (zscore + 1) * direction[i][1]] == color):score += zscorebreakzscore += 1return scoregameover:
游戲結束,統計得分,輸出結果
def gameover(s): #游戲結束判定global s1global conditionglobal blackglobal whitecondition = 0 #將游戲標記置為 0,游戲停止if s == 'score': #如果是分數過程,則計算黑白棋的個數print('Game Over!')black = 0white = 0for i in range(l):for j in range(l):if board[i][j] == 1:black += 1if board[i][j] == -1:white += 1if (black > white):print('X : O =', black, ':', white)print('X player wins!')elif (black < white):print('O player wins!')print('X : O =', black, ':', white)else:print('Draw!')s1 = str(black) + 'to' + str(white) # s1 中存儲的是要輸進文件去的文本return s1if s == 'illegal':print('Invaild move!')s1 = 'Invaild move!'if s == 'gg':print('Human gave up!')s1 = 'Human gave up!'print('Game Over!')if (color == 1): #這里是非法輸入和gg投子的輸出設置,color在主程序中更改print('O player wins.')else:print('X player wins.')return s1saveinfo:
把每次人機對弈的信息作為一行寫入文件reversi.csv中,這些信息包括:游戲 開始的時間、單次游戲使用的時間、棋盤大小、黑棋玩家、白棋玩家、游戲比分,信息之間使用逗號字 符分隔。
def saveinfo(): #文件存儲time3 = time.time() #游戲結束時間long = time3-time2 #游戲持續時長f = open('Reversi.csv', 'a+')s = str(time1) + ',' + str(long) + ',' + str(l) + '*' + str(l) + ',' + Xplayer + ',' + Oplayer + ',' + s1f.write(s) #向文件中輸入f.write('\n')f.close()main
import datetime import time time1 = datetime.datetime.now().strftime('%Y-%m-%d %T') #保存游戲時間 time2 = time.time() #游戲開始時間 l = int(input('Enter the board dimension:')) # l 中保存了棋盤的長度 board = Init_board() #初始化棋盤 x1 = input('Computer plays (X/O):') # x1中保存電腦的棋子顏色 if (x1 == 'X'): # 電腦執黑 hcolor = -1Xplayer = 'computer'Oplayer = 'human' #這個是要輸進文件里的東西condition = 1 #游戲標記置1,游戲進行printBoard() #打印 、while condition: #如果游戲標志為1(沒進gameover),則可以繼續運行legal = []for i in range(l):for j in range(l):if (board[i][j] == 0):if (check_legal_move(i, j, 1) != 0):legal.append([i, j, check_legal_move(i, j, 1)]) #將所有可下位置存進legal列表中legal.sort(key=lambda x: x[1], reverse=False)legal.sort(key=lambda x: x[0], reverse=False)legal.sort(key=lambda x: x[2], reverse=True) #對列表進行先值,再行,再列的排序if (legal != []): #如果存在合法輸入row = legal[0][0]col = legal[0][1]color = 1check = check_legal_move(row, col, color)xp = 1 #證明X仍可下op = 1 #證明O仍可下computer_move(row, col, 1) #將分值存進check里,進行落子print('Computer places X at', chr(ord('a') + row), chr(ord('a') + col), '.')else:xp = 0 #如果不存在落子位置print('X player has no vaild move.') #不進行落子printBoard() #進行棋盤打印check_board() #進行檢測棋局是否結束if (condition != 0): #如果棋局沒有結束的話進行人類行棋legal = [] for i in range(l):for j in range(l):if board[i][j] == 0:if (check_legal_move(i, j, -1) != 0) and (board[i][j] == 0):legal.append([i, j, check_legal_move(i, j, -1)]) #檢測是否有行棋位置if (legal != []):inter = input('Enter move for O (Rowcol):')if inter == 'gg': #貼心的投降設置gameover('gg')breakrow = ord(inter[0]) - 97 #把輸入的字母通過ASCII碼變成輸出col = ord(inter[1]) - 97color = -1if (row>=l) or (col>=l) or (row<0) or (col <0): #如果輸入超過邊界,則為非法輸入gameover('illegal')breakcheck = check_legal_move(row, col, color) #檢測落子是否存在翻轉xp = 1op = 1human_move(ord(inter[0]) - 97, ord(inter[1]) - 97, -1) #人類落子check_board() #檢測當前棋盤是否滿足結束條件if check:printBoard() #如果通過,則進行打印else:op = 0 #白棋無棋可下 print("O player has no vaild move.")saveinfo() #游戲結束之后進行游戲記錄的保存else: #之后為電腦執白棋的情況,與上文黑棋的情況類似hcolor = 1Oplayer = 'computer'Xplayer = 'human'condition = 1printBoard()while condition:legal = []for i in range(l):for j in range(l):if (board[i][j] == 0):if (check_legal_move(i, j, 1) != 0):legal.append([i, j, check_legal_move(i, j, 1)])if (legal != []):inter = input('Enter move for X (Rowcol):')if inter == 'gg':gameover('gg')breakrow = ord(inter[0]) - 97col = ord(inter[1]) - 97color = 1if (row>=l) or (col>=l) or (row<0) or (col <0):gameover('illegal')breakcheck = check_legal_move(row, col, color)xp = 1op = 1human_move(ord(inter[0]) - 97, ord(inter[1]) - 97, 1)check_board()if check:printBoard()else:op = 0print("O player has no vaild move.")if condition:legal = []for i in range(l):for j in range(l):if (board[i][j] == 0):if (check_legal_move(i, j, -1) != 0):legal.append([i, j, check_legal_move(i, j, -1)])legal.sort(key=lambda x: x[1], reverse=False) legal.sort(key=lambda x: x[0], reverse=False)legal.sort(key=lambda x: x[2], reverse=True)if (legal != []):xp = 1op = 1row = legal[0][0]col = legal[0][1]color = -1check = check_legal_move(row, col, color)computer_move(row, col, -1)else:xp = 0print("X player has no vaild move.")check_board()printBoard()print('Computer places O at', chr(ord('a') + row), chr(ord('a') + col), '.')saveinfo()后記
程序可能的優化方向
程序可能出現的錯誤!存在一種情況就是一方本來無棋可下,另一方行棋之后突然有棋可下了!所以需要重置“是否有棋可下”的條件碼,這里等到大作業截止之后再做修改
1)將電腦執黑或執白的情況分開來討論太麻煩了(相當于代碼量*2),能用一個函數解決它嗎?
2)其實判斷落子位置,分值和是否合法用的也是同一套函數,可以使用函數進行代碼的復用,就不用寫那么多了。
3)因為后面基本就是用pycharm來做逐步debug了,所以一些判斷和list語句可能寫的沒那么好看
4)程序的穩定性有待檢驗(實在是懶得跟電腦下那么多盤棋)
5)gameover的函數多入口情況是否可以優化?
總結
以上是生活随笔為你收集整理的Python 实现黑白棋的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 聊聊写代码的20个反面教材
- 下一篇: Linux用wget和curl下载jdk