본문 바로가기
AI 웹개발반

[TIL] 게임 코드 작성기 feat.불안한 코드와 그걸 지켜보는 나

by 째깍단 2023. 3. 28.

장장 4일간의 게임 코드 작성기...

 

부끄럽지만 작성한 코드를 올려본다.

더보기

ㅠ,ㅠ 대부분 내가 고친 오류가 없어서 아쉽다

 

import time
import random
import sys

# 필요한 기능들 함수로 정의하기


def y_or_n(question):
    while "input yes or no":
        reply = str(input(question+' (y/n): ')).lower().strip()
        if reply[0] == 'y':
            return True
        if reply[0] == 'n':
            return False
        if reply[0] != 'y' or 'n':
            print("y나 n을 입력해주세요.")


def run_game(player, enemy):
    while "둘 중 한 유닛hp가 0이 될때까지":
        in_game.player_turn(player, enemy)
        time.sleep(1)
        if enemy.hp <= 0:
            endings.victoryend()
            break
        elif player.hp <= 0:
            endings.defeatend()
            break
        elif player.mp <= 0:
            endings.dreamend()
            break
        in_game.enemy_turn(player, enemy)
        time.sleep(1)
        in_game.unit_status(player, enemy)
        if enemy.hp <= 0:
            endings.victoryend()
            break
        elif player.hp <= 0:
            endings.defeatend()
            break
        elif player.mp <= 0:
            endings.dreamend()
            break


class Login():  # id 입력하고 확인 및 저장
    def __init__(self):
        self.name = None

    def input_name(self):
        self.name = input("플레이어 이름을 입력하세요 >> ")

    def id_check(self):
        q_id = (f'{self.name}, 이 이름이 맞습니까? : ')

        if y_or_n(q_id) == True:
            self.start_game()
        else:
            while "유저 네임 입력시 까지, 3회입력하면 끝":
                id_count = 0
                if id_count < 3:
                    self.input_name("다시 플레이어 이름을 입력하세요 >> ")
                    id_count += 1
                else:
                    Ending.errorend()
                    break

        if self.name == None:
            while "re-input player name":
                self.input_name(("다시 플레이어 이름을 입력하세요 >> "))
                id_count = 0

                if y_or_n(q_id) is True:
                    self.start_game()
                else:
                    self.input_name("다시 플레이어 이름을 입력하세요 >> ")
                    id_count += 1
                    if id_count == 3:
                        Ending.errorend()
                        break

    def start_game(self):
        self = print(f'{self.name}(으)로 게임을 시작합니다!')


# 플레이어 코드
# 플레이어 생성 - 이름 입력
# 플레이어 공격타입 2가지, 일반, 마법

class Player():  # 플레이어
    def __init__(self, name, hp, mp, power):
        self.name = name
        self.max_hp = hp
        self.hp = hp
        self.max_mp = mp
        self.mp = mp
        self.power = power

    def attack(self, enemy):
        damage = random.randint(self.power - 2, self.power + 2)
        enemy.hp = max(enemy.hp - damage, 0)
        if damage:
            time.sleep(1)
            print(f"{self.name}의 공격! {enemy.name}몬스터에게 {damage}의 피해를 입혔습니다.")

    def magic_attack(self, enemy):
        magic_damage = random.randint(self.power - 5, self.power + 15)
        enemy.hp = max(enemy.hp - magic_damage, 0)
        if magic_damage:
            self.mp -= 20
            print('\nmp를 20 사용했다.')
            time.sleep(1)
            print(f"{self.name}의 마법 공격! {enemy.name}몬스터에게 {magic_damage}의 피해를 입혔습니다.")

    def cure(self):
        self.mp -= 10
        self.cure = random.randint(self.power * 0.7, self.power * 0.9)
        time.sleep(1)
        self.hp += self.cure
        print(f'치유 마법으로 {self.cure}만큼 치료되었다.')

    def show_status(self):
        print(
            f"\n{self.name}의 상태: HP {self.hp}/{self.max_hp}, MP {self.mp}/{self.max_mp}")


# 몬스터 코드 - random을 사용하여 임의 생성(마릿수가? 특성이?)
# 몬스터는 일반 공격


class Monster():
    def __init__(self, name='무서운', hp=100, power=15, attribute='무서운'):
        self.name = name
        self.hp = hp
        self.power = power
        self.attribute = attribute

    def attack(self, player, damage=1):
        self.hp -= damage
        damage = self.power * damage

        player.hp = max(player.hp - damage, 0)
        if damage:
            print(f"{self.name}몬스터의 공격! {player.name}에게 {damage}의 피해를 입혔습니다.")

    def status_check(self):
        print(f'{self.name}몬스터의 hp가 {self.hp} 남았다')


