AI五子棋_09 消息循環實現自動對局


AI五子棋 第九步

恭喜你到達第九步!

上一步我們已經完成了一個AI大腦的最核心功能。我們可以用它來對戰了。

訪問服務器 http://202.207.12.156:9012/join_game,會返回一個游戲編號game_id。之后你可以使用這個游戲編號,進行游戲http://2**.2**.**.1**:9012/play_game/{game_id}並查詢游戲狀態http://2**.2**.**.1**:9012/check_game/{game_id}

利用這三個功能我們就可以讓我們的AI參戰了。這個過程應該是這樣的,這是一個典型的消息循環。

  • join_game加入游戲

  • check_game檢查游戲狀態
    a. 如果游戲完成就退出
    b. 如果不輪你下,就等一會,否則使用AI確定要落子的位置,並用play_game告知服務器你落子的位置
    c. 返回到第2步用check_game檢查游戲狀態

其中join_game需要登錄,需要提交用戶名和密碼,需要使用第五步使用的加密方法對密碼加密,用戶名寫入user字段,加密后的密碼寫入password字段。另外需要傳入字段data_type,將其設為json,返回字段game_id是加入的游戲編號。

play_game也需要登錄,落子的坐標寫入coord字段。

check_game不需要登錄,返回當前的游戲狀態, 會返回以下狀態:

狀態

is_success,error 查詢是否成功及錯誤原因

step 當前是第幾步

creator 游戲一方的用戶名

creator_name 游戲一方的名字(昵稱)

creator_stone 游戲一方使用的棋子(x表示黑棋,o表示白棋)

opponent 游戲另一方的用戶名

opponent_name 游戲另一方的名字(昵稱)

opponent_stone 游戲另一方使用的棋子(x表示黑棋,o表示白棋)

ready 游戲是否就緒,兩個玩家都在線時,游戲進入就緒狀態

current_turn 當前應當落子的玩家的用戶名

current_stone 當前應當落子的玩家的使用的棋子(x表示黑棋,o表示白棋)

left_time 剩余時間,為避免玩家過長思考,限制玩家必須在60秒內落子,否則游戲結束

winner 獲勝的玩家的用戶名,當游戲沒有產生贏家時,該值為None

board 棋盤的坐標表示

last_step 上一步落子的坐標

win_step 如果一方獲勝,這個字段給出連成五子的一條線的棋子坐標

注意不要過於頻繁的檢查游戲的狀態,使用sleep函數等待服務器更新狀態,兩次檢查以5到10秒的間隔為宜。

任務 9

實現消息循環,開始作戰吧!

Python實現

import requests as re
import time as t

def fastModular(x):
	"""x[0] = base """
	"""x[1] = power"""
	"""x[2] = modulus"""
	result = 1
	while(x[1] > 0):
		if(x[1] & 1):
			result = result * x[0] % x[2]
		x[1] = int(x[1]/2)
		x[0] = x[0] * x[0] % x[2]
	return result

def str_to_num(strings):
	sum = 0
	lens = len(strings)
	for i in range(0,lens):
		sum += ord(strings[i])*256**(lens-i-1)
	return sum

def encodeLogin(password):
         # 公鑰
        power = 65537
        modulus = 135261828916791946705313569652794581721330948863485438876915508683244111694485850733278569559191167660149469895899348939039437830613284874764820878002628686548956779897196112828969255650312573935871059275664474562666268163936821302832645284397530568872432109324825205567091066297960733513602409443790146687029

        return hex(fastModular([str_to_num(password),power,modulus]))

def join_game(user, myHexPass):
        """加入游戲並返回一個 get回復包對象"""

        url = 'http://2**.2**.**.1**:9012/join_game/'
        param = {
                'user' : user,
                'password': myHexPass,
                'data_type':'json'
                }

        getHtml = re.get(url, params = param)

        print(f"Open a new game{getHtml.text}")
        return getHtml

def check_game(game_id):
        url = 'http://2**.2**.**.1**:9012/check_game/'+ str(game_id)
        getState = re.get(url)
        #print(getState.text)    # 測試顯示數據用
        return getState

def play_game(user, myHexPass, game_id, coord ):
        url = 'http://2**.2**.**.1**:9012/play_game/' + str(game_id)
        param = {
                'user' : user,
                'password': myHexPass,
                'data_type':'json',
                'coord' : coord
                }
        re.get(url, params=param)
        
def getIndexNum(coords):
        """coords y x"""
        # 0行 [0]='.'--- [14]='.'[15]='\n'
        # 1行 [16]='.'--- [30]='.'[31]='\n'
        # 2行 [32]='.'--- [46]='.'[47]='\n'
        # 15行 [240]='.'--- [254]='.'[255]='\n'
        return (ord(coords[0]) - ord('a'))*16 + ord(coords[1]) - ord('a')

def allIndexStr():
        spot = []
        for i in range(0,15):
                for j in range(0,16):
                        spot.append(chr(i+97) + chr(j+97))
        return spot

