[강화시스터즈 1기/프로젝트/환경세미나] 지뢰마스터즈 01팀
지뢰찾기 환경
예시
def __init__(params):
self.board = self.make_init_board()
# 중략
def make_init_board(self):
board = np.ones(shape=(2,self.nrows, self.ncols),dtype='object') # (revealed_or_not, game_board)
actual_board = self.seed_mines()
actual_board = self.complete_actual_board(actual_board)
board[1] = actual_board
return board
self.board
: 지뢰찾기 게임이 상황이 저장되어 있는 어트리뷰트- type :
np.array
- 구조 : (2, *map_size) → (revealed_or_not, game_board) 두 층으로 이루어져 있다.
- revealed_or_not : 까진 타일이면 0, 안 까진 타일이면 1
- game_board : 게임 판, 지뢰와 주변 지뢰 개수를 센 정보가 전부 들어있다.
- M : 지뢰
- 0 ~ 8 : 주변부 지뢰 개수
- type :
self.board
어트리뷰트는self.make_init_board()
매서드로 생성된다.self.seed_mines()
와self.complete_actual_board(actual_board)
매서드로 차례 차례 지뢰를 판에 심고, 나머지 타일을 채운다.- board[1] (aka.game_board) 에 계산한
actual_board
를 이식한다.
Env
def __init__(self, height=9, width=9, num_mines=10):
self.height = height
self.width = width
self.num_mines = num_mines
self.reset()
self.height
,self.width
,self.num_mines
: 높이, 너비, 지뢰의 개수를 매개변수로 받아서 인스턴스 변수로 초기화self.reset()
: 메서드를 호출하여 초기 게임 상태를 설정
def reset(self):
# Initialize fields
self.minefield = np.zeros((self.height, self.width), dtype = int) # Correct answer
self.playerfield = np.full((self.height, self.width), 9) # Player's view
self.exploded = False
self.done = False
# Place mines
mines = set()
while len(mines) < self.num_mines:
x, y = random.randint(0, self.height - 1), random.randint(0, self.width - 1)
if (x, y) not in mines:
mines.add((x, y))
self.minefield[x, y] = -1
# Calculate numbers
for (x, y) in mines:
for i in range(max(0, x - 1), min(self.height, x + 2)):
for j in range(max(0, y - 1), min(self.width, y + 2)):
if self.minefield[i, j] != -1:
self.minefield[i, j] += 1
# Uncover a safe cell to start
while True:
sx, sy = random.randint(0, self.height - 1), random.randint(0, self.width - 1)
if self.minefield[sx, sy] != -1:
self._uncover(sx, sy)
break
self.playerfield = self.playerfield.reshape(-1, self.width * self.height)
return self.playerfield
self.minefield
: 정답지의 역할을 하는 필드self.playerfield
: 게임하는 상황을 나타내는 필드self.exploded
: 지뢰가 터졌는지 여부를 나타냄self.done
: 게임이 끝났는지 여부를 나타냄 (지뢰를 제외하고 나머지 부분을 모두 열어 확인한 경우(이김) orself.exploded
==True
)
def step(self, action_idx):
x, y = divmod(action_idx, self.width)
reward = 0
if self.playerfield[x, y] == 9:
self._uncover(x, y)
if self.playerfield[x, y] == -1:
reward = -10
self.exploded = True
self.done = True
elif self.playerfield[x, y] == 0:
self.auto_reveal_blocks(x, y)
reward = 1
self.exploded = False
self.done = False
else:
reward = 1
self.exploded = False
self.done = False
if not self.exploded and (np.sum(self.playerfield == 9) == self.num_mines):
self.done = True
reward = 10
return self.playerfield, reward, self.exploded, self.done
action_idx
: action_idx는 0~(self.height
*self.width
)-1 에 해당하는 정수값- 2차원인 playerfield에
action_idx
적용하기 위해 divmod 함수를 사용하여 (x,y)좌표로 변환-
divmod 함수
두 개의 숫자를 인자로 받아, 첫번째 숫자를 두번째 숫자로 나눈 몫과 나머지를 튜플(tuple) 형태로 반환
-
-
보상
Case Reward 지뢰를 선택한 경우(-1) -10 0-8의 숫자가 있는 타일을 선택한 경우 (0-8) +1 지뢰를 제외한 모든 타일을 열어본 경우 +10
def _uncover(self, x, y):
self.playerfield[x, y] = self.minefield[x, y]
return self.playerfield
주어진 x, y 위치의 타일을 self.minefield
에서 가져와 self.playerfield
에 반영하고 반환한다.
def auto_reveal_blocks(self, x, y):
queue = [(x, y)]
while queue:
cx, cy = queue.pop(0)
for i in range(max(0, cx - 1), min(self.height, cx + 2)):
for j in range(max(0, cy - 1), min(self.width, cy + 2)):
if self.playerfield[i, j] == 9:
self._uncover(i, j)
if self.minefield[i, j] == 0:
queue.append((i, j))
시작 타일인 (x,y) 를 큐에 추가하고 큐가 비지 않을 때까지 큐에서 첫번째 요소를 꺼내 cx, cy로 반환
- 현재 위치
(cx, cy)
의 인접한 모든 칸을 탐색 self.playerfield
에서 숨겨진 칸(9)인 경우에만 처리- 숨겨진 칸이면
self._uncover(i, j)
로 해당 칸을 연다. - 해당 칸이 빈 칸(0)일 경우, 큐에 추가
https://velog.io/@keum0821/그래프Graph-BFS의-개념과-구현
def render(self):
render_str = ''
for x in range(self.height):
for y in range(self.width):
self.playerfield = self.playerfield.reshape(self.height, self.width) #
if self.playerfield[x, y] == 9:
render_str += 'H ' # H for hidden
elif self.playerfield[x, y] == -1:
render_str += 'M ' # M for mine
else:
render_str += f'{self.playerfield[x, y]} '
render_str += '\n'
print(render_str)
self.playerfield
를 기반으로 현재 사용자의 필드를 문자열로 만들어 출력하는 메서드
self.playerfield[x, y] == 9
: 열리지 않은 상황으로 H(hidden)으로 숨겨진 타일을 표현self.playerfield[x, y] == -1
: 지뢰가 터진 상황으로 M(mine)으로 지뢰를 표현이외의 경우
: 1~8의 숫자로 주변 지뢰의 개수를 표현
if __name__ == "__main__":
env = MinesweeperEnv()
state = env.reset()
env.render()
done = False
while not done:
action = random.randint(0, env.width * env.height - 1) # Random action for demonstration
state, reward, done, info = env.step(action)
env.render()
if done:
break
auto_reveal_blocks
나 종료조건이 잘 실행되는지 확인
```