프로젝트 코드 수정
main
import msvcrt
import os
import random
from operator import is_not_none
from game_package import map_module
from game_package import player
from game_package import monster
from game_package import item
char_x = 4
char_y = 4
is_not_dead = True
#스테이지 파일 절대경로
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# 오른쪽에 표시될 게임 메뉴얼
MANUAL_LINES = [
"🎮 GAME MANUAL 🎮",
"",
"이동 : W A S D",
"",
"공격 : R (주변 8칸)",
"",
"",
"꙰ 아이템 🗲",
"",
"✞ : HP +1 (최대 3)",
"",
"🗲 : 다음 공격력 2 (1회)",
"",
" ꙰ : 몬스터 공격 1회 무효",
]
def draw_with_hp():
os.system("cls")
# HP 표시
hp = player_.get_hp()
max_hp = player_.max_hp
hearts = "♥ " * hp + "♡ " * (max_hp - hp)
print(f"HP: {hearts}")
# 버프 표시 (A / B)
buffs = []
if hasattr(player_, "buff_attack") and player_.buff_attack:
buffs.append("🗲")
if hasattr(player_, "buff_block") and player_.buff_block:
buffs.append("꙰")
print("BUFF:", " ".join(buffs) if buffs else "-")
print()
# 스테이지 정보
print(f"STAGE : {current_stage_index + 1} / {len(stage_files)}")
print()
map_.draw_map(MANUAL_LINES) # 기존 map_.draw_map 대신 draw_with_hp() -> 체력 표시 반영
def start():
draw_with_hp()
def load_stage(stage_path):
global map_, monsters_, player_
# 맵 로드
map_ = map_module.Map(stage_path)
# 아이템 시스템 갱신
global item_system
item_system = item.Item(map_.get_map_array())
# 몬스터 초기화
monsters_ = []
# 플레이어 / 몬스터 위치 세팅
for i in range(20):
for j in range(20):
if map_.get_map_array()[i][j] == "5":
player_.set_position(j, i)
elif map_.get_map_array()[i][j] == "7":
m = monster.Monster(3, 1)
m.set_position(j, i)
monsters_.append(m)
draw_with_hp()
def update():
map_.change_map(0,0,6,1,1,3)
#print(map_.get_map_array)
draw_with_hp()
# ---------------- Stage 관리 ----------------
stage_files = [
os.path.join(BASE_DIR, "stage1.txt"),
os.path.join(BASE_DIR, "stage2.txt"),
os.path.join(BASE_DIR, "stage3.txt"),
]
current_stage_index = 0
#Awake--------------------------------------------------------------------------//
#Class 인스턴스 초기화
map_ = map_module.Map(stage_files[current_stage_index])
item_system = item.Item(map_.get_map_array()) # 아이템 처리
player_ = player.Player(3,3,1,False)
monsters_ = []
draw_with_hp()
#Start--------------------------------------------------------------------------//
start()
load_stage(stage_files[current_stage_index])
#Update--------------------------------------------------------------------------//
while is_not_dead:
if msvcrt.kbhit():
key = msvcrt.getch()
# ---------------------------atack----------------------------------------
if key in (b'r', b'R'):
attack_positions = player_.get_attack_targets(map_.get_map_array())
for m in monsters_[:]:#삭제 대비 복사
if m.get_position() in attack_positions:
damage = player_.attack_power #플레이어 공격 +1 ->2 1회성
m.take_damage(damage)
player_.consume_attack_buff()
if m.is_dead():
monsters_.remove(m)
mx, my = m.get_position()
map_.change_map(my, mx, 1, my, mx, 1)
draw_with_hp()
continue
# -----------------------------player------------------------------------------
origin_x, origin_y = player_.get_position()
movecode = player_.movement(key, map_.get_map_array())
if movecode != "f":
nx, ny = player_.get_position()
target = map_.get_map_array()[ny][nx]
# ---------------- Item 처리 ----------------
# H 아이템 (15): 체력 +1 (최대면 효과 없음), 아이템은 사라짐
if target == "15":
player_.heal()
map_.change_map(origin_y, origin_x, 1, ny, nx, 5)
draw_with_hp()
continue
# A 아이템 (16): 다음 공격력 2, 1회성
if target == "16":
player_.attack_power = 2
player_.buff_attack = True
map_.change_map(origin_y, origin_x, 1, ny, nx, 5)
draw_with_hp()
continue
# B 아이템 (17): 몬스터 공격 1회 무효
if target == "17":
player_.buff_block = True
map_.change_map(origin_y, origin_x, 1, ny, nx, 5)
draw_with_hp()
continue
# 도착지
if target == "6":
# 마지막 스테이지면 게임 클리어
if current_stage_index == len(stage_files) - 1:
print("\n 모든 스테이지를 클리어했습니다!")
print("게임 클리어!")
os.system("pause")
exit(0)
# 다음 스테이지로 이동
current_stage_index += 1
print(f"\n ▶ 다음 스테이지로 이동합니다 ({current_stage_index + 1})")
os.system("pause")
load_stage(stage_files[current_stage_index])
continue
map_.change_map(origin_y, origin_x, 1, ny, nx, 5)
#-----------------------------monster------------------------------------------
for m in monsters_:
m_origin_position = m.get_position() # monster origin position
m_movecode = m.monster_movement(map_.get_map_array()) # monster new position
if m_movecode != "f":
map_.change_map(m_origin_position[1], m_origin_position[0], 1,
m.get_position()[1], m.get_position()[0], 7)
# ---------------------- monster attack ----------------------
px, py = player_.get_position()
for m in monsters_:
mx, my = m.get_position()
# 플레이어 주변 8칸 이내
if abs(mx - px) <= 1 and abs(my - py) <= 1:
# 50% 확률 공격
if random.random() < 0.5:
if player_.consume_block_buff():
continue # 이번 공격 무효
player_.t_damage()
draw_with_hp()
if player_.dead():
draw_with_hp()
print("\nGAME OVER")
os.system("pause")
exit(0)
# -------------------------------------------------------------
draw_with_hp()
player
# 플레이어 모듈
#
# W/A/S/D 이동, R 공격 요청 플래그 세팅
# 벽/장애물/몬스터 칸 이동 금지
# 피격 시 체력 감소, 체력 0이면 dead()
# 시작 타일(5)에서 시작, 도착 타일(6) 판정
#
# 호환성 포인트
# map_module: grid[x][y] 접근(첫 인덱스가 '행(row)') 규칙 사용
# item.py: player.hp / player.max_hp / player.attack_power / player.defense 속성 사용
#
# 좌표 규칙
# x = row(행), y = col(열)
# grid[x][y]로 타일을 읽음
# w: (x-1,y), s:(x+1,y), a:(x,y-1), d:(x,y+1)
#
# 공격 처리 규칙
# 'r' 입력 시 attack_requested=True만 세팅
# 실제 공격(몬스터 피해/제거)은 skill/monster 모듈이 처리
# 공격 처리 후 반드시 player.set_attack_requested(False)로 리셋
class Player :
x = 0
y = 0
# 유효 이동 키
MOVE_CODE = {"w": 0, "a": 1, "s": 2, "d": 3}
def __init__(self, max_hp_ : int, hp_ : int, attack_count_ : int, defense_ : bool):
# item.py가 직접 접근하는 필드들
self.max_hp = max_hp_ # 3
self.hp = hp_ # 3
self.attack_count = attack_count_ # 1
self.defense = defense_ # False
# 'r' 입력 시 True
self._attack_requested = False
self.attack_power = 1 #기본 공격력
self.buff_attack = False #A 아이템 상태
self.buff_block = False #B 아이템 상태
def set_position(self, x, y):
self.x = x
self.y = y
def get_position(self):
return self.x, self.y
def get_hp(self):
return int(self.hp)
def set_hp(self, hp):
# hp는 0~max_hp로 제한
hp = int(hp)
if hp < 0:
hp = 0
if hp > int(self.max_hp):
hp = int(self.max_hp)
self.hp = hp
def get_attack_requested(self):
return bool(self._attack_requested)
def set_attack_requested(self, v):
self._attack_requested = bool(v)
def movement(self, key: bytes, grid: list) -> str:
walk_able = ("1", "6", "15", "16", "17")
if key == b'w':
if self.y - 1 >= 0 and grid[self.y - 1][self.x] in walk_able:
self.y -= 1
return "w"
elif key == b'a':
if self.x - 1 >= 0 and grid[self.y][self.x - 1] in walk_able:
self.x -= 1
return "a"
elif key == b's':
if self.y + 1 < 20 and grid[self.y + 1][self.x] in walk_able:
self.y += 1
return "s"
elif key == b'd':
if self.x + 1 < 20 and grid[self.y][self.x + 1] in walk_able:
self.x += 1
return "d"
return "f"
def t_damage(self):
# 피격: hp 1 감소(최소 0)
self.set_hp(self.get_hp() - 1)
return self.get_hp()
def dead(self):
# 사망: hp <= 0
return self.get_hp() <= 0
def reset(self, grid):
# 현재는 start와 동일(스테이지 시작 상태)
return self.start(grid)
def render(self):
# 출력용 문자(오버레이 출력 시 사용 가능)
return "P"
def is_exit(self, grid):
# 현재 칸이 도착 타일(6)인지 확인
g = self._normalize_grid(grid)
return self._tile(g, self.get_x(), self.get_y()) == self.EXIT_TILE
def get_attack_targets(self, grid):
# 인접 8칸(대각 포함) 중 몬스터(12) 좌표만 반환
g = self._normalize_grid(grid)
targets = []
x0, y0 = self.get_x(), self.get_y()
for dx in (-1, 0, 1):
for dy in (-1, 0, 1):
if dx == 0 and dy == 0:
continue
x = x0 + dx
y = y0 + dy
if not self._in_bounds(g, x, y):
continue
if self._tile(g, x, y) == self.MONSTER_TILE:
targets.append((x, y))
return targets
def _normalize_key(self, key):
# key가 bytes/str/None 어떤 형태로 와도 소문자 문자열로 통일
if key is None:
return ""
if isinstance(key, (bytes, bytearray)):
try:
key = key.decode("utf-8", errors="ignore")
except Exception:
return ""
return str(key).lower().strip()
def _normalize_grid(self, grid):
# 2D 리스트 또는 Map 객체(get_map_array)를 2D 리스트로 통일
# Map.get_map_array가 "프로퍼티"면 그대로 값이 나오고,
# "함수"면 callable이므로 아래에서 호출됨
if hasattr(grid, "get_map_array"):
grid = getattr(grid, "get_map_array")
if callable(grid):
grid = grid()
if (not isinstance(grid, list)) or (not grid) or (not isinstance(grid[0], list)):
raise ValueError("grid must be a 2D list or a Map-like object with get_map_array")
return grid
def _tile(self, grid, x, y):
# grid 내부가 "5" 같은 문자열이어도 int로 변환해 반환
return int(grid[x][y])
def _in_bounds(self, grid, x, y):
# grid 범위 안인지 확인
w = len(grid)
h = len(grid[0]) if w > 0 else 0
return 0 <= x < w and 0 <= y < h
def _find_tile(self, grid, tile):
# 특정 타일 코드(예: START_TILE=5) 위치 탐색, 못 찾으면 (0,0)
w = len(grid)
h = len(grid[0]) if w > 0 else 0
for x in range(w):
for y in range(h):
if int(grid[x][y]) == int(tile):
return (x, y)
return (0, 0)
def _dir_delta(self, key):
# 좌표 규칙: x=row, y=col
if key == "w":
return (-1, 0)
if key == "s":
return (1, 0)
if key == "a":
return (0, -1)
if key == "d":
return (0, 1)
return (0, 0)
def get_attack_targets(self, grid):
# 주변 8칸 좌표를 리스트로 반환 (유효 범위만)
targets = []
for dy in (-1, 0, 1):
for dx in (-1, 0, 1):
if dx == 0 and dy == 0:
continue # 자기 자신 제외
nx, ny = self.x + dx, self.y + dy
if 0 <= ny < len(grid) and 0 <= nx < len(grid[0]):
targets.append((nx, ny))
return targets
def heal(self): # 회복 +1
if self.hp < self.max_hp:
self.hp += 1
return True
return False
def consume_attack_buff(self): # 공격 아이템
if self.buff_attack:
self.buff_attack = False
self.attack_power = 1
def consume_block_buff(self): # 방어 아이템
if self.buff_block:
self.buff_block = False
return True
return False
아이템
import random
#아이템 코드 정의
item_heal=15 #H
item_attack=16 #A
item_defense=17 #B
item_symbol={item_heal:'H', item_attack:'A', item_defense:'B'}
#플레이어 상태(참조)
class Player_state:
def __init__(self):
self.hp=3
self.max_hp=3
self.attack_power=1
self.defense=False
class Item:
def __init__(self, map_data):
self.map_data = map_data
self.items={}
#아이템 생성
def item_create(self, item_position):
item_type=[item_heal, item_attack, item_defense]
for y,x in item_position:
if self.map_data[y][x]==1:
item=random.choice(item_type)
self.items[(y,x)]=item
self.map_data[y][x]=item
#플레이어 아이템 습득 체크(플레이어 위치에 아이템이 존재)
def check_pickup(self, player_y, player_x, player):
pos=(player_y, player_x)
if pos not in self.items: #플레이어의 위치에 아이템이 존재하지 않을 시
return
item=self.items[pos]
if item==item_heal:
self.apply_heal(player)
elif item==item_attack:
self.apply_attack(player)
elif item==item_defense:
self.apply_defense(player)
#습득시 아이템 제거
del self.items[pos]
self.map_data[player_y][player_x]=1
#회복아이템(item_heal, H, 15)
def apply_heal(self, player):
if player.hp<player.max_hp:
player.hp+=1
#공격아이템(item_attack, A, 16)
def apply_attack(self, player):
player.attack_power=2
player.buff_attack =True #공격 1회 후 자동 복구 + 중첩 불가
#방어아이템(item_defense, B, 17)
def apply_defense(self, player):
player.buff_block=True
#공격아이템 일회성
def attack_buff(self, player):
attacks=player.attack_power #현재 공격횟수 저장/기본:1, 아이템:2
player.attack_power=1 #아이템효과 초기화
return attacks
#방어아이템 일회성
def defense_buff(self, player):
if player.defense: #플레이어에게 방어막이 있는지 확인
player.defense=False #방어막 소모
return True #이번 데미지는 무효처리
return False #정상 데미지 처리
def get_item_symbol(self, y, x):
if (y,x) in self.items: #해당 좌표에 아이템 존재 여부 확인
return item_symbol[self.items[(y,x)]]
return None #아이템이 없으면 출력x
몬스터
import random
class Monster:
x = 0
y = 0
hp = 0
damage = 0
def __init__(self, hp_ : int, damage_ : int) a-> None:
self.x = 0
self.y = 0
self.hp = hp_ # 3
self.damage = damage_
def set_position(self, x, y):
self.x = x
self.y = y
def get_position(self):
return self.x, self.y
def get_hp(self):
return int(self.hp)
def take_damage(self, amount: int):
self.hp -= amount
def is_dead(self):
return self.hp <= 0
def monster_movement(self, grid : list) -> str:
direction_int = random.randint(0, 4)
# [ 0 , 위 ] [ 1 , 아래] [ 2 , 좌 ] [ 3 , 우 ]
if direction_int == 0:
if grid[self.y - 1][self.x] == "1" and self.y - 1 >= 0:
self.y -= 1
return "w"
elif direction_int == 1:
if grid[self.y + 1][self.x] == "1" and self.y + 1 < 20:
self.y += 1
return "s"
elif direction_int == 2:
if grid[self.y][self.x - 1] == "1" and self.x - 1 >= 0:
self.x -= 1
return "a"
elif direction_int == 3:
if grid[self.y][self.x + 1] == "1" and self.x + 1 < 20:
self.x += 1
return "d"
return "f"
맵
import os
class Map:
dict_enum = {
"0": " ■ ", #Wall
"1": " ", #Road
"2": " ? ",
"3": " ? ",
"4": " ? ",
"5": " ဝိူ ", #Player
"6": " ☆ ", #Exit
"7": " 𖢥 ", #Monster
"8": " ? ",
"9": " ? ",
"10": " P ",
"11": " ? ",
"15": " ✞ ", #Heal
"16": " 🗲 ", #Attack
"17": " ꙰ " #Defense
}
def __init__(self, file_path: str):
self.file_path = file_path
self.array = [] # 인스턴스 변수
self.monster_num = 0 # 인스턴스 변수
with open(file_path, "r") as f:
for line in f:
line = line.replace(" ", "").replace("\n", "")
self.array.append(line.split(","))
def get_map_array(self) -> list:
return self.array
def change_map(self, orign_x : int , orign_y : int,
change_num : int ,
new_x : int, new_y : int, new_num :int ):
self.array[orign_x][orign_y] = str(change_num)
self.array[new_x][new_y] = str(new_num)
def get_monster_num(self) -> int:
return self.monster_num
def draw_map(self, manual_lines=None):
self.monster_num = 0
for i in range(20):
row = ""
for j in range(20):
if self.array[i][j] == "7":
self.monster_num += 1
row += self.dict_enum[self.array[i][j]]
# 오른쪽 메뉴얼 붙이기
if manual_lines and i < len(manual_lines):
print(row + " " + manual_lines[i])
else:
print(row)
#if __name__ != "__main__":
실행 화면

메모
코드는 전부 구현 완료
내일 오전에 마무리
오늘 추가로 더 해볼 것:
1. 새로고침 눈 아파서 화면에서 달라지는 부분만 깜빡이도록 시도
2. 코드 최적화
'로보테크AI' 카테고리의 다른 글
| 융합_로보테크 AI 자율주행 로봇 개발자 과정-26/01/12 (0) | 2026.01.12 |
|---|---|
| 융합_로보테크 AI 자율주행 로봇 개발자 과정-26/01/09[데이터 분석] (0) | 2026.01.09 |
| 융합_로보테크 AI 자율주행 로봇 개발자 과정-26/01/07 (0) | 2026.01.07 |
| 융합_로보테크 AI 자율주행 로봇 개발자 과정-26/01/06 (1) | 2026.01.06 |
| 융합_로보테크 AI 자율주행 로봇 개발자 과정-26/01/05 (0) | 2026.01.05 |