파이썬 실습/게임 만들기

파이썬 GUI 숫자 야구게임 만들기

파기차차 2022. 10. 9. 10:18
728x90
반응형
SMALL
728x90
ㅁ 개요
 
O 프로그램 소개
 

 - 이번 프로그램은 이전글(2022.08.15 - [파이썬 실습/게임 만들기] - 파이썬 숫자 야구 게임 만들기) 숫자 야구 게임 만들기의 GUI 버전입니다.

게임의 규칙은 컴퓨터가 랜덤하게 생성한 숫자 3개와 내가 입력한 숫자 3개의 값과 위치가 같으면 끝이 나는 게임 입니다.

(본 블로그의 내용은 유튜브 동영상(파이썬 GUI 숫자 야구게임 만들기)에서 더욱 자세히 보실 수 있습니다.)

 

 

O 완성된 프로그램 실행 화면

 

 - 최종 완성된 프로그램의 결과화면은 아래와 같습니다.

 - 게임의 세부 규칙은 다음과 같습니다.(위의 그림을 보면서 설명 드리겠습니다.)

(1) 0~9까지의 숫자 3개를 중복없이, ','(콤마)로 구분하여 입력합니다. 예) 1,2,3

(2) 새로시작 버튼을 클릭합니다.

(3) 내부적으로 컴퓨터가 생성한 랜덤한 숫자 3개와 내가 입력한 숫자 3개를 값과 위치를 비교하고 결과를 화면에 보여줍니다.

 - 위에서는 내가 입력한 값(5,8,3)이 값과 위치가 컴퓨터가 생성한 값과 위치가 모두 동일하여 2번만에 모두 맞추었습니다.

(4) 컴퓨터가 생성한 랜덤한 숫자 3개의 값과 위치를 모두 맞추지 못한 경우 '숫자입력'란에 숫자를 다시 입력 후 '계속' 버튼을 눌러 게임을 계속 진행할 수 있습니다.

(5) 최종적으로 3개의 숫자 값과 위치를 모두 맞추면 게임은 끝나게 되며, '새로시작' 버튼을 클릭하면 게임을 처음부터 다시 시작할 수 있습니다.

 

 

 

 


 

ㅁ 세부 내용
 
O 완성된 소스

 

# -*- coding: utf-8 -*-
from fileinput import filename
import sys
import glob
from tracemalloc import start
from PyQt5.QtWidgets import *
from PyQt5 import uic, QtCore, QtGui, QtWidgets
import os
import time
from PyQt5.QtGui import *
from random import randint

form_class = uic.loadUiType("baseBallGame.ui")[0]

