どのようにpythonを使ってデジタルプッシュボードを開発するかを教えます。


今年の初めに、新シーズンの『最強脳』がスタートしました。第1話の選抜時にみんなでデジタルゲームを作って、『デジタル華容道』と言いました。当時、何計画君は二十数秒の成績でこのプロジェクトの優勝を獲得しました。このゲームを見てから、『デジタル華容道』のプログラムを書くことにしました。今日は実現します。
デジタルディスクゲーム(n-puzle)は知的ゲームで、よくあるタイプは十五数字のプッシュボードゲームと八数字のプッシュボードゲームなどがあります。十五の数字のプッシュ・ボードには十五の正方形と一つの大きさが正方形に相当する空席があります。15の数字が順番に並べられ、最後の格子が空席であるときは挑戦成功を表します。
本論文ではPyQt 5を用いて設計と実現を行い、PyQt 5はこのプログラムの提示方式であり、最も重要なのはアルゴリズムであり、アルゴリズムを習得し、PyGameまたはTkinterを用いて実現できる。
PyQt 5インストール:pip install PyQt5本論文の使用環境:
システム:Windows 10 64ビットPythonバージョン:3.6
1、レイアウト設計
簡単版の数字華容道を作って、レイアウトの設計は図の通りです。
在这里插入图片描述
図中の灰色の部分はゲーム全体のキャリアとしてQWidgetを使用している。黄色の部分はQGridLayoutを数字の四角形のレイアウトとして使用します。赤い部分はQLabelを数字のブロックとして使用します。
2、アルゴリズムの設計
上の図に示すように、このゲームは全部で15個のブロックが必要です。各ブロックは一つの数字を表しています。ブロック上の数字を記憶するために二次元リストを使用することができる。4 x 4のリストを作成します。0は空の位置を表します。
2.1配列を作成して初期化する
  • 配列を作成する方法:
  • は、長さ16の配列を作成し、対応する位置に0〜15を保存している。シャッフル
  • 
    import random
    
    #              
    blocks = []
    
    #       ,0       
    arr = range(16)
    numbers = random.sample(arr, 16)
    
    for row in range(4):
    	blocks.append([])
    	for column in range(4):
    		blocks[row].append(numbers[row*4 + column])
    
    #     
    for i in range(4):
    	print(blocks[i])
    
    [out]
    [2, 5, 7, 9]
    [11, 8, 4, 12]
    [6, 13, 10, 15]
    [1, 14, 0, 3]
    [Finished in 0.1s]
    
    2.2移動アルゴリズム
    前の数字位置を左図のように移動すると、左矢印を押すと、右図のようになります。
    在这里插入图片描述
    (1,2)と(1,3)の2つの位置の数字が入れ替わります。すなわち0と8が入れ替わります。右の図に示すようにもう一度左の矢印を押すと、すべての数字は変わりません。数字0は右の数がないからです。
    まとめてみます。数字0の位置が(row、column)で、そしてcolumn≠3であれば左矢印を押した後、(row、column)と(row、column+1)の位置の配列が入れ替わります。
  • 数字0の位置が(row、column)であり、column≠0の場合は右矢印を押した後、(row、column)と(row、column-1)の位置の配列が入れ替わります。
  • 数字0の位置が(row、column)であり、row≠3のように上矢印を押した後、(row、column)と(row+1、column)の位置の配列が入れ替わります。
  • 数字0の位置が(row、column)なら、row≠0は下矢印を押した後、(row、column)と(row-1、column)位置の配列が入れ替わります。
  • 移動アルゴリズムを一つの関数としてカプセル化します。
    
    #   
    # zero_row     0           ,zero_column    0           
    def move(direction):
        if(direction == 'UP'): #  
            if zero_row != 3:
                blocks[zero_row][zero_column] = blocks[zero_row + 1][zero_column]
                blocks[zero_row + 1][zero_column] = 0
                zero_row += 1
        if(direction == 'DOWN'): #  
            if zero_row != 0:
                blocks[zero_row][zero_column] = blocks[zero_row - 1][zero_column]
                blocks[zero_row - 1][zero_column] = 0
                zero_row -= 1
        if(direction == 'LEFT'): #  
            if zero_column != 3:
                blocks[zero_row][zero_column] = blocks[zero_row][zero_column + 1]
                blocks[zero_row][zero_column + 1] = 0
                zero_column += 1
        if(direction == 'RIGHT'): #  
            if zero_column != 0:
                blocks[zero_row][zero_column] = blocks[zero_row][zero_column - 1]
                blocks[zero_row][zero_column - 1] = 0
                zero_column -= 1
    
    2.3勝利検出アルゴリズムかどうか
    勝利するかどうかを測定するのは簡単です。前の15の位置はそれぞれ対応しています。最後の1つは0で勝利します。しかし、不必要な計算を避けるために、最後の1つは0ですか?具体的なコードは以下の通り実現されます。
    
    #       
    def checkResult():
            #           0
            if blocks[3][3] != 0:
                return False
    
            for row in range(4):
                for column in range(4):
                	#               0,pass  
                    if row == 3 and column == 3:
                        pass
                    #      
                    elif blocks[row][column] != row * 4 + column + 1:
                        return False
    
            return True
    
    3、実現する
    以下、すべての機能モジュールの実装について説明します。
    3.1フレーム構築
    QWidgetをゲーム全体のキャリヤーとして作成します。
    
    import sys
    from PyQt5.QtWidgets import QWidget, QApplication
    
    class NumberHuaRong(QWidget):
        """       """
        def __init__(self):
            super().__init__()
            self.initUI()
    
        def initUI(self):
            #      
            self.setFixedSize(400, 400)
            #     
            self.setWindowTitle('     ')
            #       
            self.setStyleSheet("background-color:gray;")
            self.show()
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        ex = NumberHuaRong()
        sys.exit(app.exec_())
    
    運転結果は下図のようになります。
    在这里插入图片描述
    3.2デジタルブロックの実装
    前に述べたように、2次元の配列で0〜16個の数字を保存します。最終的には数字のブロックに変換して、単独でクラスを作成します。
    
    class Block(QLabel):
        """      """
        def __init__(self, number):
            super().__init__()
    
            self.number = number
            self.setFixedSize(80, 80)
    
            #     
            font = QFont()
            font.setPointSize(30)
            font.setBold(True)
            self.setFont(font)
    
            #       
            pa = QPalette()
            pa.setColor(QPalette.WindowText, Qt.white)
            self.setPalette(pa)
    
            #       
            self.setAlignment(Qt.AlignCenter)
    
            #       \       
            if self.number == 0:
                self.setStyleSheet("background-color:white;border-radius:10px;")
            else:
                self.setStyleSheet("background-color:red;border-radius:10px;")
                self.setText(str(self.number))
    
    このクラスはQLableから継承され、初期化には一つのパラメータnumberに入る必要があります。numberは数字のブロックに表示される数字です。
    3.3数字を四角に変換してレイアウトに追加する
    レイアウトはQGridLayoutを採用して4 X 4のself.gltMainを作成し、16のBlockをself.gltMainに追加します。
    
    def updatePanel(self):
        for row in range(4):
            for column in range(4):
                self.gltMain.addWidget(Block(self.blocks[row][column]), row, column)
    
        self.setLayout(self.gltMain)
    
    3.4レイアウトを初期化する
    初期化レイアウトは、ランダムデータの生成と、数値をブロックに変換してレイアウトに追加することを含む。
    
    #      
        def onInit(self):
            #       ,0       
            arr = range(16)
            self.numbers = random.sample(arr, 16)
    
            #           
            for row in range(4):
                self.blocks.append([])
                for column in range(4):
                    temp = self.numbers[row * 4 + column]
    
                    if temp == 0:
                        self.zero_row = row
                        self.zero_column = column
                    self.blocks[row].append(temp)
                    self.gltMain.addWidget(Block(temp), row, column)
    
    3.5ボタン検査
    QWidgetにはkeyPresEventイベントのハンドルがあります。この方法を再実現するだけでいいです。
    
    #     
    def keyPressEvent(self, event):
        key = event.key()
        if(key == Qt.Key_Up or key == Qt.Key_W):
            self.move(Direction.UP)
        if(key == Qt.Key_Down or key == Qt.Key_S):
            self.move(Direction.DOWN)
        if(key == Qt.Key_Left or key == Qt.Key_A):
            self.move(Direction.LEFT)
        if(key == Qt.Key_Right or key == Qt.Key_D):
            self.move(Direction.RIGHT)
        self.updatePanel()
        if self.checkResult():
            if QMessageBox.Ok == QMessageBox.information(self, '    ', '       !'):
                self.onInit()
    
    キーを押したことを検出し、そのキーの値が「↑↓←」または「WSAD」かどうかを判断し、それに応じて移動し、移動してレイアウトを更新し、最後に挑戦が完了したかを検出し、挑戦が完了したら、ティーチングボックスをポップアップします。OKボタンをクリックしたら、ゲームを再開します。
    3.6試遊テスト
    ここで、すべての機能モジュールの紹介が終わりました。コードを早く見ないでください。まずプログラムを実行して、Bugsがあるかどうかを確認します。
    何ゲームか遊んだら、すべてのゲームが元に戻るというわけではないです。
    在这里插入图片描述
    図に示すように、14と15の正方形の位置が逆になり、どうしても話が元に戻りました。この場合はランダムに発生します。いったいどういうことですか?インターネットで検索したら、確かに2つの数字だけの位置が逆になったら、どうしても元に戻りません。これは何によるものですか?私たちの二次元配列はどうやって作られたのか覚えています。ランダムで、つまり元に戻らない場合にランダムになるかもしれません。
    このような状況を避けるにはどうすればいいですか?配列を初期化する時、すべての位置は正しい数字です。そしてmoveを使って移動シャッフルします。
    3.7改善と改善
    前に各機能モジュールを単独で作成しましたので、onInitメソッドを修正すればいいです。
    
    #      
    def onInit(self):
        #       
        self.numbers = list(range(1, 16))
        self.numbers.append(0)
    
        #           
        for row in range(4):
            self.blocks.append([])
            for column in range(4):
                temp = self.numbers[row * 4 + column]
    
                if temp == 0:
                    self.zero_row = row
                    self.zero_column = column
                self.blocks[row].append(temp)
    
        #     
        for i in range(500):
            random_num = random.randint(0, 3)
            self.move(Direction(random_num))
    
        self.updatePanel()
    
    先生は順番配列になっています。中に[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0]が保存されています。二次元配列blocksに変えてから、500回移動して、最後にレイアウトに追加します。
    4、完全コード
    
    import sys
    import random
    from enum import IntEnum
    from PyQt5.QtWidgets import QLabel, QWidget, QApplication, QGridLayout, QMessageBox
    from PyQt5.QtGui import QFont, QPalette
    from PyQt5.QtCore import Qt
    
    #         
    class Direction(IntEnum):
        UP = 0
        DOWN = 1
        LEFT = 2
        RIGHT = 3
    
    
    class NumberHuaRong(QWidget):
        """       """
        def __init__(self):
            super().__init__()
            self.blocks = []
            self.zero_row = 0
            self.zero_column = 0
            self.gltMain = QGridLayout()
    
            self.initUI()
    
        def initUI(self):      
            #       
            self.gltMain.setSpacing(10)
    
            self.onInit()
    
            #     
            self.setLayout(self.gltMain)
            #      
            self.setFixedSize(400, 400)
            #     
            self.setWindowTitle('     ')
            #       
            self.setStyleSheet("background-color:gray;")
            self.show()
    
        #      
        def onInit(self):
            #       
            self.numbers = list(range(1, 16))
            self.numbers.append(0)
    
            #           
            for row in range(4):
                self.blocks.append([])
                for column in range(4):
                    temp = self.numbers[row * 4 + column]
    
                    if temp == 0:
                        self.zero_row = row
                        self.zero_column = column
                    self.blocks[row].append(temp)
    
            #     
            for i in range(500):
                random_num = random.randint(0, 3)
                self.move(Direction(random_num))
    
            self.updatePanel()
    
        #     
        def keyPressEvent(self, event):
            key = event.key()
            if(key == Qt.Key_Up or key == Qt.Key_W):
                self.move(Direction.UP)
            if(key == Qt.Key_Down or key == Qt.Key_S):
                self.move(Direction.DOWN)
            if(key == Qt.Key_Left or key == Qt.Key_A):
                self.move(Direction.LEFT)
            if(key == Qt.Key_Right or key == Qt.Key_D):
                self.move(Direction.RIGHT)
            self.updatePanel()
            if self.checkResult():
                if QMessageBox.Ok == QMessageBox.information(self, '    ', '       !'):
                    self.onInit()
    
        #       
        def move(self, direction):
            if(direction == Direction.UP): #  
                if self.zero_row != 3:
                    self.blocks[self.zero_row][self.zero_column] = self.blocks[self.zero_row + 1][self.zero_column]
                    self.blocks[self.zero_row + 1][self.zero_column] = 0
                    self.zero_row += 1
            if(direction == Direction.DOWN): #  
                if self.zero_row != 0:
                    self.blocks[self.zero_row][self.zero_column] = self.blocks[self.zero_row - 1][self.zero_column]
                    self.blocks[self.zero_row - 1][self.zero_column] = 0
                    self.zero_row -= 1
            if(direction == Direction.LEFT): #  
                if self.zero_column != 3:
                    self.blocks[self.zero_row][self.zero_column] = self.blocks[self.zero_row][self.zero_column + 1]
                    self.blocks[self.zero_row][self.zero_column + 1] = 0
                    self.zero_column += 1
            if(direction == Direction.RIGHT): #  
                if self.zero_column != 0:
                    self.blocks[self.zero_row][self.zero_column] = self.blocks[self.zero_row][self.zero_column - 1]
                    self.blocks[self.zero_row][self.zero_column - 1] = 0
                    self.zero_column -= 1
    
        def updatePanel(self):
            for row in range(4):
                for column in range(4):
                    self.gltMain.addWidget(Block(self.blocks[row][column]), row, column)
    
            self.setLayout(self.gltMain)
    
        #       
        def checkResult(self):
            #           0
            if self.blocks[3][3] != 0:
                return False
    
            for row in range(4):
                for column in range(4):
                    #               0,pass  
                    if row == 3 and column == 3:
                        pass
                    #     
                    elif self.blocks[row][column] != row * 4 + column + 1:
                        return False
    
            return True
    
    class Block(QLabel):
        """      """
    
        def __init__(self, number):
            super().__init__()
    
            self.number = number
            self.setFixedSize(80, 80)
    
            #     
            font = QFont()
            font.setPointSize(30)
            font.setBold(True)
            self.setFont(font)
    
            #       
            pa = QPalette()
            pa.setColor(QPalette.WindowText, Qt.white)
            self.setPalette(pa)
    
            #       
            self.setAlignment(Qt.AlignCenter)
    
            #       \       
            if self.number == 0:
                self.setStyleSheet("background-color:white;border-radius:10px;")
            else:
                self.setStyleSheet("background-color:red;border-radius:10px;")
                self.setText(str(self.number))
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        ex = NumberHuaRong()
        sys.exit(app.exec_())
    
    5、まとめ
    作っている間に最大のピットがランダム配列で復元できなくなります。また、このゲームをする時、私はもう復元の法則を見つけました。このようにテストの時には完全なテストができます。そうしないとテストができないです。
    また、「最強脳」に対して、このプロジェクトはもともとひいきがあります。遊んだことがある人はすぐにできます。遊んだことがない人は法則を探す時間が長くなります。私は携帯で4 X 4を一番早く再現して33秒使っていますが、この番組の優勝(遊んだことがあります)にはとても尊敬しています。
    ここでは、pythonを使ってデジタルのプッシュボードゲームを開発する方法についての記事を紹介します。pythonの開発に関するデジタルのプッシュボードゲームの内容については、以前の文章を検索したり、次の関連記事を見たりしてください。これからもよろしくお願いします。