Assignment 1: Search (Pac-Man AI Homework)

CS451/551 · Adapted from UC Berkeley CS 188 Project 1 · Modified by Amin D. Alamdari, 2026

Pac-Man

In this project your Pac-Man agent will find paths through his maze world — both to reach a particular location and to collect food efficiently. You will build general search algorithms and apply them to different Pac-Man scenarios.

The game engine, display system, and test infrastructure are already built for you. Your job is to implement the search algorithms and problem formulations that make Pac-Man smart.

CS 451/551 Course Link (TBA) Berkeley Project 1 Page Homework's Github Repository Repository README

Compatibility & Prerequisites

This project has been tested and works on Linux, macOS, and Windows.

Linux users: Tkinter is not bundled with Python on most Linux distributions. Install it before launching the game:
sudo apt-get install python3-tk        # Debian / Ubuntu
sudo dnf install python3-tkinter       # Fedora / RHEL
sudo pacman -S tk                      # Arch
The pytest tests run fully headless and work without Tkinter.

Verify your setup

python main.py

You should see a Pac-Man game window open. Use the arrow keys to play manually and confirm everything works.

Install pytest (if needed)

pip install pytest

Learning Objectives

Project Structure

Show / hide directory tree
pac-man-search/
├── main.py                        # Entry point — run this to start Pac-Man
│
├── pacman/                        # Main game package
│   ├── search.py                  # ✏  search algorithms   (YOU EDIT THIS)
│   ├── search_agents.py           # ✏  search problems & heuristics (YOU EDIT THIS)
│   ├── engine.py                  # Game runner and command-line interface
│   ├── game.py                    # Core types: GameState, Agent, Directions, Grid
│   ├── layout.py                  # Maze loading and parsing from .lay files
│   ├── util.py                    # Data structures: Stack, Queue, PriorityQueue
│   ├── eight_puzzle.py            # 8-puzzle domain (tests generic search)
│   ├── agents/
│   │   ├── pacman_agents.py       # Pre-built Pac-Man agents
│   │   ├── ghost_agents.py        # Ghost behaviour controllers
│   │   └── keyboard_agents.py    # Human-playable keyboard agent
│   └── display/
│       ├── graphics_display.py    # Tkinter graphical display
│       ├── graphics_utils.py      # Graphics helper utilities
│       └── text_display.py        # Text-only display (no GUI needed)
│
├── layouts/                       # 37 maze layout files (.lay)
│
└── tests/
    └── check.py                   # 45 self-check tests — run with pytest

Files you will edit

You only need to modify two files. All places are marked with # TODO comments. Do not modify any other files.

FileWhat You Implement
pacman/search.pyGeneric search algorithms (DFS, BFS, UCS, A*)
pacman/search_agents.pySearch problem definitions and heuristic functions

Exact functions & methods to implement

pacman/search.py — 4 functions

FunctionQDescription
depth_first_search(problem)Q1DFS graph search — use util.Stack
breadth_first_search(problem)Q2BFS graph search — use util.Queue
uniform_cost_search(problem)Q3UCS graph search — use util.PriorityQueue, priority = cumulative cost
a_star_search(problem, heuristic)Q4A* graph search — priority = g(n) + h(n)

pacman/search_agents.py — 7 methods and functions

Class / FunctionMethodQDescription
CornersProblemget_start_state()Q5Hashable state encoding Pac-Man's position + visited corners
CornersProblemis_goal_state(state)Q5Return True when all four corners have been visited
CornersProblemget_successors(state)Q5Return (successor, action, 1) triples with updated corner info
corners_heuristic(state, problem)free functionQ6Admissible & consistent heuristic for CornersProblem
food_heuristic(state, problem)free functionQ7Admissible & consistent heuristic for FoodSearchProblem
AnyFoodSearchProblemis_goal_state(state)Q8Return True if position (x, y) contains food
ClosestDotSearchAgentfind_path_to_closest_dot(game_state)Q8Use a search function on AnyFoodSearchProblem to return path to nearest dot

Getting Started

Step 1 — Play the game manually

python main.py

Use the arrow keys to control Pac-Man. This helps you understand the game mechanics.

Step 2 — Try different layouts

python main.py --layout smallMaze
python main.py --layout mediumMaze
python main.py -l bigMaze

Step 3 — Understand SearchProblem

