Wumpus World는 논리 기반 추론 에이전트를 구현하기 위한 대표적인 AI 문제 환경이다.
이번 프로젝트에서는 Python과 Tkinter를 사용하여 Wumpus World 환경을 구현하고,
에이전트가 percept 정보를 바탕으로 안전한 위치를 추론하며 자동으로 이동하도록 구현하는 것을 목표로 한다.
처음에는 Wumpus World의 내부 구조가 정상적으로 생성되는지 확인하기 위해 텍스트 기반으로 구현하였다.
프로젝트 요구사항은 4×4 격자 환경이지만, 상하좌우 벽을 포함하여 내부적으로는 6×6 배열 구조로 월드를 구성하였다. 외곽은 벽으로 설정하고, 내부 4×4 공간에 Wumpus, Pit, Gold를 배치하였다.
구현 내용
실행 결과
#은 벽, .은 빈 공간, W는 Wumpus, P는 Pit, G는 Gold를 의미한다.
1단계에서는 Wumpus World를 텍스트 기반으로 출력하였다. 하지만 텍스트 출력만으로는 에이전트의 위치나 환경 구조를 직관적으로 확인하기 어렵다고 판단하여, Tkinter를 활용한 GUI 방식으로 확장하였다.
이번 단계에서는 Wumpus, Pit, Gold와 같은 객체 이미지는 아직 적용하지 않고, 먼저 6×6 격자 구조를 화면에 출력하는 것을 목표로 하였다. 외곽 벽은 회색으로 표시하고, 실제 탐험 가능한 내부 4×4 공간은 흰색으로 표시하였다.
구현 내용
실행 결과
2단계에서는 Tkinter를 활용하여 6×6 격자 구조를 GUI 화면으로 출력하였다. 하지만 현재 상태에서는 Wumpus, Pit, Gold, Agent의 위치를 확인할 수 없다는 문제가 있었다.
이를 해결하기 위해 3단계에서는 월드 데이터에 저장된 객체 정보를 GUI에 연결하여 각 칸에 오브젝트를 표시하도록 수정하였다.
이번 단계에서는 이미지 적용 전 단계로, 각 객체를 문자로 표시하여 위치가 정상적으로 반영되는지 확인하였다.
또한 에이전트는 시작 위치인 (1,1)에 해당하는 내부 좌표에 배치하였으며, Wumpus, Pit, Gold는 world.py에 저장된 위치를 기준으로 출력되도록 구현하였다.
구현 내용
2단계 → 3단계 코드 변화
| 기능 | 2단계 - GUI 기반 격자 출력 | 3단계 - 오브젝트 배치 |
|---|---|---|
| Agent 시작 위치 | # 없음 | agent_row = 4agent_col = 1 |
| 오브젝트 문자 설정 | # 없음 | if cell == "wumpus": text = "W"elif cell == "pit": text = "P"elif cell == "gold": text = "G" |
| Agent 표시 | # 없음 | if row == agent_row and col == agent_col: text = "A" |
| 화면 출력 기능 | 격자만 출력 | canvas.create_text(...) |
변경 내용 정리
2단계에서는 6×6 격자 구조만 GUI로 출력하였다.
3단계에서는 월드 데이터에 저장된 Wumpus, Pit, Gold와 Agent의 위치를 GUI에 문자 형태로 출력하도록 기능을 확장하였다.
또한 Canvas의 텍스트 출력 기능을 추가하여 각 객체의 위치를 화면에서 직관적으로 확인할 수 있도록 개선하였다.
실행 결과
3-1단계에서는 Wumpus, Pit, Gold, Agent를 문자(A, W, P, G) 형태로 출력하였다. 하지만 문자 기반 출력 방식은 시각적인 표현이 부족하고 실제 게임 환경처럼 보이지 않는다는 문제가 있었다.
이를 개선하기 위해 3-2단계에서는 PNG 이미지를 활용하여 각 객체를 시각적으로 표현하도록 수정하였다.
또한 이미지 크기가 격자를 벗어나거나 배경이 표시되는 문제가 발생하였기 때문에, Pillow 라이브러리를 활용하여 이미지 크기를 조정하고 투명 배경 PNG 이미지를 적용하였다.
이번 단계에서는 다음과 같은 이미지를 사용하였다.
구현 내용
3-1단계 → 3-2단계 코드 변화
| 기능 | 3-1단계 - 오브젝트 문자 출력 | 3-2단계 - 이미지 기반 출력 |
|---|---|---|
| 이미지 라이브러리 | 사용하지 않음 | from PIL import Image, ImageTk |
| 이미지 로드 함수 | 없음 | load_image() 함수 추가 |
| 객체 출력 방식 | text = "W" | canvas.create_image(...) |
| Agent 출력 방식 | 문자 "A" | agent.png 이미지 출력 |
| Wumpus / Pit / Gold | 문자 출력 | PNG 이미지 출력 |
| 이미지 크기 조정 | 없음 | image.resize((70, 70)) 추가 |
변경 내용 정리
3-1단계에서는 문자 기반으로 객체를 출력하였지만, 시각적인 표현이 부족하다는 문제가 있었다.
3-2단계에서는 Pillow 라이브러리를 활용하여 PNG 이미지를 불러오고, Canvas의 이미지 출력 기능을 통해 Agent, Wumpus, Pit, Gold를 실제 게임 형태처럼 표현하도록 개선하였다.
또한 이미지 크기를 격자 크기에 맞게 조정하여 객체가 셀 내부에 자연스럽게 배치되도록 수정하였다.
실행 결과
GUI 화면에서 각 객체가 이미지 형태로 정상 출력되는 것을 확인할 수 있었다.
3-2단계에서는 Agent, Wumpus, Pit, Gold를 이미지로 표현하여 GUI 화면에 출력하였다. 하지만 현재 상태에서는 에이전트가 시작 위치에 고정되어 있어 실제 탐험 기능을 확인할 수 없었다.
따라서 4-1단계에서는 먼저 에이전트가 격자 환경 안에서 정상적으로 이동할 수 있는지 확인하기 위해 방향키 기반 이동 기능을 구현하였다.
이 단계는 최종 목표인 자동 추론 에이전트 구현 전, 이동 로직과 충돌 처리가 정상적으로 동작하는지 검증하기 위한 중간 단계이다.
구현 내용
3-2단계 → 4-1단계 코드 변화
| 기능 | 3-2단계 - 이미지 기반 출력 | 4-1단계 - 방향키 이동 구현 |
|---|---|---|
| Agent 상태 | 고정 위치 출력 | agent_row, agent_col 값 변경 |
| 입력 방식 | 없음 | window.bind("<KeyPress>", move_agent) |
| 이동 함수 | 없음 | move_agent(event) 함수 추가 |
| 벽 충돌 | 없음 | if game.world[new_row][new_col] == "wall" |
| 위험 처리 | 단순 이미지 표시 | Wumpus / Pit 도달 시 시작 위치 복귀 |
| 화면 갱신 | 최초 1회 출력 | 이동 후 draw_world() 재호출 |
변경 내용 정리
3-2단계에서는 에이전트가 시작 위치에 고정되어 화면에 표시되었다.
4-1단계에서는 방향키 입력을 통해 에이전트의 위치 값을 변경하고, 변경된 위치를 GUI 화면에 다시 그리도록 수정하였다.
또한 외곽 벽에 이동하려는 경우 Bump 메시지를 출력하고 이동을 제한하도록 하였다. Wumpus 또는 Pit에 도달한 경우에는 에이전트가 사망한 것으로 처리하고 시작 위치로 되돌아가도록 구현하였다.
이 단계는 에이전트가 자동으로 판단하는 단계는 아니지만, 이후 자동 이동과 추론 기능을 구현하기 전에 이동 및 충돌 처리 로직이 정상적으로 작동하는지 확인하기 위한 과정이다.
실행 결과
4-1단계에서는 방향키 입력을 통해 에이전트를 직접 이동시키는 기능을 구현하였다. 하지만 기존 방식은 사용자가 누른 방향대로 즉시 좌표가 변경되는 구조였기 때문에, 실제 Wumpus World의 행동(Action) 구조와는 차이가 있었다.
Wumpus World에서 에이전트는 단순히 좌표를 이동하는 것이 아니라, 현재 바라보고 있는 방향(Direction)을 기준으로 행동을 수행해야 한다.
따라서 4-2단계에서는 방향(Direction) 개념을 추가하고, 에이전트의 행동을 다음과 같은 Action 기반 구조로 변경하였다.
이번 단계에서는 방향(Direction)을 기준으로 회전 및 이동이 가능하도록 시스템을 구성하였다.
구현 내용
Direction 시스템 구성
에이전트는 현재 바라보는 방향(Direction)을 상태로 저장한다.
초기 방향은 프로젝트 요구사항에 맞게 EAST(동쪽)으로 설정하였다.
self.direction = "EAST"
에이전트는 다음 4가지 방향 상태를 가진다.
방향 전환은 TurnLeft, TurnRight를 통해 수행된다.
turn_left()
turn_right()
GoForward 행동은 현재 방향을 기준으로 다음 위치를 계산한 뒤 이동하도록 구성하였다
get_forward_position()
go_forward(
변경 내용 정리
4-1단계에서는 사용자가 방향키를 누르면 해당 방향으로 즉시 좌표가 변경되는 방식이었다.
하지만 실제 Wumpus World에서는 에이전트가 방향(Direction)을 가진 상태에서 행동(Action)을 수행해야 하기 때문에, 단순 좌표 이동 방식으로는 프로젝트 요구사항을 만족하기 어려웠다.
4-2단계에서는 방향 상태(Direction)를 추가하고, 이동 구조를 TurnLeft, TurnRight, GoForward 기반으로 변경하였다.
또한 GUI 화면에서 에이전트가 현재 바라보는 방향을 직관적으로 확인할 수 있도록 방향 화살표를 함께 출력하였다.
이를 통해 이후 단계에서 Shoot, DFS 탐색, 자동 행동 선택 등의 기능을 구현할 수 있는 기반 구조를 완성하였다.
실행 결과
파일 첨부
이번 단계에서는 에이전트가 사용자의 방향키 입력 없이 스스로 탐색할 수 있도록 DFS(Depth-First Search) 기반 자동 탐색 구조를 구현하였다.
에이전트는 현재 위치에서 얻은 Percept 정보를 바탕으로 안전한 칸을 추론하고, 방문하지 않은 안전 칸을 우선적으로 탐색한다.
또한 더 이상 이동 가능한 안전 칸이 없으면 이전 위치로 되돌아가는 DFS Backtracking 구조를 적용하였다.
구현 내용
DFS 탐색 방식
에이전트는 다음 순서로 탐색을 수행한다.
예를 들어 다음과 같은 탐색 흐름이 발생할 수 있다.
(1,1)
→ (1,2)
→ (1,3)
→ (1,2)
→ (2,2)
여기서:
(1,3) → (1,2)
과 같은 이동은 DFS의 Backtracking 과정이다.
Percept 기반 추론
에이전트는 이동 후 다음과 같은 Percept를 감지한다.
이를 기반으로 주변 칸의 위험 여부를 추론하였다.
또한 사망 후에도 이전 탐험 정보를 유지하도록 구현하여, 반복 탐험을 통해 환경 정보를 축적할 수 있도록 구성하였다.
실행 결과
파일 첨부
현재까지는 5단계에서 DFS 기반 자동 탐색 에이전트까지 구현하였다.
에이전트는 현재까지의 방문 기록과 Percept 정보를 바탕으로 안전하다고 판단한 칸을 우선적으로 탐색하고, 더 이상 이동할 수 없는 경우 DFS Backtracking을 통해 이전 위치로 되돌아가는 구조를 갖는다.
이후 단계에서는 Wumpus World의 전체 Action 요구사항을 만족하기 위해 Shoot, Grab, Climb 기능을 추가로 구현할 예정이다.
이전 단계에서는 DFS 기반 탐색과 위험 지역 추론을 구현하였다.
이번 단계에서는 Wumpus World의 핵심 Action인 Shoot, Grab, Climb 기능을 추가하고, 최종적으로 Gold를 획득한 뒤 안전하게 탈출하는 전체 흐름을 완성하였다.
Wumpus World에서는 Wumpus의 위치를 추론한 뒤 화살을 사용하여 제거할 수 있다.
이번 단계에서는 Stench 정보를 기반으로 Wumpus의 위치를 추론하고, 방향을 조준한 뒤 Shoot을 수행하도록 구현하였다.
에이전트는 Stench를 느낀 칸들의 주변 칸을 Wumpus 후보로 저장한다.
예를 들어 다음과 같은 상황이 있다고 가정하면:
(1,3) → Stench
(2,2) → Stench + Breeze
각 칸의 주변 후보를 계산할 수 있다.
(1,3)의 후보
→ (1,4), (2,3)
(2,2)의 후보
→ (1,2), (2,3), (3,2), (2,1)
이미 방문하여 안전하다고 판단된 칸들을 제외하면:
공통 후보 = (2,3)
따라서 에이전트는 (2,3)에 Wumpus가 있다고 추론할 수 있다.
처음에는 DFS 탐색을 계속 진행한 뒤, 이동 가능한 칸이 없을 때 Shoot을 수행하도록 구성하였다.
하지만 이 방식에서는 에이전트가 (2,2)처럼 Stench를 느끼고 Wumpus 위치를 확정한 상황에서도 DFS Backtracking을 먼저 수행하여, Wumpus를 제거하지 않고 다른 칸으로 이동하는 문제가 발생하였다.
따라서 최종적으로는 다음과 같이 우선순위를 수정하였다.
1. 현재 위치에서 Stench 감지
2. Wumpus 위치 확정
3. 화살이 남아 있음
4. Wumpus 방향으로 조준 가능
5. Shoot 수행
6. 위 조건이 아니면 DFS 탐색 수행
즉, Wumpus 위치를 확정할 수 있고 현재 위치에서 Stench를 느끼는 경우에는 DFS 이동보다 Shoot을 우선하도록 구현하였다.
이를 통해 에이전트가 위험 요소인 Wumpus를 먼저 제거하고, 이후 안전하게 Gold 방향으로 탐색을 계속할 수 있도록 개선하였다.
Stench 감지
→ Wumpus 후보 추론
→ Wumpus 위치 확정
→ 방향 조준
→ Shoot
→ Scream 발생
→ Wumpus 제거
Wumpus 제거 이후에는 해당 칸을 위험 지역에서 제거하고, 안전한 칸으로 다시 탐색 가능하도록 수정하였다.
Wumpus World의 목표는 Gold를 획득하는 것이다.
에이전트가 Gold 위치에 도착했을 때 Glitter를 감지하고, Grab Action을 수행하도록 구현하였다.
Gold 위치 도착
→ Glitter 감지
→ Grab 수행
→ Gold 획득 상태 저장
Grab 이후에는 복귀 모드로 전환되도록 구현하였다.
Gold를 획득한 뒤에는 시작 위치 (1,1)로 돌아와야 한다.
초기에는 DFS 탐색 경로를 역순으로 따라가도록 구현하였다.
하지만 이 방식은 실제로 왔던 길을 그대로 되돌아가기 때문에 비효율적인 이동이 발생하였다.
따라서 복귀 로직은 다음과 같이 수정하였다.
방문했던 안전한 칸들 중에서 최단 경로 계산
복귀 시에는 DFS가 아니라 BFS를 이용하여 시작 위치까지의 최단 안전 경로를 계산하였다.
복귀 경로 계산 시 사용한 조건은 다음과 같다.
1. 실제 방문했던 칸만 사용
2. 위험 지역 제외
3. 안전한 칸만 이동 가능
이를 통해 에이전트는 단순히 왔던 길을 역순으로 이동하는 것이 아니라,
탐색했던 안전한 영역 안에서 가장 짧은 경로로 복귀할 수 있게 되었다.
Grab 수행
→ BFS 기반 최단 안전 경로 계산
→ 시작 위치로 복귀
Gold를 획득한 뒤 시작 위치로 돌아오면 Climb Action을 수행하여 동굴을 탈출하도록 구현하였다.
DFS 탐색
→ 위험 지역 추론
→ Wumpus 위치 추론
→ Shoot 수행
→ Wumpus 제거
→ Gold 탐색
→ Grab 수행
→ 최단 안전 경로 계산
→ 시작 위치 복귀
→ Climb
→ 탐험 종료
최종적으로 에이전트는 다음 기능들을 수행할 수 있게 되었다.
또한 GUI를 통해 안전 지역, 위험 후보 지역, 확정 위험 지역 등을 시각적으로 확인할 수 있도록 구현하였다.
이전 단계까지는 고정된 Wumpus World 환경에서 DFS 탐색, 위험 지역 추론, Shoot, Grab, Climb 기능을 구현하였다.
하지만 실제 Wumpus World는 항상 같은 환경이 아니라 매 실행마다 새로운 환경이 생성된다. 또한 에이전트는 단순히 정해진 경로를 따라 이동하는 것이 아니라, 다양한 환경에서도 스스로 위험을 추론하고 목표를 달성할 수 있어야 한다.
따라서 마지막 단계에서는 교수님 피드백을 반영하여 랜덤 환경 생성 기능을 추가하고, 죽더라도 정보를 유지하며 탐색을 이어갈 수 있도록 개선하였다.
초기 버전에서는 Wumpus와 Pit의 위치를 직접 지정하여 사용하였다.
world[2][2] = self.WUMPUS
world[3][3] = self.PIT
이 방식은 DFS 탐색과 추론 로직을 검증하기에는 편리했지만, 항상 동일한 환경만 테스트할 수 있다는 한계가 있었다.
따라서 최종 버전에서는 Wumpus와 Pit의 위치를 랜덤하게 생성하도록 수정하였다.
def _place(self, item):
for x in range(1, 5):
for y in range(1, 5):
if (x, y) in [(1, 1), (1, 2), (2, 1)]:
continue
if self.world[x][y] != self.EMPTY:
continue
if random.random() < 0.1:
self.world[x][y] = item
return
self.world[random.randint(2, 3)][random.randint(2, 3)] = item
교수님 피드백에 따라 다음 조건을 만족하도록 구현하였다.
이를 통해 매 실행마다 새로운 환경을 생성하면서도 에이전트가 최소한의 안전 영역을 확보한 상태에서 탐색을 시작할 수 있도록 하였다.
교수님 가이드라인에 따라 하나의 RUN 동안에는 Wumpus와 Pit의 위치를 유지하도록 구현하였다.
즉, 에이전트가 탐색 중 사망하더라도 환경은 그대로 유지되며 에이전트만 시작 위치로 복귀한다.
def reset_position(self):
self.row = 1
self.col = 1
self.direction = "EAST"
위 함수는 에이전트의 위치와 방향만 초기화하며, Wumpus World 객체는 다시 생성하지 않는다.
game = WumpusWorld()
Wumpus World 객체는 프로그램 시작 시 한 번만 생성되므로 사망 후에도 Wumpus와 Pit의 위치는 그대로 유지된다.
탐색
↓
위험 지역 진입
↓
사망
↓
(1,1) 복귀
↓
기존 정보 유지
↓
재탐색
이를 통해 에이전트는 이전 탐색 과정에서 얻은 정보를 활용하여 점진적으로 위험 지역을 추론할 수 있도록 구현하였다.
랜덤 환경에서는 Gold로 가는 경로가 Wumpus에 의해 막히는 상황이 발생할 수 있다.
이를 해결하기 위해 여러 Stench 정보를 종합하여 Wumpus의 위치를 추론하도록 구현하였다.
common_wumpus = set.intersection(*wumpus_candidate_sets)
if len(common_wumpus) == 1:
self.confirmed_wumpus.add(
next(iter(common_wumpus))
)
예를 들어 다음과 같은 상황이 발생했다고 가정해보자.
(1,3) → Stench
(2,2) → Stench
각 위치에서 가능한 Wumpus 후보를 계산하면 다음과 같다.
(1,3)의 후보
→ (1,4), (2,3)
(2,2)의 후보
→ (1,2), (2,3), (3,2), (2,1)
이미 방문하여 안전하다고 판단된 칸들을 제외하면:
공통 후보 = (2,3)
따라서 에이전트는 (2,3)에 Wumpus가 있다고 추론할 수 있다.
처음에는 DFS 탐색을 계속 진행한 뒤, 이동 가능한 칸이 없을 때 Shoot을 수행하도록 구성하였다.
하지만 이 방식에서는 에이전트가 (2,2)처럼 Stench를 느끼고 Wumpus 위치를 확정한 상황에서도 DFS Backtracking을 먼저 수행하여, Wumpus를 제거하지 않고 다른 칸으로 이동하는 문제가 발생하였다.
따라서 최종적으로는 다음과 같이 우선순위를 수정하였다.
1. 현재 위치에서 Stench 감지
2. Wumpus 위치 확정
3. 화살이 남아 있음
4. Wumpus 방향으로 조준 가능
5. Shoot 수행
6. 위 조건이 아니면 DFS 탐색 수행
즉, Wumpus 위치를 확정할 수 있고 현재 위치에서 Stench를 느끼는 경우에는 DFS 이동보다 Shoot을 우선하도록 구현하였다.
이를 통해 에이전트가 위험 요소인 Wumpus를 먼저 제거하고, 이후 안전하게 Gold 방향으로 탐색을 계속할 수 있도록 개선하였다.
Wumpus 제거 이후에는 Scream Percept를 발생시키고, 해당 위치를 다시 안전한 칸으로 변경하였다.
def on_scream(self):
self.wumpus_destroyed = True
for cell in self.confirmed_wumpus:
self.safe_cells.add(cell)
self.confirmed_wumpus.clear()
이를 통해 Wumpus 때문에 막혀 있던 경로도 다시 탐색할 수 있도록 구현하였다.
Gold를 획득한 뒤에는 시작 위치로 복귀해야 한다.
초기에는 DFS 탐색 경로를 역순으로 따라가는 방식으로 구현하였다.
하지만 이 방식은 실제로 왔던 길을 그대로 되돌아가기 때문에 비효율적인 이동이 발생하였다.
따라서 최종 버전에서는 BFS를 이용하여 방문했던 안전한 칸들 중 최단 경로를 계산하도록 수정하였다.
def find_shortest_safe_path(self, start, goal, world):
queue = [(start, [])]
visited = {start}
while queue:
current, path = queue.pop(0)
if current == goal:
return path
실제로 방문했던 칸
+
안전한 칸
+
위험 지역 제외
위 조건을 만족하는 경로 중 최단 경로를 선택하도록 구현하였다.
그 결과 Gold를 획득한 뒤 보다 효율적으로 시작 위치까지 복귀할 수 있게 되었다.
최종적으로 에이전트는 다음 순서로 동작한다.
DFS 탐색
↓
Percept 수집
↓
위험 지역 추론
↓
죽더라도 정보 유지
↓
Wumpus / Pit 위치 추론
↓
Shoot
↓
Gold 획득 (Grab)
↓
BFS 기반 최단 경로 복귀
↓
Climb
↓
탐험 종료
프로젝트 초기에는 DFS 기반 자동 탐색만 수행하는 에이전트로 시작하였다.
이후 Breeze와 Stench를 이용한 위험 지역 추론, Shoot, Grab, Climb 기능을 구현하였고, 마지막으로 랜덤 환경 생성과 학습 기반 재탐색 기능을 추가하였다.
최종적으로 에이전트는 매 실행마다 달라지는 환경에서도 스스로 탐색을 수행하고, 위험을 추론하며, 필요 시 Wumpus를 제거한 뒤 Gold를 획득하고 탈출할 수 있도록 구현하였다.
이를 통해 Wumpus World의 핵심 개념인 탐색(Exploration), 추론(Reasoning), 의사결정(Decision Making) 을 모두 구현할 수 있었다.