class FireMonster(Monster):
    def __init__(self):
        super().__init__('불꽃', 100, 15, '불꽃')

    def attack(self, player, damage=1.5):
        super().attack(player, damage)


class IceMonster(Monster):
    def __init__(self):
        super().__init__('서리', 120, 10, '서리')

    def attack(self, player, damage=1.2):
        super().attack(player, damage)


# while을 사용해 종료조건 충족할 때 까지 턴제로 전투 진행

class RunGame():
    def create_unit(self):
        enemy = random.choice([Monster, FireMonster, IceMonster])
        enemy = enemy()

        print(f'{enemy.name}몬스터와 마주쳤다!')
        enemy.status_check()
        return enemy

    def player_turn(self, player, enemy):
        print(f'\n\n{player.name}의 차례')

        print('< 스킬 선택 >')
        print('1 물리 공격')
        print('2 마법 공격')
        print('3 치유 마법')
        print('4 도망간다')
        ps_input = int(input('사용할 스킬의 번호를 입력하세요. : '))

        while ps_input in range(1, 5):
            if ps_input == 1:
                player.attack(enemy)
                enemy.status_check()
                break
            elif ps_input == 2:
                player.magic_attack(enemy)
                enemy.status_check()
                break
            elif ps_input == 3:
                player.cure()
                enemy.status_check()
                break
            elif ps_input == 4:
                print(f"{player.name}은 도망가기로 했다")
                endings.badend()
                sys.exit()
            else:
                print('잘못된 입력! 1 2 3 4 중 하나를 입력하세요.')
                continue

    def enemy_turn(self, player, enemy):
        print(f'\n\n{enemy.name}몬스터의 차례')
        time.sleep(0.5)
        enemy.attack(player)

    def unit_status(self, player, enemy):
        time.sleep(0.5)
        player.show_status()
        enemy.status_check()

    # 종료 조건 : 플레이어의 패배, 모든 몬스터에게 승리


class Ending():
    def __init__(self):
        pass

    def victoryend(self):  # 승리 시
        print("\n당신은 연인을 지켜내었습니다!")
        print("환상의 동물이 👍를 눌러주었습니다^^ -끝-")

    def defeatend(self):  # 패배 시
        print("\n전투에서 패배했다!")
        time.sleep(1)
        print("연인이 환상의 동물이 되어 사라졌습니다... -끝-")

    def badend(self):  # 도망친다를 선택했을 때
        print("\n도망가는 중...")
        time.sleep(2)
        print("...")
        time.sleep(3)
        print("\n연인을 지키지 못한 당신은 평생을 홀로 살았습니다.. -끗-")

    def dreamend(self):
        print("\n당신은 꿈에서 깨어났습니다.")
        time.sleep(3)
        print(f"옆을 돌아본 {login.name}의 눈가에 눈물이 맺혔습니다.")
        time.sleep(1)
        print("-꿈-")

    def errorend(self):
        print("Error!")
        print("갈팡질팡하다 패배했다.")

    def retry(self):
        if y_or_n("\nretry?ß ") == True:
            print("\n새로운 적이 나타났다! 내 이름은..")
            time.sleep(1)
            login.input_name()

            player = Player(login.name, 100, 100, 20)
            in_game = RunGame()
            enemy = in_game.create_unit()
            run_game(player, enemy)
            endings.retry

        else:
            ("게임을 종료합니다.")
            time.sleep(2)
            sys.exit()


# 게임 실행


"""여기부터 게임 실행 코드"""
login = Login()
endings = Ending()

# print하는 함수를 만들기

# 게임 play 내용 입력
# class 호출 : 변수를 만들어 인스턴스를 생성하고, 메소드를 호출한다.

print("평화로운 서울 한복판에 몬스터가 나타났다!!")
time.sleep(1.5)
print("당신은 함께있던 연인을 지켜야합니다!")
time.sleep(1.5)

if y_or_n("싸울까?") == False:
    if y_or_n("\n..정말로 도망갈까?") == True:
        endings.badend()
        sys.exit()
    else:
        time.sleep(1)
        print("연인을 보고 용기를 냈다.")
        time.sleep(0.5)

print("내 이름은..\n")
time.sleep(0.5)
login.input_name()
if login.name:
    login.id_check()

    player = Player(login.name, 100, 100, 20)
    in_game = RunGame()
    enemy = in_game.create_unit()
    run_game(player, enemy)
    endings.retry()

 

과정: 

처음이니까, 객체지향을 바로 진행 하기 어려울 것이라고 생각했다.

그래서 발제 조건을 보며 주석을 먼저 적었다.

그리고 지난 미니프로젝트를 떠올리며 함수 공부를 먼저 하기로 했다.

함수 공부를 진행하며 주석에 쓸만한 함수들은 적어둔 주석에 추가했다.

 

