- Python AI游戲編程入門:基于Pygame和PyTorch
- 肖凱
- 1819字
- 2024-10-25 14:10:49
2.3 面向對象編程
2.2節中實現井字棋游戲的代碼體現了一種面向過程的編程設計思路。例如,從接收玩家輸入到判斷勝負,再到顯示新的棋盤,將整體游戲邏輯根據相應過程拆開,然后分別用函數實現每個過程的邏輯。當程序功能非常簡單時,基于過程的設計思路沒什么問題。但當程序功能越來越復雜時,我們需要一種更靈活的設計思路,也就是面向對象編程,其英文名為Object-Oriented Programming,通常縮寫為OOP。
面向對象編程的主要觀點是不應該將程序拆分為若干過程,而應該將其拆分為自然對象的模型。面向對象編程涉及幾個關鍵概念,包括類、對象、組件、屬性和行為。我們分別來解釋這些關鍵概念。類可以看作一張藍圖或圖紙,代表了抽象概念,而對象是具體的事物。類和對象的關系是抽象和具體的關系,就好比漢字“馬”所代表的抽象概念,和一匹正在草原上奔跑的駿馬所代表的具體事物的關系。戰國時期所說的“白馬非馬”的故事,就包含抽象和具體的關系。
一個復雜的類可能是由多個組件構成的。例如,汽車是由發動機、車身、底盤等組件構成的,而這些組件本身也是類,如發動機類、車身類或底盤類。類是有屬性的,屬性是類的數據特征,例如馬是有顏色的,顏色是馬這個類的屬性,如果具體到作為對象的一匹馬,則有的馬是白色的,有的馬是黑色的。不同的具體的馬作為對象,可以有不同的屬性值。
類也是有行為的。行為定義了類可以做什么事(具有什么功能),例如馬可以奔跑,這是馬的一種功能。
面向對象編程的設計思路體現了一種模塊化建造、分而治之的思想,就像造汽車,工廠根據不同部件的關聯程度將汽車分成幾個模塊,分發到不同的車間來建造。例如發動機車間專門造發動機,車身車間專門造車身,底盤車間專門造底盤,總裝車間根據接口將它們組裝起來,各車間只需專注自己的模塊的建造,互不干擾。而且在研發新款車型時,可能只需要更改底盤,而老款的發動機仍可以使用。面向對象編程的優點歸納如下。
■ 分別用類來封裝各自的數據和函數,代碼相對獨立,更容易修改和管理。
■ 讓設計思路更清晰,編程更高效,代碼更容易理解且不容易出錯。
■ 可以直接使用現成的類,代碼能更好地重用。
這里還是以2.2節的井字棋游戲為例進行說明。在面向對象編程的設計思路下,游戲本身是一個類,某個特定的游戲是一個對象,它是這個類的具體實例。游戲類包括一個重要的組件,也就是棋盤類。棋盤類包含兩個屬性,也就是棋盤大小和棋盤空間本身。棋盤類也包括若干函數,例如顯示棋盤信息函數、判斷勝負函數等。作為游戲類的組件,棋盤類還有一個屬性,即當前需要落子的玩家。另外游戲類也包括若干函數,例如處理玩家輸入的函數等。
我們可以根據游戲邏輯,將游戲程序用類圖的形式進行重新設計。圖2-4所示為設計好的游戲類Game和棋盤類Board的類圖。類圖中顯示了各個類中包含的屬性和函數。它可以讓我們更清楚地檢查類的設計,以及類之間的關系。

