[강화시스터즈 1기/프로젝트/환경세미나] 지뢰마스터즈 02팀
핵심 애트리뷰트
-
self.minefield
: 지뢰 찾기 게임 상황이 저장된 애트리뷰트(정답지)0: 지뢰 없음 / 1: 지뢰 있음
- type: np.array
- 구조: grid_size x grid_size 크기의 2D NumPy 배열
-
self.playerfield
: 플레이어가 보는 지뢰밭 상태-1(hiddenmine), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9(hidden)
- type: np.array
- 구조: grid_size x grid_size 크기의 2D NumPy 배열
- 초기값: 모든 셀이 9
-
self.explode
: 플레이어가 지뢰를 밟았는지 여부타입:
bool
-
self.done
: 게임이 끝났는지 여부타입:
bool
-
self.first_move
: 현재 움직임이 첫 번째 움직임인지 여부타입:
bool
처음 open하는 타일은 safe: 지뢰가 아닌 타일 중 무작위 선택
init()
class Environment:
def __init__(self):
self.grid_size = 9
self.num_mines = 10
# 실제 정답 minefield 초기화
self.minefield = np.zeros((self.grid_size, self.grid_size), dtype=int)
# 실제 화면 playerfield hidden 상태(9)로 초기화
self.playerfield = np.full((self.grid_size, self.grid_size), 9, dtype=int)
self.state_size = self.minefield.size # (81 반환됨)
self.explode = False # 폭발 여부
self.done = False # 게임 끝 여부
self.first_move = True # 처음 open하는지 여부
Environment
객체를 초기화
reset()
def reset(self):
self.minefield = np.zeros((self.grid_size, self.grid_size), dtype=int)
self.playerfield = np.full((self.grid_size, self.grid_size), 9, dtype=int)
self.explode = False
self.done = False
self.first_move = True
self.place_mines()
reset_state = self.get_state() // playerfield 를 flatten
return reset_state
에피소드를 초기 상태로 되돌리는 역할
place_mines()
호출- 새로운 게임에 필요한 지뢰를 배치
- 반환값:
reset_state
place_mines()
: num.mines만큼 지뢰 심기- minefield의 값을 -1으로 설정
- -1이 아닌 좌표에 대해 값을 주변 지뢰 개수(0~8) 설정 :
count_adjacent_mines()
이용
count_adjacent_mines()
: 경계 고려, 좌우 탐색하여 주변 지뢰 개수 count
def count_adjacent_mines(self, x, y):
count = 0
# (x,y) 주변 지뢰 개수
for i in range(max(0, x - 1), min(self.grid_size, x + 2)): # 좌우 탐색 (경계 고려)
for j in range(max(0, y - 1), min(self.grid_size, y + 2)): # 상하 탐색 (경계 고려)
if self.minefield[i, j] == -1:
count += 1
return count
step()
def step(self, action):
state = self.get_state() # state은 (flatten된) playerfield
minefield_state = self.minefield.flatten()
reward = 0
done = False
if state[action] == 9: # hidden 상태(9) 타일을 열면
state[action] = minefield_state[action] # 타일에 정답 복사
# 지뢰를 선택하면 done
if state[action] == -1:
done = True
self.explode = True
reward = -10
# 선택한 타일이 0인 경우
elif state[action] == 0:
x, y = divmod(action, self.grid_size) # 1차원 인덱스를 2차원 좌표로 변환
state = self.auto_reveal_tiles(x, y) # 주변 타일 열기
num_hidden_tiles = np.count_nonzero(state == 9) # 남은 hidden tile 수
if num_hidden_tiles == self.num_mines: # 지뢰를 제외하고 전부 연 경우
done = True
reward = 100
else:
done = False
reward = 10
# 지뢰가 아닌 숫자(1~8) 타일을 선택한 경우
else:
num_hidden_tiles = np.count_nonzero(state == 9) # 남은 hidden tile 수
if num_hidden_tiles == self.num_mines: # 지뢰를 제외하고 전부 연 경우
explode = False
done = True
reward = 100
else:
explode = False
done = False
reward = 10
self.playerfield = state
self.playerfield = self.playerfield.reshape(self.grid_size, self.grid_size)
self.done = done
return state, reward, done
step
메소드: 에이전트가 환경에 한 단계(action)를 수행할 때 호출
- 현재 상태, 보상, 종료 여부를 반환합니다.
주요 동작 방식
-
현재 상태와 지뢰밭 상태 가져오기→보상, 종료 여부 초기화→선택한 타일이 hidden 상태(9)일 때 반환값 저장 →
playerfield
업데이트 → 결과 반환 - 현재 상태와 지뢰밭 상태 가져오기
self.get_state()
호출: 현재playerfield
를 1차원으로 변환self.minefield.flatten()
호출: 현재minefield
를 1차원으로 변환
state = self.get_state() # state는 (flatten된) playerfield minefield_state = self.minefield.flatten()
-
reward
과done
초기화reward = 0 done = False
-
선택한 타일이 hidden 상태(9)일 때
if state[action] == 9: state[action] = minefield_state[action] # 타일에 정답 복사
-
지뢰 선택 시
if state[action] == -1: done = True self.explode = True reward = -10
-
선택한 타일이 0일 경우 (주변 타일 열기)
python코드 복사 elif state[action] == 0: x, y = divmod(action, self.grid_size) # 1차원 인덱스를 2차원 좌표로 변환 state = self.auto_reveal_tiles(x, y) # 주변 타일 열기 num_hidden_tiles = np.count_nonzero(state == 9) # 남은 hidden tile 수 if num_hidden_tiles == self.num_mines: # 지뢰를 제외하고 전부 연 경우 (성공) done = True reward = 100 else: done = False reward = 10
-
지뢰가 아닌 숫자(1~8) 타일 선택 시
else: num_hidden_tiles = np.count_nonzero(state == 9) # 남은 hidden tile 수 if num_hidden_tiles == self.num_mines: # 지뢰를 제외하고 전부 연 경우 explode = False done = True reward = 100 else: explode = False done = False reward = 10
-
-
playerfield
업데이트 및 종료 여부 설정self.playerfield = state self.playerfield = self.playerfield.reshape(self.grid_size, self.grid_size) self.done = done
-
결과 반환
return state, reward, done
auto_reveal_tiles()
: 타일 열기 함수
check_boundary()
호출해서 경계 확인
def auto_reveal_tiles(self, x, y):
visited = set() # 중복된 값 허용 X
def reveal(x, y):
if (x, y) in visited:
return
visited.add((x, y))
self.playerfield[x, y] = self.minefield[x, y]
# 주변 8개 타일 확인
if self.minefield[x, y] == 0:
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
nx, ny = x + dx, y + dy
# 인덱스가 게임판 범위 내에 있는지 확인
if self.check_boundary(nx, ny) and (nx, ny) not in visited:
reveal(nx, ny)
reveal(x, y)
return self.playerfield
render()
render()
:playerfield
를 시각적으로 출력- pygame 으로 대체할 예정
def render(self): # 인수 설정 cnt = 1 for x in range(self.grid_size): for y in range(self.grid_size): tile = self.playerfield[x, y] if cnt % self.grid_size != 0: if tile == 9: print('.', end=' ') cnt += 1 elif tile == -1: print('X', end=' ') cnt += 1 else: print(tile, end=' ') cnt += 1 else: if tile == 9: print('.') cnt += 1 elif tile == -1: print('X') cnt += 1 else: print(tile) cnt += 1 print('\n')