파이썬 강좌 – 실습 – 원카드 게임 만들기 (1)

  1. 프롤로그
  2. 개발 첫걸음
    1. 컴퓨터 구성요소 – 컴퓨터는 어떤 걸 할 수 있나?
    2. 개발과 관련된 용어
    3. 파이썬의 선택 – 왜 파이썬인가?
    4. 파이썬 설치 – Hello World 출력하기
    5. Visual Studio Code 의 편리한 기능
    6. REPL과 콘솔 창 – 파이썬 동작시키기
  3. 파이썬 기초
    1. 기초 입출력 – 소통하기
    2. 변수와 대입 – 기억하기
    3. 연산자 – 계산하기
    4. 조건문 – 분기를 만들기
    5. 반복문 – 비슷한 작업을 반복하기
    6. 반복문 코딩하기
    7. 변수와 리스트 – 비슷한 변수들을 묶기
    8. for, range – 리스트의 항목을 다루기
    9. 함수와 메소드의 호출 – 편리한 기능 이용하기
    10. 모듈 설치와 사용 – 유용한 기능 끌어다 쓰기
    11. 문자열 – 텍스트 다루기
  4. 파이썬 중급
    1. 함수를 직접 만들기 – 자주 쓰는 기능을 묶기
    2. 딕셔너리, 튜플, 세트 – 변수를 다양한 방법으로 묶기
    3. 클래스와 객체 – 변수를 사람으로 진화시키기
    1. 상속 – 클래스를 확장하기
    2. 정체성과 동질성 – 객체의 성질
    3. 특별 메소드와 연산자 – 파이썬의 내부 작동방식 이해하기
    4. 다양한 함수 인수 – 유연한 함수 만들기
    5. 슬라이싱 – 리스트 간편하게 접근하기
    6. 지능형 리스트(List Comprehension) – 리스트 갖고 놀기
    7. namedtuple - 데이터 묶음 손쉽게 만들기
    8. 조건 표현식 (Conditional Expression) - 간단한 분기 나타내기
    9. 코드 스타일 - 코드의 일관성 유지하기
    10. 명령문, 표현식 – 문법을 이루는 것들
    11. 본격적인 검색 해보기
  5. 파이썬 고급
    1. 일급 함수 다루기
    2. NotImplementedError와 NotImplemented
    3. 어노테이션 – 수월하게 프로그래밍하기
    1. 내장 함수 톺아보기
    2. 예외와 에러 – 예상치 못한 상황에 대응하기 (v0.1)
    3. 변수의 범위 – 이름 검색의 범위
  6. 파이썬 심화
    1. 시퀀스와 반복자 – 반복과 순회를 자유자재로 다루기
    2. 데코레이터 – 함수의 기능을 강화하기
    3. 프로퍼티
    4. 제너레이터
    5. async와 await
    6. 객체로서의 클래스 – 클래스를 동적으로 정의하기
  7. 파이썬 프로젝트 실습
    1. 원카드 게임 만들기 (1)
    2. 원카드 게임 만들기 (2)
    3. 원카드 게임 만들기 (3) (작성중)
    4. 턴제 자동 전투 게임 만들기 (작성중)
  8. 실전 (파이썬 외적인 것들)
    1. 정규표현식 – 문자열을 검색하고 치환하기 (작성중)
    2. 유니코드 – 컴퓨터에서 문자를 표기하는 방법
    3. html, css, 인터넷 – 자동화 첫 걸음 내딛기
    4. 네트워크 – 인터넷으로 통신하는 방법
    5. 문서 – 문맥을 읽어보기

들어가며

하나의 실용적인 프로그램을 만드는 것은 어렵습니다. 아주 간단한 프로그램은 만들기 쉽지만 대개 교육의 목적이기 때문에 심히 실용적이지 않습니다. 그렇다고 실용적인 프로그램을 직접 만들겠다 하면, 정말 많은 변수에 대해서 모두 핸들링할 수 있어야 합니다. 그래서 초기의 설계 단계에서부터 실제 구현 단계까지 생각해야 할 것이 한 두 가지가 아닙니다. 실전 경험이 없는 초보자들은 자신이 다룰 수 있는 도구의 사용법을 모두 익혔다고 해도 그것을 응용하기가 굉장히 어렵습니다.

