MineSweeper Game In Python
Introduction
Minesweeper is a classic single-player puzzle game that challenges players with a grid-based minefield. The objective is to uncover all non-mine squares without detonating any mines. Here’s how it unfolds:
The game begins with a grid of squares, each initially hidden. Mines are randomly scattered throughout the grid, and the number of mines is predetermined based on the difficulty level selected (Beginner, Intermediate, Expert, or Custom).
The game’s strategy revolves around using these numbered clues to deduce the locations of mines. For instance, if a square shows “3”, it means there are three mines in its surrounding squares. Players can strategically deduce and mark suspected mine locations by flagging them (typically by right-clicking or using a flag icon). This helps avoid accidental clicks on potentially dangerous squares.
To win the game, players must uncover all safe squares on the grid without triggering any mines. It requires careful logic, deduction, and sometimes a bit of luck to navigate the minefield successfully.
Minesweeper is known for its addictive nature and the mental challenge it offers in terms of logical thinking and strategic planning. Despite its seemingly simple rules, mastering Minesweeper can take time and practice, making it a timeless puzzle game enjoyed by players of all ages.
Coding a Minesweeper in Python
Creating a Minesweeper game in Python is an excellent way to explore game development and improve your programming skills. Minesweeper, a classic puzzle game, challenges players to uncover cells on a grid without detonating hidden mines. Coding this game involves implementing key features such as grid management, mine placement, and user interaction.
By using libraries like Pygame, you can create a graphical interface that enhances the user experience with visually engaging elements. This project not only reinforces fundamental programming concepts but also introduces you to more advanced topics like event handling and game state management, making it a valuable exercise in both logic and creativity.
What You Need To Know
To effectively code a Minesweeper game in Python, you need a solid understanding of several key concepts. Firstly, familiarity with Python’s basic syntax and control structures is essential, including loops, conditionals, and functions. Understanding data structures such as lists and dictionaries is crucial for managing the game’s grid and storing information about mines and revealed cells.
Knowledge of graphical libraries like Pygame can significantly enhance your project by allowing you to create an interactive and visually appealing game interface. You’ll need to grasp how to handle user inputs, draw game elements on the screen, and update the display based on player actions.
Moreover, implementing game logic involves creating algorithms for mine placement, calculating adjacent mines for each cell, and determining win/loss conditions. You’ll also need to handle edge cases, such as what happens when a cell with no adjacent mines is revealed.
Additionally, understanding event-driven programming will help you manage user interactions, such as mouse clicks for cell selection and right-clicks for flagging potential mines.
By mastering these concepts, you’ll be able to create a fully functional Minesweeper game that is both engaging and robust.
Minesweeper With Python (Full Code)
import pygame # Import the pygame module
import sys # Import the sys module
import random # Import the random module
# Initialize Pygame
pygame.init()
# Set up the screen
WIDTH, HEIGHT = 1000, 600 # Set the dimensions of the game screen
screen = pygame.display.set_mode((WIDTH, HEIGHT)) # Create a window with the specified dimensions
pygame.display.set_caption("Minesweeper") # Set the title of the window
# Define colors
GRAY = (192, 192, 192) # Define the color gray
WHITE = (255, 255, 255) # Define the color white
BLACK = (0, 0, 0) # Define the color black
RED = (255, 0, 0) # Define the color red
# Define game variables
GRID_SIZE = 10 # Define the size of the game grid
CELL_SIZE = 50 # Define the size of each cell in the grid
MINE_COUNT = 10 # Define the number of mines in the grid
EXPLOSION_RADIUS = CELL_SIZE // 2 # Define the radius of the explosion
# Define fonts
font = pygame.font.Font(None, 36) # Create a font object with size 36
# Load explosion image
explosion_img = pygame.image.load("explosion.png") # Load the explosion image
explosion_img = pygame.transform.scale(explosion_img, (EXPLOSION_RADIUS * 2, EXPLOSION_RADIUS * 2)) # Scale the image
# Function to create the game grid
def create_grid():
# Create an empty grid with zeros
grid = [[0 for _ in range(GRID_SIZE)] for _ in range(GRID_SIZE)]
# Place mines randomly on the grid
mines = random.sample(range(GRID_SIZE * GRID_SIZE), MINE_COUNT)
for mine in mines:
row = mine // GRID_SIZE
col = mine % GRID_SIZE
grid[row][col] = -1 # Mark the cell as a mine
# Increment the count of adjacent cells
for dr in [-1, 0, 1]:
for dc in [-1, 0, 1]:
if 0 <= row + dr < GRID_SIZE and 0 <= col + dc < GRID_SIZE and grid[row + dr][col + dc] != -1:
grid[row + dr][col + dc] += 1
return grid
# Function to reveal cell
def reveal_cell(row, col, grid, revealed):
# Check if the cell is within the grid and has not been revealed
if not 0 <= row < GRID_SIZE or not 0 <= col < GRID_SIZE or revealed[row][col]:
return
revealed[row][col] = True # Mark the cell as revealed
if grid[row][col] == 0:
# If the cell is empty, recursively reveal adjacent cells
for dr in [-1, 0, 1]:
for dc in [-1, 0, 1]:
reveal_cell(row + dr, col + dc, grid, revealed)
# Main game function
def main():
# Create the game grid and initialize revealed cells
grid = create_grid()
revealed = [[False for _ in range(GRID_SIZE)] for _ in range(GRID_SIZE)]
game_over = False
restart_button = pygame.Rect(WIDTH - 180, HEIGHT // 2 + 50, 140, 40) # Define the restart button
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
col = mouse_pos[0] // CELL_SIZE
row = mouse_pos[1] // CELL_SIZE
if not revealed[row][col] and not game_over:
reveal_cell(row, col, grid, revealed)
if grid[row][col] == -1:
game_over = True
# Check if the restart button is clicked
if game_over and restart_button.collidepoint(mouse_pos):
return
# Draw everything
screen.fill(GRAY) # Fill the screen with gray color
for row in range(GRID_SIZE):
for col in range(GRID_SIZE):
cell_rect = pygame.Rect(col * CELL_SIZE, row * CELL_SIZE, CELL_SIZE, CELL_SIZE)
pygame.draw.rect(screen, BLACK, cell_rect, 2) # Draw cell border
if revealed[row][col]:
if grid[row][col] == -1:
screen.blit(explosion_img, cell_rect.topleft) # Draw explosion
else:
text = font.render(str(grid[row][col]), True, BLACK)
text_rect = text.get_rect(center=cell_rect.center)
screen.blit(text, text_rect) # Draw adjacent mine count
# Draw game information
pygame.draw.rect(screen, WHITE, pygame.Rect(WIDTH - 200, 0, 200, HEIGHT))
info_text = font.render("Minesweeper", True, BLACK)
screen.blit(info_text, (WIDTH - 150, 50))
mine_count_text = font.render("Mines: " + str(MINE_COUNT), True, BLACK)
screen.blit(mine_count_text, (WIDTH - 150, 100))
# Draw restart button if the game is over
if game_over:
pygame.draw.rect(screen, BLACK, restart_button)
restart_text = font.render("Restart", True, WHITE)
restart_text_rect = restart_text.get_rect(center=restart_button.center)
screen.blit(restart_text, restart_text_rect)
pygame.display.flip() # Update the display
# Run the game
if __name__ == "__main__":
main()
Main Algorithms To Understand
Creating the Game Grid:
The create_grid
function sets up a Minesweeper game grid by creating a two-dimensional list where each cell is represented as a dictionary.
def create_grid(rows, cols):
"""
Create a Minesweeper game grid.
Parameters:
rows (int): Number of rows in the grid.
cols (int): Number of columns in the grid.
Returns:
list: A 2D list representing the game grid.
"""
grid = []
for row in range(rows):
row_list = []
for col in range(cols):
# Each tile is represented as a dictionary
# 'is_mine': False means the tile is not a mine
# 'revealed': False means the tile is covered
# 'flagged': False means the tile is not flagged
# 'neighbors': Number of neighboring mines
tile = {'is_mine': False, 'revealed': False, 'flagged': False, 'neighbors': 0}
row_list.append(tile)
grid.append(row_list)
return grid
# Example usage
rows = 10
cols = 10
grid = create_grid(rows, cols)
Each dictionary tracks whether the cell contains a mine ('is_mine'
), if it has been revealed ('revealed'
), if it is flagged ('flagged'
), and the number of neighboring mines ('neighbors'
). This initialization allows for managing the game state efficiently, enabling features like mine placement, cell revealing, and flagging. The grid is constructed based on the specified number of rows and columns, providing a structured framework for the game’s logic and interactions.
Reveal The Cell:
The reveal_cell
function uncovers the contents of a specified cell in a Minesweeper grid. It takes the grid and the cell’s row and column indices as parameters.
def reveal_cell(grid, row, col):
"""
Reveal the contents of a cell in the grid.
Parameters:
grid (list): The Minesweeper game grid.
row (int): The row index of the cell to reveal.
col (int): The column index of the cell to reveal.
Returns:
bool: True if the cell contains a mine, False otherwise.
"""
# Check if the cell is valid (inside the grid)
if 0 <= row < len(grid) and 0 <= col < len(grid[0]):
cell = grid[row][col]
# Check if the cell is already revealed or flagged
if not cell['revealed'] and not cell['flagged']:
# Mark the cell as revealed
cell['revealed'] = True
# If the cell is not a mine and has no neighboring mines, recursively reveal its neighbors
if not cell['is_mine'] and cell['neighbors'] == 0:
for i in range(-1, 2):
for j in range(-1, 2):
reveal_cell(grid, row + i, col + j)
return cell['is_mine']
return False
# Example usage
# Assuming the grid is already created
# Let's reveal the cell at row 3, column 4
revealed_mine = reveal_cell(grid, 3, 4)
The function first checks if the cell is within the grid’s bounds and not already revealed or flagged. If valid, it marks the cell as revealed. If the cell is not a mine and has no neighboring mines, it recursively reveals adjacent cells to ensure that the entire empty region is exposed.
The function returns True
if the revealed cell contains a mine and False
otherwise. This recursive approach helps to efficiently manage the game’s exploration process, particularly in uncovering large areas of safe cells.
Main Game Function:
The play_minesweeper
function orchestrates the Minesweeper game’s core mechanics, guiding the game’s progression from start to finish.
def play_minesweeper():
# Initialize the game grid
grid = initialize_grid()
# Main game loop
while True:
# Display game UI
render_grid(grid)
# Get player action (click or flag)
action = get_player_action()
# Handle player action
if action == "click":
row, col = get_click_position()
if grid[row][col]['is_mine']:
# Player clicked on a mine, game over
game_over()
break
else:
reveal_cell(grid, row, col)
if check_win(grid):
# All non-mine cells revealed, player wins
game_win()
break
elif action == "flag":
row, col = get_click_position()
flag_cell(grid, row, col)
# Check for win or loss conditions
if check_loss(grid):
game_over()
break
Initially, it sets up the game grid using initialize_grid()
, which prepares the board with mines and calculates adjacent mine counts. The main loop then handles the gameplay: it updates the user interface with render_grid()
, processes player inputs through get_player_action()
, and performs actions based on player choices.
If the player chooses to click, the function retrieves the cell’s coordinates with get_click_position()
and checks if the cell contains a mine. If it does, the game ends with game_over()
. If not, reveal_cell()
is called to uncover the cell, and check_win()
evaluates whether all non-mine cells have been revealed to declare a win.
If the player opts to flag a cell, flag_cell()
is used to mark it. The game periodically checks for loss conditions with check_loss()
to ensure the game ends if a mine is accidentally clicked. This function encapsulates the essential gameplay loop, ensuring the Minesweeper game runs smoothly and responds to player interactions effectively.
Initialize Grid:
The initialize_grid
function sets up the Minesweeper game grid, placing mines and calculating the number of neighboring mines for each cell.
def initialize_grid(rows, cols, num_mines):
grid = [[{'is_mine': False, 'revealed': False, 'neighbors': 0} for _ in range(cols)] for _ in range(rows)]
# Randomly place mines
for _ in range(num_mines):
row, col = random.randint(0, rows-1), random.randint(0, cols-1)
while grid[row][col]['is_mine']:
row, col = random.randint(0, rows-1), random.randint(0, cols-1)
grid[row][col]['is_mine'] = True
# Update neighboring cells
for dr in range(-1, 2):
for dc in range(-1, 2):
if (dr != 0 or dc != 0) and 0 <= row+dr < rows and 0 <= col+dc < cols:
grid[row+dr][col+dc]['neighbors'] += 1
return grid
It places a specified number of mines randomly across the grid while ensuring no cell contains more than one mine. After placing each mine, the function updates the neighbor count for adjacent cells to reflect how many nearby mines they have. This setup involves careful boundary checking to prevent errors and ensures that the grid is properly prepared for gameplay, providing an accurate and functional game environment.
Check Winner and Loser:
The check_win
function evaluates whether the player has won the game by verifying if all non-mine cells have been revealed.
def check_win(grid):
for row in grid:
for cell in row:
if not cell['is_mine'] and not cell['revealed']:
return False
return True
def check_loss(grid):
for row in grid:
for cell in row:
if cell['is_mine'] and cell['revealed']:
return True
return False
It iterates through each cell in the grid, returning False
if any non-mine cell remains covered. Conversely, the check_loss
function determines if the player has lost by checking if any mines have been revealed. It scans the grid for any cells marked as mines that are also revealed, returning True
if such a condition is met. Both functions are crucial for managing the game’s end conditions and providing appropriate feedback to the player.
Summary
The Minesweeper game we’ve developed demonstrates a comprehensive approach to game programming with Python. By integrating grid initialization, cell revealing, and user actions, we’ve created a fully functional game that challenges players with strategic thinking and careful planning.
The key functions—such as initialize_grid
, reveal_cell
, and play_minesweeper
—work together to manage the game’s state, handle player interactions, and determine win or loss conditions. This project not only illustrates the practical application of game logic and algorithms but also provides a foundation for further enhancements and customizations. Overall, the completed Minesweeper game serves as an excellent example of how programming skills can be applied to create engaging and interactive software experiences.
Post Comment