이후 천천히 절차지향 코드를 적어나가보았다.

그리고 class로 더듬더듬 바꿔 객체를 만들어갔는데.. 객체를 만들자마자 문제가 발생하기 시작한다.

 

1. <built-in function id> 로 게임이 진행되는 문제..

2. input자체가 출력되지 않는 문제..

3. 괜히 욕심내서 yes or no 하려다가 while 루프에 빠져서 이름만 입력하는 문제

4. 아예 게임 실행을 못하는 문제 등등등..

 

많은 문제가 있었지만..

코딩 중 고민을 많이했던 문제를 몇가지 기록한다.

 

 

 


class Login():  # id 입력하고 확인 및 저장
    def __init__(self, name):
        self.name = name

    def input_name(self):
        self.name = input("플레이어 이름을 입력하세요 >> ")

    def id_check(self):
        q_id = (f'{self.name}, 이 이름이 맞습니까?')

 

 

문제점

1. class에 대한 이해 부족으로 대체 왜 문제가 생기는지 알수가 없었고..

2. class를 변수로 지정하여 호출했을때 input 값과 함께 None이 자꾸 같이 떴다.

3. class로 묶어 객체로 만들고 상속했을때 built-in function id ... 기본 함수값이 출력되었다.

 

4.IndexError: string index out of range..

시작부터 string 이외의 것을 입력하니 아예 실행되지 않는 오류가 생긴다. 스페이스 등등

 

 

해결방법..

def __init__(self):
        self.name = None

    def input_name(self):
        self.name = input("플레이어 이름을 입력하세요 >> ")

    def id_check(self):
        q_id = (f'{self.name}, 이 이름이 맞습니까? : ')

1.input을 받아 출력하는데에는 굳이 name을 지정하고 저장할 필요가 없었다... 그래서 init에 self만 넣었다

  이후 해당 class를 변수 login으로 넣고 

  login.name하면 그냥 출력이 되어서 Player클래스에 굳이 상속할 필요도 없었다!

 > 3번 문제도 함께 해결!

 

2. 굳이 함수를 변수로 호출해놓고 자꾸 print로 감쌌다. 그러니 주소값인 None이 함께 떴던 것.   

대략 print를 잔뜩 넣었던 이런 코드였는데 변수로 호출하고 또 프린트로 감싼게 문제였다.

print(Login().name)
print(Ending.happyend())
print(login.name)

 

변수 = 클래스()

로 지정해주었을때, print를 붙이지 않고 변수만 불러도 출력된다.

클래스 내부 함수를 부르고 싶으면 변수.내부함수() 로 출력이 가능하다.

login = Login()
endings= Ending()

login.name  
endings.happyend()

함수에 print를 붙이면 None이 신나게 나온다...

메모...메모...

 

 

 

 

 

 

4. 아예 예외처리를 해야한다고 한다.

 

try - except문,

else: 로 나머지 싹 처리하기

아예 함수로 정의하기.. 등등

유효성 검사라고 하는데, 찾아보면 정말 많은 코드가 있다.

 


 

문제점

yes or no 를 구분하려 정의한 함수에서

다른 문자열을 입력하면 오류가 나는 문제..

 

시도1 yes or no 함수 정의에서.

def y_or_n(question):
    while "input yes or no":
        reply = str(input(question+' (y/n): ')).lower().strip()
        if reply[0] == 'y':
            return True
        if reply[0] == 'n':
            return False
        elif reply != 'y' or 'n':
            print("다시 입력해주세요 : ")
            continue

!=를 사용해 elif문을 추가해보았다.

다시 if y.. 에서 걸린다.

 

 

시도2 :

while "input yes or no":
    reply = str(input(question+' (y/n): ')).lower().strip()
    if reply != 'y' or 'n':
        print("다시 입력해주세요 : ")
        continue
    if reply[0] == 'y':
        return Tru

생각해보니 코드가 위에서 순서대로 도니까 걸러보려고 해도 y문에 먼저 들어가서 오류가 나는거였다!

순서상 아닌걸 먼저 걸러낸다음 y, n 을 체크할 수 있어야하는 것!

 

그런데 이렇게 넣어두면 "다시 입력해주세요"가 계속 나온다~

 

 

 

 

 

 

왜 이러는거야..ㅋㅋㅋ

 

 

시도3 :

def y_or_n(question):
    while "input yes or no":
        reply = str(input(question+' (y/n): ')).lower().strip()
        if reply[0] == 'y':
            return True
        if reply[0] == 'n':
            return False
        if reply[0] != 'y' or 'n':
            print("y나 n을 입력해주세요.")

잘 되는 것 같기도..?

일단 새로 출력하고자하는 부분이 앞에 있어서 우선 확인하느라 그런 것 같다