그래서 교육적이고 간단한 프로그램과 실용적이고 복잡한 프로그램 가운데 어딘가에 끼여 있는, 교육용이기는 하지만 실용적인 프로그램을 만들 때 생각해야 할 것들을 정리할 수 있는 하나의 프로젝트를 진행하고자 합니다. 프로젝트는 현실에서 우리가 흔하게 접할 수 있어서 친근하게 느껴져야 하며, 또한 지금까지 배워온 여러가지 기법을 충분히 활용할 수 있는, 너무 작지 않은 규모의 프로젝트여야 합니다. 저는 간단한 게임을 만들면 좋겠다 싶었고, 어린 시절부터 간간히 친구들과 즐겨온 원 카드 게임을 직접 만들어보면 어떨까 생각이 들었습니다. (원 카드 게임에 대해 전혀 모르시는 분이라면 가까운 지인과 직접 해보시기를 권장드리며, 그것이 여의치 않다면 규칙 정하기를 아주 꼼꼼히 읽어주세요)


사전 공부

  • 변수와 리스트의 개념과 사용법
  • 연산자
  • 함수
  • 클래스

규칙 정하기

우리는 원카드 게임을 만들 것입니다. 근데 원카드 게임이 무엇이죠?

  • 앞의 사람이 낸 카드와 모양이 같거나 숫자가 같은 카드를 낸다.

정확합니다! 게임을 만약 한 문장으로 정리한다면 위와 같이 간단하게 정리할 수 있겠습니다. 하지만 우리의 컴퓨터에게는 처음부터 끝까지 일일히 가르쳐줘야 합니다. 저러한 자연어를 충분히 이해하려면 인공지능의 수준은 아직 멀었습니다. 사실 원카드를 전혀 모르는 사람 입장에서도 저 말은 굉장히 뜬금없습니다. "카드를 어떻게 얻는 것인가?", "카드를 낼 수 있는건가?", "카드를 어디에서 어디로 낸다는 것인가?", "카드를 아무 때에나 내도 되는 것인가?"등등 질문 투성이로 머리가 복잡하게 얽힐 것입니다. 지금부터는 컴퓨터가 이해할 수 있을 만큼 게임의 규칙을 상세하고 명확하게 정해나갈 것입니다. 원 카드를 전혀 모르는 원시인을 가르친다고 상상합시다.

  • 처음 게임을 시작하게 되면, 플레이어들은 7장씩 나눠갖는다.
  • 플레이어는 자신의 차례가 될 때마다 카드를 내거나, 먹어야 한다.
  • 플레이어는 앞의 사람이 낸 카드와 모양이 같거나 숫자가 같은 카드를 낼 수 있다.
  • 낼 수 있는 카드가 없다면, 한 장 먹어야 한다.
  • 행동을 마치게 되면 다음 플레이어의 차례로 넘어간다.

