역할: player
3-1. 플레이어 상태 초기화: 스테이지 시작 시 플레이어 좌표를 시작 지점으로 설정(4)하고, 체력을 기본값(3)으로 설정한다.
3-2. 입력 처리(스킬 분기): 사용자의 키 입력을 받아 w/a/s/d는 이동 스킬로, r은 공격 스킬로 처리한다. 그 외 입력은 무시한다(플레이어 상태 변화 없음).
3-3. 이동 스킬(w/a/s/d): 입력 방향으로 1칸 이동을 시도한다. 이동 가능 여부는 맵 모듈의 판정 결과를 따른다(이동 불가면 좌표 유지).
3-4. 공격 스킬(r): “몬스터와 마주친 상태”에서만 공격을 적용한다. 공격 성공 시 대상(인접 몬스터)의 체력을 1 감소시키도록 몬스터 처리 함수를 호출한다. 인접 대상이 없으면 변화 없이 종료한다.
3-5. 피격/체력 갱신: 몬스터로부터 피격 이벤트가 발생했을 때 체력을 1 감소시킨다. 체력이 0이 되면 게임 오버 상태를 상위 루프에 알리고, 플레이어를 스테이지 시작 상태로 초기화한다(좌표/체력).
3-6. 스테이지 종료 조건 전달: 플레이어가 도착 지점(5)에 도달했는지 여부를 판정(또는 외부 판정값 수신)하여 “스테이지 종료” 신호를 상위 루프에 반환한다.
player = 플레이어
P로 화면에 표시
W,A,S,D = 이동
R = 공격 스킬
start(): 상태 초기화, 좌표 시작 지점 설정, 체력 3 설정
handle(): wasd 입력, r 공격, 그 외 무시
move(): handle() 입력으로 이동 가능하면 T, 안되면 F 반환
t_damage(): 피격 이벤트 발생시 체력 1 감소
dead(): 체력 0되면 게임 오버
reset(): 게임 오버시 스테이지 시작 상태로 초기화(좌표, 체력, 아이템, 몬스터)
render(): P로 화면에 표시

