KEEP GOING

[python] (4) 블랙잭 GUI 게임 : 코드 개선 본문

project/project4: blackjack GUI programming

[python] (4) 블랙잭 GUI 게임 : 코드 개선

jmHan 2021. 12. 15. 14:06
반응형

 

 

 

버튼 파일을 꾸미기 위한 stylesheet 파일이나 게임 결과 text를 담아두는 파일을 gameText.py로 따로 분리하였다.

그리고나서 메인 로직이 구현된 secondWindow.py의 코드를 개선해보았다.

추가적으로 내부 동작 알고리즘을 구현한 innerCode.py에 대한 unittest를 진행하였는데 unitTest.py에 기록해두었다. 

 

1. button.py

from PyQt5.QtWidgets import *


class Button(QToolButton):
    def __init__(self, text, callback):
        super().__init__()
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        self.setText(text)
        self.clicked.connect(callback)

    def sizeHint(self):
        size = super(Button, self).sizeHint()
        size.setHeight(size.height() + 50)
        size.setWidth(max(size.width(), size.height()))
        return size

2. firstWindow.py

from PyQt5.QtGui import QIcon, QPixmap, QCursor
from PyQt5.QtWidgets import QLabel, QPushButton, QHBoxLayout, QGridLayout, QDesktopWidget
from musicPlayer import *
from PyQt5 import QtCore
from PyQt5.QtCore import Qt
from gameText import style_sheet


def styleButton(button):
    button.setCursor(Qt.PointingHandCursor)
    button.setStyleSheet(style_sheet[4])


class FirstWindow(QWidget):
    switch_window = QtCore.pyqtSignal()

    def __init__(self):
        super().__init__()
        self.setWindowTitle("BlackJack Game")
        self.setWindowIcon(QIcon("./PNG-cards-1.3/blackjack.png"))
        self.setGeometry(0, 0, 900, 600)
        self.setStyleSheet('background-color: green')

        self.music = MusicPlayer()
        self.music.playAudioFile()

        self.image = QPixmap("./PNG-cards-1.3/title.png")
        self.label = QLabel()
        self.label.setAlignment(Qt.AlignCenter)
        self.label.setPixmap(self.image)

        self.start_button = QPushButton("PLAY")
        self.volumeUpButton = QPushButton("+")
        self.volumeUpButton.clicked.connect(self.music.volumeUp)
        self.volumeDownButton = QPushButton("-")
        self.volumeDownButton.clicked.connect(self.music.volumeDown)
        self.volumeMuteButton = QPushButton("Mute")
        self.volumeMuteButton.clicked.connect(self.music.volumeMute)

        styleButton(self.volumeUpButton)
        styleButton(self.volumeDownButton)
        styleButton(self.volumeMuteButton)

        self.volume_layout = QHBoxLayout()
        self.volume_layout.addWidget(self.volumeDownButton)
        self.volume_layout.addWidget(self.volumeMuteButton)
        self.volume_layout.addWidget(self.volumeUpButton)

        self.start_button.setCursor(QCursor(Qt.PointingHandCursor))
        self.start_button.setStyleSheet(style_sheet[5])

        self.start_button.clicked.connect(self.secondWindowEmit)
        self.grid = QGridLayout()
        self.grid.addWidget(self.label, 1, 1)
        self.grid.addWidget(self.start_button, 2, 1)
        self.grid.addLayout(self.volume_layout, 3, 1)
        self.setLayout(self.grid)

        self.center()
        self.show()

    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def secondWindowEmit(self):
        self.switch_window.emit()

3. gameText.py

fight_message = ["You lose!", "You win!", "Draw!", "Congratulations! \nBlack Jack!", "Burst!", "Draw"]

style_sheet = ["""QMessageBox
            {
            background-color: white;
            font-family: 'Georgia';
            }
            """,
               """QLabel
                           {
                           font-size: 18px;
                           font-family: 'Georgia';
                           color: blue;
                           }
                           """
               , """QLabel
            {
            font-size: 18px;
            font-family: 'Georgia';
            color: blue;
            }
            """,
               """QToolButton{background-color: rgb(249, 228, 183);
                           color: black;
                           border-radius: 5px;
                           font-family: 'Georgia';
                           font-size: 20px;
                           }"""
               """QToolButton::hover
               {
               background-color: white;
               }
               """,
            """QPushButton{background-color: rgb(249, 228, 183);
                    color: black;
                    border-radius: 15px;
                    font-family: 'Georgia';
                    font-size: 25px;
                    padding: 5px 0;}"""
            """QPushButton::hover
            {
            background-color : white;
            }
            """,
            """QPushButton{background-color: rgb(249, 228, 183);
                color: black;
                border-radius: 25px;
                font-family: 'Georgia';
                font-size: 40px;
                margin-bottom: 5px;
                padding: 8px 0;}"""
            """QPushButton::hover
            {
            background-color : white;
            }
            """
            ]