규칙이 어느정도 상세해졌습니다. 가능하다면 규칙을 더욱 세부적으로 짜본 다음에 개발 순서를 정해보도록 합시다.

  • 게임 준비 단계
    • 덱은 조커 포함 54장의 모든 트럼프카드를 무작위로 섞는다.
    • 처음 게임을 시작하게 되면, 플레이어들은 덱에서 7장씩 나눠갖는다.
    • 낸 카드 뭉치에 덱에서 한 장을 꺼내 보이도록 놓는다.
  • 게임 진행 단계
    • 플레이어는 자신의 차례가 될 때마다 카드를 내거나, 덱의 카드를 먹어야 한다.
    • 플레이어는 앞의 사람이 낸 카드와 모양이 같거나 숫자가 같은 카드를 낼 수 있다.
    • 플레이어는 2, A, Joker 카드를 통해 공격할 수 있다. 공격받은 사람은 더 강력한 카드로 공격을 이어나갈 수 있으며, 받아칠 수 없다면 부과된 만큼 카드를 먹는다. 공격을 이어나갈 때는 부과되는 카드가 누적된다.
    • Joker 카드는 언제든지 낼 수 있다. Joker는 Joker끼리 받아칠 수 있다.
    • 낼 수 있는 카드가 없다면, 한 장 먹어야 한다.
    • 행동을 마치게 되면 다음 플레이어의 차례로 넘어간다.
  • 게임 끝 단계
    • 플레이어의 모든 카드가 소모되면 그 플레이어는 즉시 승리한다.
  • 공격 카드
    • 2 카드는 카드 2개 공격을 할 수 있다.
    • A 카드는 카드 3개 공격을 할 수 있다.
    • 흑백 조커 카드는 카드 5개 공격을 할 수 있다.
    • 색 조커 카드는 카드 10개 공격을 할 수 있다.
  • 특수 카드
    • 7 카드를 낼 때 원하는 모양으로 바꿀 수 있다. 즉 스페이드 7을 내면서 다이아몬드를 선언하면 다이아몬드 7을 낸 것과 동일한 취급을 받는다.
    • K 카드를 내면 즉시 한 번 더 진행할 수 있다. 낼 카드가 없다면 한 장 먹어야 한다.
    • J 카드를 내면 다음 플레이어의 차례를 건너뛰고 다다음 플레이어부터 진행한다. (1:1은 K 카드와 동일한 효과를 가진다)
    • Q 카드를 내면 플레이어간의 차례를 뒤바꾼다. (1:1은 아무런 효과가 없다)
    • 3 카드로 A 또는 2의 공격을 무효화할 수 있다.
  • 특수 상황
    • 플레이어의 누구든 카드가 한 장 남은 순간에 "원카드"를 외칠 수 있다. 카드를 낸 본인이 "원카드"를 먼저 외쳤다면 아무런 일 없이 계속 게임이 진행된다. 본인이 아닌 다른 사람이 "원카드"를 먼저 외쳤다면 한 장 남은 플레이어는 카드 한 장을 즉시 먹는다.
    • 만약 덱의 모든 카드가 소모되었다면 낸 카드의 가장 윗 카드만 남겨두고 모두 섞어 다시 덱 자리에 놓는다

개발 단계 대략적으로 정해보기

게임은 항상 일반적인 상황과 예외적인 상황으로 구분할 수 있습니다. 예를 들어 원카드 게임에서 게임 준비 단계와 게임 끝 단계는 절대 변하지 않습니다. 이런 것부터 개발 우선순위를 높게 잡습니다.

일반적인 카드를 내는 상황은 공격 카드를 내는 상황보다 훨씬 빈번합니다. 또한 공격 카드를 내는 상황이 7, J, Q, K 등의 특수카드를 내는 상황보다 더 빈번합니다. 그러므로 우리는 일반카드를 내는 메커니즘을 먼저 구상해본 뒤, 그 다음 공격 메커니즘, 그 다음 특수 카드 메커니즘을 실현해볼 것입니다.

graph TD a["게임 시작과 끝 부분 구현"] b["일반적인 카드를 내는 상황 구현<br>(플레이어간 차례 전환 포함)"] c["공격 카드를 내는 상황 구현"] d["특수 카드를 내는 상황 구현"] a-->b-->c-->d

개발 흐름 정해보기


테스트 하기 쉬운 상황 고려하기

