Introduction:
Mazes have intrigued puzzle enthusiasts and software developers for an extended period; the task of maneuvering through an intricate grid, maneuvering around obstacles, and ultimately achieving the objective has remained a pursuit that transcends time. Within this guide, we will explore the process of developing a maze traversal game in C++, enabling our users to direct the ball (referred to as an object) through a maze.
Problem Statement:
The task at hand involves developing a text-based maze game in C++. In this game, a player maneuvers a ball (symbolizing an object) through the maze by utilizing directional controls such as up, down, left, and right. The maze itself is pre-defined and is represented by a 2D array. Within this array, '#' denotes walls, while empty spaces are represented by blank spaces (' '). Unlike typical 2D arrays in C++ that use '0' and other numbers to indicate values, our array solely uses ' ' to signify empty spaces. The primary objective of the game is to guide the ball from its initial position to a designated goal location while avoiding collisions with walls.
Given a 2D matrix maze with dimensions M x N, a single ball is released in each column. The maze is designed with open top and bottom walls, featuring diagonal walls in each cell that can redirect the ball to the left or right side. The "R" symbol is used to indicate a cell that diverts the ball to the right, covering the top-left to bottom-right corners. On the other hand, the "L" symbol denotes a cell that redirects the ball to the left, spanning the top-right to bottom-left corners. Depending on the maze's design, the ball can either exit through the bottom or remain trapped within the maze. A ball is considered trapped if it encounters both "R" and "L" cells on the sides or if it gets redirected into a wall. Upon releasing a ball from the ith column at the top, the function should return an array called answer with N elements, where answer[i] represents the column where the ball exits at the bottom, or -1 if the ball gets trapped within the maze.
Program 1:
Let's consider an illustration to demonstrate the movement of spheres within a labyrinth using C++.
#include <iostream>
#include <unistd.h> // For sleep() function (for Unix/Linux)
using namespace std;
const int ROWS = 10;
const int COLS = 10;
char maze[ROWS][COLS] = {
{'#', '#', '#', '#', '#', '#', '#', '#', '#', '#'},
{'#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#'},
{'#', ' ', '#', '#', '#', ' ', '#', '#', ' ', '#'},
{'#', ' ', '#', ' ', '#', ' ', ' ', '#', ' ', '#'},
{'#', ' ', '#', ' ', '#', '#', '#', '#', ' ', '#'},
{'#', ' ', ' ', ' ', ' ', ' ', ' ', '#', ' ', '#'},
{'#', '#', '#', '#', '#', ' ', '#', '#', ' ', '#'},
{'#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#'},
{'#', ' ', '#', '#', '#', '#', '#', '#', '#', '#'},
{'#', '#', '#', '#', '#', '#', '#', '#', '#', '#'}
};
struct Position {
int x;
int y;
};
Position ballPos;
void displayMaze() {
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
cout << maze[i][j];
}
cout << endl;
}
}
bool isWall(int x, int y) {
return maze[y][x] == '#';
}
void moveBall(char direction) {
int newX = ballPos.x;
int newY = ballPos.y;
switch (direction) {
case 'w':
newY--;
break;
case 's':
newY++;
break;
case 'a':
newX--;
break;
case 'd':
newX++;
break;
}
if (!isWall(newX, newY)) {
maze[ballPos.y][ballPos.x] = ' ';
ballPos.x = newX;
ballPos.y = newY;
maze[ballPos.y][ballPos.x] = 'O';
}
}
int main() {
// Initialize ball position
ballPos.x = 1;
ballPos.y = 1;
maze[ballPos.y][ballPos.x] = 'O';
char input;
displayMaze();
while (true) {
cout << "Enter direction (w/a/s/d): ";
cin >> input;
moveBall(input);
system("clear"); // Clear screen on Unix/Linux
displayMaze();
sleep(1); // Wait for 1 second to slow down the movement (optional)
}
return 0;
}
Output:
##########
# #
# ### ## #
# # # # #
# # #### #
# # #
##### ## #
# #
#O########
##########
Enter direction (w/a/s/d): d
Note: The output is the continuous display of the maze grid with the ball's updated position after each movement. The given output is a sample one.
Explanation:
- Header Files:
- #include <iostream>: This header file is included for input/output operations.
- #include <conio.h>: This header file is specific to Windows and is used for console input. It provides the _getch function, which reads a single character from the console without echoing it.
- using namespace std;: This line enable us to utilize standard C++ functions and objects without prefixing them with std::.
- Constants:
- const int ROWS = 10; and const int COLS = 10;: These constants define the dimensions of the maze grid.
- Maze Grid:
- The maze array represents the maze grid. It is a 2D array of characters where '#' represents walls, ' ' represents empty spaces, and 'O' represents the ball's current position.
- The maze is initialized with predefined characters to form a simple maze layout.
- Position Struct:
- The Position struct represents the position of the ball in the maze. It contains x and y coordinates to track the ball's position.
- Display Maze Function (displayMaze):
- This function iterates over each cell of the maze grid and prints its content to the console.
- isWall Function:
- The isWall function checks if a given position (x, y) in the maze grid contains a wall ('#'). If it's a wall, it returns true; otherwise, it returns false.
- Move Ball Function (moveBall):
- This function moves the ball in the maze based on user input.
- It takes a direction ('w' for up, 's' for down, 'a' for left, 'd' for right) as input and updates the ball's position accordingly.
- If the new position after moving is not a wall, the ball's position is updated, and the maze grid is updated to reflect the new position of the ball.
- Main Function:
- The main function initializes the ball's starting position ( x = 1 and ballPos.y = 1 ) and updates the maze grid accordingly.
- After that, it enters an infinite loop where it continuously listens for user input using the _getch
- Based on the input character received, it calls the moveBall function to move the ball in the maze.
- After each move, it clears the console screen using system("cls") (for Windows) and redisplays the updated maze using the displayMaze
- User Input:
- The user can control the movement of the ball using the arrow keys or other designated keys for up, down, left, and right movements.
Time and Space complexities:
Time Complexity:
- This displayMaze function iterates over each cell of the maze grid exactly once to print its content. As there are ROWS COLS cells in the grid, the time complexity of this function is O(ROWS COLS) .
- The moveBall function updates the position of the ball based on the given direction.
- It performs a constant-time operation for updating the ball's position and checking whether the new position is a wall (isWall function) .
- Therefore, the time complexity of this function is O(1) .
- Overall, the time complexity of the provided code is O(ROWS * COLS) for initializing and displaying the maze, plus a linear time complexity for user interaction with the ball movement.
Space Complexity:
- The maze grid is a 2D array of characters with dimensions ROWS * COLS.
- Therefore, the space complexity of the maze grid is O(ROWS * COLS).
- The ballPos struct stores the position of the ball (x, y coordinates).
- It requires constant space, O(1) .
- The variable input stores the user input character for ball movement.
- It requires constant space, O(1).
Approach 2: Grids Optimization Problem
This is a scenario involving the Grids optimization issue, which can be resolved using dynamic programming by iterating through each ball individually. Additionally, a dp array is upheld where dpi holds the ultimate column index when the ball is present in cell (i, j).
We have the option to employ a recursive method to determine the column where the ball will drop and predict its movement upon reaching that cell by evaluating the criteria for the ball's movement to the right and left directions.
If the current cell contains an "R" and is not located in the last position of the row, the ball will shift towards the right; moreover, the adjacent cell on the right must not hold an "L." In case the current cell contains an "L" and is not positioned as the first cell in the row, the ball will shift towards the left, and the adjacent cell on the left should not contain an "R."
Program 2:
#include <iostream>
#include <vector>
using namespace std;
// Function to find the final column for a ball in cell (rowNo, colNo)
int findColumn(vector<vector<char>>& maze, int M, int N, vector<vector<int>>& dp, int rowNo, int colNo) {
// Base case
if (rowNo >= M)
return colNo;
// Check if we already know the final column for this cell (rowNo, colNo)
if (dp[rowNo][colNo] != -2)
return dp[rowNo][colNo];
int nextCol = -1;
// Check if the ball can move to the right
if (maze[rowNo][colNo] == 'R' && colNo + 1 < N && maze[rowNo][colNo + 1] != 'L') {
nextCol = findColumn(maze, M, N, dp, rowNo + 1, colNo + 1);
}
// Check if the ball can move to the left
if (maze[rowNo][colNo] == 'L' && colNo - 1 >= 0 && maze[rowNo][colNo - 1] != 'R') {
nextCol = findColumn(maze, M, N, dp, rowNo + 1, colNo - 1);
}
// Memoize the result and return the final column
return dp[rowNo][colNo] = nextCol;
}
// Function to find the final positions of the ball for each starting column
vector<int> solve(vector<vector<char>>& maze) {
int M = maze.size(), N = maze[0].size();
// Ans vector to store the final positions
vector<int> ans(N, 0);
// dp[][] to store the column that the ball falls out of at the bottom
vector<vector<int>> dp(M, vector<int>(N, -2));
// Iterate through each starting column and find the final position of the ball
for (int i = 0; i < N; i++) {
ans[i] = findColumn(maze, M, N, dp, 0, i);
}
return ans;
}
int main() {
// Input
vector<vector<char>> maze = {
{'R', 'R', 'R', 'L', 'L'},
{'R', 'R', 'R', 'R', 'L'},
{'L', 'L', 'L', 'R', 'R'},
{'R', 'R', 'R', 'L', 'L'},
{'L', 'L', 'L', 'L', 'L'}
};
vector<int> ans = solve(maze);
// Output
for (int i = 0; i < ans.size(); i++)
cout << ans[i] << " ";
cout << endl;
return 0;
}
Output:
1 2 -1 -1 -1
Explanation:
- Header File:
We include certain libraries needed in our program, like iostream (for input and output operations) or vector(s) for using vectors.
- Namespace: We're using std namespace for our convenience. We can get anything related to standard containers and algorithms without writing out std::.
- Function Definitions :
- findColumn: The function uses recursion to locate the final column behind a ball in a particular point (rowNo,colNo) of cells from m cost. There are some parameters for this function, such as a maze, size of maze m and n, a memoization array dp, and the current position (rowNo, colNo) of the ball.
- solve: In this function move through every starting column, and then by column get the ball's ending position. Initialize the ans vecto as our last position and array dp for intermediate results to be remembered.
- Main Function:
- In the main function, we initialize the maze with the provided input values, where 'R' represents the right direction and 'L' represents the left direction.
- After that, we call the solve function to obtain the final positions of the ball for each starting column.
- Finally, we output the final positions of the ball.
- Input:
- The input is a 2-dimensional vector of characters, each character representing which direction can be taken by that cell in order to come out of the bottom row (either 'r' for right or 'l' for left).
- Output:
- The output contains a set of integers in a vector representing the final column where the ball falls out of the bottom for each starting column.
- Temporal Complexity: O(N * M) , where M represents the total rows, and N denotes the total columns within the provided matrix maze.
- Additional Space Usage: O(N * M)
Conclusion:
In summary, this charming rolling ball labyrinth game was developed and executed using C++. Players have the ability to manipulate the maze by tilting or rotating it (90 degrees) while playing, simply by dragging the left mouse button. This adds an extra layer of difficulty as you navigate your ball through the maze, factoring in realistic physics and gravitational forces.
The implementation of the rolling ball maze game imparts crucial concepts regarding game creation, detecting collisions, managing user inputs, generating mazes, rendering graphics, and more. Above all, it enhances the player's spatial cognition, problem-solving abilities, and reflexes within an enjoyable framework.