Python을 사용하여 주사위 굴리기 애플리케이션 구축

2023. 12. 23. 17:20python/basic

이 프로젝트에서는 주사위 굴리기 이벤트를 시뮬레이션하는 애플리케이션을 코딩하게 됩니다. 이를 위해 Python의 random 모듈을 사용합니다.

이 튜토리얼에서는 다음 방법을 배웁니다.

  • 주사위 굴리기 이벤트 시뮬레이션 하기 위하여 random.randint() 사용
  • 내장된 input()함수를 사용하여 사용자 입력 요청
  • 파싱 및 검증 사용자 입력
  • .center() 및 .join()와 같은 방법을 사용하여 문자열을 조작합니다.

프로젝트 개요

실행할 작업사용할 도구작성할 코드
사용자에게 굴릴 6면체 주사위 수를 선택하라는 메시지를 표시한 다음 사용자의 입력을 읽습니다. Python 내장 input() 함수 적절한 인수를 사용하여 input() 호출
사용자 입력을 구문 분석하고 검증합니다. 문자열 메서드, 비교 연산자 및 조건문 parse_input() 라는 사용자 정의 함수
주사위 굴리기 시뮬레이션 실행 Python의 random 모듈, 특히 randint() 함수 roll_dice() 라는 사용자 정의 함수
결과 주사위 면으로 ASCII 다이어그램을 생성합니다. 루프, list.append() 및 str.join() generate_dice_faces_diagram() 라는 사용자 정의 함수
주사위 면의 다이어그램을 화면에 표시 Python 내장 print() 함수 적절한 인수를 사용하여 print() 호출

전제조건

  • Python에서 스크립트 실행 방법
  • Python의 import 메커니즘
  • 기본Python 데이터 유형, 주로 문자열 및 정수
  • 기본 데이터 구조, 특히 목록
  • Python 변수 및 상수
  • Python 비교 연산자
  • 부울 값 및 논리식
  • 조건부문
  • Python for 루프
  • 파이썬에서 기본입력, 출력 및 문자열 형식 지정

1단계: Python 주사위 굴리기 앱의 TUI 코딩

명령줄에서 사용자 입력 받기

In [ ]:
# dice.py

# ~~~ App's main code block ~~~
# 1. Get and validate user's input
num_dice_input = input("How many dice do you want to roll? [1-6] ")
num_dice = parse_input(num_dice_input)

사용자 입력 구문 분석 및 유효성 검사

In [ ]:
# dice.py

def parse_input(input_string):
    """Return `input_string` as an integer between 1 and 6.

    Check if `input_string` is an integer number between 1 and 6.
    If so, return an integer with the same value. Otherwise, tell
    the user to enter a valid number and quit the program.
    """
    if input_string.strip() in {"1", "2", "3", "4", "5", "6"}:
        return int(input_string)
    else:
        print("Please enter a number from 1 to 6.")
        raise SystemExit(1)

# ~~~ App's main code block ~~~
# ...
  • 3번째 행은 입력 문자열을 인수로 사용하는 parse_input()를 정의합니다.
  • 4~9행은 함수의 독스트링을 제공합니다. 유익하고 올바른 형식의 독스트링을 함수에 포함시키는 것은 Python 프로그래밍의 모범 사례입니다. 독스트링을 사용하면 코드를 문서화할 수 있기 때문입니다.
  • 10행은 사용자 입력이 굴릴 주사위 수에 대한 유효한 값인지 확인합니다. .strip() 호출은 입력 문자열 주위에 불필요한 공백을 제거합니다. in 연산자는 입력이 허용된 주사위 수 집합 내에 있는지 확인합니다. 이 경우 Python에서 멤버십 테스트를 수행하므로 세트를 사용합니다. 데이터 구조는 매우 효율적입니다.
  • 11행은 입력을 정수로 변환하여 호출자에게 반환합니다.
  • 13행은 해당되는 경우 사용자에게 잘못된 입력을 알리는 메시지를 화면에 인쇄합니다.
  • 14행은 SystemExit 예외 및 상태 코드1 문제가 발생했음을 알리는 신호입니다.

