Building an AI Dou Dizhu Card‑Playing Assistant with DouZero and PyQt5
This article explains how to create an AI‑driven Dou Dizhu card‑playing assistant by integrating the open‑source DouZero model with a PyQt5 GUI, covering UI layout, hand‑card and role recognition via screen capture, AI decision making, and step‑by‑step deployment instructions.
The article introduces an AI‑based Dou Dizhu (Chinese poker) assistant built on the open‑source DouZero project. It first outlines the core functionality: using a trained AI model to provide optimal card‑playing suggestions, recognizing the three players' roles (landlord, landlord_up, landlord_down), and displaying hand cards, opponent cards, and win‑rate.
Core Feature Design includes UI layout, hand‑card and play‑data detection, and AI move output. The UI must show the three bottom cards, AI role data areas (previous player, next player, win‑rate), the AI player's hand, and start/stop controls.
Implementation Steps
1. UI Design with PyQt5
The following PyQt5 code creates the main window and widgets:
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(440, 395)
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(9)
font.setBold(True)
Form.setFont(font)
self.WinRate = QtWidgets.QLabel(Form)
self.WinRate.setGeometry(QtCore.QRect(240, 180, 171, 61))
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.setPointSize(14)
self.InitCard.setFont(font)
self.InitCard.setObjectName("InitCard")
self.UserHandCards = QtWidgets.QLabel(Form)
self.UserHandCards.setGeometry(QtCore.QRect(10, 260, 421, 41))
font.setPointSize(14)
self.UserHandCards.setFont(font)
self.UserHandCards.setAlignment(QtCore.Qt.AlignCenter)
self.UserHandCards.setObjectName("UserHandCards")
# ... (other widgets omitted for brevity) ...
self.retranslateUi(Form)
self.InitCard.clicked.connect(Form.init_cards)
self.Stop.clicked.connect(Form.stop)
QtCore.QMetaObject.connectSlotsByName(Form)The retranslateUi method sets widget texts such as "胜率:--%", "开始", "手牌", etc.
2. Hand‑Card and Play‑Data Recognition
Screen capture is used to locate card templates within predefined regions. Example functions:
# Filter duplicate detections
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
# Get player's hand cards
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
# Get landlord's three bottom cards
def find_three_landlord_cards(self, pos):
three_landlord_cards_real = ""
img = pyautogui.screenshot(region=pos).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_realSimilar logic is applied to detect the landlord flag and other players' plays.
3. AI Decision Output
After initializing the game environment with the detected cards, the DouZero AI model provides action suggestions and win‑rate predictions:
# Create AI player and load model
ai_players = [0, 0]
ai_players[0] = self.user_position
ai_players[1] = DeepAgent(self.user_position, self.card_play_model_path_dict[self.user_position])
self.env = GameEnv(ai_players)
# Game loop
while not self.env.game_over:
if self.play_order == 0: # AI's turn
action_message = self.env.step(self.user_position)
self.PredictedCard.setText(action_message["action"] if action_message["action"] else "不出")
self.WinRate.setText("胜率:" + action_message["win_rate"])
# Update UI with new hand cards
elif self.play_order == 1:
# Wait for right‑hand player, capture their play or pass
pass_flag = pyautogui.locateOnScreen('pics/pass.png', region=self.RPlayedCardsPos, confidence=self.LandlordFlagConfidence)
if pass_flag is None:
self.other_played_cards_real = self.find_other_cards(self.RPlayedCardsPos)
else:
self.other_played_cards_real = ""
self.env.step(self.user_position, [RealCard2EnvCard[c] for c in self.other_played_cards_real])
self.RPlayedCard.setText(self.other_played_cards_real if self.other_played_cards_real else "不出")
self.play_order = 2
elif self.play_order == 2:
# Similar handling for left‑hand player
...
# Cycle play_order accordingly
print("{}胜,本局结束!".format("农民" if self.env.winner == "farmer" else "地主"))
QMessageBox.information(self, "本局结束", "{}胜!".format("农民" if self.env.winner == "farmer" else "地主"))
self.env.reset()
self.init_display()The assistant validates card counts (3 bottom cards, 17 cards for each farmer, 20 for the landlord) and reports errors via message boxes.
4. Deployment and Usage
Required third‑party libraries are listed (torch, GitPython, PyAutoGUI, PyQt5, Pillow, opencv‑python, rlcard). After installing them, adjust the screen‑capture coordinates to match the user’s display, then run the program, click the "开始" button, and let the AI guide the game.
Overall, the guide provides a complete pipeline from UI creation, visual card detection, role identification, AI decision making, to practical execution for the Dou Dizhu game.
Python Programming Learning Circle
A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.