우리가 실제 상황처럼 동작하는 것, 즉 완전히 완성된 프로그램과 개발 중인 프로그램의 개발은 다를 수 밖에 없습니다. 우리는 처음부터 아주 다양한 상황을 고려하여 차근차근 프로그램을 설계할 수도 있지만 이는 숙련된 프로그래머조차 넘기 힘든 난관입니다. 그러므로 우리는 프로그램을 다소 축소하여 더 개발하기 쉽도록 만들어 볼 것입니다.

  • 원카드를 외치는 메커니즘은 아예 만들지 않는다. (또는 추후에 만든다)
  • 플레이어를 2명으로 정하고, 한 명을 컴퓨터로 한다.
  • 컴퓨터는 낼 수 있는 카드 중 아무 카드를 랜덤으로 낸다.

코드 조각 구상하기

지금껏 우리는 규칙을 대충 정해봤습니다. 하지만 이러한 규칙은 컴퓨터가 이해할 수 없지요. 이런 규칙을 컴퓨터가 이해할 수 있도록 바꾸어 보는 단계입니다. 우리는 각종 변수를 만들고, 분기를 만들 때 if를 사용하고, 반복하고자 할 때 forwhile을 사용하고, 반복되는 일은 함수로 묶는 등의 작업을 할 수 있습니다.

아래 규칙 문장들은 우선적으로 생각해볼 것들을 필자의 머릿속에 떠오른 대로 적어본 것입니다. 이 규칙들을 어떻게 실제로 코딩할지 한번 생각해봅시다. 그 다음에 아래 쪽에 구상의 실제 코드를 살펴봅시다. 구상 예시는 단지 필자가 생각하는 구현 방법에 지나지 않으며, 더 효율적이고 좋은 구상 방법이 있을 수 있습니다.

  • 뽑을 수 있는 카드뭉치, 플레이어가 가지고 있는 카드 등을 어떻게 나타낼까요? (힌트-리스트)
  • 카드를 먹는다는 행위를 어떻게 나타낼까요?
  • 카드 하나하나를 어떻게 나타낼까요?
  • 플레이어가 낼 수 있는 카드를 어떻게 판단할까요?

코드 조각 구상 예시

  • 뽑을 수 있는 카드뭉치, 플레이어가 가지고 있는 카드 등을 어떻게 나타낼까요?
    deck, player 등의 리스트를 만듭니다. 이 리스트는 카드 뭉치나 우리의 패라고 뜻을 정할 수 있습니다.
deck = []
player = []
  • 카드를 먹는다는 행위를 어떻게 나타낼까요?
    deck 리스트의 마지막 요소를 삭제하고, 그 항목을 player 리스트에 추가합니다.
last_card = deck.pop()
player.append(last_card)
  • 카드 요소를 어떻게 나타낼까요?
    → 간단하게 튜플로 나타낼 수 있을 것 같습니다. ('♥', '5')라면 하트 모양의 5인 카드를 나타낼 수 있겠습니다. 리스트로 나타내도 되지만 카드 요소를 수정할 일은 없으므로 더 간단한 형태인 튜플로 나타냅니다.
    → 숫자를 숫자형인 5로 하지 않고 '5'로 한 이유는 이후 J,Q,K 등의 숫자가 아닌 숫자(?)도 추가되기 때문입니다.
    shape 속성과 number 속성을 가진 Card 클래스로도 나타낼 수 있을 것 같습니다.
some_card = ('♥', '5')

class Card:
    def __init__(self, shape, num):
        self.shape = shape
        self.num = num
some_card2 = Card('♥', '5')
  • 플레이어가 낼 수 있는 카드를 어떻게 판단할까요?
    → 플레이어가 낼 수 있는 카드는 이미 냈던 카드와 모양이 같거나 숫자가 같은 카드입니다. 이미 냈던 카드 또한 리스트로 put 변수를 둡시다. 이 리스트의 가장 마지막 요소는 put[-1]로 나타낼 수 있습니다. 낼 수 있는 카드의 모음을 available 리스트로 나타난다고 가정했을 때 코드로는 다음과 같이 작성할 수 있겠습니다.
put = []

# 카드가 단순한 튜플일 때
available = []
for card in player:
    if put[-1][0] == card[0] or put[-1][1] == card[1]:
        available.append(card)