class MyWindow(QMainWindow, form_class):
    def __init__(self):
        global tries

        super().__init__()

        # self.setFixedSize(570,582)
        # self.setWindowIcon(QIcon("pagichacha.png"))
        self.setupUi(self)

        self.pushButton.clicked.connect(self.newStart)
        # self.toolButton_3.clicked.connect(self.keywordSelectFile)
        # self.toolButton.clicked.connect(self.selectFolder)
        # self.toolButton_2.clicked.connect(self.selectSaveFile)
        self.pushButton_2.clicked.connect(self.continue_)
        

    def newStart(self):
        global mine, tries, ANSWER, start

        start = time.time()
        print("게임이 처음으로 시작되어 컴퓨터가 번호를 새롭게 생성합니다.")

        ##################################################################
        # 1. 컴퓨터가 생성한 정답 스트라이크 숫자
        ##################################################################
        numbers = []

        while len(numbers) < 3:
            new_number = randint(0,9)
            if new_number not in numbers:
                numbers.append(new_number)
        ANSWER = numbers
        print("컴퓨터가 생성한 스트라이크 숫자 : ", ANSWER)

        tries = 0
        ##################################################################
        # 2. 내가 입력한 스트라이크 숫자
        ##################################################################

        if self.lineEdit.text():
            userInput = self.lineEdit.text()
            userInput = userInput.strip()

            newuserInput = userInput.split(',')
            print(newuserInput)
            self.textEdit.clear()

            mine = []

            for n in newuserInput:
                n = n.strip()
                if n.isdigit() != True or (int(n) < 0 or int(n) > 9):
                    print("0에서 9까지의 수를 입력해 주세요.")
                    self.textEdit.append("0에서 9까지의 수를 입력해 주세요.")
                    break
                elif int(n) in mine:
                    print("중복되는 숫자입니다. 다시 입력하세요.")
                    self.textEdit.append("중복되는 숫자입니다. 다시 입력하세요.")
                    break
                else:
                    mine.append(int(n))

            # self.textEdit.clear()
            print(mine)
            self.textEdit.append("내가 입력한 스트라이크 숫자: "+str(mine))

            if len(mine) == 3:
                ##################################################################
                # 3. 컴퓨터의 스트라이크 숫자와 내가 입력한 숫자를 비교
                ##################################################################
                strike_count = 0
                ball_count = 0

                for i in range(3):
                    if mine[i] == ANSWER[i]:
                        strike_count += 1
                    elif mine[i] in ANSWER:
                        ball_count += 1
                s, b = strike_count, ball_count

                tries += 1

                if s == 3:
                    self.textEdit.append("컴퓨터가 생성한 스트라이크 숫자 : "+ str(ANSWER))
                    print("\n축하합니다.!! ",s," 스트라이크로 모두 맞추셨습니다.\n")
                    self.textEdit.append("\n축하합니다.!! "+str(tries)+" 번만에 "+str(s)+" 스트라이크를 모두 맞추셨습니다.\n")
                    end = time.time()
                    et = end - start
                    et = format(et, ".2f")
                    print("타자 시간 :", et, " 초")
                    
                else:
                    print("\n아쉽습니다.!! ",s," 스트라이크 ", b, " 볼 입니다.\n")
                    self.textEdit.append("\n아쉽습니다.!! "+str(s)+" 스트라이크 "+str(b)+" 볼 입니다. "+str(tries)+" 번째 시도 하셨습니다.\n")
            else:
                print("0~9까지의 올바른 3개의 숫자를 입력하여 주시기 바랍니다.")
                self.textEdit.append("\n\n#########################################################################################")
                self.textEdit.append("0~9까지의 올바른 3개의 숫자를 입력하여 주시기 바랍니다.")
                self.textEdit.append("예: 중복된 숫자 입력 불가, 문자입력 불가, 3개가 아닌 2개, 4개 등 숫자 개수가 틀린 입력 불가 등")
                self.textEdit.append("#########################################################################################")
        else:
            self.textEdit.setText("0에서 9까지의 수를, (콤마)로 구분하여 3개 입력해 주세요!\n예: 1,2,3")



    def continue_(self):
        global mine, tries, ANSWER, start

        print("게임이 이미 시작되어 컴퓨터가 번호를 새롭게 생성하지 않습니다.")

        try:
            ##################################################################
            # 2. 내가 입력한 스트라이크 숫자
            ##################################################################
            if self.lineEdit.text():
                userInput = self.lineEdit.text()
                userInput = userInput.strip()

                newuserInput = userInput.split(',')
                print(newuserInput)
                self.textEdit.clear()

                mine = []

                for n in newuserInput:
                    n = n.strip()
                    if n.isdigit() != True or (int(n) < 0 or int(n) > 9):
                        print("0에서 9까지의 수를 입력해 주세요!")
                        self.textEdit.append("0에서 9까지의 수를 입력해 주세요.")
                        break
                    elif int(n) in mine:
                        print("중복되는 숫자입니다. 다시 입력하세요.")
                        self.textEdit.append("중복되는 숫자입니다. 다시 입력하세요.")
                        break
                    else:
                        mine.append(int(n))

                self.textEdit.clear()
                print(mine)
                self.textEdit.append("내가 입력한 스트라이크 숫자 : "+str(mine))

                if len(mine) == 3:
                    ##################################################################
                    # 3. 컴퓨터의 스트라이크 숫자와 내가 입력한 숫자를 비교
                    ##################################################################
                    strike_count = 0
                    ball_count = 0

                    for i in range(3):
                        if mine[i] == ANSWER[i]:
                            strike_count += 1
                        elif mine[i] in ANSWER:
                            ball_count += 1
                    s, b = strike_count, ball_count
                    tries += 1

                    if s == 3:
                        self.textEdit.append("컴퓨터가 생성한 스트라이크 숫자 : "+str(ANSWER))
                        print("\n축하합니다.!! ",s," 스트라이크로 모두 맞추셨습니다.\n")
                        self.textEdit.append("\n축하합니다.!! "+str(tries)+" 번 만에 "+str(s)+" 스트라이크를 모두 맞추셨습니다.\n")
                        end = time.time()
                        et = end - start
                        et = format(et, ".2f")
                        print("타자 시간 : ", et, " 초")
                        self.textEdit.append("걸린 시간 : "+str(et)+" 초\n")
                    else:
                        print("\n아쉽습니다.!! ",s," 스트라이크 ", b, " 볼 입니다.\n")
                        self.textEdit.append("\n아쉽습니다.!! "+str(s)+" 스트라이크 "+str(b)+" 볼 입니다. "+str(tries)+" 번째 시도 하셨습니다.")
                else:
                    print("0~9까지의 올바른 3개의 숫자를 입력하여 주시기 바랍니다.")
                    self.textEdit.append("\n\n#########################################################################################")
                    self.textEdit.append("0~9까지의 올바른 3개의 숫자를 입력하여 주시기 바랍니다.")
                    self.textEdit.append("예: 중복된 숫자 입력 불가, 문자입력 불가, 3개가 아닌 2개, 4개 등 숫자 개수가 틀린 입력 불가 등")
                    self.textEdit.append("\n\n#########################################################################################")
            else:
                self.textEdit.setText("0에서 9까지의 수를, (콤마)로 구분하여 3개 입력해 주세요!\n예: 1,2,3")

        except:
            print("처음 시작인 경우 먼저 '새로시작' 버튼을 클릭해 주세요.")
            self.textEdit.append("\n\n#########################################################################################")
            self.textEdit.append("처음 시작인 경우 먼저 '새로시작' 버튼을 클릭해 주세요.")
            self.textEdit.append("\n\n#########################################################################################")


