最近在网上看到一个有意思的开源项目,快手团队开发的开源AI斗地主——DouZero。今天我们就一起来学习制作一个基于DouZero的欢乐斗地主出牌器,看看AI是如何来帮助我们斗地主,赢欢乐豆,实现财富自由的吧!
首先一起来看看AI斗地主出牌器的效果:
私信小编01即可获取大量python学习资源
下面,我们开始介绍这个AI出牌器的制作过程。
首先我们这款出牌器是基于DouZero开发的,核心是需要利用训练好的AI模型来帮助我们,给出最优出牌方案。
其次关于出牌器,先要需要确认一个AI出牌角色,代表我们玩家自己。我们只要给这个AI输入玩家手牌和三张底牌。确认好地主和农民的各个角色,告诉它三个人对应的关系,这样就可以确定队友和对手。我们还要将每一轮其他两人的出牌输入,这样出牌器就可以根据出牌数据,及时提供给我们最优出牌决策,带领我们取得胜利!
那么如何获取三者之间的关系呢?谁是地主?谁是农民?是自己一人作战还是农民合作?自己玩家的手牌是什么?三张底牌是什么?这些也都需要在开局后确认好。
拆解需求,大致可以整理出核心功能如下:
UI设计排版布局
- 显示三张底牌
- 显示AI角色出牌数据区域,上家出牌数据区域,下家出牌数据区域,本局胜率区域
- AI玩家手牌区域
- AI出牌器开始停止
手牌和出牌数据识别
- 游戏刚开始根据屏幕位置,截图识别AI玩家手牌及三张底牌
- 确认三者之间的关系,识别地主和农民角色,确认队友及对手关系
- 识别每轮三位玩家依次出了什么牌,刷新显示对应区域
AI出牌方案输出
- 加载训练好的AI模型,初始化游戏环境
- 每轮出牌判断,根据上家出牌数据给出最优出牌决策
- 自动刷新玩家剩余手牌和本局胜率预测
根据上述功能,我们首先考虑进行简单的UI布局设计,这里我们使用的是pyqt5。核心设计代码如下:
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(440, 395)
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(9)
font.setBold(True)
font.setItalic(False)
font.setWeight(75)
Form.setFont(font)
self.WinRate = QtWidgets.QLabel(Form)
self.WinRate.setGeometry(QtCore.QRect(240, 180, 171, 61))
font = QtGui.QFont()
font.setPointSize(14)
self.WinRate.setFont(font)
self.WinRate.setAlignment(QtCore.Qt.AlignCenter)
self.WinRate.setObjectName("WinRate")
self.InitCard = QtWidgets.QPushButton(Form)
self.InitCard.setGeometry(QtCore.QRect(60, 330, 121, 41))
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(14)
font.setBold(True)
font.setWeight(75)
self.InitCard.setFont(font)
self.InitCard.setStyleSheet("")
self.InitCard.setObjectName("InitCard")
self.UserHandCards = QtWidgets.QLabel(Form)
self.UserHandCards.setGeometry(QtCore.QRect(10, 260, 421, 41))
font = QtGui.QFont()
font.setPointSize(14)
self.UserHandCards.setFont(font)
self.UserHandCards.setAlignment(QtCore.Qt.AlignCenter)
self.UserHandCards.setObjectName("UserHandCards")
self.LPlayer = QtWidgets.QFrame(Form)
self.LPlayer.setGeometry(QtCore.QRect(10, 80, 201, 61))
self.LPlayer.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.LPlayer.setFrameShadow(QtWidgets.QFrame.Raised)
self.LPlayer.setObjectName("LPlayer")
self.LPlayedCard = QtWidgets.QLabel(self.LPlayer)
self.LPlayedCard.setGeometry(QtCore.QRect(0, 0, 201, 61))
font = QtGui.QFont()
font.setPointSize(14)
self.LPlayedCard.setFont(font)
self.LPlayedCard.setAlignment(QtCore.Qt.AlignCenter)
self.LPlayedCard.setObjectName("LPlayedCard")
self.RPlayer = QtWidgets.QFrame(Form)
self.RPlayer.setGeometry(QtCore.QRect(230, 80, 201, 61))
font = QtGui.QFont()
font.setPointSize(16)
self.RPlayer.setFont(font)
self.RPlayer.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.RPlayer.setFrameShadow(QtWidgets.QFrame.Raised)
self.RPlayer.setObjectName("RPlayer")
self.RPlayedCard = QtWidgets.QLabel(self.RPlayer)
self.RPlayedCard.setGeometry(QtCore.QRect(0, 0, 201, 61))
font = QtGui.QFont()
font.setPointSize(14)
self.RPlayedCard.setFont(font)
self.RPlayedCard.setAlignment(QtCore.Qt.AlignCenter)
self.RPlayedCard.setObjectName("RPlayedCard")
self.Player = QtWidgets.QFrame(Form)
self.Player.setGeometry(QtCore.QRect(40, 180, 171, 61))
self.Player.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.Player.setFrameShadow(QtWidgets.QFrame.Raised)
self.Player.setObjectName("Player")
self.PredictedCard = QtWidgets.QLabel(self.Player)
self.PredictedCard.setGeometry(QtCore.QRect(0, 0, 171, 61))
font = QtGui.QFont()
font.setPointSize(14)
self.PredictedCard.setFont(font)
self.PredictedCard.setAlignment(QtCore.Qt.AlignCenter)
self.PredictedCard.setObjectName("PredictedCard")
self.ThreeLandlordCards = QtWidgets.QLabel(Form)
self.ThreeLandlordCards.setGeometry(QtCore.QRect(140, 10, 161, 41))
font = QtGui.QFont()
font.setPointSize(16)
self.ThreeLandlordCards.setFont(font)
self.ThreeLandlordCards.setAlignment(QtCore.Qt.AlignCenter)
self.ThreeLandlordCards.setObjectName("ThreeLandlordCards")
self.Stop = QtWidgets.QPushButton(Form)
self.Stop.setGeometry(QtCore.QRect(260, 330, 111, 41))
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(14)
font.setBold(True)
font.setWeight(75)
self.Stop.setFont(font)
self.Stop.setStyleSheet("")
self.Stop.setObjectName("Stop")
self.retranslateUi(Form)
self.InitCard.clicked.connect(Form.init_cards)
self.Stop.clicked.connect(Form.stop)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "AI欢乐斗地主--Dragon少年"))
self.WinRate.setText(_translate("Form", "胜率:--%"))
self.InitCard.setText(_translate("Form", "开始"))
self.UserHandCards.setText(_translate("Form", "手牌"))
self.LPlayedCard.setText(_translate("Form", "上家出牌区域"))
self.RPlayedCard.setText(_translate("Form", "下家出牌区域"))
self.PredictedCard.setText(_translate("Form", "AI出牌区域"))
self.ThreeLandlordCards.setText(_translate("Form", "三张底牌"))
self.Stop.setText(_translate("Form", "停止"))
实现效果如下:
2. 手牌和出牌数据识别下面我们需要所有扑克牌的模板图片与游戏屏幕特定区域的截图进行对比,这样才能获取AI玩家手牌、底牌、每一轮出牌、三者关系(地主、地主上家、地主下家)。
识别AI玩家手牌及三张底牌:
我们可以截取游戏屏幕,根据固定位置来识别当前AI玩家的手牌和三张底牌。核心代码如下:
# 牌检测结果滤波
def cards_filter(self, location, distance):
if len(location) == 0:
return 0
locList = [location[0][0]]
count = 1
for e in location:
flag = 1 # “是新的”标志
for have in locList:
if abs(e[0] - have) <= distance:
flag = 0
break
if flag:
count = 1
locList.append(e[0])
return count
# 获取玩家AI手牌
def find_my_cards(self, pos):
user_hand_cards_real = ""
img = pyautogui.screenshot(region=pos)
for card in AllCards:
result = pyautogui.locateAll(needleImage='pics/m' card '.png', haystackImage=img, confidence=self.MyConfidence)
user_hand_cards_real = card[1] * self.cards_filter(list(result), self.MyFilter)
return user_hand_cards_real
# 获取地主三张底牌
def find_three_landlord_cards(self, pos):
three_landlord_cards_real = ""
img = pyautogui.screenshot(region=pos)
img = img.resize((349, 168))
for card in AllCards:
result = pyautogui.locateAll(needleImage='pics/o' card '.png', haystackImage=img,
confidence=self.ThreeLandlordCardsConfidence)
three_landlord_cards_real = card[1] * self.cards_filter(list(result), self.OtherFilter)
return three_landlord_cards_real
效果如下所示:
地主、地主上家、地主下家:
同理我们可以根据游戏屏幕截图,识别地主的图标,确认地主角色。核心代码如下:
# 查找地主角色
def find_landlord(self, landlord_flag_pos):
for pos in landlord_flag_pos:
result = pyautogui.locateOnScreen('pics/landlord_words.png', region=pos, confidence=self.LandlordFlagConfidence)
if result is not None:
return landlord_flag_pos.index(pos)
return None