4. innerCode.py

import random

marks = ['spades', 'diamonds', 'hearts', 'clubs']
card_english = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']


# 파일 입출력
def load():
    try:
        f = open("money.dat", 'r')
        return int(f.readline())
    except FileNotFoundError:
        f = open("money.dat", 'w')
        f.write('100000')
        return 100000


def write(money):
    f = open("money.dat", 'w')
    f.write(money)
    f.close()


def set_money(now, betting, num):
    # lose : 0
    if num == 0 or num == 4:
        write(str(now - betting))
        return now - betting
    # win : 1
    elif num == 1:
        write(str(now + betting))
        return now + betting
    # Black jack : 3
    elif num == 3:
        write(str(int(now + (1.5 * betting))))
        return int(now + (betting * 1.5))
    else:
        return now


# 카드 두장(베팅 시 카드 지급)지급후에 카드뭉치에서 제거
def twocard(card):
    cardList = []
    for i in range(2):
        cardList.append(card.pop(0))
    return cardList


# 새로운 카드 받기
def cardappend(cardlist, card):
    cardlist.append(card.pop(0))


# end 버튼 클릭 이벤트, Lose: 0 Win: 1 Draw 2
def fight(player_result, dealer_result):
    if player_result > dealer_result or dealer_result > 21:
        return 1
    elif player_result < dealer_result or dealer_result == 21:
        return 0
    if player_result == dealer_result:
        return 2


def burst(result):
    if result > 21:
        return 4
    elif result == 21:
        return 3
    else:
        return 5  # nothing


def set_card():
    return random.sample(range(52), 17)


def count(card):
    result = 0
    cnt = 0
    for data in card:
        if data % 13 >= 10:
            result += 10
        else:
            result += data % 13 + 1
            # if data == A
            if data % 13 == 0:
                cnt += 1

    for _ in range(cnt):
        # A : 1 or 11
        if result + 10 <= 21:
            result += 10
        else:
            break

    return result