2단계: Python에서 육면체 주사위 굴리기 시뮬레이션

In [ ]:
# dice.py
import random

# ...

def roll_dice(num_dice):
    """Return a list of integers with length `num_dice`.

    Each integer in the returned list is a random number between
    1 and 6, inclusive.
    """
    roll_results = []
    for _ in range(num_dice):
        roll = random.randint(1, 6)
        roll_results.append(roll)
    return roll_results

# ~~~ App's main code block ~~~
# ...
  • 2행은 random을 현재 네임스페이스로 가져옵니다.
  • 6행은 roll_dice()를 정의하며, 이는 주어진 호출에서 굴릴 주사위 수를 나타내는 인수를 취합니다.
  • 7~11행은 함수의 docstring을 제공합니다.
  • 12행은 빈 목록, roll_results을 생성합니다. 주사위 굴리기 시뮬레이션 결과를 저장합니다.
  • 13행은 사용자가 굴리려는 각 주사위에 대해 한 번씩 반복하는 for 루프를 정의합니다.
  • 14행은 randint()를 호출하여 1부터 6까지의 의사 난수 정수를 생성합니다. 이 호출은 각 반복마다 단일 숫자를 생성합니다. 이 숫자는 6각형 주사위를 굴린 결과를 나타냅니다.
  • 15행은 roll_results에 현재 주사위 굴리기 결과를 추가합니다.
  • 16행은 주사위 굴리기 시뮬레이션 결과 목록을 반환합니다.

새로 생성된 함수를 시험해 보려면 dice.py 파일 끝에 다음 코드 줄을 추가하세요.

In [ ]:
# dice.py
# ...

# ~~~ App's main code block ~~~
# 1. Get and validate user's input
num_dice_input = input("How many dice do you want to roll? [1-6] ")
num_dice = parse_input(num_dice_input)
# 2. Roll the dice
roll_results = roll_dice(num_dice)

print(roll_results)  # Remove this line after testing the app

3단계: 주사위 면의 ASCII 다이어그램 생성 및 표시

In [ ]:
# dice.py
import random

DICE_ART = {
    1: (
        "┌─────────┐",
        "│         │",
        "│    ●    │",
        "│         │",
        "└─────────┘",
    ),
    2: (
        "┌─────────┐",
        "│  ●      │",
        "│         │",
        "│      ●  │",
        "└─────────┘",
    ),
    3: (
        "┌─────────┐",
        "│  ●      │",
        "│    ●    │",
        "│      ●  │",
        "└─────────┘",
    ),
    4: (
        "┌─────────┐",
        "│  ●   ●  │",
        "│         │",
        "│  ●   ●  │",
        "└─────────┘",
    ),
    5: (
        "┌─────────┐",
        "│  ●   ●  │",
        "│    ●    │",
        "│  ●   ●  │",
        "└─────────┘",
    ),
    6: (
        "┌─────────┐",
        "│  ●   ●  │",
        "│  ●   ●  │",
        "│  ●   ●  │",
        "└─────────┘",
    ),
}
DIE_HEIGHT = len(DICE_ART[1])
DIE_WIDTH = len(DICE_ART[1][0])
DIE_FACE_SEPARATOR = " "

# ...

4~47행에서는 ASCII 문자를 사용하여 6개의 주사위 면을 그립니다. 각 얼굴을 해당 정수 값에 매핑하는 사전인 DICE_ART에 얼굴을 저장합니다.

라인 48은 주어진 면이 차지할 행 수를 보유하는 DIE_HEIGHT을 정의합니다. 이 예에서 각 면은 5개의 행을 차지합니다. 마찬가지로 49행에서는 주사위 면을 그리는 데 필요한 열 수를 유지하도록 DIE_WIDTH를 정의합니다. 이 예에서 너비는 11자입니다.

마지막으로 50행에서는 공백 문자를 포함하는 DIE_FACE_SEPARATOR을 정의합니다. 이 모든 상수를 사용하여 애플리케이션에 맞는 주사위 면의 ASCII 다이어그램을 생성하고 표시하게 됩니다.

주사위 면의 다이어그램 생성

In [ ]:
# dice.py

# ...