# 카드가 클래스로 만든 객체일 때
available = []
for card in player:
    if put[-1].shape == card.shape or put[-1].num == card.num:
        available.append(card)


게임 준비 부분 코딩

게임 준비 부분부터 차근차근 개발해봅시다. 우선 카드를 만들어야겠지요. 클래스를 이용할 수도 있겠지만, 그냥 튜플을 써보도록 합시다. 반복되는 부분은 for로 처리하고, 조커와 같은 특수 카드는 손수 추가해줍시다. deck을 리스트로 먼저 만듭니다.

deck = []

그 다음 for문을 돌릴 것들을 생각해봅시다.

for shape in ??:
    for num in ??:
        deck.append((shape, num))

?? 안에 뭐가 들어가면 좋을까요? 다음과 같이 문자열로 하여 한 글자씩 반복하게 하면 좋을 것 같습니다.

# nums과 shapes 정의
shapes = '♥♣♠◆'
nums = []
for i in range(2,11):
    nums.append(str(i))
for c in 'JQKA':
    nums.append(c)

# 덱 만들기
for shape in shapes:
    for num in nums:
        deck.append((shape, num))

이제 특수 카드를 추가해줍시다. 조커는 모양 칸에 ‘Joker’가, 숫자 칸에는 색깔 정보가 들어가도록 합시다. 이는 애시당초의 카드 설계와 다소 어긋나지만, 특수 카드의 종류가 많은 것도 아니고 예외로 간주하여 적당히 처리할 수 있을 것 같습니다.

deck.append(('Joker', 'black'))
deck.append(('Joker', 'colored'))

이제 카드를 섞어줍니다. random 모듈을 이용합니다.

import random
random.shuffle(deck)

그리고 플레이어와 컴퓨터에 카드를 분배해줍니다.

# 플레이어에게 카드 나누기
player = []
computer = []

for i in range(7):
    player.append(deck.pop())
    computer.append(deck.pop())

마지막으로, 덱에서 낸 카드로 카드 하나만 옮겨줍니다.

# 낸 카드에 하나 올려놓기
put = []
put.append(deck.pop())

이로써 게임 준비 부분의 모든 코드는 완료되었습니다. 아래 코드를 참조해주세요.


게임의 끝 부분 만들기

본격적으로 코딩할 차례입니다. 게임이 끝나는 시점은 누군가가 패가 전부 떨어질 때입니다. 즉 게임은 플레이어나 컴퓨터의 패가 다 떨어질 때까지 무제한으로 반복합니다. 무제한 반복은 while True: 로 구현할 수 있겠지요? 크게 틀만 잡도록 합시다.

# 게임 시작
while True:

    # 플레이어의 차례
    # 플레이어가 무언가를 한다..

    if len(player) == 0:
        print("플레이어가 이겼습니다!")
        break

    # 컴퓨터의 차례
    # 컴퓨터가 무언가를 한다..

    if len(computer) == 0:
        print("컴퓨터가 이겼습니다!")
        break

프로그램은 Ctrl+C로 강제종료합니다.

이제 루프를 무한대로 돌 것입니다. 즉 프로그램이 무한대로 돌아간다는 것을 의미합니다. 물론 종료되는 조건은 패를 다 썼을때로 명확하게 있지만, 그렇다면 프로그램을 끝내고 싶을 때마다 카드를 다 소모할 때까지 게임을 계속해야 할까요? 그렇지 않습니다. 처음 CLI에 대해 배웠을 때, 콘솔에서 강제 종료를 의미하는 단축키가 있었습니다. 바로 Ctrl+C 입니다. 이를 이용하면 언제든지 프로그램 실행 중간에 강제 종료할 수 있습니다.


게임의 핵심 부분 코딩하기

게임의 핵심 부분에 대한 순서를 간단히 생각해보도록 합시다.

