ให้นักศึกษาเขียนโปรแกรม 8-Puzzle Game ที่มีคุณสมบัติดังนี้:
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
├───┼───┼───┤
│ 4 │ 5 │ 6 │
├───┼───┼───┤
│ 7 │ 8 │ │
└───┴───┴───┘
หรือในรูปแบบ Array: [[1, 2, 3], [4, 5, 6], [7, 8, 0]]
| คำสั่ง | คีย์ลัด | คำอธิบาย |
|---|---|---|
| เลื่อนขึ้น | W หรือ U |
เลื่อนช่องว่างขึ้นบน (สลับกับช่องด้านบน) |
| เลื่อนลง | S หรือ D |
เลื่อนช่องว่างลงล่าง (สลับกับช่องด้านล่าง) |
| เลื่อนซ้าย | A หรือ L |
เลื่อนช่องว่างไปซ้าย (สลับกับช่องด้านซ้าย) |
| เลื่อนขวา | D หรือ R |
เลื่อนช่องว่างไปขวา (สลับกับช่องด้านขวา) |
| แก้ปัญหา | SOLVE |
ใช้ A* Search หาวิธีแก้ปัญหา |
| รีเซ็ต | RESET |
คืนค่า Puzzle กลับสู่สถานะเริ่มต้น (Goal State) |
| สุ่ม | SHUFFLE |
สุ่มตำแหน่ง Puzzle (ต้องแน่ใจว่าแก้ได้) |
| ออก | Q หรือ QUIT |
ออกจากโปรแกรม |
heapq, copy, randomPuzzleStateต้องมี Class สำหรับเก็บสถานะของ Puzzle โดยมี attribute และ method ดังนี้:
class PuzzleState:
def __init__(self, board, parent=None, move=None, depth=0):
"""
board: 2D list ขนาด 3x3
parent: PuzzleState ก่อนหน้า (สำหรับ backtrack)
move: การเคลื่อนที่ที่ทำให้มาถึง state นี้
depth: ความลึกจาก initial state (g(n))
"""
pass
def get_blank_position(self):
"""หาตำแหน่งช่องว่าง คืนค่า (row, col)"""
pass
def get_neighbors(self):
"""สร้าง state ใหม่จากการเลื่อนที่เป็นไปได้ทั้งหมด"""
pass
def is_goal(self):
"""ตรวจสอบว่าเป็น goal state หรือไม่"""
pass
def __eq__(self, other):
"""เปรียบเทียบ state"""
pass
def __hash__(self):
"""สำหรับใช้ใน set/dict"""
pass
def __lt__(self, other):
"""สำหรับ priority queue"""
pass
ต้อง implement heuristic function อย่างน้อย 2 แบบ:
h(n) = Σ |current_row - goal_row| + |current_col - goal_col|
สำหรับทุก tile (ยกเว้นช่องว่าง)
h(n) = จำนวน tile ที่อยู่ผิดตำแหน่ง
def a_star_search(initial_state, heuristic='manhattan'):
"""
Parameters:
initial_state: PuzzleState เริ่มต้น
heuristic: 'manhattan' หรือ 'misplaced'
Returns:
solution_path: list ของ moves ที่นำไปสู่ goal
nodes_expanded: จำนวน nodes ที่ถูก expand
"""
pass
สูตร A*: f(n) = g(n) + h(n)
g(n) = ต้นทุนจาก initial state มาถึง state ปัจจุบัน (depth)h(n) = ค่าประมาณต้นทุนจาก state ปัจจุบันไป goal stateต้องมี function ตรวจสอบว่า puzzle configuration สามารถแก้ได้หรือไม่:
def is_solvable(board):
"""
ตรวจสอบว่า puzzle แก้ได้หรือไม่
โดยนับจำนวน inversions
- ถ้า inversions เป็นเลขคู่ = แก้ได้
- ถ้า inversions เป็นเลขคี่ = แก้ไม่ได้
"""
pass
['UP', 'LEFT', 'UP', 'RIGHT', ...]Output:
╔══════════════════════════════════════╗
║ 8-Puzzle Game with A* Search ║
╚══════════════════════════════════════╝
Current State:
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
├───┼───┼───┤
│ 4 │ 5 │ 6 │
├───┼───┼───┤
│ 7 │ 8 │ │
└───┴───┴───┘
Status: SOLVED ✓
Moves: 0
Commands: W/U(Up) S/D(Down) A/L(Left) D/R(Right)
SOLVE | SHUFFLE | RESET | QUIT
Enter command: _
Input:
Enter command: U
Output:
Current State:
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
├───┼───┼───┤
│ 4 │ 5 │ │
├───┼───┼───┤
│ 7 │ 8 │ 6 │
└───┴───┴───┘
Status: Not solved
Moves: 1
Last move: UP
Enter command: _
Input ต่อเนื่อง:
Enter command: L
Enter command: U
Enter command: L
Output หลังจากคำสั่งสุดท้าย:
Current State:
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
├───┼───┼───┤
│ │ 4 │ 5 │
├───┼───┼───┤
│ 7 │ 8 │ 6 │
└───┴───┴───┘
Status: Not solved
Moves: 4
Move history: UP → LEFT → UP → LEFT
Enter command: _
Input:
Enter command: SHUFFLE
Output:
Shuffling puzzle...
Current State:
┌───┬───┬───┐
│ 2 │ 8 │ 3 │
├───┼───┼───┤
│ 1 │ 6 │ 4 │
├───┼───┼───┤
│ 7 │ │ 5 │
└───┴───┴───┘
Status: Not solved (Solvable: Yes)
Moves: 0
Enter command: _
Input:
Enter command: SOLVE
Output:
╔════════════════════════════════════════╗
║ A* Search in Progress ║
╚════════════════════════════════════════╝
Initial State:
┌───┬───┬───┐
│ 2 │ 8 │ 3 │
├───┼───┼───┤
│ 1 │ 6 │ 4 │
├───┼───┼───┤
│ 7 │ │ 5 │
└───┴───┴───┘
Searching with Manhattan Distance heuristic...
═══════════════ SOLUTION FOUND ═══════════════
Solution Path (9 moves):
UP → UP → LEFT → DOWN → RIGHT → DOWN → LEFT → UP → UP
Statistics:
├─ Nodes Expanded : 12
├─ Solution Length : 9 moves
├─ Time Elapsed : 0.003 seconds
└─ Heuristic Used : Manhattan Distance
Would you like to see step-by-step solution? (Y/N): _
Input:
Would you like to see step-by-step solution? (Y/N): Y
Output:
═══════════ STEP-BY-STEP SOLUTION ═══════════
Step 0 (Initial): Step 1 (UP):
┌───┬───┬───┐ ┌───┬───┬───┐
│ 2 │ 8 │ 3 │ │ 2 │ │ 3 │
├───┼───┼───┤ ├───┼───┼───┤
│ 1 │ 6 │ 4 │ → │ 1 │ 8 │ 4 │
├───┼───┼───┤ ├───┼───┼───┤
│ 7 │ │ 5 │ │ 7 │ 6 │ 5 │
└───┴───┴───┘ └───┴───┴───┘
Step 2 (UP): Step 3 (LEFT):
┌───┬───┬───┐ ┌───┬───┬───┐
│ │ 2 │ 3 │ │ 2 │ │ 3 │
├───┼───┼───┤ ├───┼───┼───┤
│ 1 │ 8 │ 4 │ → │ 1 │ 8 │ 4 │
├───┼───┼───┤ ├───┼───┼───┤
│ 7 │ 6 │ 5 │ │ 7 │ 6 │ 5 │
└───┴───┴───┘ └───┴───┴───┘
... (แสดงต่อจนครบทุก step) ...
Step 9 (Final - GOAL):
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
├───┼───┼───┤
│ 4 │ 5 │ 6 │
├───┼───┼───┤
│ 7 │ 8 │ │
└───┴───┴───┘
🎉 Puzzle Solved!
Press Enter to continue...
Input:
Enter command: COMPARE
Output:
═══════════ HEURISTIC COMPARISON ═══════════
Testing on current puzzle state...
┌─────────────────────┬────────────────┬──────────────────┐
│ Heuristic │ Nodes Expanded │ Time (seconds) │
├─────────────────────┼────────────────┼──────────────────┤
│ Manhattan Distance │ 12 │ 0.003 │
│ Misplaced Tiles │ 28 │ 0.008 │
└─────────────────────┴────────────────┴──────────────────┘
Both found solution of length: 9 moves
Analysis:
Manhattan Distance is more efficient because it provides
a tighter lower bound on the actual distance to goal.
Enter command: _
Input (ถ้ามีการ set state ที่แก้ไม่ได้):
Enter command: SOLVE
Output:
⚠️ WARNING: This puzzle configuration is NOT solvable!
Current inversions: 21 (odd number)
A puzzle is solvable only if the number of inversions is even.
Would you like to shuffle to a solvable configuration? (Y/N): _
Input:
Enter command: X
Output:
❌ Invalid command: 'X'
Available commands:
Movement : W/U(Up), S/D(Down), A/L(Left), D/R(Right)
Actions : SOLVE, SHUFFLE, RESET, COMPARE, QUIT
Enter command: _
Input (เมื่อช่องว่างอยู่ขอบบน):
Enter command: U
Output:
⚠️ Cannot move UP - blank is at the top edge
Current State:
┌───┬───┬───┐
│ 1 │ │ 3 │
├───┼───┼───┤
│ 4 │ 2 │ 6 │
├───┼───┼───┤
│ 7 │ 5 │ 8 │
└───┴───┴───┘
Enter command: _
8puzzle/
├── puzzle.py # PuzzleState class
├── astar.py # A* Search algorithm
├── heuristics.py # Heuristic functions
├── game.py # User interface & game loop
├── utils.py # Helper functions
└── main.py # Entry point
import heapq
# สร้าง priority queue
pq = []
# เพิ่ม item (priority, item)
heapq.heappush(pq, (priority, item))
# ดึง item ที่มี priority ต่ำสุด
priority, item = heapq.heappop(pq)
def board_to_tuple(board):
return tuple(tuple(row) for row in board)
function A_STAR(initial_state):
open_set = priority queue with initial_state
closed_set = empty set
while open_set is not empty:
current = pop state with lowest f(n) from open_set
if current is goal:
return reconstruct_path(current)
add current to closed_set
for each neighbor of current:
if neighbor in closed_set:
continue
g_score = current.g + 1
f_score = g_score + heuristic(neighbor)
if neighbor not in open_set or g_score < neighbor.g:
neighbor.g = g_score
neighbor.f = f_score
neighbor.parent = current
if neighbor not in open_set:
add neighbor to open_set
return failure (no solution)