그래서 다시 아래쪽에 가져다 붙이고 그냥 if문을 사용했다.

 

다른 사람이 만든 코드 수정하는게 제일 어렵다더니..

이렇게 하면 진행 후에도 y나 n을 입력해주세요가 또 뜨긴한다.

그래도 일단 실행되도록 두었다. 튜터님께 여쭤볼 것 +1

 


 

문제점

죽었는데도 때려 때려

while문에서 신나게 때리기만 하는 player..

 

함수 이해

while 에는 continue, break 가 있다.

continue를 넣으면 계속 while문을 진행하도록한다.

break는 while문을 빠져나가도록 한다.

 

 

해결방법

while loop는 첫 날에 빡세게 걸렸어서 금방 해결했다.

 

while문은 설정한 내부 조건이 해결될 때까지 돌아가는데,

선택하고 공격하고 while문 종료 신호를 주지 않아서 무한 때리기가 된 것이었다.

        while ps_input in range(1, 3):
            if ps_input == 1:
                player.attack(enemy)
                enemy.status_check()
                break
            elif ps_input == 2:
                player.magic_attack(enemy)
                enemy.status_check()
                break
            elif ps_input == 3:
                player.cure()
                enemy.status_check()
                break

이렇게 break를 설정하면 해결!

 

 


문제점 및 해결

 

 

Ending class 호출시 에러메세지

TypeError: retry() takes 0 positional arguments but 1 was given

 

class 내 함수의 괄호에 있는것이 positional arguments. 

이것에 self를 넣었는지 확인해보아야한다.

class Ending():
    def __init__(self):
        pass

    def victoryend(self):  # 여기에 기본적으로 self를 넣는 것 기억하기!!!
        print("\n당신은 연인을 지켜내었습니다!")
        print("환상의 동물이 👍를 눌러주었습니다^^ -끝-")

 


기억할 것:

 

함수 뒤에는 꼭!!!!!!!!! ()괄호를 넣어야된다..

이거 때매...이것 때문에!! 얼마나 많은 오류와 루프를 보았는가...

 

range를 설정하면 마지막 숫자는 나오지 않는다. 그러니까 생각한 숫자보다 +1 해서 적기

 

 

@데코레이션 이라는 것이 있다고 한다

이번 주말에 공부해야지

 

 

파이썬에서 dict, class를 잘 쓰면 코드가 아름다워진다

앞으로도 열심히 연구해야할 요소임

 

 

dict에는 key와 value가 있는데, 해당 내용을 호출하기위해

.values , .keys ,  .items 를 사용할 수 있다.

 

이번 과제해설에서는 중복되는 Character 내용을 묶어서 출력하는 방법을 배웠다.

character_select = input("직업을 정해주세요 : ")
if character_select in player_dict.keys():
	job = player_dict[job_select]
	break
   
   
class Character:
	pass

player_dict = {
	"1": Character(
		name="전사"
		hp=200
		mp=10
		power=15
        )}

대략 이런 식!  팀 과제에서 활용해봐야겠다^^..

 

 

동기분들 피드백:

게임 진행속도가 느리다

>time.sleep 조정

>유닛 power조정 및 밸런스 패치함ㅋㅋ

 

\n을 넣어 글 사이를 적당희 띄어주면 콘솔창 출력내용이 보기 좋을듯!

>채용!


 

느낀점:

 

주말을 활활~태워서 함수를 이해해보려했으나 여전히 부족했던게 아쉽다.

특히 class함수는 이해하기에 복합적인 개념들이 조금씩 섞여있어서 더 어렵게 느낀 것 같다.

 

이 외에도 엄청 많은 문제가 있었는데.. 스스로 해결하지 못한 점이 많이 아쉽다.

그래도 경험에서 배운다고 배운 것도 많아서 즐겁다:) 열심히 하자

 

왜 되는지는 아직도 잘 모르겠다.....

 

 

 

 

+ 튜터님께 여쭤볼 것.

1. yes or no 문에서 틀릴때만 뜨게하기

>> if elif else문으로 수정해보라는 제안을 받았다.

 

2.이름을 틀렸을때 input 값이 1번 더 들어가면 name input값이 2개가 되어 오류가 생긴다...

 

3.class 내 함수와  class, 함수 간의 우선순위가 있는지.

  4도망간다를 선택했을때 sys.exit가 작동하지않으면 loop가 도는 문제가 생겼는데 오류 해결하다가 생긴 궁금증.

 

>>  class내의 함수, class, 일반 함수간에는 (연산자와 같은) 우선순위가 없다.

 대신 위에서부터 작성된 순서의 우선순위가 있을 수 있다.

그런 순서의 의미에서 루프문에 걸렸던 이유를 찾아보면 좋을 것 같다...