def getLine(coord,board):
        """
        獲得中心點的四周 15 點情況 返回一個字符串列表
        coord[0] y 縱坐標 coord[1] x 控制橫坐標
        board  棋局
        """
        line = ['', '' , '' , '']
        i =0
        """ 核心思想就是 將周圍點兩個坐標x,y的限制 轉化為一個位置index的限制 """
        while(i != 15):
                if ord(coord[1])-ord('a')- 7 + i in range(0, 15) :      # line[0]是橫線 只需保證 橫坐標在棋盤里就好
                        line[0] +=board[(ord(coord[0])-ord('a'))*16 + ord(coord[1])-ord('a')- 7 + i]
                else:
                        line[0] += ' '
                if ord(coord[0])-ord('a') -7 + i in range(0, 15) :      # line[2]是豎線 只需保證 縱坐標在棋盤里就好
                        line[2] +=board[(ord(coord[0])-ord('a')- 7 + i)*16 + ord(coord[1])-ord('a')]
                else:
                        line[2] += ' '
                # - 7 + i 是從最小值上升判斷  + 7 - i 是從最大值下降判斷 兩者沒有什么不同 根據index的求法而定
                if ord(coord[1])-ord('a')- 7 + i in range(0, 15) and ord(coord[0])-ord('a') -7 + i in range(0, 15) :    # line[1]是\線 保證 橫縱坐標都在棋盤里就好
                        line[1] +=board[(ord(coord[0])-ord('a')- 7 + i)*16 + ord(coord[1])-ord('a')- 7 + i]
                else:
                        line[1] += ' '
                if ord(coord[1])-ord('a') + 7 - i in range(0, 15) and ord(coord[0])-ord('a') - 7 + i in range(0, 15) :   # line[3]是/線 保證 橫縱坐標都在棋盤里就好
                        line[3] +=board[(ord(coord[0])-ord('a')- 7 + i)*16 + ord(coord[1])-ord('a')+ 7 - i]
                else:
                        line[3] += ' '
                        
                i += 1
        return line

def judge(testOrder):
        if (len(testOrder)//2) % 2 == 0:     # 我是黑方
                return 'MO'
        else:                           # 我是白方
                return 'OM'

def RuleWithPoints():
        RWP = {
                ("CMMMM","MCMMM","MMCMM","MMMCM","MMMMC") : 10000,
                ("COOOO","OCOOO","OOCOO","OOOCO","OOOOC") : 6000,
                (".CMMM.",".MCMM.",".MMCM.",".MMMC.") : 5000,
                ("COOO.",".OOOC",".OOCO.",".OCOO.") :2500,
                ("OCMMM.","OMCMM.","OMMCM.","OMMMC.",".CMMMO",".MCMMO",".MMCMO",".MMMCO"):2000,
                (".MMC.",".MCM.",".CMM.") : 400,
                (".OOC","COO.","MOOOC","COOOM") : 400,
                (".MMCO",".MCMO",".CMMO","OMMC.","OMCM.","OCMM.","MOOC","COOM") : 200,
                (".MC.",".CM.") : 50,
                ('.') : 1
                }
        return RWP

def getMaxCoords(Order,RWP, indexSrc):
        """對於每一個當下的棋局 返回一個最成功的下點"""

        board = ''              # 棋板
        for i in range(0,15):
                board += '...............' + '\n'
                
        step = 0 # 步數 用於判斷黑白 黑方先走
        BW = judge(Order)

        
        for i in range(0, len(Order), 2): # i = 0 2 4 6 8 

                index = getIndexNum(Order[i:i+2])

                # Python不允許直接修改字符串 只能用拼接的方法
                if (step % 2) == 0:
                        board = board[0: index] + BW[0] + board[index + 1:]
                else:
                        board = board[0: index] + BW[1] + board[index + 1:]
                step += 1
        print(board) # 測試顯示數據用

        maxCoord = ''
        maxPoints = 0
        for i in range(0,len(board)):
                if board[i] == '.':
                        tempBoard = board[0: i] + 'C' + board[i + 1:]
                        coord = indexSrc[i]
                        lines4 = ','.join(getLine(coord,tempBoard))
                        points = 0
                        for rules,value in RWP.items():
                                for rul in range(0, len(rules)) :
                                        if rules[rul] in lines4:
                                                points += value * lines4.count(rules[rul])
                
                        if points > maxPoints :
                                maxPoints = points
                                maxCoord = coord
                                
        print(f"{maxCoord} {maxPoints}",end=' ')
        return maxCoord


        
user = 'yyds'
password = 'xxxxxx'
myHexPass = encodeLogin(password)
RWP = RuleWithPoints()
indexSrc = allIndexStr()


game_id = join_game(user, myHexPass ).json()["game_id"]
state = check_game(game_id).json()

print("Looking forgame partners ...")
while state['ready'] == "False":
        state = check_game(game_id).json()
        print(state['ready'],end=" ")
        t.sleep(5)
		
if state['creator'] != user:
        opponent = state['creator']
else:
        opponent = state['opponent_name']    
		
while state['ready'] == "True":
        if state['current_turn'] == user :
                order = state['board']
                coord = getMaxCoords(order, RWP, indexSrc)
                play = play_game(user, myHexPass, game_id, coord)
                print(f"Playing {coord}")
        else:
                print(f"Waiting for {opponent} to play")
                
        t.sleep(5)
        state = check_game(game_id).json()

        if state['winner'] != "None":
                
                print(f"The winner is {state['winner']}")
                break
        



直接運行 即可開始一場對局

程序運行解釋

注意用戶名,密碼。以及各種鏈接。

修改為周圍15點的判斷。黑棋可以直接判斷走中心點。

首先開啟一場對局,等待其他人加入。

如果有人加入則開始對局,每次對局輪到自己下棋時會出現當前的棋局。程序算出最佳點后返回給服務器。

等待對手下棋。

下一次輪到我們的時候,我們之前下了一顆,對手下了一顆,所以比上一盤多兩顆棋子。

同時,程序運行時。也可以打開網站,點擊開戰功能,觀察整個對局過程。

加油吧少年,根據這個博客你也可以寫出一個相對智能的五子棋程序,甚至更強的AI算法!

文章會隨時改動,注意到博客里去看。一些網站會爬取本文章,但是可能會有出入。
https://www.cnblogs.com/asmurmur/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM