우선 과제를 듣고 어디서 어떻게 해야할지 막막했다. 그래서 천천히 요구사항, 가이드, 힌트를 천천히 읽으면서 분석을 시작했다.
요구사항
이름을 입력해 플레이어를 생성할 수 있어야 합니다.
→ 클래스에서 init 함수를 쓰면 되겠다!
몬스터는 임의 생성할 수 있어야 합니다.
→ 이것도 몬스터 클래스 만들어서 init 하면 되겠다!
while 반복문을 사용해 종료 조건을 충족할 때까지 턴제 플레이어와 몬스터간 전투를 반복 진행해야 합니다.
→ 오! while를 어떻게 사용하는지 완전히 익혀놔야겠다!
플레이어는 공격 타입을 선택할 수 있어야 합니다. ex) 일반공격 , 마법공격
→ input을 이용해서 구현하면 될 것 같기는 한데.. 정확히는 모르겠다.
몬스터는 일반 공격을 할 수 있어야 합니다.
→ 함수를 이용해서 hp에서 감소시키면 되겠다!
매 전투시 플레이어와 몬스터의 상태 정보를 출력해야 합니다.
→ 일단 옼케!
모든 공격은 캐릭터의 파워 기준으로 랜덤성을 가지고있어야 합니다. ex) 파워가 10인경우 일반공격은 8~12사이의 랜덤한 값으로 공격
→ 일단 옼케!
몬스터나 플레이어의 HP가 0이되면 전투를 종료하고 승리 또는 패배를 출력해야 합니다.
→ 일단 옼케!
가능한 기본 가이드를 지켜 과제를 진행해주세요
시작
플레이어 생성 (Player)
이름
HP
MP
파워
일반공격
마법파워
마법공격
몬스터 생성 (Monster)
이름
HP
일반공격
전투
몬스터와 턴제 전투 (While 반복문 사용)
플레이어, 몬스터 상태 출력
플레이어의 공격 방법 선택 (일반, 마법)
플레이어 공격
몬스터 공격
종료
승리 or 패배
→ 가이드 순서를 따라서 코드를 작성해야겠다고 생각을 했다.
힌트
힌트의 코드가 각각 무엇을 의미하는지 공부를 하기 시작.
class Character:
"""
모든 캐릭터의 모체가 되는 클래스
"""
#인스턴스를 만드는 함수.각각의 변수에 인자를 저장한다.
def __init__(self, name, hp, power):
self.name = name
self.max_hp = hp
self.hp = hp
self.power = power
#공격 함수
def attack(self, other):
#데미지가 공격력의 ± 2 사이 랜덤하게 선택.
damage = random.randint(self.power - 2, self.power + 2)
#1인자의 체력은 인자의 (hp - 데미지,0) 중 높은 수를 선택하게 되어있네.
#이게 왜 필요하지?
other.hp = max(other.hp - damage, 0)
#어택 함수의 출력 문장.
print(f"{self.name}의 공격! {other.name}에게 {damage}의 데미지를 입혔습니다.")
#2아~ 인자의 hp가 0이면 출력문이 나오는거네..
if other.hp == 0:
print(f"{other.name}이(가) 쓰러졌습니다.")
#수정할 것 :#1과 2. 인자의 체력이 0보다 작으면 출력이 나오도록 하고 max도 빼버린다.
#상태를 확인하는 함수.
def show_status(self):
#현재hp / max hp가 출력 되네.
print(f"{self.name}의 상태: HP {self.hp}/{self.max_hp}")
그리고 가이드를 보면서 코드를 작성하기 시작했다.
플레이어 생성 부분부터!
플레이어 생성 (Player)
이름
HP
MP
파워
일반공격
마법파워
마법공격
class Character:
"""
모든 캐릭터의 모체가 되는 클래스
"""
def __init__(self, name, hp, mp, power, magic_power):
self.name = name
self.max_hp = hp
self.hp = hp
self.max_mp = mp
self.mp = mp
self.power = power
self.magic_power = magic_power
print(f'{name}이(가) 생성되었다.')
print(f'체력{hp}, 마나{mp}, 일반공격력{power} ±2, 마법공격력{magic_power} ±20')
class Monster():
"""
모든 몬스터의 모체가 되는 클래스
"""
def __init__(self, name, hp, power):
self.name = name
self.max_hp = hp
self.hp = hp
self.power = power
print(f'{name}이(가) 생성되었다.')
print(f'체력{hp},공격력{power} ±2')
print(f'{name}이(가)와 조우했다.')
그리고 C1의 공격일 때 행동을 선택해야해서 용사의 체력이 0 이하가 아닐 때 행동선택을 프린트하고 각 선택에 어떤 함수를 불러오고 출력할지 적어줌.
while C1.hp >= 0 or M1.hp >= 0:
if C1.hp <= 0:
print('패배..')
exit()
print(prompt)
number = int(input())
if number ==1:
C1.attack(M1)
if number ==2:
if C1.mp < 30:
print('마나가 부족합니다!')
continue
else:
C1.magic_attack(M1)
if number ==3:
print('스테이터스를 확인합니다.')
C1.show_status()
M1.show_status()
continue
if number ==4:
print('도망쳤다.')
print('용기를 가져라!')
exit()
if M1.hp <= 0:
print('승리!')
exit()
else:
M1.attack(C1)
이름을 입력해 플레이어를 생성할 수 있어야 합니다.
→ 클래스에서 init 함수를 쓰면 되겠다!
몬스터는 임의 생성할 수 있어야 합니다.
→ 이것도 몬스터 클래스 만들어서 init 하면 되겠다!
while 반복문을 사용해 종료 조건을 충족할 때까지 턴제 플레이어와 몬스터간 전투를 반복 진행해야 합니다.
→ 오! while를 어떻게 사용하는지 완전히 익혀놔야겠다!
플레이어는 공격 타입을 선택할 수 있어야 합니다. ex) 일반공격 , 마법공격
→ input을 이용해서 구현하면 될 것 같기는 한데.. 정확히는 모르겠다.
몬스터는 일반 공격을 할 수 있어야 합니다.
→ 함수를 만들고 이용해서 hp에서 감소시키면 되겠다!
매 전투시 플레이어와 몬스터의 상태 정보를 출력해야 합니다.
→ 일단 옼케!
모든 공격은 캐릭터의 파워 기준으로 랜덤성을 가지고있어야 합니다. ex) 파워가 10인경우 일반공격은 8~12사이의 랜덤한 값으로 공격
→일단 옼케!
몬스터나 플레이어의 HP가 0이되면 전투를 종료하고 승리 또는 패배를 출력해야 합니다.
→ 일단 옼케!
이번주는 운빨이 끝내줬는지 막막하던 과제를 잘 해결하게 되었다. 재미있는 경험이었고 자신감 소폭 상승! 오옷!
아 그리고 오늘 아침도 상속을 사용하면 더 코드가 깔끔해지지 않을까 했는데 상속받는 Monster 클래스를 전부다 수정해서 사용했기 때문에 상속이 의미가 없을 거라 판단하여 작업을 중지했다.
경로를 검색할때와 나타낼 때가 다르기 때문에 오류가 발생 할 수 있어서 replace를 써서 통일 시켜주는게 안전.
glob은 기본적으로 하위폴더를 탐색하지 않음.
recursive=True를 사용하면 하위폴더를 탐색함../venv/**별 두개를 붙여줘야함./*.py를 붙여주면 파이썬 파일만 찾음.
open을 활용한 파일 다루기
f = open("file.txt", "w", encoding="utf-8")
같은 경로의 파일을 열게 되고 w는 write모드로 열겠다 라는 뜻이다. 인코딩 설정을 잘 못하면 글자가 깨진다.
f.write("파이썬 파일 쓰기 테스트!\n") 파일을 생성하고 입력하게 된다.
f.close() 파일을 닫는다는 뜻이다.
모드 : w 쓰기 r 읽기 a 추가(append). w는 파일의 내용이 있든 없든 처음부터 새로 씀.
with open("file.txt", "a", encoding="utf-8") as w: w.write("파이썬 내용 추가 테스트!")
with는 따로 close 하지 않아도 된다.
with open("file.txt", "r", encoding="utf-8") as r: print(r.readlines())
readlines은 파일의 모든 내용을 list 자료형으로 한번에 읽어들인다.
내용이 수백 줄이면 readline으로 한줄만 읽는다.
itertools
루핑 : 계속하여 반복 중.
데카르트 곱
from itertools import product
sample1 = ["A", "B", "C", "D", "E"]
sample2 = [1, 2, 3, 4]
# 행 / 열을 구분하여 프린트 하기 위해 enumerate 사용
for i, v in enumerate(product(sample1, sample2), 1):
print(v, end=" ")
if i % len(sample2) == 0:
print("")
서로 다른 n개의 원소에서 r개를 중복없이 순서에 상관있게 선택하는 혹은 나열하는 것을 순열
원소의 개수가 n개임 순열
```python
from itertools import permutations
sample = ["A", "B", "C"]
# 원소의 개수가 3개인 순열 출력. 2로 바꾸면 2개로 나옴.
for i in permutations(sample, 3):
print(i)
# result output
"""
('A', 'B', 'C')
('A', 'C', 'B')
('B', 'A', 'C')
('B', 'C', 'A')
('C', 'A', 'B')
('C', 'B', 'A')
"""
```
원소의 개수가 n개인 조합 구하기
from itertools import combinations
sample = ["A", "B", "C"]
# 원소의 개수가 2개인 조합 출력
for i in combinations(sample, 2):
print(i)
원소의 개수가 n개인 조합 구하기 (중복허용)
from itertools import combinations_with_replacement
sample = ["A", "B", "C"]
# 중복을 포함한 원소의 개수가 3개인 조합 출력
for i in combinations_with_replacement(sample, 3):
print(i)
# result output
"""
('A', 'A', 'A')
('A', 'A', 'B')
('A', 'A', 'C')
('A', 'B', 'B')
('A', 'B', 'C')
('A', 'C', 'C')
('B', 'B', 'B')
('B', 'B', 'C')
('B', 'C', 'C')
('C', 'C', 'C')
"""
requests
파이썬에서 http 통신을 가능하게 해주는 모듈.
웹크롤링 하거나 api 통신이 필요할 때 사용.
requests 모듈을 사용하면 네이버를 코드로 받아 올 수 있다.는 의미.
네가지 메소드.
GET : 데이터 정보 요청.
POST : 데이터 생성 요청.
PUT : 데이터 수정 요청
DELETE : 데이터 삭제 요청.
네가지 메소드나 status code를 받아오는 방식은 http 통신의 특성. 파이썬과 관련이 없다.
http 통신을 파이썬 코드로 구현하는 것.
요청을 하면 response 을 내려주고 content 와 status code를 받아오게 된다.
import random
a = [1, 2, 3, 4, 5,]
random.shuffle(a)
print(a)
[1, 4, 5, 2, 3]
1에서 10까지 무작위 숫자 생성
import random
a = random.randint(1, 10)
print(a)
time : 시간 다루기
임포트 해줘야한다. : import time
import time
a = time.time() #시작 시간 저장
time.sleep(1) # 1초간 대기
b = time.time() #종료 시간 저장
print(f"코드 실행 시간 : {b-a:.5f}") #5f는 소수점 다섯째 자리 까지 출력
코드 실행 시간 : 1.00065
datetime : 날짜 다루기
임포트 해줘야한다. : from datetime import datetime, timedelta
from datetime import datetime
print(datetime.now()) #현재시간 출력하기
2023-03-23 14:10:19.784002
datetime은 클래스로 되어있어서 spilt 하거나 join 하거나 하려면 str로 바꿔줘야한다.
from datetime import datetime
a = datetime.now()
b = a.split(" ")
print(b)
AttributeError: 'datetime.datetime' object has no attribute 'split'
from datetime import datetime
a = datetime.now()
print(a, type(a))
2023-03-23 14:21:45.828411 <class 'datetime.datetime'>
from datetime import datetime
a = str(datetime.now())
b = a.split(" ")
print(b)
['2023-03-23', '14:20:40.195673']
%y : 두 자리 연도 / 20, 21, 22
%Y : 네 자리 연도 / 2020, 2021, 2022
%m : 두 자리 월 / 01, 02 ... 11 ,12
%d : 두 자리 일 / 01, 02 ... 30, 31
%I : 12시간제 시간 / 01, 02 ... 12
%H : 24시간제의 시간 / 00, 01 ... 23
%M : 두 자리 분 / 00, 01 ... 58, 59
%S : 두 자리 초 / 00, 01 ... 58, 59
strftime() : datetime.datetime 클래스를 문자열로 변경
date_str = datetime.strftime(now, "%y/%m/%d") 형식은 변경 할 수 있다.
from datetime import datetime, timedelta
now = datetime.now()
date_str = datetime.strftime(now, "%y/%m/%d %H:%M:%S")
print(date_str, type(date_str))
23/03/23 14:33:42 <class 'str'>
어떻게 쓰든 __init__에서 첫 번째로 들어가는 self(첫번째 변수)는 CookieFrame 클래스 스스로를 지칭하게 된다. self를 다른걸로 고쳐도 된다.
class CookieFrame():
def __init__(self, name): #name 이라는 인자를 받는다.
print(f"생성 된 과자의 이름은 {name} 입니다!")
self.name = name #name이라는 변수에 name 인자를 받는다.
cookie1 = CookieFrame("cookie1") #쿠키 1이라는 이름으로 쿠키1 인자를 준다. 객체가 생성되었다.
cookie2 = CookieFrame("cookie2")
print(cookie1.name) #cookie1이라는 변수의 name으로 어떤 인자값을 받았는지 알 수 있다.
print(cookie2.name)
cookie1
cookie2
인자에 디폴트값을 줄 수 있다. 디폴트값 없이 인자 값이 없다면 에러가 난다.
class CookieFrame():
def __init__(self, name="default"):
print(f"생성 된 과자의 이름은 {name} 입니다!")
self.name = name
cookie1 = CookieFrame("")
cookie2 = CookieFrame("cookie2")
print(cookie1.name)
print(cookie2.name)
default
cookie2
class 상속
클래스를 생성할 때 다른 클래스의 변수나 메소드 등을 가져와 사용하는 기능.
동일한 코드를 여러 클래스에서 조금씩 수정해서 사용하거나 모듈에 내장된 클래스를 변경할 때 주로 사용.
상속 해주는 클래스를 부모(parents),슈퍼(super) 클래스. 상속 받는 클래스는 자식(child),서브(sub) 클래스.
모듈 : 파이썬으로 이루어진 파일에는 함수,변수,클래스 등이 있을 것. 이 파일을 import해서 모듈로 사용하는 것.
상속 받은 클래스를 기반으로 새로운 클래스 만들기
class Monster():
def __init__(self, hp):
self.hp = hp
def attack(self, damage):
self.hp -= damage
def status_check(self):
print(f"monster's hp : {self.hp}")
class FireMonster(Monster):
def __init__(self, hp):
self.attribute = "fire"
#init 함수를 실행하면 파이어 속성을 만들고
#부모의 init 함수를 또 사용하여 hp가 있는 객체가 만들어진다.
super().__init__(hp)
# 부모 클래스에 존재하는 status_check 메소드를 overriding 합니다.
#오버라이딩 : 부모클래스의 메서드와 같은 이름, 매개변수를 재정의 하는것.
# 더 좋은 코드는self.attribute = "fire"를 출력하도록 하면 메서드를 만들 필요도 없었을 것.
def status_check(self):
print(f"fire monster's hp : {self.hp}")
class IceMonster(Monster):
def __init__(self, hp):
self.attribute = "ice"
super().__init__(hp)
def status_check(self):
print(f"ice monster's hp : {self.hp}")
fire_monster = FireMonster(hp=100)
# FireMonster 클래스에는 attack 메소드가 없지만
# 부모 클래스에서 상속받았기 때문에 별도의 선언 없이 사용 가능합니다.
fire_monster.attack(20)
fire_monster.status_check()
ice_monster = IceMonster(hp=200)
ice_monster.attack(50)
ice_monster.status_check()
상속 받은 클래스의 특정 코드를 변경해 사용하기
모듈의 코를 수정하여 사용하는 것 많은 버그를 생기게 할 가능성이 많다.
그래서 새로운 클래스를 하나 만들고 오버라이딩 해서 코드를 수정해서 사용하는게 더 효율적이다.
class 객체(object) 다루기
객체 = object = 인스턴스
list와 dict 같은 자료형 데이터를 생성하고 활용 했던 것이 클래스의 객체를 생성하고 매소드를 활용하는 과정.
객체에서 사용 가능한 메소드들은 검색,docstring, 구현 코드 등을 확인하여 찾을 수 있다.
구현 코드를 확인하는 걸 권장하는 이유는 다른 사람이 짠 코드를 보다보면 새로운 것을 배울 수 있기 때문.