preface
First let’s take a look at the effect of AI doulandlord card player:
Let’s start with the process of making this AI card player.
I. Design of core functions
First of all, our card dealer is developed based on DouZero. The core is to use the trained AI model to help us and give the optimal card scheme.
Secondly, regarding the card player, we first need to confirm an AI card player character, representing us players. We just feed the AI the player’s hand and three cards from the bottom. Identify the roles of the landlord and the farmer, and tell them what their relationships are so that you can identify your teammates and opponents. We also input the cards of the other two players in each round, so that the card puller can provide us with the best decision in time based on the card puller data and lead us to victory!
So how do you get the relationship between the three? Who is the landlord? Who are the farmers? Do you fight alone or do you cooperate with peasants? What is your player’s hand? What are the three bottom cards? These also need to be confirmed after the opening.
After disassembling the requirements, the core functions can be roughly sorted out as follows:
UI design layout layout
-
Show three bottom cards
-
Show the AI character’s card playing data area, the previous card playing data area, the next card playing data area, the game’s win rate area
-
AI player hand area
-
AI player starts to stop
Hand and play data identification
-
At the beginning of the game, the AI player’s hand and three bottom cards are identified by screen position and screenshot
-
Identify the relationships among the three, identify the roles of landlord and farmer, identify the relationships of teammates and opponents
-
Identify which cards are played by the three players in each round and refresh the corresponding areas
AI card scheme output
-
Load the trained AI model and initialize the game environment
-
In each round, the optimal decision is made according to the data of the previous round
-
Automatically refreshes the remaining cards in the player’s hand and the prediction of the game’s win rate
Two, implementation steps
1. UI design and layout
Based on the above functionality, we first considered a simple UI layout design, which we used pyQt5. The core design code is as follows:
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", Self.winrate.settext (_translate("Form", "WinRate: The self - % ")). InitCard. SetText (_translate (" Form ", "start")) self. UserHandCards. SetText (_translate (" Form ", Self.rplayedcard. setText(_translate("Form", "last card area ")) self.rplayedcard. setText(_translate("Form"," last card area ")) self.rplayedcard. setText(_translate("Form", "last card area ")) self.rplayedcard. setText(_translate("Form", "Under a brand zone")) self. PredictedCard. The setText (_translate (" Form ", "AI play area")) self. ThreeLandlordCards. SetText (_translate (" Form ", "Three CARDS")) self. Stop the setText (_translate (" Form ", "Stop"))Copy the code
The implementation effect is as follows:
2. Identification of hand and playing card data
Now we need the template images of all the playing cards to be compared with screenshots of specific areas of the game screen to get the AI player’s hand, bottom card, each turn of the card, and the relationship between the three (landlord, landlord up, landlord down).
Identify AI player’s hand and three bottom cards:
We can capture the game screen and identify the current AI player’s hand and three bottom cards based on their fixed position. The core code is as follows:
Def cards_filter(self, location, distance): if len(location) == 0: Return 0 locList = [location[0][0]] count = 1 for e in location: flag = 1 if abs(e[0] - have) <= distance: flag = 0 break if flag: Count += 1 loclist.append (e[0]) return count # 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_realCopy the code
The effect is as follows:
Landlord, landlord, landlord:
Similarly, we can identify the icon of the landlord and identify the role of the landlord based on the screenshot of the game. The core code is as follows:
Def landlord_flag_pos (self, 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 NoneCopy the code
So we can get the player’s AI hand, the other player’s hand (prediction), the landlord’s three bottom cards, the relationship between the three roles, and the order in which the cards are played. The core code is as follows:
MyHandCardsPos = (414, 804, 1041, 59) Self.rplayedcardspos = (1010, 470, 380, 160) self.landLordFlagpos = [(1320, 300, 110, 140) (320, 720, 110, 140), (500, 300, 110, 140)] # landlord sign screenshot area (right - I - left) self. ThreeLandlordCardsPos = (817, 36, 287. Def cards(self) def cards(self): Self. User_hand_cards_env = [] # other players play self.other_played_cards_real = "" Self.other_played_cards_env = [] # Other player's hand (entire deck minus player's hand, Self.other_hand_cards = [] # three_landlord_cards_real = "" self.landlord_cards_env = [] # Player character code: 0- landlord shangjia, 1- landlord, Self. user_position_code = None self.user_position = "" 0- player plays, 1- player plays next, 2- Player's last card self.play_order = 0 self.env = None # Identify player's hand self.hand_cards_real = self.find_my_cards(self.MyHandCardsPos) self.UserHandCards.setText(self.user_hand_cards_real) self.user_hand_cards_env = [RealCard2EnvCard[c] for c in list(self.user_hand_cards_real)] self.landlord_cards_real = Self. Find_three_landlord_cards (self. ThreeLandlordCardsPos) self. ThreeLandlordCards. SetText (" card: " + self.three_landlord_cards_real) self.three_landlord_cards_env = [RealCard2EnvCard[c] for c in Self.user_position_code = self.find_landlord(self.landlordflagpos) if self.user_position_code is None: Item, okPressed = QinputDialog. getItem(self, "Select role "," Not identified, ") Please manually select the role :", items, 0, False) if okPressed and item: self.user_position_code = items.index(item) else: return self.user_position = ['landlord_up', 'landlord', 'landlord_down'][self.user_position_code] for player in self.Players: player.setStyleSheet('background-color: rgba(255, 0, 0, 0); ') self.Players[self.user_position_code]. SetStyleSheet ('background-color: rgba(255, 0, 0, 0.1); ') # subtracting the cards in the player's hand, that is, the cards in other players' hands, and assigning them to the other two characters (how to assign them does not affect AI judgment) self.other_hand_cards.extend([i] * (AllEnvCard.count(i) - self.user_hand_cards_env.count(i))) self.card_play_data_list.update({ 'three_landlord_cards': self.three_landlord_cards_env, ['landlord_up', 'landlord', 'landlord_down'][(self.user_position_code + 0) % 3]: self.user_hand_cards_env, ['landlord_up', 'landlord', 'landlord_down'][(self.user_position_code + 1) % 3]: self.other_hand_cards[0:17] if (self.user_position_code + 1) % 3 ! = 1 else self.other_hand_cards[17:], ['landlord_up', 'landlord', 'landlord_down'][(self.user_position_code + 2) % 3]: self.other_hand_cards[0:17] if (self.user_position_code + 1) % 3 == 1 else self.other_hand_cards[17:] }) Print (self.card_play_data_list) # if len(self.card_play_data_list["three_landlord_cards"]) = 3: QMessageBox. Critical (self, "card identification error "," card must be 3! ") , QMessageBox.Yes, QMessageBox.Yes) self.init_display() return if len(self.card_play_data_list["landlord_up"]) ! = 17 or \ len(self.card_play_data_list["landlord_down"]) ! = 17 or \ len(self.card_play_data_list["landlord"]) ! = 20: QMessageBox. Critical (self, "error "," error ", QMessageBox.Yes, Qmessagebox.yes) self.init_display() return # get the order self.play_order = 0 if self.user_position == "landlord" else 1 if self.user_position == "landlord_up" else 2Copy the code
The effect is as follows:
3. Output of AI card playing scheme
Next we need to use DouZero open source AI Doudizhu. DouZero project address: github.com/kwai/DouZer…
Create an AI player character, initialize the game environment, load the model, call the cards of each round, and control the start and end of a game. The core code is as follows:
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) # self.start() def Start (self): self.env.card_play_init(self.card_play_data_list) print(" start ") while not self.env.game_over: # players to play through the agent for the action, when otherwise by identifying for other players to play the if self. Play_order = = 0: self. PredictedCard. SetText ("..." ) action_message = self. The env. Step (self. User_position) # update interface self. UserHandCards. SetText (" hand: " + str(''.join( [EnvCard2RealCard[c] for c in self.env.info_sets[self.user_position].player_hand_cards]))[::-1]) Self. PredictedCard. SetText (action_message [] "action" if action_message [] "action" else "no"), the self. The WinRate. SetText (" odds: "+ action_message["win_rate"]) print("\n hand: ", STR (". Join ([EnvCard2RealCard[c] for c in self.env.info_sets[self.user_position].player_hand_cards])) print(" Action_message ["action"] if action_message["action"] else ", action_message["win_rate"]) while self.have_white(self.RPlayedCardsPos) == 1 or \ pyautogui.locateOnScreen('pics/pass.png', region=self.RPlayedCardsPos, confidence=self.LandlordFlagConfidence): Print (" Elapsed ") self.counter.restart() while self.counter.Elapsed () < 100: QtWidgets.QApplication.processEvents(QEventLoop.AllEvents, 50) self.play_order = 1 elif self.play_order == 1: self.RPlayedCard.setText("..." ) pass_flag = None while self.have_white(self.RPlayedCardsPos) == 0 and \ not pyautogui.locateOnScreen('pics/pass.png', region=self.RPlayedCardsPos, confidence=self.LandlordFlagConfidence): Print (" Elapsed ") self.counter. Restart () while self.counter. Elapsed () < 500: print(" Elapsed ") self.counter. QtWidgets.QApplication.processEvents(QEventLoop.AllEvents, 50) self.counter.restart() while self.counter.elapsed() < 500: QtWidgets.QApplication.processEvents(QEventLoop.AllEvents, PNG ('pics/pass.png', region= self.rplayedcardspos, Confidence = self. LandlordFlagConfidence) # is not found "no" if pass_flag is None: Self.other_played_cards_real = self.find_other_cards(self.rplayedcardspos) else: Self.other_played_cards_real = "" print("\n ", self.other_played_cards_real) self.other_played_cards_env = [RealCard2EnvCard[c] for c in list(self.other_played_cards_real)] self.env.step(self.user_position, Self.other_played_cards_env) # update interface self.rPlayedcard. setText(self.other_played_cards_real if Self. play_order = 2 elif self.play_order == 2: self.lplayedcard.settext ("...") self.play_order = 2 elif self.play_order == 2: self.lplayedcard.settext ("...") ) while self.have_white(self.LPlayedCardsPos) == 0 and \ not pyautogui.locateOnScreen('pics/pass.png', region=self.LPlayedCardsPos, confidence=self.LandlordFlagConfidence): Print (" Elapsed () ") self.counter. Restart () while self.counter. Elapsed () < 500: print(" Elapsed () ") self.counter. QtWidgets.QApplication.processEvents(QEventLoop.AllEvents, 50) self.counter.restart() while self.counter.elapsed() < 500: QtWidgets.QApplication.processEvents(QEventLoop.AllEvents, PNG ('pics/pass.png', region= self.lplayedcardspos, Confidence = self. LandlordFlagConfidence) # is not found "no" if pass_flag is None: Self.other_played_cards_real = self.find_other_cards(self.lplayedcardspos) else: Self.other_played_cards_real = "" print("\n ", self.other_played_cards_real) self.other_played_cards_env = [RealCard2EnvCard[c] for c in list(self.other_played_cards_real)] self.env.step(self.user_position, Self.other_played_cards_env) self.play_order = 0 # update interface self.lplayedcard.settext (self.other_played_cards_real if Self.other_played_cards_real else "o ") else: pass self.counter.restart() while self.counter.Elapsed () < 100: QtWidgets. QApplication. ProcessEvents (QEventLoop AllEvents, 50) print (" {}, the bureau over! \n". Format (" farmer" if self.env.winner == "farmer" else "farmer")) qmessagebox.information (self, "this game ends ", "{} win! ") .format(" farmer" if self.env. Winner == "farmer" else "farmer"), QMessageBox. QMessageBox.Yes) self.env.reset() self.init_display()Copy the code
Here, the whole AI doulandlord card process has been basically completed.
Three, the use of card
Following the above process, the AI card maker has been made. How should it be used later? If you don’t want to study the source code, just want to use the AI dou landlord card player, verify the effect, how to configure the environment to run the AI card player? Here’s how to start.
1. Configure the environment
First we need to install these third-party libraries and configure the relevant environment, as shown below:
Torch == 1.9.0gitPython == 3.0.5gitDB2 ==2.0.6 PyAutoGUI==0.9.50 PyQt5==5.13.0 PyQt5-SIP ==12.8.1 Pillow>=5.2.0 opencv-python rlcardCopy the code
2. Confirm coordinate adjustment
We can open the game interface to maximize the operation of the game window mode, and move the AI card player program window to the lower right corner, which can not block the key positions of hand cards, landlord signs, bottom cards and historical cards.
Second, we need to make sure that each area captured in the screenshot is correct. If there is a problem, adjust the area position coordinate.
MyHandCardsPos = (414, 804, 1041, 59) # self.lplayedcardspos = (530, 470, 380) Self.rplayedcardspos = (1010, 470, 380, 160) self.landLordFlagpos = [(1320, 300, 110, 140) (320, 720, 110, 140), (500, 300, 110, 140)] # landlord sign screenshot area (right - I - left) self. ThreeLandlordCardsPos = (817, 36, 287. 136) # Resize the screenshot area to 349x168Copy the code
3. Run the tests
When all the environment is configured and the coordinates of each area are confirmed, we can run the program directly to test the effect
First of all, we run the AI card extractor program, open the game interface and enter the game. Once the players are in position, the hands are handed out, and the landlord’s identity is confirmed, we can click the start button in the screen and ask the AI to help us fight the landlord.
The end:
Everyone like to remember a little praise, interested in the source can be private letter to me yo!
👇
Click on this line of font!