def generate_dice_faces_diagram(dice_values):
    """Return an ASCII diagram of dice faces from `dice_values`.

    The string returned contains an ASCII representation of each die.
    For example, if `dice_values = [4, 1, 3, 2]` then the string
    returned looks like this:

    ~~~~~~~~~~~~~~~~~~~ RESULTS ~~~~~~~~~~~~~~~~~~~
    ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
    │  ●   ●  │ │         │ │  ●      │ │  ●      │
    │         │ │    ●    │ │    ●    │ │         │
    │  ●   ●  │ │         │ │      ●  │ │      ●  │
    └─────────┘ └─────────┘ └─────────┘ └─────────┘
    """
    # Generate a list of dice faces from DICE_ART
    dice_faces = []
    for value in dice_values:
        dice_faces.append(DICE_ART[value])

    # Generate a list containing the dice faces rows
    dice_faces_rows = []
    for row_idx in range(DIE_HEIGHT):
        row_components = []
        for die in dice_faces:
            row_components.append(die[row_idx])
        row_string = DIE_FACE_SEPARATOR.join(row_components)
        dice_faces_rows.append(row_string)

    # Generate header with the word "RESULTS" centered
    width = len(dice_faces_rows[0])
    diagram_header = " RESULTS ".center(width, "~")

    dice_faces_diagram = "\n".join([diagram_header] + dice_faces_rows)
    return dice_faces_diagram

# ~~~ App's main code block ~~~
# ...
  • 5행은 dice_values이라는 단일 인수로 generate_dice_faces_diagram()를 정의합니다. 이 인수는 roll_dice()를 호출하여 얻은 주사위 굴림 정수 값 목록을 보유합니다.
  • 6~18행 함수의 독스트링을 제공합니다.
  • 20행은 dice_faces라는 빈 목록을 생성하여 입력된 주사위 값 목록에 해당하는 주사위 면을 저장합니다. 이 주사위 면은 최종 ASCII 다이어그램에 표시됩니다.
  • 21행은 주사위 값을 반복하는 for 루프를 정의합니다.
  • 22행은 DICE_ART에서 현재 주사위 값에 해당하는 주사위 면을 검색하여 dice_faces에 추가합니다.
  • 25행는 최종 주사위 면 다이어그램의 행을 보관할 빈 목록을 만듭니다.
  • 26행은 0에서 DIE_HEIGHT - 1까지의 인덱스를 반복하는 루프를 정의합니다. 각 색인은 주사위 면 다이어그램에서 특정 행의 색인을 나타냅니다.
  • 27행은 row_components를 주어진 행을 채울 주사위 면의 일부를 담는 빈 목록으로 정의합니다.
  • 28행은 중첩된 for 루프를 시작하여 주사위 면을 반복합니다.
  • 29행은 각 행 구성요소를 저장합니다.
  • 30행은 행 구성요소를 최종 행 문자열로 결합하고 개별 구성요소를 공백으로 구분합니다.
  • 31행은 최종 다이어그램을 구성할 행이 포함된 목록에 각 행 문자열을 추가합니다.
  • 34행은 현재 주사위 면 다이어그램의 width를 보유할 임시 변수를 생성합니다.
  • 35행은 RESULTS라는 단어를 표시하는 헤더를 생성합니다. 이를 위해 다이어그램의 width및 물결표 기호() ~ 와 함께 str.center()를 인수로 사용합니다.
  • 37행은 최종 주사위 면 다이어그램을 포함하는 문자열을 생성합니다. 줄 바꿈 문자(\n)는 행 구분 기호로 사용됩니다. .join()에 대한 인수는 다이어그램 헤더와 주사위 면을 형성하는 문자열(행)을 연결하는 문자열 목록입니다.
  • 38행은 인쇄 가능한 주사위 면 다이어그램을 호출자에게 반환합니다.

앱의 메인 코드를 완성하고 주사위를 굴려보세요

In [ ]:
# dice.py

# ...

