主頁 > 後端開發 > 斗地主老是輸?一起用Python做個AI出牌器,歡樂豆蹭蹭漲!

斗地主老是輸?一起用Python做個AI出牌器,歡樂豆蹭蹭漲!

2021-08-04 07:57:07 後端開發

前言

最近在網上看到一個有意思的開源專案,快手團隊開發的開源AI斗地主——DouZero,今天我們就一起來學習制作一個基于DouZero的歡樂斗地主出牌器,看看AI是如何來幫助我們斗地主,贏歡樂豆,實作財富自由的吧!

首先一起來看看AI斗地主出牌器的效果:
在這里插入圖片描述
下面,我們開始介紹這個AI出牌器的制作程序,

一、核心功能設計

首先我們這款出牌器是基于DouZero開發的,核心是需要利用訓練好的AI模型來幫住我們,給出最優出牌方案,

其次關于出牌器,先要需要確認一個AI出牌角色,代表我們玩家自己,我們只要給這個AI輸入玩家手牌和三張底牌,確認好地主和農民的各個角色,告訴它三個人對應的關系,這樣就可以確定隊友和對手,我們還要將每一輪其他兩人的出牌輸入,這樣出牌器就可以根據出牌資料,及時提供給我們最優出牌決策,帶領我們取得勝利!

那么如何獲取三者之間的關系呢?誰是地主?誰是農民?是自己一人作戰還是農民合作?自己玩家的手牌是什么?三張底牌是什么?這些也都需要在開局后確認好,

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

  • UI設計排版布局

    • 顯示三張底牌
    • 顯示AI角色出牌資料區域,上家出牌資料區域,下家出牌資料區域,本局勝率區域
    • AI玩家手牌區域
    • AI出牌器開始停止
  • 手牌和出牌資料識別

    • 游戲剛開始根據螢屏位置,截圖識別AI玩家手牌及三張底牌
    • 確認三者之間的關系,識別地主和農民角色,確認隊友及對手關系
    • 識別每輪三位玩家依次出了什么牌,重繪顯示對應區域
  • AI出牌方案輸出

    • 加載訓練好的AI模型,初始化游戲環境
    • 每輪出牌判斷,根據上家出牌資料給出最優出牌決策
    • 自動重繪玩家剩余手牌和本局勝率預測

二、實作步驟

1. UI設計排版布局

根據上述功能,我們首先考慮進行簡單的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

在這里插入圖片描述
這樣我們就可以得到玩家AI手牌,其他玩家手牌(預測),地主三張底牌,三者角色關系,出牌順序,核心代碼如下:

# 坐標
self.MyHandCardsPos = (414, 804, 1041, 59)  # AI玩家截圖區域
self.LPlayedCardsPos = (530, 470, 380, 160)  # 左側玩家截圖區域
self.RPlayedCardsPos = (1010, 470, 380, 160)  # 右側玩家截圖區域
self.LandlordFlagPos = [(1320, 300, 110, 140), (320, 720, 110, 140), (500, 300, 110, 140)]  # 地主標志截圖區域(右-我-左)
self.ThreeLandlordCardsPos = (817, 36, 287, 136)      # 地主底牌截圖區域,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 = []
    # 其他玩家手牌(整副牌減去玩家手牌,后續再減掉歷史出牌)
    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 = ""
    # 開局時三個玩家的手牌
    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, "選擇角色", "未識別到地主,請手動選擇角色:", 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);')

    # 整副牌減去玩家手上的牌,就是其他人的手牌,再分配給另外兩個角色(如何分配對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: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, "底牌識別出錯", "底牌必須是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, "手牌識別出錯", "初始手牌數目有誤", 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專案地址:https://github.com/kwai/DouZero,我們需要將該開源專案下載并匯入專案中,

創建一個AI玩家角色,初始化游戲環境,加載模型,進行每輪的出牌判斷,控制一局游戲流程的進行和結束,核心代碼如下:

# 創建一個代表玩家的AI
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("開始出牌\n")
    while not self.env.game_over:
        # 玩家出牌時就通過智能體獲取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("{}勝,本局結束!\n".format("農民" if self.env.winner == "farmer" else "地主"))
    QMessageBox.information(self, "本局結束", "{}勝!".format("農民" if self.env.winner == "farmer" else "地主"),
                            QMessageBox.Yes, QMessageBox.Yes)
    self.env.reset()
    self.init_display()

到這里,整個AI斗地主出牌流程基本已經完成了,
在這里插入圖片描述

三、出牌器用法

按照上述程序,這款AI出牌器已經制作完成了,后面應該如何使用呢?如果不想研究原始碼,只想使用這款AI斗地主出牌器,驗證下效果,該怎么配置環境運行這個AI出牌器呢?下面就開始介紹,

1. 環境配置

首先我們需要安裝這些第三方庫,配置相關環境,如下所示:

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. 坐標調整確認

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

其次我們要確認螢屏截圖獲取的各個區域是否正確,如果有問題需要進行區域位置坐標調整,

# 坐標
self.MyHandCardsPos = (414, 804, 1041, 59)  # 我的截圖區域
self.LPlayedCardsPos = (530, 470, 380, 160)  # 左邊截圖區域
self.RPlayedCardsPos = (1010, 470, 380, 160)  # 右邊截圖區域
self.LandlordFlagPos = [(1320, 300, 110, 140), (320, 720, 110, 140), (500, 300, 110, 140)]  # 地主標志截圖區域(右-我-左)
self.ThreeLandlordCardsPos = (817, 36, 287, 136)      # 地主底牌截圖區域,resize成349x168

在這里插入圖片描述

3. 運行測驗

當所有環境配置完成,各區域坐標位置確認無誤之后,下面我們就可以直接運行程式,測驗效果啦~

首先我們運行AI出牌器程式,打開歡樂斗地主游戲界面,進入游戲,當玩家就位,手牌分發完畢,地主身份確認之后,我們就可以點擊畫面中開始按鈕,讓AI來幫助我們斗地主了,

下面可以一起來看看這款AI出牌器的實驗效果喔,看看AI是如何帶領農民打倒地主,取得勝利的!

<iframe id="0Jpaddqp-1627864534814" src="https://live.csdn.net/v/embed/174210" allowfullscreen="true" data-mediaembed="csdn"></iframe>

今天我們就到這里,明天繼續努力!
在這里插入圖片描述
若本篇內容對您有所幫助,請三連點贊,關注,收藏支持下,

創作不易,白嫖不好,各位的支持和認可,就是我創作的最大動力,我們下篇文章見!

Dragon少年 | 文

如果本篇博客有任何錯誤,請批評指教,不勝感激 !

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/291656.html

標籤:python

上一篇:Python小白的數學建模課-22.插值方法

下一篇:Gateway網關匹配規則

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more