graph TD st[플레이어의 턴 시작] st-->imf[게임 진행 정보 출력]-->a a[낼 수 있는 카드를<br>모두 구해본다.]-->b{낼 수 있는<br>카드가<br>있는가?} b-->|Yes|c[이들 중 카드를<br>골라서 낸다.]-->en b-->|No|d[카드를 한 장<br>먹는다.] d-->en[플레이어의 턴 끝] st2[컴퓨터의 턴 시작] st2-->a2 a2[낼 수 있는 카드를<br>모두 구해본다.]-->b2{낼 수 있는<br>카드가<br>있는가?} b2-->|Yes|c2[이들 중 카드를<br>랜덤으로 골라서<br>낸다.]-->en2 b2-->|No|d2[카드를 한 장<br>먹는다.] d2-->en2[컴퓨터의 턴 끝]

게임의 순서도


간단한 정보 출력하기

게임의 핵심 부분을 차례로 만들어갑시다. 우선 플레이어와 게임의 상태가 어떤지 출력해줄 수 있도록 합니다.

# 플레이어의 현재 패 출력
print("플레이어의 차례입니다.")
print("현재 패 >>", player)
print("놓여진 카드 >>", put[-1])

어떤 리스트에 append할 때마다 리스트의 마지막에 항목이 추가되며, 어떤 리스트의 마지막 요소는 [-1]로 구할 수 있습니다. 그러므로 put[-1]은 낸 카드 중의 마지막 카드를 가리키게 됩니다.


낼 수 있는 카드인지 판단하기

이제 낼 수 있는 카드를 판단해봅시다. 아직까지는 공격 메커니즘을 넣지 않았으므로 공격을 전혀 고려하지 않아도 됩니다. 낼 수 있는 카드인지 판단하는 순서는 다음과 같습니다.

graph TD a[패에 있는 어떤 카드 card와 <br>이미 놓여져 있는 카드<br>put을 고려한다.] a-->b b{card와 put의<br>모양이 같은가?}-->|Yes|y c{card와 put의<br>숫자가 같은가?}-->|Yes|y d{card가 조커인가?}-->|Yes|y e{put이 조커인가?}-->|Yes|y b-->|No|c-->|No|d-->|No|e-->|No|f[card는 낼 수 없다.] y[card는 낼 수 있다.]

낼 수 있는 카드 판단

카드를 선택하는 시점은 낼 수 있는 카드를 모두 알게 된 다음이기 때문에 우선 모든 카드를 낼 수 있는지를 검사하고, 가능한 카드를 available이라는 임시 리스트에 넣어둡니다. 그리고 낼 수 있는 카드가 제대로 나왔는지 print를 통해 확인해봅시다.

# 가능한 카드 출력
available = []
for card in player:
    if (card[0] == put[-1][0]
        or card[1] == put[-1][1]
        or card[0] == 'Joker'
        or put[-1][0] == 'Joker'):
        available.append(card)
        
print("낼 수 있는 카드:", available)
플레이어의 차례입니다.
현재 패 >> [('♣', '10'), ('♠', '3'), ('♣', '3'), ('◆', '6'), ('♣', 'K'), ('♥', '3'), ('♠', '8')]
놓여진 카드 >> ('♣', '6')
낼 수 있는 카드: [('♣', '10'), ('♣', '3'), ('◆', '6'), ('♣', 'K')]

좋습니다. 제대로 동작합니다. 이제 이것들 중 플레이어가 선택할 수 있도록 해봅시다.


플레이어가 카드 선택하기

사용자의 입력을 받는 방법은 굉장히 다양합니다. 우리는 사용 가능한 카드에 번호를 매기고, 이 번호를 통해 입력을 받는 방법을 취할 것입니다. 번호로 입력받기 위해서 리스트의 인덱스를 바로 얻는 방법이 있을 수 있겠습니다.

print("낼 수 있는 카드:", available)
i = int(input("몇 번째 카드를 내시겠습니까?"))
i -= 1
selected = available[i]
player.remove(selected)
put.append(selected) 