# ~~~ App's main code block ~~~
# 1. Get and validate user's input
num_dice_input = input("How many dice do you want to roll? [1-6] ")
num_dice = parse_input(num_dice_input)
# 2. Roll the dice
roll_results = roll_dice(num_dice)
# 3. Generate the ASCII diagram of dice faces
dice_face_diagram = generate_dice_faces_diagram(roll_results)
# 4. Display the diagram
print(f"\n{dice_face_diagram}")

generate_dice_faces_diagram() 구현으로 돌아가면 코드의 해당 부분이 수행하는 작업을 지적하는 몇 가지 주석이 포함되어 있음을 알 수 있습니다.

라인 12는 generate_dice_faces_diagram() roll_results를 인수로 사용하여 호출합니다. 이 호출은 현재 주사위 굴리기 결과에 해당하는 주사위 면의 다이어그램을 작성하고 반환합니다. 14행에서는 print() 호출하여 화면에 다이어그램을 표시합니다.

In [ ]:
def generate_dice_faces_diagram(dice_values):
    # ...
    # Generate a list of dice faces from DICE_ART
    dice_faces = []
    for value in dice_values:
        dice_faces.append(DICE_ART[value])

    # Generate a list containing the dice faces rows
    dice_faces_rows = []
    for row_idx in range(DIE_HEIGHT):
        row_components = []
        for die in dice_faces:
            row_components.append(die[row_idx])
        row_string = DIE_FACE_SEPARATOR.join(row_components)
        dice_faces_rows.append(row_string)

    # Generate header with the word "RESULTS" centered
    width = len(dice_faces_rows[0])
    diagram_header = " RESULTS ".center(width, "~")

    dice_faces_diagram = "\n".join([diagram_header] + dice_faces_rows)
    return dice_faces_diagram

4단계: 주사위면 다이어그램을 생성하는 코드 리팩터링

추출 메소드추출 메소드라는 리팩터링 기술이 있습니다. 독립적으로 작동할 수 있는 기능을 추출하여 코드를 개선하는 데 도움을 줍니다. 예를 들어, generate_dice_faces_diagram()의 이전 구현에서 20~22행의 코드를 추출하여 _get_dice_faces()인 비공개 도우미 함수에 배치할 수 있습니다.

In [ ]:
def _get_dice_faces(dice_values):
    dice_faces = []
    for value in dice_values:
        dice_faces.append(DICE_ART[value])
    return dice_faces
In [ ]:
# dice.py

# ...

def generate_dice_faces_diagram(dice_values):
    """Return an ASCII diagram of dice faces from `dice_values`.

    The string returned contains an ASCII representation of each die.
    For example, if `dice_values = [4, 1, 3, 2]` then the string
    returned looks like this:

    ~~~~~~~~~~~~~~~~~~~ RESULTS ~~~~~~~~~~~~~~~~~~~
    ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
    │  ●   ●  │ │         │ │  ●      │ │  ●      │
    │         │ │    ●    │ │    ●    │ │         │
    │  ●   ●  │ │         │ │      ●  │ │      ●  │
    └─────────┘ └─────────┘ └─────────┘ └─────────┘
    """
    dice_faces = _get_dice_faces(dice_values)
    dice_faces_rows = _generate_dice_faces_rows(dice_faces)

    # Generate header with the word "RESULTS" centered
    width = len(dice_faces_rows[0])
    diagram_header = " RESULTS ".center(width, "~")

    dice_faces_diagram = "\n".join([diagram_header] + dice_faces_rows)
    return dice_faces_diagram

def _get_dice_faces(dice_values):
    dice_faces = []
    for value in dice_values:
        dice_faces.append(DICE_ART[value])
    return dice_faces

def _generate_dice_faces_rows(dice_faces):
    dice_faces_rows = []
    for row_idx in range(DIE_HEIGHT):
        row_components = []
        for die in dice_faces:
            row_components.append(die[row_idx])
        row_string = DIE_FACE_SEPARATOR.join(row_components)
        dice_faces_rows.append(row_string)
    return dice_faces_rows

# ~~~ App's main code block ~~~
# ...

소스 코드 가져오기: https://realpython.com/bonus/python-dice-roll-project-code/를 클릭하여 Python 주사위 굴리기 앱을 빌드하는 데 사용할 소스 코드를 가져오세요.