if __name__ == '__main__':
    app=QApplication(sys.argv)
    window = MyWindow()
    window.show()
    print("Before event loop")
    app.exec_()
    print("After event loop")

 

O 소스 다운로드
 - 아래 소스(*.ui, *.py)를 동일 경로에 놓고 실행하시기 바랍니다.

baseBallGame_GUI.py
0.01MB
baseBallGame.ui
0.00MB

 

 
 
 
O 핵심 내용
 

1. form_class = uic.loadUiType("baseBallGame.ui")[0] : 유저가 만든 UI를 로드하여 form_class변수에 할당

2. app=QApplication(sys.argv) : app객체 생성

3. class MyWindow(QMainWindow, form_class): QMainWindow 모듈은 PyQt5.QtWidgets 패키지에서 가져옴

4. window = MyWindow() : 생성자의 self는 window를 전달받음, 여기서 self는 MyWindow 클래스 그 자신을 의미

5. widow.show() : 창을 한번만 띄움

6. app.exec_() : app객체를 실행, exec_()함수의 내장 루프로 계속 창이 뜨게 만듦

7. self.setupUi(self) : form_class를 UI로 사용하겠다라는 의미

8. self.pushButton.clicked.connect(self.newStart) : pushButton클릭시 newStart함수 호출(먼저 Qt Desiner로 PushButton을 UI작업 후 코딩해야 에러가 없음)

9. global mine, tries, ANSWER, start : 클래스 내 다른 함수와 외부 공유를 위해 선언

10. start = time.time() : 게임 시간을 측정하기 위한 시작시간 변수