플레이어가 번호를 입력하여, 인덱스로 변환시킨 뒤 인덱스로 항목을 접근하고 있습니다. 리스트의 remove 메소드를 통해 항목을 삭제함으로써 player가 카드를 냈다는것을 표현할 수 있습니다. 낸 카드는 put.append 해줌으로써 카드를 냈다고 처리합시다.

앗, 잠깐만요, 만약 낼 수 있는 카드가 없어서 카드를 먹을 수 밖에 없는 상황이라면 어떡하죠?


낼 수 있는 카드가 없는 경우 카드 먹기

카드를 선택하기 전에 카드를 먹어야 하는 상황인지에 대해서 먼저 판단해야 할 것 같습니다. 우리가 앞서 카드 하나하나에 대해 검사한 후 available을 구해 보았습니다. 이 available 리스트의 길이를 구한다면 우리가 낼 수 있는 카드가 하나라도 있는지 없는지 알 수 있을 것입니다. 다음이 코드입니다.

# 낼 수 있는 카드가 있는 경우
if len(available) > 0:
    i = int(input("몇 번째 카드를 내시겠습니까?"))
    i -= 1
    selected = available[i]
    player.remove(selected)
    put.append(selected)

# 낼 수 있는 카드가 없는 경우
else:
    print("낼 수 있는 카드가 없어 먹습니다.")
    player.append(deck.pop())

좋습니다. 우리가 게임의 끝 부분까지 녹여서 코드를 만든다면 커다란 while 문의 내용은 다음과 같이 완성되었을 것입니다.

while True:

    # 플레이어의 차례
    print("플레이어의 차례입니다.")
    print("현재 패 >>", player)
    print("놓여진 카드 >>", put[-1])

    # 가능한 카드 출력
    available = []
    for card in player:
        if (card[0] == put[-1][0]
            or card[1] == put[-1][1]
            or card[0] == 'Joker'
            or put[-1][0] == 'Joker'):
            available.append(card)
        
    print("낼 수 있는 카드:", available)

    # 낼 수 있는 카드가 있는 경우
    if len(available) > 0:
        i = int(input("몇 번째 카드를 내시겠습니까?"))
        i -= 1
        selected = available[i]
        player.remove(selected)
        put.append(selected)

    # 낼 수 있는 카드가 없는 경우
    else:
        print("낼 수 있는 카드가 없어 먹습니다.")
        player.append(deck.pop())

    if len(player) == 0:
        print("플레이어가 이겼습니다!")
        break

컴퓨터의 차례 만들기

컴퓨터의 차례는 지금 껏 작성한 코드를 복사하여 그대로 붙여넣은 뒤, 일부만 수정해주면 됩니다.

  • 컴퓨터는 패의 정보를 자세하게 출력할 필요가 없습니다.
  • 컴퓨터는 랜덤으로 가능한 카드를 선택합니다.

while의 컴퓨터 부분의 코드는 다음과 같습니다.

# 게임 시작
while True:

    # 플레이어의 차례
    # .. 중략 ..
   
    # 컴퓨터의 차례
    print("컴퓨터의 차례입니다.")
    print("놓여진 카드 >>", put[-1])

    # 가능한 카드
    available = []
    for card in computer:
        if (card[0] == put[-1][0]
            or card[1] == put[-1][1]
            or card[0] == 'Joker'
            or put[-1][0] == 'Joker'):
            available.append(card)

    # 낼 수 있는 카드가 있는 경우
    if len(available) > 0:
        selected = random.choice(available)
        computer.remove(selected)
        put.append(selected)
        print("컴퓨터가", selected, "를 냈습니다.")

    # 낼 수 있는 카드가 없는 경우
    else:
        print("낼 수 있는 카드가 없어 먹습니다.")
        computer.append(deck.pop())

    if len(computer) == 0:
        print("컴퓨터가 이겼습니다!")
        break

중간 마무리