객체지향 구조 / 단순 if 처리 X
임시 코드
player.py
# player.py
# 변수(필드)
# _x, _y: 플레이어 좌표
# _hp: 체력(0~3)
# attack_requested: 공격 요청 플래그
#
# 상수(타일 코드)
# WALL=0, START_TILE=5, EXIT_TILE=6, MONSTER_TILE=12
#
# 함수(메서드)
# start(grid) -> (x, y) : 시작 위치로 좌표 설정, 체력 3, 공격 플래그 False
# handle(key, grid) -> (x, y) : W/A/S/D 이동, R 공격 플래그 True, 그 외 무시
# move(key, grid) -> True/False : 이동 가능하면 이동 후 True, 불가하면 False
# t_damage() -> hp : 피격 시 체력 1 감소
# dead() -> True/False : 체력 0이면 True
# reset(grid) -> (x, y) : start와 동일
# render() -> "P" : 화면 표시 문자
# is_exit(grid) -> True/False : 현재 칸이 도착지점인지 확인
# get_attack_targets(grid) -> [(x,y), ...] : 플레이어 기준 인접 8칸(대각선 포함) 몬스터 좌표 목록
# get_x/get_y/get_hp/get_attack_requested : getter
# set_x/set_y/set_hp/set_attack_requested : setter
#
# 호출 방법 ex)
# from player import Player
# p = Player()
# x, y = p.start(grid)
# x, y = p.handle(key, grid)
# if p.get_attack_requested():
# targets = p.get_attack_targets(grid)
# 공격 처리 후 p.set_attack_requested(False)
# if p.dead(): 게임 오버 처리
# if p.is_exit(grid): 스테이지 처리(맵에서 체크)
class Player:
WALL = 0
START_TILE = 5
EXIT_TILE = 6
MONSTER_TILE = 12
MOVE_CODE = {"w": 0, "a": 1, "s": 2, "d": 3}
def __init__(self):
self._hp = 3
self._x = 0
self._y = 0
self._attack_requested = False
def get_x(self):
return self._x
def set_x(self, x):
self._x = int(x)
def get_y(self):
return self._y
def set_y(self, y):
self._y = int(y)
def get_hp(self):
return self._hp
def set_hp(self, hp):
hp = int(hp)
if hp < 0:
hp = 0
if hp > 3:
hp = 3
self._hp = hp
def get_attack_requested(self):
return self._attack_requested
def set_attack_requested(self, v):
self._attack_requested = bool(v)
def start(self, grid):
self.set_hp(3)
self.set_attack_requested(False)
x, y = self._find_tile(grid, self.START_TILE)
self.set_x(x)
self.set_y(y)
return (self.get_x(), self.get_y())
def handle(self, key, grid):
k = (key or "").lower().strip()
if k in self.MOVE_CODE:
self.move(k, grid)
return (self.get_x(), self.get_y())
if k == "r":
self.set_attack_requested(True)
return (self.get_x(), self.get_y())
return (self.get_x(), self.get_y())
def move(self, key, grid):
k = (key or "").lower().strip()
if k not in self.MOVE_CODE:
return False
dx, dy = self._dir_delta(k)
nx = self.get_x() + dx
ny = self.get_y() + dy
if not self._in_bounds(grid, nx, ny):
return False
t = self._tile(grid, nx, ny)
if t == self.WALL:
return False
if t == self.MONSTER_TILE:
return False
self.set_x(nx)
self.set_y(ny)
return True
def t_damage(self):
self.set_hp(self.get_hp() - 1)
return self.get_hp()
def dead(self):
return self.get_hp() <= 0
def reset(self, grid):
return self.start(grid)
def render(self):
return "P"
def is_exit(self, grid):
return self._tile(grid, self.get_x(), self.get_y()) == self.EXIT_TILE
def get_attack_targets(self, grid):
targets = []
x0 = self.get_x()
y0 = 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(grid, x, y):
continue
if self._tile(grid, x, y) == self.MONSTER_TILE:
targets.append((x, y))
return targets
def _tile(self, grid, x, y):
return grid[x][y]
def _in_bounds(self, grid, x, y):
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):
w = len(grid)
h = len(grid[0]) if w > 0 else 0
for x in range(w):
for y in range(h):
if grid[x][y] == tile:
return (x, y)
return (0, 0)
def _dir_delta(self, key):
if key == "w":
return (0, -1)
if key == "s":
return (0, 1)
if key == "a":
return (-1, 0)
if key == "d":
return (1, 0)
return (0, 0)
1) 모듈 개요
player.py는 플레이어의 좌표(x, y), 체력(hp), 공격 입력 상태를 관리하는 모듈이다.
메인(map.py)은 이 모듈을 import해서 매 턴 입력을 전달하고, 플레이어의 현재 위치와 상태를 확인한다.
이 모듈이 직접 처리하는 것은 다음과 같다.
W/A/S/D 입력에 따른 이동(벽/몬스터 칸 이동 금지 포함)
R 입력 발생 여부 저장(공격 요청 플래그 세팅)
피격 이벤트 발생 시 체력 감소
체력 0 도달 여부(게임 오버 판정)
시작/리셋 시 초기 상태 세팅
화면 출력에서 플레이어를 나타내는 문자("P") 제공
이 모듈이 직접 처리하지 않는 것은 다음과 같다.
공격 실행(몬스터 제거/길로 변경 등) 자체는 skill 모듈이 담당한다.
스테이지 클리어 신호는 player가 직접 전달하지 않고, map이 is_exit()로 체크한다.
2) 변수(필드) 설명
_x, _y (플레이어 좌표)
플레이어의 현재 위치를 저장한다.
grid는 grid[x][y] 방식으로 접근하므로 _x는 첫 번째 인덱스, _y는 두 번째 인덱스를 의미한다.
외부에서 바로 접근하지 않고 getter/setter로 읽고/설정하도록 구성되어 있다.
_hp (플레이어 체력)
플레이어의 체력을 저장한다.
체력은 항상 최대 3 기준이며, 내부적으로는 0~3 범위로 제한된다.
피격 이벤트 발생 시 t_damage()로 1 감소한다.
dead()는 _hp가 0인지 검사해 게임 오버 판정에 사용된다.
_attack_requested (공격 요청 플래그)
플레이어가 R 키를 눌렀는지 기록하는 플래그이다.
handle()에서 R 입력이 들어오면 True로 설정된다.
실제 공격 처리(대상 확인, 몬스터 제거 등)는 skill 모듈에서 수행하며, 처리가 끝난 뒤에는 map 또는 skill에서 set_attack_requested(False)로 직접 꺼준다.
이 플래그는 “공격이 발생했다”가 아니라 “공격을 실행해야 한다”를 의미한다.
3) 상수(타일 코드) 설명
WALL = 0
벽을 의미한다.
이동하려는 칸이 0이면 플레이어는 그 칸으로 이동할 수 없다.
START_TILE = 5
시작 지점을 의미한다.
start(grid)는 맵 전체를 탐색하여 5의 위치를 찾아 플레이어 좌표를 그 위치로 설정한다.
EXIT_TILE = 6
도착 지점을 의미한다.
player는 도착 신호를 직접 반환하지 않으며, map이 is_exit(grid)를 호출해 현재 좌표가 도착인지 검사한다.
MONSTER_TILE = 12
몬스터를 의미한다.
이동 시 몬스터 칸(12)으로는 이동할 수 없도록 막는다.
공격 대상 탐색 시 인접 8칸에서 12를 찾아 공격 대상으로 반환한다.
4) 함수(메서드) 설명
start(grid) -> (x, y)
스테이지 시작 시 호출한다.
하는 일은 다음과 같다.
체력을 3으로 초기화한다.
공격 요청 플래그를 False로 초기화한다.
grid 전체에서 시작 타일(5)을 찾아 그 좌표로 플레이어 위치를 설정한다.
반환값은 (x, y) 튜플이며, map은 이 값을 받아 플레이어 좌표를 사용할 수 있다.
handle(key, grid) -> (x, y)
매 턴 입력을 처리하는 메인 함수다.
map은 입력을 받은 후 handle()에 전달한다.
입력 규칙은 다음과 같다.
W/A/S/D: move()를 호출해 이동을 시도한다.
R: 공격 요청 플래그를 True로 설정한다. (공격 실행은 skill 담당)
그 외: 아무 것도 하지 않고 무시한다.
반환은 항상 (x, y) 튜플이다.
즉, map은 handle 결과로 좌표만 받도록 고정된다.
move(key, grid) -> True/False
이동을 실제로 수행하는 함수다.
key가 W/A/S/D 중 하나이면 이동 방향을 계산한다.
다음 조건이면 이동 실패(False)로 처리한다.
맵 범위를 벗어나는 경우
이동 대상 칸이 벽(0)인 경우
이동 대상 칸이 몬스터(12)인 경우
이동 가능하면 좌표를 갱신하고 True를 반환한다.
t_damage() -> hp
피격 이벤트가 발생했을 때 호출한다.
체력을 1 감소시키고, 감소 후 체력 값을 반환한다.
체력은 0 미만으로 내려가지 않는다.
dead() -> True/False
게임 오버 판정 함수다.
현재 체력이 0이면 True, 아니면 False를 반환한다.
map은 매 턴 또는 피격 후 dead()를 확인해 게임 오버 처리를 한다.
reset(grid) -> (x, y)
게임 오버 후 스테이지 재시작 시 호출한다.
내부 동작은 start(grid)와 동일하다.
반환값도 (x, y)로 통일한다.
render() -> "P"
화면 출력용 표시 문자 반환 함수다.
map이 화면을 그릴 때 플레이어 위치에 "P"를 출력한다.
is_exit(grid) -> True/False
현재 플레이어 위치가 도착 지점(6)인지 확인한다.
True면 도착 지점, False면 그 외 칸이다.
스테이지 클리어 처리는 player가 직접 하지 않고, map이 이 함수 결과로 판단한다.
get_attack_targets(grid) -> [(x, y), ...]
공격 대상 계산 함수다.
플레이어 기준 인접 8칸(상/하/좌/우 + 대각선 4칸)을 검사한다.
인접 칸 중 몬스터 타일(12)이 있는 좌표만 리스트로 모아 반환한다.
반환된 좌표 리스트는 skill 모듈이 받아서 몬스터를 길로 바꾸거나(직접 grid 수정) 처리를 수행한다.
몬스터가 없으면 빈 리스트를 반환한다.
5) getter / setter 목록과 의미
Getter
get_x(), get_y() : 현재 좌표 읽기
get_hp() : 현재 체력 읽기
get_attack_requested() : 공격 요청 플래그 읽기
Setter
set_x(x), set_y(y) : 좌표 설정
set_hp(hp) : 체력 설정(0~3으로 자동 제한)
set_attack_requested(True/False) : 공격 요청 플래그 설정
getter/setter를 통해 외부 모듈(map/skill)이 플레이어 상3태를 안정적으로 읽고 변경할 수 있다.
맵 20 * 20(400px)
구조: Map(Player(skill), Monster, Item)
MAP
스테이지 별로 20 *20 으로 구성된 미로의 시작 지점과 도착지점 구성
Player
벽(0)에 부딪히면 상호작용 X, 길(1)로 wasd로 한칸씩 이동
아이템에 닿으면 자동으로 인벤토리에 저장, 사용 가능
skill(r)로 몬스터와 마주쳤을 때 공격 가능
플레이어 기준 주변 8칸(대각선 포함)까지 공격 범위
체력은 3으로 1번 피격마다 1씩 감소
Skill
player가 r입력시 동작
주변 8칸 내 monster가 있다면 monster의 체력 1 감소
주변에 monster가 없다면 자동으로 입력 안함
Monster
player가 다가왔을 때 일정 확률로 공격
스테이지마다 체력 증가
플레이어의 길 막기
피격 범위는 player와 같음
Item
H: 체력 +1(한 스테이지 적용)
B: 보호막 +1(일회용)
A: 공격력 +1(일회용)
메모
오늘은 추상화 + 맵 코드(박선준 님) 보고 기본적인 함수 구현까지
skill 담당 조혜림 님에게 함수 이름 전달해서 r 상호작용 잘 되나 확인해야 함
getter, setter 사용
wasd는 return 1, 2, 3, 4형식으로 반환, map 말고
입력은 main인 map에서 하고, player는 들어온 입력으로 이동이랑 상호작용
플레이어가 벽 너머로 공격할 수 없도록 피격 범위는 애초에 주변 8칸이라 예외 필요없음
플레이어가 아이템 먹을 경우 사라지는 구현?
공격 당했을 경우 피 감소, 아이템으로 피 회복 구현?
map(main)에는 x, y 좌표값만 반환 -> 나머지는 player 내에서 아니면 다른 모듈이랑?
'로보테크AI' 카테고리의 다른 글
| 융합_로보테크 AI 자율주행 로봇 개발자 과정-26/01/09[데이터 분석] (0) | 2026.01.09 |
|---|---|
| 융합_로보테크 AI 자율주행 로봇 개발자 과정-26/01/08 (0) | 2026.01.08 |
| 융합_로보테크 AI 자율주행 로봇 개발자 과정-26/01/06 (1) | 2026.01.06 |
| 융합_로보테크 AI 자율주행 로봇 개발자 과정-26/01/05 (0) | 2026.01.05 |
| 융합_로보테크 AI 자율주행 로봇 개발자 과정-26/01/02 (0) | 2026.01.02 |