2020計算機博弈大賽幻影圍棋分組第一天
從學長那拿到了2018,2019年參加幻影圍棋比賽的代碼,鹹魚了幾天之後,我組三人正式開始準備2020年的比賽,這篇部落客要記錄我參與的部分。
在看到源碼的時候,不禁感歎,源碼之混亂(畢竟祖傳代碼傳了不止一代),是以下決心對源碼進行重構,并且加深對源碼的了解。
首先來看學長給的程式結構圖,這張結構圖清晰易懂,重構的大體架構還是按他這個來。

首先從最簡單也是最基礎的資訊互動子產品開始寫
資訊互動子產品
這個子產品的功能與平台進行資訊互動,從評估系統子產品接收下棋點,更新棋盤資訊等等。
引擎與平台通信的技術是程序間通信,是以我們隻需要用标準輸入輸出函數即可實作與平台的通信。
這是平台與引擎的通信協定規範
連結: https://pan.baidu.com/s/1OxH4YbOE5pEIk21rPn0pHQ
提取碼: u54e
引擎向平台發送的指令隻有move, 表明在哪下棋, 格式為 move + ’ ’ + positionlist, 例如
printf("move %c%c\n",step.point.x+'A',step.point.y+'A');
幻影圍棋允許pass
printf("move pass\n");
鑒于平台可能有更改通信格式的可能,是以我們将輸出封裝為一些函數,引擎調用該方法即可,指令格式改變不會影響整體程式的的運作。
"""
這裡有向平台發送指令和參數的函數, 已經封裝好了
"""
def sendMove(point):
"""
與平台通信,發送Move指令 格式 move 12 12是坐标1, 2
:param point: 向平台送出下棋的點坐标
:return: None
"""
print("move {0}{1}".format(chr(point.x + ord('A')), chr(point.y + ord('A'))))
def sendMoveByXY(x, y):
"""
與平台通信,發送Move指令 格式 move 12 12是坐标1, 2
:param point: 向平台送出下棋的點坐标
:return: None
"""
print("move {0}{1}".format(chr(x + ord('A')), chr(y + ord('A'))))
def sendMovePass():
print("move pass")
def sendName(name):
"""
向平台發送引擎名稱
:param name:名稱
:return: None
"""
print("name {0}".format(name))
資訊互動子產品中,當平台向引擎發送指令以及資料時,對于不同的指令都會有一些處理邏輯,最後可能導緻資訊互動子產品檔案if/else每條分支都很長,代碼可讀性和可維護性都大大降低。是以這裡我們将處理每條指令的邏輯都封裝為一個方法。
class BoardControlInterface(object):
"""
這是一個棋盤控制的接口,所有的棋盤控制類都應該繼承他,并且實作裡面的方法
"""
def move(self, msg):
"""
:param msg: 平台發送過來的資訊
:return:
"""
print("move action")
pass
def accept(self):
"""
如果下法被平台接受了,則執行這個方法
:return:
"""
print("accept action")
pass
def refuse(self):
"""
如果被平台拒絕了則執行這個方法
:return:
"""
print("refuse action")
pass
def take(self):
"""
如果我方提子,則執行這個方法
:return:
"""
print("take action")
pass
def taked(self, msg):
"""
如果我方被對方提子,則執行這個方法
:param msg:平台發送過來的資訊
:return:
"""
print("taked action")
pass
def new(self, msg):
"""
建立對局
:param msg:平台發送過來的資訊
:return:
"""
print("new action")
pass
接收平台資訊的子產品,原來的子產品代碼量大,處理邏輯複雜,重構就要解決這個問題,提高程式的可讀性和可維護性。沒有什麼是加一層不能解決的,如果有,那就加兩層,這裡将原來的資訊互動子產品分為了兩層,一層負責接收,另一層負責處理。
import sys
import go_configuration.game_conf as gf
import go_interaction.order_to_be_send as order
import go_board_control.board_control_interface as BoardControl
sys.path.append("..")
"""
這裡是總控制檔案,當處理的條件少的時候直接用if/else結構簡單清晰,條件變多的時候建議用反射機制實作
"""
def run():
boardControl = BoardControl.BoardControlInterface()
while True:
sys.stdout.flush()
msg = input().split(" ")
sys.stdin.flush()
if gf.GameStatus.isGuessMode == True:
pass
elif 'new' in msg[0]:
boardControl.new(msg)
pass
elif 'move' in msg[0]:
boardControl.move(msg)
order.sendMove(1, 2)
pass
elif 'accept' in msg[0]:
boardControl.accept()
pass
elif 'refuse' in msg[0]:
boardControl.refuse()
pass
elif 'take' in msg[0]:
boardControl.take()
pass
elif 'taked' in msg[0]:
boardControl.taked(msg)
pass
elif 'error' in msg[0]:
pass
elif 'name?' in msg[0]:
order.sendName("Blizzard")
msg.clear()
pass
總結:
1. 對于經常用到的語句,都把他們封裝為函數,以函數調用的方式執行,讓程式的可維護性大大提高。
2. 對于代碼邏輯複雜,單檔案過長導緻可讀性和可維護性都低的情況,可以在原有基礎上分層,每層盡量隻做一件事情。