<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          AI斗地主?我lbw沒有開掛!

          共 33777字,需瀏覽 68分鐘

           ·

          2021-08-21 03:58

          前言

          上兩天在公眾號發(fā)過的一個(gè)開源項(xiàng)目 DouZero,不知道大家還記不記得了:

          用 AI 斗地主,就問你秀不秀!

          有個(gè)網(wǎng)友基于 DouZero 制作了一個(gè)歡樂斗地主出牌器,看看 AI 是如何來幫助我們斗地主,贏歡樂豆,實(shí)現(xiàn)財(cái)富自由的吧!

          首先一起來看看AI斗地主出牌器的效果:

          下面,我們開始介紹這個(gè)AI出牌器的制作過程。


          一、核心功能設(shè)計(jì)

          首先我們這款出牌器是基于DouZero開發(fā)的,核心是需要利用訓(xùn)練好的AI模型來幫住我們,給出最優(yōu)出牌方案。

          其次關(guān)于出牌器,先要需要確認(rèn)一個(gè)AI出牌角色,代表我們玩家自己。我們只要給這個(gè)AI輸入玩家手牌和三張底牌。確認(rèn)好地主和農(nóng)民的各個(gè)角色,告訴它三個(gè)人對應(yīng)的關(guān)系,這樣就可以確定隊(duì)友和對手。我們還要將每一輪其他兩人的出牌輸入,這樣出牌器就可以根據(jù)出牌數(shù)據(jù),及時(shí)提供給我們最優(yōu)出牌決策,帶領(lǐng)我們?nèi)〉脛倮?/span>


          那么如何獲取三者之間的關(guān)系呢?誰是地主?誰是農(nóng)民?是自己一人作戰(zhàn)還是農(nóng)民合作?自己玩家的手牌是什么?三張底牌是什么?這些也都需要在開局后確認(rèn)好。

          拆解需求,大致可以整理出核心功能如下:

          UI設(shè)計(jì)排版布局

          • 顯示三張底牌

          • 顯示AI角色出牌數(shù)據(jù)區(qū)域,上家出牌數(shù)據(jù)區(qū)域,下家出牌數(shù)據(jù)區(qū)域,本局勝率區(qū)域

          • AI玩家手牌區(qū)域

          • AI出牌器開始停止


          手牌和出牌數(shù)據(jù)識別

          • 游戲剛開始根據(jù)屏幕位置,截圖識別AI玩家手牌及三張底牌

          • 確認(rèn)三者之間的關(guān)系,識別地主和農(nóng)民角色,確認(rèn)隊(duì)友及對手關(guān)系

          • 識別每輪三位玩家依次出了什么牌,刷新顯示對應(yīng)區(qū)域


          AI出牌方案輸出

          • 加載訓(xùn)練好的AI模型,初始化游戲環(huán)境

          • 每輪出牌判斷,根據(jù)上家出牌數(shù)據(jù)給出最優(yōu)出牌決策

          • 自動(dòng)刷新玩家剩余手牌和本局勝率預(yù)測


          二、實(shí)現(xiàn)步驟

          1. UI設(shè)計(jì)排版布局

          根據(jù)上述功能,我們首先考慮進(jìn)行簡單的UI布局設(shè)計(jì),這里我們使用的是pyqt5。核心設(shè)計(jì)代碼如下:

          def setupUi(self, Form):
              Form.setObjectName("Form")
              Form.resize(440395)
              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(24018017161))
              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(6033012141))
              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(1026042141))
              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(108020161))
              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(0020161))
              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(2308020161))
              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(0020161))
              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(4018017161))
              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(0017161))
              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(1401016141))
              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(26033011141))
              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""上家出牌區(qū)域"))
              self.RPlayedCard.setText(_translate("Form""下家出牌區(qū)域"))
              self.PredictedCard.setText(_translate("Form""AI出牌區(qū)域"))
              self.ThreeLandlordCards.setText(_translate("Form""三張底牌"))
              self.Stop.setText(_translate("Form""停止"))

          實(shí)現(xiàn)效果如下:

          2. 手牌和出牌數(shù)據(jù)識別

          下面我們需要所有撲克牌的模板圖片與游戲屏幕特定區(qū)域的截圖進(jìn)行對比,這樣才能獲取AI玩家手牌、底牌、每一輪出牌、三者關(guān)系(地主、地主上家、地主下家)。

          識別AI玩家手牌及三張底牌:

          我們可以截取游戲屏幕,根據(jù)固定位置來識別當(dāng)前AI玩家的手牌和三張底牌。核心代碼如下:

          # 牌檢測結(jié)果濾波
          def cards_filter(self, location, distance):  
              if len(location) == 0:
                  return 0
              locList = [location[0][0]]
              count = 1
              for e in location:
                  flag = 1  # “是新的”標(biāo)志
                  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((349168))
              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

          效果如下所示:

          地主、地主上家、地主下家:

          同理我們可以根據(jù)游戲屏幕截圖,識別地主的圖標(biāo),確認(rèn)地主角色。核心代碼如下:

          # 查找地主角色
          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


          這樣我們就可以得到玩家AI手牌,其他玩家手牌(預(yù)測),地主三張底牌,三者角色關(guān)系,出牌順序。核心代碼如下:


          # 坐標(biāo)
          self.MyHandCardsPos = (414804104159)  # AI玩家截圖區(qū)域
          self.LPlayedCardsPos = (530470380160)  # 左側(cè)玩家截圖區(qū)域
          self.RPlayedCardsPos = (1010470380160)  # 右側(cè)玩家截圖區(qū)域
          self.LandlordFlagPos = [(1320300110140), (320720110140), (500300110140)]  # 地主標(biāo)志截圖區(qū)域(右-我-左)
          self.ThreeLandlordCardsPos = (81736287136)      # 地主底牌截圖區(qū)域,resize成349x168

          def init_cards(self):
              # 玩家手牌
              self.user_hand_cards_real = ""
              self.user_hand_cards_env = []
              # 其他玩家出牌
              self.other_played_cards_real = ""
              self.other_played_cards_env = []
              # 其他玩家手牌(整副牌減去玩家手牌,后續(xù)再減掉歷史出牌)
              self.other_hand_cards = []
              # 三張底牌
              self.three_landlord_cards_real = ""
              self.three_landlord_cards_env = []
              # 玩家角色代碼:0-地主上家, 1-地主, 2-地主下家
              self.user_position_code = None
              self.user_position = ""
              # 開局時(shí)三個(gè)玩家的手牌
              self.card_play_data_list = {}
              # 出牌順序:0-玩家出牌, 1-玩家下家出牌, 2-玩家上家出牌
              self.play_order = 0
              self.env = None
              # 識別玩家手牌
              self.user_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.three_landlord_cards_real = self.find_three_landlord_cards(self.ThreeLandlordCardsPos)
              self.ThreeLandlordCards.setText("底牌:" + self.three_landlord_cards_real)
              self.three_landlord_cards_env = [RealCard2EnvCard[c] for c in list(self.three_landlord_cards_real)]
              # 識別玩家的角色
              self.user_position_code = self.find_landlord(self.LandlordFlagPos)
              if self.user_position_code is None:
                  items = ("地主上家""地主""地主下家")
                  item, okPressed = QInputDialog.getItem(self, "選擇角色""未識別到地主,請手動(dòng)選擇角色:", items, 0False)
                  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);')

              # 整副牌減去玩家手上的牌,就是其他人的手牌,再分配給另外兩個(gè)角色(如何分配對AI判斷沒有影響)
              for i in set(AllEnvCard):
                  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:17if (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:17if (self.user_position_code + 1) % 3 == 1 else self.other_hand_cards[17:]
              })
              print(self.card_play_data_list)
              # 生成手牌結(jié)束,校驗(yàn)手牌數(shù)量
              if len(self.card_play_data_list["three_landlord_cards"]) != 3:
                  QMessageBox.critical(self, "底牌識別出錯(cuò)""底牌必須是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, "手牌識別出錯(cuò)""初始手牌數(shù)目有誤", QMessageBox.Yes, QMessageBox.Yes)
                  self.init_display()
                  return
              # 得到出牌順序
              self.play_order = 0 if self.user_position == "landlord" else 1 if self.user_position == "landlord_up" else 2


          效果如下:

          3. AI出牌方案輸出

          下面我們就需要用到DouZero開源的AI斗地主了。DouZero項(xiàng)目地址:https://github.com/kwai/DouZero


          我們需要將該開源項(xiàng)目下載并導(dǎo)入項(xiàng)目中。


          創(chuàng)建一個(gè)AI玩家角色,初始化游戲環(huán)境,加載模型,進(jìn)行每輪的出牌判斷,控制一局游戲流程的進(jìn)行和結(jié)束。核心代碼如下:


          # 創(chuàng)建一個(gè)代表玩家的AI
          ai_players = [00]
          ai_players[0] = self.user_position
          ai_players[1] = DeepAgent(self.user_position, self.card_play_model_path_dict[self.user_position])
          # 初始化游戲環(huán)境
          self.env = GameEnv(ai_players)
          # 游戲開始
          self.start()

          def start(self):
              self.env.card_play_init(self.card_play_data_list)
              print("開始出牌\n")
              while not self.env.game_over:
                  # 玩家出牌時(shí)就通過智能體獲取action,否則通過識別獲取其他玩家出牌
                  if self.play_order == 0:
                      self.PredictedCard.setText("...")
                      action_message = self.env.step(self.user_position)
                      # 更新界面
                      self.UserHandCards.setText("手牌:" + 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 "不出")
                      self.WinRate.setText("勝率:" + action_message["win_rate"])
                      print("\n手牌:", 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("等待玩家出牌")
                          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("等待下家出牌")
                          self.counter.restart()
                          while self.counter.elapsed() < 500:
                              QtWidgets.QApplication.processEvents(QEventLoop.AllEvents, 50)
                      self.counter.restart()
                      while self.counter.elapsed() < 500:
                          QtWidgets.QApplication.processEvents(QEventLoop.AllEvents, 50)
                      # 不出
                      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 = ""
                      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.RPlayedCard.setText(self.other_played_cards_real if self.other_played_cards_real else "不出")
                      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("等待上家出牌")
                          self.counter.restart()
                          while self.counter.elapsed() < 500:
                              QtWidgets.QApplication.processEvents(QEventLoop.AllEvents, 50)
                      self.counter.restart()
                      while self.counter.elapsed() < 500:
                          QtWidgets.QApplication.processEvents(QEventLoop.AllEvents, 50)
                      # 不出
                      pass_flag = pyautogui.locateOnScreen('pics/pass.png',
                                                           region=self.LPlayedCardsPos,
                                                           confidence=self.LandlordFlagConfidence)
                      # 未找到"不出"
                      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
                      # 更新界面
                      self.LPlayedCard.setText(self.other_played_cards_real if self.other_played_cards_real else "不出")
                  else:
                      pass

                  self.counter.restart()
                  while self.counter.elapsed() < 100:
                      QtWidgets.QApplication.processEvents(QEventLoop.AllEvents, 50)

              print("{}勝,本局結(jié)束!\n".format("農(nóng)民" if self.env.winner == "farmer" else "地主"))
              QMessageBox.information(self, "本局結(jié)束""{}勝!".format("農(nóng)民" if self.env.winner == "farmer" else "地主"),
                                      QMessageBox.Yes, QMessageBox.Yes)
              self.env.reset()
              self.init_display()


          到這里,整個(gè)AI斗地主出牌流程基本已經(jīng)完成了。

          三、出牌器用法

          按照上述過程,這款A(yù)I出牌器已經(jīng)制作完成了。后面應(yīng)該如何使用呢?如果不想研究源碼,只想使用這款A(yù)I斗地主出牌器,驗(yàn)證下效果,該怎么配置環(huán)境運(yùn)行這個(gè)AI出牌器呢?下面就開始介紹。

          1. 環(huán)境配置

          首先我們需要安裝這些第三方庫,配置相關(guān)環(huán)境,如下所示:

          torch==1.9.0
          GitPython==3.0.5
          gitdb2==2.0.6
          PyAutoGUI==0.9.50
          PyQt5==5.13.0
          PyQt5-sip==12.8.1
          Pillow>=5.2.0
          opencv-python
          rlcard

          2. 坐標(biāo)調(diào)整確認(rèn)

          我們可以打開歡樂斗地主游戲界面,將游戲窗口模式下最大化運(yùn)行,把AI出牌器程序窗口需要移至右下角,不能遮擋手牌、地主標(biāo)志、底牌、歷史出牌這些關(guān)鍵位置。

          其次我們要確認(rèn)屏幕截圖獲取的各個(gè)區(qū)域是否正確。如果有問題需要進(jìn)行區(qū)域位置坐標(biāo)調(diào)整。


          # 坐標(biāo)
          self.MyHandCardsPos = (414804104159)  # 我的截圖區(qū)域
          self.LPlayedCardsPos = (530470380160)  # 左邊截圖區(qū)域
          self.RPlayedCardsPos = (1010470380160)  # 右邊截圖區(qū)域
          self.LandlordFlagPos = [(1320300110140), (320720110140), (500300110140)]  # 地主標(biāo)志截圖區(qū)域(右-我-左)
          self.ThreeLandlordCardsPos = (81736287136)      # 地主底牌截圖區(qū)域,resize成349x168

          3. 運(yùn)行測試

          當(dāng)所有環(huán)境配置完成,各區(qū)域坐標(biāo)位置確認(rèn)無誤之后,下面我們就可以直接運(yùn)行程序,測試效果啦~

          首先我們運(yùn)行AI出牌器程序,打開歡樂斗地主游戲界面,進(jìn)入游戲。當(dāng)玩家就位,手牌分發(fā)完畢,地主身份確認(rèn)之后,我們就可以點(diǎn)擊畫面中開始按鈕,讓AI來幫助我們斗地主了。


          下面可以一起來看看這款A(yù)I出牌器的實(shí)驗(yàn)效果喔,看看AI是如何帶領(lǐng)農(nóng)民打倒地主,取得勝利的!



          版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。本文鏈接:

          https://blog.csdn.net/hhladminhhl/article/details/119304504

          ·················END·················

          推薦閱讀

          ?   好家伙,用Python寫了個(gè)網(wǎng)易云?   這,就很抽象?   我一年的堅(jiān)持,值了

          瀏覽 41
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一区毛片 | 国产无码中文字幕在线观看 | 肏逼视频在线观看 | 欧美大鸡吧操逼 | 久久小视频 |