最近在网上看到一个有意思的开源项目,基于快手团队开发的开源AI斗地主——DouZero做的一个“成熟”的AI,项目开源地址【–tianqiraf】。
今天我们就一起来学习下是如何制作一个基于DouZero的出牌器,看看AI是如何来帮助斗地主的!
一、核心功能设计
首先这款出牌器是基于DouZero开发的,核心是需要利用训练好的AI模型来帮住我们,给出最优出牌方案。
其次关于出牌器,先要需要确认一个AI出牌角色,代表我们玩家自己。我们只要给这个AI输入玩家手牌和三张底牌。确认好地主和农民的各个角色,告诉它三个人对应的关系,这样就可以确定队友和对手。
我们还要将每一轮其他两人的出牌输入,这样出牌器就可以根据出牌数据,及时提供给我们最优出牌决策,带领我们取得胜利!
那么如何获取三者之间的关系呢?谁是地主?谁是农民?是自己一人作战还是农民合作?自己玩家的手牌是什么?三张底牌是什么?这些也都需要在开局后确认好。
大致可以整理出要实现的核心功能如下:
UI设计排版布局
显示三张底牌
显示AI角色出牌数据区域,上家出牌数据区域,下家出牌数据区域,本局胜率区域
AI玩家手牌区域
AI出牌器开始停止
手牌和出牌数据识别
游戏刚开始根据屏幕位置,截图识别AI玩家手牌及三张底牌
确认三者之间的关系,识别地主和农民角色,确认队友及对手关系
识别每轮三位玩家依次出了什么牌,刷新显示对应区域
AI出牌方案输出
加载训练好的AI模型,初始化游戏环境
每轮出牌判断,根据上家出牌数据给出最优出牌决策
自动刷新玩家剩余手牌和本局胜率预测
二、实现步骤
1.UI设计排版布局
根据上述功能,首先考虑进行简单的UI布局设计,使用的是pyqt5。核心设计代码如下:defsetupUi(self,Form):
("Form")
(440,395)
font=()
("Arial")
(9)
(True)
(False)
(75)
(font)
=(Form)
((240,180,171,61))
font=()
(14)
(font)
()
("WinRate")
=(Form)
((60,330,121,41))
font=()
("Arial")
(14)
(True)
(75)
(font)
("")
("InitCard")
=(Form)
((10,260,421,41))
font=()
(14)
(font)
()
("UserHandCards")
=(Form)
((10,80,201,61))
()
()
("LPlayer")
=()
((0,0,201,61))
font=()
(14)
(font)
()
("LPlayedCard")
=(Form)
((230,80,201,61))
font=()
(16)
(font)
()
()
("RPlayer")
=()
((0,0,201,61))
font=()
(14)
(font)
()
("RPlayedCard")
=(Form)
((40,180,171,61))
()
()
("Player")
=()
((0,0,171,61))
font=()
(14)
(font)
()
("PredictedCard")
=(Form)
((140,10,161,41))
font=()
(16)
(font)
()
("ThreeLandlordCards")
=(Form)
((260,330,111,41))
font=()
("Arial")
(14)
(True)
(75)
(font)
("")
("Stop")
(Form)
(_cards)
()
(Form)
defretranslateUi(self,Form):
_translate=
(_translate("Form","AI欢乐斗地主--Dragon少年"))
(_translate("Form","胜率:--%"))
(_translate("Form","开始"))
(_translate("Form","手牌"))
(_translate("Form","上家出牌区域"))
(_translate("Form","下家出牌区域"))
(_translate("Form","AI出牌区域"))
(_translate("Form","三张底牌"))
(_translate("Form","停止"))
2.手牌和出牌数据识别
接下来需要所有扑克牌的模板图片与游戏屏幕特定区域的截图进行对比,这样才能获取AI玩家手牌、底牌、每一轮出牌、三者关系(地主、地主上家、地主下家)。
识别AI玩家手牌及三张底牌:
我们可以截取游戏屏幕,根据固定位置来识别当前AI玩家的手牌和三张底牌。核心代码如下:
“是新的”标志
forhaveinlocList:
ifabs(e[0]-have)=distance:
flag=0
break
ifflag:
count+=1
(e[0])
returncount
坐标
=(414,804,1041,59)左侧玩家截图区域
=(1010,470,380,160)地主标志截图区域(右-我-左)
=(817,36,287,136)玩家手牌
_hand_cards_real=""
_hand_cards_env=[]
其他玩家手牌(整副牌减去玩家手牌,后续再减掉历史出牌)
_hand_cards=[]
玩家角色代码:0-地主上家,1-地主,2-地主下家
_position_code=None
_position=""
出牌顺序:0-玩家出牌,1-玩家下家出牌,2-玩家上家出牌
_order=0
=None
识别三张底牌
_landlord_cards_real=_three_landlord_cards()
("底牌:"+_landlord_cards_real)
_landlord_cards_env=[RealCard2EnvCard[c]forcinlist(_landlord_cards_real)]
整副牌减去玩家手上的牌,就是其他人的手牌,再分配给另外两个角色(如何分配对AI判断没有影响)
foriinset(AllEnvCard):
_hand_([i]*((i)-_hand_cards_(i)))
_play_data_({
'three_landlord_cards':_landlord_cards_env,
['landlord_up','landlord','landlord_down'][(_position_code+0)%3]:
_hand_cards_env,
['landlord_up','landlord','landlord_down'][(_position_code+1)%3]:
_hand_cards[0:17]if(_position_code+1)%3!=1_hand_cards[17:],
['landlord_up','landlord','landlord_down'][(_position_code+2)%3]:
_hand_cards[0:17]if(_position_code+1)%3==1_hand_cards[17:]
})
print(_play_data_list)
得到出牌顺序
_order=0_position=="landlord"_position=="landlord_up"elae2
效果如下:
3.AI出牌方案输出
下面我们就需要用到DouZero开源的AI斗地主了。DouZero项目地址:。我们需要将该开源项目下载并导入项目中。
创建一个AI玩家角色,初始化游戏环境,加载模型,进行每轮的出牌判断,控制一局游戏流程的进行和结束。核心代码如下:
初始化游戏环境
=GameEnv(ai_players)
玩家出牌时就通过智能体获取action,否则通过识别获取其他玩家出牌
_order==0:
("")
action_message=(_position)
不出
pass_flag=('pics/',
region=,
confidence=)
识别下家出牌
_played_cards_real=_other_cards()
更新界面
(_played_cards__played_cards_realelse"不出")
_order=2
_order==2:
("")
_white()==0and\
('pics/',
region=,
confidence=):
print("等待上家出牌")
()
()500:
(,50)
()
()500:
(,50)
未找到"不出"
ifpass_flagisNone:
找到"不出"
else:
_played_cards_real=""
print("\n上家出牌:",_played_cards_real)
_played_cards_env=[RealCard2EnvCard[c]forcinlist(_played_cards_real)]
(_position,_played_cards_env)
_order=0
坐标
=(414,804,1041,59)左边截图区域
=(1010,470,380,160)地主标志截图区域(右-我-左)
=(817,36,287,136)#地主底牌截图区域,resize成349x168
3.运行测试
当所有环境配置完成,各区域坐标位置确认无误之后,下面我们就可以直接运行程序,测试效果啦~
基于这个DouZero项目做一个“成熟”的AI,项目开源地址【–tianqiraf】。