圖2-4
下面我們根據面向對象編程的設計思路來修改原有的代碼。首先定義棋盤類,也就是Board類。在初始化函數中定義兩個屬性,分別是棋盤大小屬性size,以及棋子信息屬性pieces。
class Board:
def __init__(self,size):
self.size=size
self.pieces = ['.'] * size * size
Board類有顯示棋盤的函數show。
def show(self):
print("\n")
print("%s|%s|%s"%(self.pieces[0],self.pieces[1],self.pieces[2]))
print("-+-+-")
print("%s|%s|%s"%(self.pieces[3],self.pieces[4],self.pieces[5]))
print("-+-+-")
print("%s|%s|%s"%(self.pieces[6],self.pieces[7],self.pieces[8]))
Board類也有用于判斷有沒有空白位置可以落子的hasMovesLeft函數。
def hasMovesLeft(self):
return'.'inself.pieces
Board類還需要有一個用于判斷當前落子位置是否符合游戲規則的isMoveValid函數,以及用于落子后修改棋子信息的setMove函數。因為用戶的輸入是二維的坐標數據,所以需要建立一個輔助函數locToMove,負責將輸入從二維的棋盤坐標轉換為一維的列表索引。
def locToMove(self,loc):
return int(loc[1]+loc[0]*self.size)
def isMoveValid(self,loc):
move = self.locToMove(loc)
if self.pieces[move]=='.':
return True
else:
return False
def setMove(self,loc,player):
move = self.locToMove(loc)
self.pieces[move]=player
Board類當然也包括判斷棋局勝負的hasWon函數。這里的實現邏輯和2.2節中代碼顯示的一樣。
def hasWon(self,player):
winningSet = [player in range(self.size)]
row1 = self.pieces[:3]
row2 = self.pieces[3:6]
row3 = self.pieces[6:]
if winningSet in [row1,row2,row3]:
return True
col1=[self.pieces[0],self.pieces[3],self.pieces[6]]
col2=[self.pieces[1],self.pieces[4],self.pieces[7]]
col3=[self.pieces[2],self.pieces[5],self.pieces[8]]
if winningSet in [col1,col2,col3]:
return True
diag1=[self.pieces[0],self.pieces[4],self.pieces[8]]
diag2=[self.pieces[6],self.pieces[4],self.pieces[2]]
if winningSet in [diag1,diag2]:
return True
return False
然后在Board類基礎上構造游戲類,即Game類。Game類的初始化函數中包括當前玩家屬性currentPlayer,以及前面定義好的Board類,將其實例化,把生成的棋盤對象作為Game類的一個組件。
class Game:
def __init__(self,boardSize,startPlayer):
self.currentPlayer = startPlayer
self.board=Board(boardSize)
print("井字棋游戲開始")
print("規則:三子連成直線即勝利")
print("X先手,O后手")
Game類中包括輪換玩家的getNextPlayer函數。
@staticmethod
def getNextPlayer(currentPlayer):
if currentPlayer=='X':
return'O'
else:
return'X'
Game類中還包括處理玩家輸入的getPlayerMove函數。
def getPlayerMove(self):
while(True):
userMove=input(f'\n玩家{self.currentPlayer}輸入棋盤坐標(坐標取值0,1,2):X,Y?')
userMoveLoc=[int(char) for char in userMove.split(',')]
if self.board.isMoveValid(userMoveLoc):
self.board.setMove(userMoveLoc,self.currentPlayer)
break
然后將完整的游戲邏輯整合到play函數中。
def play(self):
self.board.show()
while self.board.hasMovesLeft():
self.getPlayerMove()
self.board.show()
if self.board.hasWon(self.currentPlayer):
print('\n玩家'+self.currentPlayer+'勝利!')
break
self.currentPlayer=self.getNextPlayer(self.currentPlayer)
最終的main入口非常簡潔。我們只需要將Game類實例化,生成game對象,再調用其play函數即可。在代碼重構過程中,我們將原本零散的代碼進行了劃分,按類進行重構,使其邏輯層次更為清晰、更容易理解。完整的代碼可以參見第2章的對應代碼文件tic_human_class.py。
if __name__ == '__main__':
game = Game(boardSize=3,startPlayer='X')
game.play()
- Java高并發核心編程(卷2):多線程、鎖、JMM、JUC、高并發設計模式
- Web程序設計(第二版)
- MySQL數據庫基礎實例教程(微課版)
- 信息技術應用基礎
- 微信小程序入門指南
- 用戶體驗增長:數字化·智能化·綠色化
- C語言程序設計
- Getting Started with LLVM Core Libraries
- Integrating Facebook iOS SDK with Your Application
- 持續輕量級Java EE開發:編寫可測試的代碼
- 算法設計與分析:基于C++編程語言的描述
- Continuous Delivery and DevOps:A Quickstart Guide Second Edition
- ASP.NET開發寶典
- IBM RUP參考與認證指南
- Scratch超人漫游記:創意程序設計:STEAM創新教育指南