def intToString_card(card):
    card_list = []
    for data in card:
        card = str(marks[data//13]) + str(card_english[data % 13])
        card_list.append(card)
    return card_list

5. musicPlayer.py

from PyQt5.QtWidgets import QWidget
from PyQt5 import QtCore
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
import os


class MusicPlayer(QWidget):
    def __init__(self):
        super().__init__()
        self.player = QMediaPlayer()

    def volumeUp(self):
        current_volume = self.player.volume()
        # print(currentVolume)
        self.player.setVolume(current_volume + 10)

    def volumeDown(self):
        current_volume = self.player.volume()
        # print(currentVolume)
        self.player.setVolume(current_volume - 10)

    def volumeMute(self):
        self.player.setMuted(not self.player.isMuted())

    def playAudioFile(self):
        full_file_path = os.path.join(os.getcwd(), './sounds/bgm.mp3')
        url = QtCore.QUrl.fromLocalFile(full_file_path)
        content = QMediaContent(url)

        self.player.setMedia(content)
        self.player.play()

6. secondWindow.py

from PyQt5.QtWidgets import QWidget, QMessageBox, QVBoxLayout, QHBoxLayout, QLabel, QDesktopWidget
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtCore import Qt
from button import Button
from innerCode import *
from gameText import *


class SecondWindow(QWidget):

    def __init__(self):
        super().__init__()

        self.setWindowTitle("BlackJack Game")
        self.setWindowIcon(QIcon(f"./PNG-cards-1.3/blackjack.png"))
        # setting  the geometry of window
        self.setGeometry(0, 0, 1200, 900)
        self.setStyleSheet("background-color: green")
        self.center()

        self.q_msg_box = QMessageBox()
        self.q_msg_box.setWindowTitle("Result")
        self.q_msg_box.setWindowIcon(QIcon("./PNG-cards-1.3/blackjack.png"))
        self.q_msg_box.setStyleSheet(style_sheet[0])

        self.money = load()
        self.betting_cost = 1000
        # self.dealCount = 0

        self.display = QLabel()
        self.b_display = QLabel('bet: ' + str(self.betting_cost))
        self.b_display.setStyleSheet(style_sheet[1])
        self.m_display = QLabel('money: ' + str(self.money))
        self.m_display.setStyleSheet(style_sheet[2])

        display_vbox = QVBoxLayout()
        display_vbox.addStretch(1)
        display_vbox.addWidget(self.display)
        display_vbox.addWidget(self.b_display)
        display_vbox.addWidget(self.m_display)

        betting_vbox = QVBoxLayout()
        hbox = QHBoxLayout()
        hbox.addLayout(betting_vbox)

        self.components_btn = list()  # deal, stay, append, reset, plus, minus
        button_groups = [
            {'buttons': ["+100", "-100"], 'layout': betting_vbox},
            {'buttons': ["deal", "new card", "stay", "reset"], 'layout': hbox},
            ]

        for label in button_groups:
            i = 0
            for btnText in label['buttons']:
                self.components_btn.append(Button(btnText, self.button_clicked))
                self.styleButton(self.components_btn[-1])
                label['layout'].addWidget(self.components_btn[-1])
                i += 1

        vbox = QVBoxLayout()
        vbox.addStretch(1)
        vbox.addLayout(display_vbox)
        vbox.addLayout(hbox)
        self.setLayout(vbox)

        self.cntLst = [0, 150, 300, 450, 600, 750]
        self.dLabel = []
        self.pLabel = []
        for _ in range(len(self.cntLst)):
            pl = QLabel(self)
            dl = QLabel(self)
            self.dLabel.append(dl)
            self.pLabel.append(pl)

        # 카드 배치, 베팅 초기화
        self.clear()
        # show all the widgets
        self.show()

    def loadCard(self, label, cardsuit, cnt, num):
        self.pixmap = QPixmap(f"./PNG-cards-1.3/{cardsuit}").scaledToWidth(150)
        label.setPixmap(self.pixmap)
        label.move(cnt, num)  # player 300, dealer 0
        label.resize(self.pixmap.width(), self.pixmap.height())

    # 프로그램 센터에 배치
    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def clear(self):
        for pl in self.pLabel:
            idx = self.pLabel.index(pl)
            if idx < 2:
                self.loadCard(pl, 'background', self.cntLst[idx], 300)
            else:
                self.loadCard(pl, 'green', self.cntLst[idx], 300)

        for dl in self.dLabel:
            idx = self.dLabel.index(dl)
            if idx < 2:
                self.loadCard(dl, 'background', self.cntLst[idx], 0)
            else:
                self.loadCard(dl, 'green', self.cntLst[idx], 0)

        self.betting_display('', 1000)
        self.components_btn[3].setDisabled(True)
        self.components_btn[4].setDisabled(True)

    def styleButton(self, button):
        button.setCursor(Qt.PointingHandCursor)
        button.setStyleSheet(style_sheet[3])

    def QMessageBoxExec(self, msg):
        msg_box = self.q_msg_box
        msg_box.setText(msg)
        msg_box.exec()
        self.components_btn[3].setDisabled(True)
        self.components_btn[4].setDisabled(True)
        self.components_btn[5].setDisabled(False)
        self.display.setText('If you wanna restart, click reset button')

    def button_clicked(self):
        button = self.sender()
        key = button.text()
        if key == '+100':
            self.betting_display("", self.betting_cost + 100)
        elif key == '-100':
            self.betting_display("", self.betting_cost - 100)
        elif key == 'deal':
            if self.betting_cost < 0:
                self.betting_display("Bet on the positive value.", 1000)
            elif self.betting_cost > 0:
                if self.betting_cost < 1000:
                    self.betting_display("betting min is 1000", 1000)
                elif self.betting_cost > self.money:
                    self.betting_display("You don't have much money", 1000)
                else:
                    self.display.setText("let's start!")
                    self.components_btn[5].setDisabled(True)
                    self.components_disable(False)

                    self.card = set_card()
                    self.intPlayercards = twocard(self.card)
                    # print(self.intPlayercards)
                    # [34, 5]
                    self.intDealercards = twocard(self.card)
                    self.dealercards = intToString_card(self.intDealercards)
                    self.playercards = intToString_card(self.intPlayercards)
                    # print(self.intToString_card)
                    # ['hearts9', 'spades6']
                    self.loadCard(self.pLabel[0], self.playercards[0], self.cntLst[0], 300)
                    self.loadCard(self.pLabel[1], self.playercards[1], self.cntLst[1], 300)
                    self.loadCard(self.dLabel[0], self.dealercards[0], self.cntLst[0], 0)
                    # self.loadDealerCard(self.dLabel[1], self.dealercards[1], self.cntLst[1])

                    if count(self.intPlayercards) == 21:
                        self.money_display(fight_message[3], 3)
            else:
                self.display.setText("Please click betting number")
        elif key == 'new card':
            cardappend(self.intPlayercards, self.card)
            # print(self.intPlayercards)
            # [34, 5, 7]
            self.playercards = intToString_card(self.intPlayercards)
            for pl in self.pLabel:
                idx = self.pLabel.index(pl)
                if idx < len(self.intPlayercards):
                    self.loadCard(pl, self.playercards[idx], self.cntLst[idx], 300)
            res = burst(count(self.intPlayercards))
            if res != 5:
                self.money_display(fight_message[res], res)
        elif key == 'stay':
            self.loadCard(self.dLabel[1], self.dealercards[1], self.cntLst[1], 0)
            # 딜러 카드 합이 17이상이면 더이상 추가 카드를 받을 수 없음
            while count(self.intDealercards) < 17:
                cardappend(self.intDealercards, self.card)
                self.dealercards = intToString_card(self.intDealercards)
                for dl in self.dLabel:
                    idx = self.dLabel.index(dl)
                    if idx < len(self.intDealercards):
                        self.loadCard(dl, self.dealercards[idx], self.cntLst[idx], 0)
            res = fight(count(self.intPlayercards), count(self.intDealercards))
            self.money_display(fight_message[res], res)
        elif key == 'reset':
            self.components_disable(True)
            self.clear()
            self.display.setText('Play more? Click deal button')

    def components_disable(self, check):
        # deal, stay, append, reset, plus, minus
        self.components_btn[3].setDisabled(check)
        self.components_btn[4].setDisabled(check)

        self.components_btn[0].setDisabled(not check)
        self.components_btn[1].setDisabled(not check)
        self.components_btn[2].setDisabled(not check)

    def betting_display(self, message, cost):
        self.display.setText(message)
        self.betting_cost = cost
        self.b_display.setText('bet: ' + str(self.betting_cost))

    def money_display(self, message, num):
        self.QMessageBoxExec(message)
        self.money = set_money(self.money, self.betting_cost, num)
        self.m_display.setText('money: ' + str(self.money))

7. unitTest.py

import unittest

from inner import *


class TestInner(unittest.TestCase):

    def testShow_card(self):
        card = [0]
        self.assertEqual(show_card(card), '♠A, ')

    def testCount(self):
        card = [7, 42]
        # ♠8 + ♣4
        self.assertEqual(count(card), 12)

    def testBlackJack(self):
        card = [0, 50]
        self.assertEqual(black(card), 'blackjack')


if __name__ == '__main__':
    unittest.main()




#inner 현재 구gui호환이라 함수 이름이 다름

marks = ['♠', '◆', '♥', '♣']
card_english = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']

def show_card(card):
    card_list = ''
    for i in card:
        card_list += marks[i // 13] + card_english[i % 13] + ", "
    return card_list
    
def count(card):
    result = 0
    count_a = 0
    for i in card:
        if i % 13 >= 10:
            result += 10
        else:
            result += i % 13 + 1
            if i % 13 == 0:
                count_a += 1

    for i in range(count_a):
        if result + 10 <= 21:
            result += 10
        else:
            break
    return result   
    
#보드에 있던 조건을 함수로 구현
def black(player):
    black = 0

    for i in player:
        if i % 13 == 0:
            black += 1
        elif i % 13 >= 10:
            black += 2
        else:
            pass
    if black == 3:
        return 'blackjack'

8. mainGame.py

from firstWindow import FirstWindow
from secondWindow import SecondWindow
from PyQt5.QtWidgets import QApplication
import sys


class MyController:
    def __init__(self):
        self.first_window = FirstWindow()
        self.window = SecondWindow()
        self.window.close()

    def show_window1(self):
        self.first_window.switch_window.connect(self.show_window2)
        self.first_window.show()

    def show_window2(self):
        self.first_window.close()
        self.window.show()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    controller = MyController()
    controller.show_window1()
    sys.exit(app.exec_())

 

반응형
Comments