지금까지 대략적으로 핵심적인 게임 기능을 구현해보았습니다. 실제로 플레이해보면 제대로 동작하고 있음을 확인할 수 있습니다. 하지만 아직 갈 길이 멀었습니다. 다음 시간에는 함수를 이용해서 비슷한 기능을 최대한 묶은 다음 공격 메커니즘이나 특수한 상황을 더 처리해볼 것입니다. 지금까지 작성한 모든 코드는 아래를 펼쳐 확인할 수 있습니다.

  1. 프롤로그
  2. 개발 첫걸음
    1. 컴퓨터 구성요소 – 컴퓨터는 어떤 걸 할 수 있나?
    2. 개발과 관련된 용어
    3. 파이썬의 선택 – 왜 파이썬인가?
    4. 파이썬 설치 – Hello World 출력하기
    5. Visual Studio Code 의 편리한 기능
    6. REPL과 콘솔 창 – 파이썬 동작시키기
  3. 파이썬 기초
    1. 기초 입출력 – 소통하기
    2. 변수와 대입 – 기억하기
    3. 연산자 – 계산하기
    4. 조건문 – 분기를 만들기
    5. 반복문 – 비슷한 작업을 반복하기
    6. 반복문 코딩하기
    7. 변수와 리스트 – 비슷한 변수들을 묶기
    8. for, range – 리스트의 항목을 다루기
    9. 함수와 메소드의 호출 – 편리한 기능 이용하기
    10. 모듈 설치와 사용 – 유용한 기능 끌어다 쓰기
    11. 문자열 – 텍스트 다루기
  4. 파이썬 중급
    1. 함수를 직접 만들기 – 자주 쓰는 기능을 묶기
    2. 딕셔너리, 튜플, 세트 – 변수를 다양한 방법으로 묶기
    3. 클래스와 객체 – 변수를 사람으로 진화시키기
    1. 상속 – 클래스를 확장하기
    2. 정체성과 동질성 – 객체의 성질
    3. 특별 메소드와 연산자 – 파이썬의 내부 작동방식 이해하기
    4. 다양한 함수 인수 – 유연한 함수 만들기
    5. 슬라이싱 – 리스트 간편하게 접근하기
    6. 지능형 리스트(List Comprehension) – 리스트 갖고 놀기
    7. namedtuple - 데이터 묶음 손쉽게 만들기
    8. 조건 표현식 (Conditional Expression) - 간단한 분기 나타내기
    9. 코드 스타일 - 코드의 일관성 유지하기
    10. 명령문, 표현식 – 문법을 이루는 것들
    11. 본격적인 검색 해보기
  5. 파이썬 고급
    1. 일급 함수 다루기
    2. NotImplementedError와 NotImplemented
    3. 어노테이션 – 수월하게 프로그래밍하기
    1. 내장 함수 톺아보기
    2. 예외와 에러 – 예상치 못한 상황에 대응하기 (v0.1)
    3. 변수의 범위 – 이름 검색의 범위
  6. 파이썬 심화
    1. 시퀀스와 반복자 – 반복과 순회를 자유자재로 다루기
    2. 데코레이터 – 함수의 기능을 강화하기
    3. 프로퍼티
    4. 제너레이터
    5. async와 await
    6. 객체로서의 클래스 – 클래스를 동적으로 정의하기
  7. 파이썬 프로젝트 실습
    1. 원카드 게임 만들기 (1)
    2. 원카드 게임 만들기 (2)
    3. 원카드 게임 만들기 (3) (작성중)
    4. 턴제 자동 전투 게임 만들기 (작성중)
  8. 실전 (파이썬 외적인 것들)
    1. 정규표현식 – 문자열을 검색하고 치환하기 (작성중)
    2. 유니코드 – 컴퓨터에서 문자를 표기하는 방법
    3. html, css, 인터넷 – 자동화 첫 걸음 내딛기
    4. 네트워크 – 인터넷으로 통신하는 방법
    5. 문서 – 문맥을 읽어보기

2 thoughts on “파이썬 강좌 – 실습 – 원카드 게임 만들기 (1)

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 항목은 *(으)로 표시합니다

Scroll to top