![]() |
一個常數maxRank被用于一場游戲的結束。它被設置為System.Int32.MaxValue-64。這可以確保任何會導致游戲結束的移動將總是比其它移動具有更高的等級(負數或正數)。
從系統的最大整數值減去64允許我們把最后的得分添加到等級上,這樣贏得10個圓盤將比贏得2個圓盤具有高的等級。這可以使得計算機玩家在贏了時最大化自己的得分(或在輸了時最小化對手玩家的得分)。
當前的實現還不能匹配更好的AI玩家,但是如果它和一個小人對手(至少,這個小人對手)玩得話,已經比較難了。同樣,如果你用Google搜索一下,會找到許多描述此游戲策略和AI方法的資源。
(二) 游戲部件
1. 平板類
Board類描述了一個游戲平板。它使用一個二維數組來跟蹤每個平板方格的內容,它可以是定義在類中的下列的常數值之一:
·Black=-1
·Empty=0
·White=1
該類提供了兩個構造器。一個用于創建一個新的空的平板,而另一個創建一個已存在平板的拷貝。
它提供象MakeMove()這樣的公共方法-這個方法把一個圓盤添加到平板上,并能翻動任何可翼側包圍的對手圓盤。例如,IsValidMove()可被用來確定是否一個給定的移動對于一個給定玩家是有效的。如果該給定玩家不能作任何合法的移動,HasAnyValidMove()將返回false。
另外,它還為每一個玩家跟蹤圓盤的數目-該數目被用于機器的移動AI例程。這些數目包括圓盤總數、邊界圓盤數和每種顏色的安全圓盤(或未翻動的圓盤)數。
2. 移動結構
在主要的ReversiForm類中,定義了一對結構用于存儲游戲移動。這兩個結構都包含了一個行與列索引對以相應于一個特別的平板方格。
ComputerMove結構用于計算機AI。除了移動位置之外,它還有一個等級成員。這是被用于跟蹤一次移動的好或壞-這是在向前搜索過程中決定的。
MoveRecord結構用于存儲在游戲中每次移動的信息。為了允許移動的撤消/重做特性,建立了一個數組來跟蹤每一輪游戲中的該平板。一次移動記錄包含一個描述這次特定移動之前的游戲平板,還有用來指示哪一個玩家將做下次移動的值。針對每個玩家的每次移動建立一個相應的數組以允許游戲復位到在移動過程中的任一點的狀態。
RestoreGameAt()方法實現把游戲復位到一個特定的移動數字。盡管它潛在地允許游戲可以恢復到當前移動歷史中的任何移動;但是,主表單程序中的菜單和工具條選項目前僅提供了一次移動的撤消/重做或所有移動的撤消/重做。一種將來的增加可能是允許用戶點擊移動列表中的項來把游戲恢復相應的移動數字。
(三) 圖形和用戶接口
1. 游戲平板
平板上的方格被一個叫SquareControl的用戶控件所描述。對于每個方格都有一個這種控件顯示于游戲平板上。該控件包含信息-用于顯示方格和它的內容(空的或一個黑的或白的圓盤),包括圓盤動畫和任何高亮。
2. 顯示圓盤
每一個圓盤被動態繪制。其基本形狀是一個圓-具有某種高亮和一個陰影來給它一個偽裝的3D外觀。這些形狀被按比例縮放,依賴于方形控制的當前尺寸。通過以這種方式對其著色,代之使用靜態的圖像,平板可以被動態地調整大小以匹配表單窗口的大小。
在ReversiForm內部控制的方格控件的Click事件允許用戶一次移動到一個特定的方格(假定它是一個合法的移動)。同樣,當這些選項激活時,MouseMove和MouseLeave事件被控制通過有效的移動或預覽一次移動來更新平板顯示。
3. 移動動畫
圓盤反轉動畫是通過使用一個定義在SquareControl類中的計數器并伴隨一個System.Windows.Forms.Timer定時器實現的?;旧?,這是一個被操作系統所控制的線程-它周期性地引發一個你的表單應用程序能夠響應的事件。
在做一次移動后,如果移動動畫選項處于活動狀態,每個受影響的方格控制把它的計數器初始化并且激活定時器。在每次時鐘滴答響時,主表單的AnimateMove()方法被調用(見下面)。這個方法更新方格計數器并且重畫它們的顯示。該動畫基本上包含把圓盤形狀從一個圓改變成一個更扁的橢圓,然后又變回到一個完整的圓,只是以相反的顏色罷了。這個動畫的光滑度和速度依賴于初始的計數值的大小(由常數SquareControl.AnimationStart所設置)和時鐘多長時間滴答響一次(由主表單中的常數animationTimerInterval所設置)。
利用Visual C#實現Reversi游戲開發 :
(四) 玩游戲
下列變量用于控制一次游戲過程:
//游戲參數 private GameState gameState; private int currentColor; private int moveNumber; |
moveNumber應該是顯然的。currentColor顯示現在輪著哪一個玩家移動(黑色或白色)。gameState被設置為下列枚舉值之一:
//定義游戲的狀態 private enum GameState{ GameOver, //游戲完了(也適合于起始的狀態) InMoveAnimation, //產生一次移動并且該動畫是活動的 InPlayerMove, //等待用戶移動 InComputerMove, //等待計算機移動 MoveCompleted //一次移動完成 //(包括動畫,如果是活動的) } |
大多數游戲都是在事件驅動下玩的,因此gameState的使用允許各種事件處理器來決定要采取的適當行動。例如,當用戶點擊平板方格,SquareControl_Click()被調用。如果游戲狀態是InPlayerMove,則在那個方格上作一次移動。但是如果游戲在其它狀態,則說明還沒輪到用戶移動,所以這次點擊將被忽略。
同樣,如果用戶點擊工具條"Undo Move"按鈕,我們想要檢查該游戲狀態來看一下是否需要做任何事情,在把游戲復位到前一次移動之前。例如,如果狀態是InMoveAnimation,那么動畫定時器需要停下來,而該方格控制需要它們的計時器并且顯示重置。如果狀態是InComputerMove,那么該程序現在在用一個獨立的線程進行一次向前搜索(見下面)-它將需要停下來。
1. 程序流程
下圖說明了在一個游戲過程中的通用程序流程:
![]() |
StartTurn()在每次游戲的開始當任何一個玩家做一次移動后以及無論何時執行一次撤消/重做之后被調用。它負責評估游戲狀況并且為下次移動作準備。
它首先檢查是否當前玩家能夠作一合法的移動。如果不能,它切換到其它玩家并且檢查是否那個玩家有任何合法的移動。當兩個玩家都不能移動時,根據規則,游戲結束并且它將結束該游戲。
否則,該函數將為當前玩家作出移動作好準備。如果當前玩家在用戶控制下,它簡單地退出。然后用戶通過在一個有效的方形上點按鼠標指針或通過輸入一個有效的列字母和行數字可以作一次移動。這將導致一次對MakePlayerMove()的調用-它完成一些清理工作,然后調用MakeMove()來實現移動。如果當前玩家在計算機控制下,它就啟動向前搜索以找到最佳移動。
2. 使用一個工作者線程
因為向前搜索是深度優先計算的,所以它被用一個工作者線程來專門實現;否則,主表單屏幕將被凍結并且在計算最佳移動時響應滯后。因此,StartTurn()創建一個工作者線程來執行CalculateComputerMove()方法并且啟動它。
鎖機制被用在主游戲平板對象上以防止不滿足游戲條件。作為一個例子,MakeComputerMove()和UndoMove()方法都因游戲平板而改變。這兩個方法首先試圖把一個鎖放在它上面。因此,如果一個方法碰巧被調用,而另一個正在更新該平板時,它將被強迫等待,直到那些變化完成并且該鎖被釋放為止。
一旦發現一個移動,CalculateComputerMove()方法完成一次回調以在主表單屏幕上運行MakeComputerMove()。這個方法鎖定平板并調用MakeMove()來實現移動。
3. 實現移動
MakeMove()完成實際的平板更新,把新的圓盤放置到指定的位置。它也實現一些維護如撤消/重做移動歷史和高亮搬遷任何方形等。
然后,如果移動動畫選項置為Off狀態,它簡單地調用EndMove()-它將切換當前顏色并以一個對StartTurn()的調用啟動下一個回合。
但是如果動畫選項置為On狀態,它將代之來初始化圓盤-使其動起來并且啟動動畫定時器。如以前所討論的,該定時器將會使AnimateMove()每幾個毫秒運行一次,更新顯示并且相應地每次減少動畫計數器。最后,該計數器將到點,而AnimateMove()將調用EndMove()來完成移動。
(五) 未來的增強
在玩家AI方面還有很大的改進余地。向前搜索算法可以被擴充-用打開的移動或一系列被預先定級別的角和邊模式??梢允褂眠x擇性深度,這樣查找深度可以針對對游戲有較強影響的移動(例如在角落附近)而加以擴展。另外的改進將是存儲向前搜索樹。這將使它被搜索到一個更深的層次,因為該程序不會在每次重新生成相同的移動。