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/

AI五子棋_09 消息循环实现自动对局的更多相关文章

  1. 揭开.NET消息循环的神秘面纱(GetMessage()无法取得任何消息,就会进入Idle(空闲)状态,进入睡眠状态(而不是Busy Waiting)。当消息队列不再为空的时候,程序会自动醒过来)

    揭开.NET消息循环的神秘面纱(-) http://hi.baidu.com/sakiwer/item/f17dc33274a04df2a9842866 曾经在Win32平台下奋战的程序员们想必记得, ...

  2. 深入探讨MFC消息循环和消息泵

    首先,应该清楚MFC的消息循环(::GetMessage,::PeekMessage),消息泵(CWinThread::PumpMessage)和MFC的消息在窗口之间的路由是两件不同的事情.在MFC ...

  3. 安卓中的消息循环机制Handler及Looper详解

    我们知道安卓中的UI线程不是线程安全的,我们不能在UI线程中进行耗时操作,通常我们的做法是开启一个子线程在子线程中处理耗时操作,但是安卓规定不允许在子线程中进行UI的更新操作,通常我们会通过Handl ...

  4. 异步IO(协程,消息循环队列)

    同步是CPU自己主动查看IO操作是否完成,异步是IO操作完成后发出信号通知CPU(CPU是被通知的) 阻塞与非阻塞的区别在于发起IO操作之后,CPU是等待IO操作完成再进行下一步操作,还是不等待去做其 ...

  5. Windows消息循环

    首先理解一句话:“Windows”向应用程序发送了一条消息.这里是指Windows调用了该程序内部的一个函数. 当UpdateWindow被调用后,新建的窗口在屏幕便完全可见了.此时,Windows会 ...

  6. Win32消息循环机制等【转载】http://blog.csdn.net/u013777351/article/details/49522219

    Dos的过程驱动与Windows的事件驱动 在讲本程序的消息循环之前,我想先谈一下Dos与Windows驱动机制的区别: DOS程序主要使用顺序的,过程驱动的程序设计方法.顺序的,过程驱动的程序有一个 ...

  7. 【转】QT CEF3 消息循环处理

    初次写博客,可能有点乱, 按照自己的实际经历谈一下CEF3钟遇到的一些坑,希望对以后的小伙有些帮助. 先说一下经历,当初第一次接触CEF3的时候,没做特殊处理,直接将cef3封装成控件,嵌入到QT程序 ...

  8. [译]理解Windows消息循环

    出处:http://www.cnblogs.com/zxjay/archive/2009/06/27/1512372.html 理解消息循环和整个消息传送机制对Windows编程来说非常重要.如果对消 ...

  9. 理解Windows消息循环机制

    理解消息循环和整个消息传送机制对Windows编程十分重要.如果对消息处理的整个过程不了解,在windows编程中会遇到很多令人困惑的地方. 什么是消息(Message)每个消息是一个整型数值,如果查 ...

  10. Android Handler 消息循环机制

    前言 一问起Android应用程序的入口,很多人会说是Activity中的onCreate方法,也有人说是ActivityThread中的静态main方法.因为Java虚拟机在运行的时候会自动加载指定 ...

随机推荐

  1. 【转载】 Alpha-beta剪枝

    原地址:https://www.jiqizhixin.com/graph/technologies/56dbb21e-c3f9-4e06-b16a-2e28f25b26c8 ============= ...

  2. Apache DolphinScheduler 如何实现自动化打包+单机/集群部署?

    Apache DolphinScheduler 是一款开源的分布式任务调度系统,旨在帮助用户实现复杂任务的自动化调度和管理.DolphinScheduler 支持多种任务类型,可以在单机或集群环境下运 ...

  3. 大模型时代的程序员:不会用AIGC编程,未来5年将被淘汰?

    作者 | 郭炜 策划 | 凌敏 前言 下面是一段利用 Co-Pilot 辅助开发的小视频,这是 Apache SeaTunnel 开发者日常开发流程中的一小部分.如果你还没有用过 Co-Pilot.C ...

  4. 数字名片工具 BBlog:使用一个链接,快速创建和分享你的信息主页和数字花园

    数字名片 BBlog:使用一个链接,快速创建和分享你的信息主页和数字花园 随着移动互联网技术的快速发展,数字名片产品已成为现代社交和网络营销的重要工具.数字名片可以帮助个人和企业在各种场合中展示和分享 ...

  5. Linux下C语言操作网卡的几个代码实例?特别实用

    前面写了一篇关于网络相关的文章:如何获取当前可用网口. <简简单单教你如何用C语言列举当前所有网口!> 那么如何使用C语言直接操作网口? 比如读写IP地址.读写MAC地址等. 一.原理 主 ...

  6. debian10环境安装rtpengine

    操作系统 :debian 10.13_x64 rtpengine版本:10.5 最新的debian12环境可通过apt直接安装rtpengine,但工作中有时候还会涉及到debian10这样的老系统, ...

  7. 不是 PHP 不行了,而是 MySQL 数据库扛不住啊

    大家好,我是码农先森. 大多数的业务场景下 PHP 还没有达到性能瓶颈,然而 MySQL 数据库就先行驾崩了.但我们总是不分青红皂白,一股脑的把原因归结于是 PHP 语言不行了,每当遇到这种情形我就会 ...

  8. JAVA SDK防反编译处理(原创)

    一.前言 网上找的资料是加密Jar包,运行时需要输入密码才能运行,这种方式的加密仅仅能于有main函数入口的加密,而不能满足对外提供SDK.我们的需求是对class文件加密防反编译,但又不影响别人二次 ...

  9. 为 OpenWrt 路由器编译 minieap-sysu 项目

    学校的校园网要使用锐捷认证,于是想把认证客户端装到刷了 OpenWrt 的路由器上面.然而认证客户端 Linux 版只支持 x86 架构,我的路由器处理器却是 AArch64 架构,装不了,所以只能想 ...

  10. Snap 使用

    Snap 是一个或多个应用程序的捆绑包,可在许多不同的 Linux 发行版中使用,无需依赖或修改.Snap 可从 Snap Store(一个拥有数百万用户的公共应用程序商店)中发现和安装.很多常用的软 ...