Every search problem implements the abstract class defined in search.py:

class SearchProblem:
    def get_start_state(self):      # Returns the start state
    def is_goal_state(self, state): # Returns True if state is a goal
    def get_successors(self, state):# Returns list of (state, action, step_cost)
    def get_cost_of_actions(self, actions): # Total cost of action sequence

Step 4 — Understand the provided data structures (pacman/util.py)

stack = util.Stack()          # LIFO — use for DFS
queue = util.Queue()          # FIFO — use for BFS
pq    = util.PriorityQueue()  # Min-heap — use for UCS and A*

pq.push(item, priority)
pq.update(item, priority)     # Update priority if lower

# All three support:
data_structure.is_empty()

Homework Tasks (Q1–Q8)

Q1 — 9 pts

Depth-First Search

Implement depth_first_search with graph search in pacman/search.py.

  • Use util.Stack as frontier
  • Track visited states to avoid cycles
  • Return a list of actions
python main.py -l tinyMaze -p SearchAgent
python main.py -l mediumMaze -p SearchAgent
python main.py -l bigMaze -z .5 -p SearchAgent
Q2 — 9 pts

Breadth-First Search

Implement breadth_first_search using util.Queue. Guarantees the shortest path (fewest actions).

  • Also works on the 8-puzzle (no Pac-Man-specific code)
python main.py -l mediumMaze -p SearchAgent -a fn=bfs
python main.py -l bigMaze -p SearchAgent -a fn=bfs -z .5
Q3 — 9 pts

Uniform Cost Search

Implement uniform_cost_search with cumulative-cost priorities. Handles varying step costs unlike BFS.

python main.py -l mediumMaze -p SearchAgent -a fn=ucs
python main.py -l mediumDottedMaze -p StayEastSearchAgent
python main.py -l mediumScaryMaze -p StayWestSearchAgent
Q4 — 9 pts

A* Search

Implement a_star_search using f(n) = g(n) + h(n). When heuristic=null_heuristic it reduces to UCS.

python main.py -l bigMaze -z .5 -p SearchAgent \
  -a fn=astar,heuristic=manhattan_heuristic
Q5 — 9 pts

Corners Problem

Depends on Q2 (BFS)

Define state, goal test, and successor function for all-corners coverage. State must be hashable.

  • Encode position and which corners are visited
  • Use a tuple, not a list
python main.py -l tinyCorners -p SearchAgent \
  -a fn=bfs,prob=CornersProblem
python main.py -l mediumCorners -p SearchAgent \
  -a fn=bfs,prob=CornersProblem
Q6 — 9 pts

Corners Heuristic

Depends on Q4 (A*)

Create a consistent heuristic for CornersProblem. Graded by nodes expanded on mediumCorners:

Nodes expandedPoints
> 20000 / 9
≤ 20003 / 9
≤ 16006 / 9
≤ 12009 / 9
python main.py -l mediumCorners \
  -p AStarCornersAgent -z 0.5
Q7 — 12 pts

Food Heuristic

Depends on Q4 (A*)

Design an admissible/consistent heuristic for FoodSearchProblem. Graded by nodes expanded on trickySearch:

Nodes expandedPoints
> 15 0003 / 12
≤ 15 0006 / 12
≤ 12 0009 / 12
≤ 7 00012 / 12
python main.py -l trickySearch \
  -p AStarFoodSearchAgent
Q8 — 9 pts

Closest Dot Search

Implement AnyFoodSearchProblem.is_goal_state (check self.food[x][y]) and ClosestDotSearchAgent.find_path_to _closest_dot (BFS on AnyFoodSearchProblem).

python main.py -l bigSearch \
  -p ClosestDotSearchAgent -z .5

General Algorithm Hints

Graph Search Pattern (all four algorithms share this skeleton)

1. Initialize the frontier with the start state
2. Initialize an empty set of visited states
3. While the frontier is not empty:
   a. Pop a node from the frontier
   b. If this node is the goal → return the path
   c. If this node has been visited → skip it
   d. Mark this node as visited
   e. For each successor not yet visited → add to frontier
4. Return failure (no path found)

The only difference between the four algorithms is the frontier data structure and priority:

p
AlgorithmFrontierPriority
DFSStackLast in, first out
BFSQueueFirst in, first out
UCSPriorityQueueCumulative path cost g(n)
A*PriorityQueueg(n) + h(n)