11. 컴퓨터가 생성할 숫자 3개를 처리하는 부분

while len(numbers) < 3:
            new_number = randint(0,9)                # 랜덤한 숫자 생성
            if new_number not in numbers:        # numbers리스트에 없으면 추가(즉, 중복된 숫자는 포함하지 않음)
                numbers.append(new_number)

12 userInput = userInput.strip() : 양쪽 공백 제거

13. self.textEdit.clear() : 텍스트에디트 창의 내용을 지움

14. self.textEdit.append(str()) : 해당 문자열을 텍스트에디트에 append해줌

 


 

O 기본 내용

-app객체를 생성 후 MyWindow클래스를 window에 할당하고, 윈도우를 띄운 후(window.show()) 지속적으로 실행(app.exec_())하게 만듦(즉 GUI 창을 띄움)

 

 

 - 컴퓨터가 3개의 랜덤한 숫자를 생성하는 코드로, numbers리스트의 길이(len(numbers))가 3보다 작으면 while 루프를 돌면서 numbers 리스트에 이미 동일 숫자가 있으면 제외하고, numbers리스트에 0~9 사이의 숫자를 3개까지 넣습니다.

 

 - '숫자입력' 박스에 내가 입력한 숫자를 ','로 구분하여 3개의 숫자를 입력 후 '새로시작' 버튼을 누르면 텍스트에디트에 결과가 출력됩니다.

 

 

- 위에서 컴퓨터가 생성한 숫자와 내가 입력한 숫자를 비교합니다. 아래에서는 컴퓨터가 생성한 숫자는 [5,6,2]이고, 내가 입력한 숫자는 [1,2,3] 이므로 숫자 2가 값은 같지만, 위치(컴퓨터는 3번째 자리, 나의 입력값은 2번째 자리)가 다르므로 1볼입니다.

 

 

 - 아래는 사용자가 입력한 입력값을 검증하는 루틴으로 0~9사이의 숫자가 아닌경우(if n.isdigit() != True or (int(n) < 0 or int(n) > 9): ) 안내문구를 보여주고 있습니다.

 

 - 마찬가지로 중복된 수가 입력된 경우(elif int(n) in mine: )에도 '중복되는 숫자입니다. 다시 입력하세요' 라는 메시지를 보여주고 있습니다.

 

 - 숫자가 아닌 문자인 경우도 처리해 주었습니다.(if n.isdigit() != True)

 

 

 - 최종적으로  3개의 숫자를 입력 후 최초 '새로시작' 버튼을 클릭하면 결과가 출력 되고, 맞추지 못한 경우

 

 - 다시 '계속' 버튼을 클릭하면 문제를 이어서 풀 수 있습니다. 아래에서는 2번만에 3스트라이크를 모두 맞춘 것을 볼 수 있습니다.

 

 

 


 

ㅁ 정리
 
O 우리가 배운 내용
 
 - 숫자야구게임을 GUI환경에서 만들어 보았습니다.
 - 위의 코드는 이해를 돕기위해 순차적으로 코딩하였으나, 중복된 코드가 많습니다. 스스로 함수나 클래스 형태로 중복된 코드를 개선해 보신다면 본인의 실력향상에 매우 큰 도움을 줄것 입니다.
 - 이번 프로그램은 하나 하나 살펴보면 어려운 부분은 없으나, 전체적으로 어떻게 프로그램이 돌아가는지 로직을 생각하는 부분(아래)이 중요한 것 같습니다.
 > 1. 컴퓨터가 생성한 숫자 3개
 > 2. 내가 입력한 숫자 3개(입력 오류 처리 등)
 > 3. 컴퓨터가 생성한 숫자와 내가 입력한 숫자를 비교하는 로직
 > 4. 마지막으로 게임을 연속해서 진행하는 방법과 새로 시작하는 방법에 대한 고민
 

 

위의 내용이 유익하셨다면, 좋아요와 구독 부탁드립니다.

 

 

감사합니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90
반응형
LIST