Difficulty: Intermediate | Prerequisites: 2D dynamic arrays, pointers, basic random number generation in C++, loops and conditionals.
This practical asks you to build a complete turn-based game called CHASER on a 2D grid. The point is not the game itself but the skills underneath it: managing a dynamically allocated grid, implementing a game loop with user input, writing movement logic with boundary and collision checks, and giving non-player entities (targets) simple autonomous behaviour. These are foundational patterns in simulation and game programming. If you understand 2D dynamic arrays from the companion notes, you have the data structure; this document covers what you do with it.
The player moves around a grid trying to catch all targets before running out of energy. Every turn costs 1 energy; catching a target gives 10 back. Targets move randomly on their own (50% chance per turn, including diagonals). The game loop repeats until all targets are caught (win) or energy hits zero (loss).
Player (P)
The human-controlled character on the grid. Starts at a random empty cell in the first column. Moves one cell per turn in a cardinal direction (up, down, left, right). Cannot move diagonally.
Target (T)
An entity the player must catch ("arrest"). When the player moves onto a target's cell, the target disappears and the player gains 10 energy. Targets have autonomous movement each turn.
Immovable obstacle (#)
A static cell that neither the player nor targets can enter. Placed randomly during initialisation. Does not move or change for the entire game.
Energy
The player's resource. Starts at a user-defined value (greater than 1). Decreases by 1 every turn, regardless of whether the move succeeded or was blocked. Increases by 10 when a target is arrested. Reaching zero ends the game in defeat.
Arrest
The act of catching a target. Occurs when the player moves onto a cell containing a target. The target is removed from the grid and replaced by the player.
Turn
One cycle of the game loop: the player chooses a direction, the move is validated and executed, energy is updated, then each surviving target has a chance to move.
Game loop
The repeating structure that drives the simulation: display the grid, prompt for input, process the player's move, process target movement, check win/loss conditions, repeat.
The overall structure follows a standard simulation loop:
Display the current grid and energy.
Prompt the player for a direction (W/S/A/D) or Q to quit.
Validate and execute the player's move.
Deduct 1 energy.
If the player landed on a target, remove the target and add 10 energy.
Move each surviving target (50% chance each).
Check end conditions: energy at zero (defeat) or no targets remaining (victory).
Repeat from step 1.
When the player presses a direction key, the program computes the destination cell:
W (up): newRow = currentRow - 1, same column.
S (down): newRow = currentRow + 1, same column.
A (left): same row, newCol = currentCol - 1.
D (right): same row, newCol = currentCol + 1.
Before moving, three checks run in order:
Boundary check: Is the destination within the grid (0 to rows-1, 0 to cols-1)? If not, the move is rejected. Energy is still deducted.
Obstacle check: Is the destination an obstacle (#)? If so, the move is rejected. Energy is still deducted.
Target check: Is the destination a target (T)? If so, the player moves there, the target is removed, and energy increases by 10.
If the destination is empty space, the player moves normally. In all cases (even a blocked move), energy drops by 1. This is a key design point: attempted moves into walls or obstacles still cost energy.
Each turn, every surviving target independently:
Has a 50% chance of attempting to move. If the random check fails, the target stays put.
If it does move, it picks a random cell from its eight neighbours (including diagonals).
The chosen cell must be empty space (.). A target cannot move into the player, an obstacle, or another target.
If no valid neighbouring cell is available, or the randomly chosen cell is occupied, the target stays.
Targets move after the player has moved, within the same turn.
The grid must be populated in this specific sequence:
Fill every cell with the empty marker (space/dot).
Scatter obstacles randomly. Each obstacle must land on an empty cell.
Scatter targets randomly. Each target must land on an empty cell (not on an obstacle or another target).
Place the player at a random row in column 0, on an empty cell.
This order guarantees no two items share a cell. Each placement uses a loop that generates random coordinates until it finds an empty cell.
Victory: The player catches every target. The target count reaches zero.
Defeat: The player's energy reaches zero (or below).
The game also ends if the player presses Q (quit), which counts as neither victory nor defeat.
The grid is printed to the console each turn, showing all cells. Below the grid, the current energy is displayed. The player is prompted with the direction keys (W, S, A, D) and Q for quit. Input should be case-insensitive for usability, though the spec shows uppercase.
Energy update per turn:
energy = energy - 1
if (destination == target):
energy = energy + 10
targetCount = targetCount - 1Player move validation pseudocode:
compute (newRow, newCol) from direction
if newRow < 0 or newRow >= rows or newCol < 0 or newCol >= cols:
reject move (out of bounds)
else if grid[newRow][newCol] == '#':
reject move (obstacle)
else:
if grid[newRow][newCol] == 'T':
add 10 to energy
decrement target count
grid[oldRow][oldCol] = '.'
grid[newRow][newCol] = 'P'
update player position
always: energy = energy - 1Target movement pseudocode:
for each target:
if random(0,1) < 0.5:
skip (target stays)
else:
pick random dx in {-1, 0, 1}
pick random dy in {-1, 0, 1}
newR = targetRow + dx
newC = targetCol + dy
if (newR, newC) is in bounds AND grid[newR][newC] == '.':
grid[targetRow][targetCol] = '.'
grid[newR][newC] = 'T'
update target positionTurn-based grid simulations are the foundation of roguelike games, chess engines, and cellular automata (like Conway's Game of Life). The pattern of validating a move against boundaries and obstacles before executing it appears in robotics path planning and warehouse automation, where a robot must check for walls and other robots before moving to the next cell.
Students often forget that energy is deducted even when a move is blocked (by a boundary or obstacle). The spec is explicit: every turn costs 1 energy, regardless of outcome.
Some students let targets move before the player. The correct order is player first, then targets.
A common error is allowing targets to move into occupied cells (other targets, obstacles, or the player). Targets may only move into empty space.
Students sometimes place the player at a fixed position like (0, 0) instead of a random row in column 0. The row must be random and the cell must be empty.
Another mistake is not tracking targets separately from the grid. You need a way to iterate over all surviving targets each turn, which means storing their positions in a list or scanning the grid for 'T' characters.
⚠️ Algorithm implementation carries 20 marks, the single largest category. Your movement logic, target AI, and game loop must all work correctly.
⚠️ Program design (movement logic) carries 10 marks and requires a UML Activity Diagram. The diagram must model the player's movement decision tree: input, boundary check, obstacle check, target check, energy update.
⚠️ Initialisation carries 10 marks. The population order (space, obstacles, targets, player) must be followed exactly.
⚠️ User interaction carries 10 marks. The grid must display correctly, energy must be shown, and the input prompt must work.
⚠️ The bonus section (C++20 modules, 20 marks) is submitted separately. If you attempt it, submit it as a separate file with BONUS in the name.
True or false: The player can move diagonally. (False. Only up, down, left, right.)
Fill in the blank: When the player catches a target, energy increases by ______ units. (10.)
True or false: If the player tries to move into an obstacle, no energy is lost. (False. Energy is lost on every turn, even failed moves.)
Fill in the blank: Each target has a ______% chance of moving each turn. (50%.)
True or false: Targets can move diagonally. (True. Targets can move into any of their eight neighbouring cells.)
Q: Describe the complete sequence of events in one turn of the CHASER game.
A: The grid and energy are displayed. The player is prompted for a direction. The move is validated (boundary, obstacle, target checks). Energy is decremented by 1. If the player landed on a target, the target is removed and energy increases by 10. Then each surviving target rolls a 50% chance to move; those that move pick a random neighbour and move there if it is empty space. Finally, the win/loss conditions are checked.
Q: Why does the player lose energy even when moving into an obstacle?
A: The spec defines energy loss as per-turn, not per-successful-move. An attempted move (including one blocked by an obstacle or boundary) still counts as a turn. This adds strategic pressure: bumping into walls wastes energy.
Q: A 10x10 grid has 15 obstacles and 5 targets. The player is in cell (7, 0). A target is at (6, 0). The player presses W. Describe what happens.
A: The destination is (6, 0). The boundary check passes (row 6 is valid). The obstacle check passes (cell contains T, not #). The target check triggers: the target at (6, 0) is removed, the player moves to (6, 0), energy decreases by 1 and increases by 10 (net +9), and the target count drops by 1.
Q: Can a target move into the cell the player just vacated?
A: Yes, if the player has moved out of that cell during this turn and it is now empty space (.), a target can move into it during the target movement phase.
Q: What data structure would you use to track target positions, and why?
A: A dynamic array (or vector) of coordinate pairs. This allows you to iterate over all surviving targets each turn without scanning the entire grid. When a target is arrested, you remove its entry from the list.
The game loop pattern here is the same structure used in event-driven programming and real-time systems: read input, update state, render output. Target movement introduces basic AI concepts (randomised decision-making and collision avoidance) that scale up into agent-based modelling in later courses. The boundary-checking logic is directly reusable in image processing, where you must avoid reading pixels outside the image dimensions.
turn-based game C++, grid simulation, game loop C++, player movement logic, target AI, autonomous movement, boundary checking, collision detection grid, energy system game, win lose conditions, CHASER game, CSC01A1 Practical 8, ChaseSpace namespace, UML activity diagram game, 2D array game board, random movement C++, command-line arguments game, arrest target, roguelike movement