Common mistakes to avoid

Testing Your Implementation

Self-check tests (tests/check.py) — 45 tests total

# Run all tests
pytest tests/check.py -v

# Run tests for a specific question
pytest tests/check.py -v -k "q1"
pytest tests/check.py -v -k "q2"
pytest tests/check.py -v -k "q3"
pytest tests/check.py -v -k "q4"
pytest tests/check.py -v -k "q5"
pytest tests/check.py -v -k "q6"
pytest tests/check.py -v -k "q7"
pytest tests/check.py -v -k "q8"

# Run a single test by name
pytest tests/check.py -v -k "test_q1_pacman_tiny_maze"

Official autograder

python grader/autograder.py --no-graphics           # all questions
python grader/autograder.py --no-graphics -q q1      # one question
python grader/autograder.py --no-graphics --mute

What each pytest test checks

Q1–Q4: 6 tests each

TestWhat It Verifies
returns_listFunction returns a list, not None
simple_path / finds_shortest_pathCorrect result on a small graph
graph_backtrack / algorithm-specificAlgorithm-specific behaviour
handles_cyclesVisited set prevents infinite loops
start_is_goalReturns [] when start state is already the goal
pacman_tiny_mazeRuns on an actual Pac-Man maze layout

Q5 (Corners Problem): 5 tests

TestWhat It Verifies
get_start_stateReturns a non-None, hashable state
start_not_goalStart is not a goal (corners not yet visited)
get_successorsValid (state, action, cost) triples with hashable states
tiny_cornersBFS solves tinyCorners in exactly 28 steps (optimal)
successor_updates_cornersState correctly tracks visited corners

Q6 / Q7 heuristics print your grade tier:

tests/check.py::TestQ6_CornersHeuristic::test_q6_medium_corners PASSED
  [Q6] mediumCorners: expanded 1100 nodes → 9/9

tests/check.py::TestQ7_FoodHeuristic::test_q7_tricky_search PASSED
  [Q7] trickySearch: expanded 6800 nodes → 12/12

Useful Command-Line Options

python main.py --help                # Show all options

# Layout selection
python main.py -l smallMaze

# Agent and algorithm selection
python main.py -p SearchAgent -a fn=bfs
python main.py -p SearchAgent -a fn=ucs
python main.py -p SearchAgent -a fn=astar,heuristic=manhattan_heuristic
python main.py -p SearchAgent -a fn=bfs,prob=CornersProblem
python main.py -p AStarCornersAgent
python main.py -p AStarFoodSearchAgent

# Display options
python main.py --frameTime 0         # Instant moves (no animation delay)
python main.py -z 0.5                # Zoom out for large mazes
python main.py -q                    # Quiet mode (no graphics)
python main.py --textGraphics        # Text-only display (no Tkinter needed)

Troubleshooting

"Method not implemented" / SystemExit: 1

You haven't filled in a # TODO section yet. The stub calls util.raise_not_defined(). Replace it with your implementation.

TypeError: unhashable type: 'list'

Your state uses a list somewhere. Convert to a tuple:

# Wrong
state = (position, [True, False, True, False])

# Correct
state = (position, (True, False, True, False))

"Path goes through a wall"

Your get_successors() generates invalid moves. Check self.walls[next_x][next_y] before adding a successor.

Q5 fails even though Q1–Q4 pass

Q5 depends on Q2 (BFS). Verify BFS first:

pytest tests/check.py -v -k "q2"

Q6 / Q7 pass but with a low grade tier

Your heuristic is admissible but not tight enough. Tips:

Tests hang or run forever

Your algorithm has an infinite loop — you are doing tree search instead of graph search. Add a visited set and skip states you've already expanded.

No graphics / Tkinter errors

python main.py -q               # No graphics at all
python main.py --textGraphics   # Text-only display

The pytest tests in tests/check.py never require graphics — they always work headless.

Points Breakdown

QuestionTopicPoints
Q1Depth-First Search9
Q2Breadth-First Search9
Q3Uniform Cost Search9
Q4A* Search9
Q5Corners Problem (state representation)9
Q6Corners Heuristic9
Q7Food Search Heuristic12
Q8Closest Dot Search9
Code Subtotal75
ReportWritten report25
Total100

Academic Integrity