How to resolve the algorithm Minesweeper game step by step in the Ruby programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm Minesweeper game step by step in the Ruby programming language

Table of Contents

Problem Statement

There is an n by m grid that has a random number (between 10% to 20% of the total number of tiles, though older implementations may use 20%..60% instead) of randomly placed mines that need to be found. Positions in the grid are modified by entering their coordinates where the first coordinate is horizontal in the grid and the second vertical. The top left of the grid is position 1,1; the bottom right is at n,m. The Task is to create a program that allows you to play minesweeper on a 6 by 4 grid, and that assumes all user input is formatted correctly and so checking inputs for correct form may be omitted. You may also omit all GUI parts of the task and work using text input and output. Note: Changes may be made to the method of clearing mines to more closely follow a particular implementation of the game so long as such differences and the implementation that they more accurately follow are described. C.F: wp:Minesweeper (computer game)

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Minesweeper game step by step in the Ruby programming language

The provided Ruby code is a text-based implementation of the classic Minesweeper game. Here's a detailed explanation of the code:

  1. Game Introduction:

    • The program starts by displaying the rules of Minesweeper:
      • A grid is randomly populated with mines, and the player's goal is to uncover all non-mine cells.
      • The player can mark suspected mine positions or clear non-mine cells.
      • Clearing a non-mine cell may recursively uncover adjacent non-mine cells.
  2. Game Constants:

    • The game is played on a fixed-size grid: WIDTH = 6 (horizontal cells) and HEIGHT = 4 (vertical cells).
    • The percentage of cells that are mines is defined by PCT = 0.15, resulting in NUM_MINES = 3 mines in this grid size.
  3. Grid Representation:

    • Two data structures represent the game state:
      • $mines: A 2D array that stores the true positions of mines on the grid. Initially, it's set to nil until the player starts uncovering cells.
      • $screen: A 2D array that represents the player's view of the grid. Initially, all cells are obscured with a single dot (.).
  4. Game Initialization:

    • The game starts with a welcome message and an empty grid (show_grid).
  5. Input Handling:

    • The game loop continuously prompts the player for input.
    • The player can enter one of the following commands:
      • m <x> <y>: Toggle a mine mark at position <x>, <y>
      • c <x> <y>: Clear the cell at position <x>, <y>
      • p: Print the current state of the grid
      • quit, exit, x, q: Quit the game
  6. Mine Placement:

    • When the player starts uncovering cells, the create_mines method is called to randomly place the NUM_MINES mines on the grid, avoiding the initial position entered by the player.
  7. Marking Cells:

    • The m <x> <y> command toggles the mark status of the cell at position <x>, <y> between a dot (. - unmarked) and a question mark (? - marked).
  8. Clearing Cells:

    • The c <x> <y> command triggers the following actions:
      • It checks if the cell at position <x>, <y> is a mine. If it is, the player loses, and the mines are revealed.
      • If the cell is not a mine, the clear_space method is called to recursively uncover adjacent non-mine cells.
      • The clear_space method counts the number of surrounding mines and updates the cell with the count or a space ( ) if there are no surrounding mines.
  9. Victory Check:

    • The victory? method checks if the player has correctly identified all mines. If all mines are marked and no clear cells remain unmarked, the player wins.
  10. Quitting the Game:

  • The player can quit the game by entering any of the following commands: quit, exit, x, or q.
  1. Main Game Loop:
  • The game loop continues until the player quits or wins.
  • Each iteration of the loop prompts the player for input, validates it, and performs the corresponding action.

Source code in the ruby programming language

puts <<EOS
    Minesweeper game.

    There is an n by m grid that has a random number of between 20% to 60%
    of randomly hidden mines that need to be found. 

    Positions in the grid are modified by entering their coordinates
    where the first coordinate is horizontal in the grid and the second
    vertical. The top left of the grid is position 1,1; the bottom right is
    at n,m.

    * The total number of mines to be found is shown at the beginning of the
    game.
    * Each mine occupies a single grid point, and its position is initially
    unknown to the player
    * The grid is shown as a rectangle of characters between moves.
    * You are initially shown all grids as obscured, by a single dot '.'
    * You may mark what you think is the position of a mine which will show
    as a '?'
    * You can mark what you think is free space by entering its coordinates.
    :*  If the point is free space then it is cleared, as are any adjacent
    points that are also free space- this is repeated recursively for
    subsequent adjacent free points unless that point is marked as a mine
    or is a mine.
    ::*   Points marked as a mine show as a '?'.
    ::*   Other free points show as an integer count of the number of adjacent
    true mines in its immediate neighbourhood, or as a single space ' ' if the
    free point is not adjacent to any true mines.
    * Of course you loose if you try to clear space that starts on a mine.
    * You win when you have correctly identified all mines.


    When prompted you may:
        Toggle where you think a mine is at position x, y:
          m <x> <y>
        Clear the grid starting at position x, y (and print the result):
          c <x> <y>
        Print the grid so far:
          p
        Quit
          q
    Resigning will first show the grid with an 'N' for unfound true mines, a
    'Y' for found true mines and a '?' for where you marked clear space as a
    mine
EOS

WIDTH, HEIGHT = 6, 4
PCT = 0.15
NUM_MINES = (WIDTH * HEIGHT * PCT).round

def create_mines sx, sy
  arr = Array.new(WIDTH) { Array.new(HEIGHT, false) }
  NUM_MINES.times do
    x, y = rand(WIDTH), rand(HEIGHT)
    # place it if it isn't at (sx, sy) and we haven't already placed a mine
    redo if arr[x][y] or (x == sx and y == sy)
    arr[x][y] = true
  end
  arr
end

def num_marks
  $screen.inject(0) { |sum, row| sum + row.count("?") }
end

def show_grid revealed = false
  if revealed
    puts $mines.transpose.map { |row| row.map { |cell| cell ? "*" : " " }.join(" ") }
  else
    puts "Grid has #{NUM_MINES} mines, #{num_marks} marked."
    puts $screen.transpose.map{ |row| row.join(" ") }
  end
end

SURROUND = [-1,0,1].product([-1,0,1]) - [[0,0]]     # surround 8
def surrounding x, y
  # apply the passed block to each spot around (x, y)
  SURROUND.each do |dx, dy|
    # don't check if we're out of bounds, or at (0,0)
    yield(x+dx, y+dy) if (0...WIDTH).cover?(x+dx) and (0...HEIGHT).cover?(y+dy)
  end
end

def clear_space x, y
  return unless $screen[x][y] == "."
  # check nearby spaces
  count = 0
  surrounding(x, y) { |px, py| count += 1 if $mines[px][py] }
  if count == 0
    $screen[x][y] = " "
    surrounding(x, y) { |px, py| clear_space px, py }
  else
    $screen[x][y] = count.to_s
  end
end

def victory?
  return false if $mines.nil?  # first one, don't need to check
  return false if num_marks != NUM_MINES
  mines_left = NUM_MINES
  WIDTH.times do |x|
    HEIGHT.times do |y|
      mines_left -= 1 if $mines[x][y] and $screen[x][y] == "?"
    end
  end
  
  mines_left == 0
end

def check_input x, y
  x, y = x.to_i - 1, y.to_i - 1
  [x, y] if (0...WIDTH).cover?(x) and (0...HEIGHT).cover?(y)
end

$mines = nil
$screen = Array.new(WIDTH) { Array.new(HEIGHT, ".") }

puts "Welcome to Minesweeper!"
show_grid

loop do
  print "> "
  action = gets.chomp.downcase
  
  case action
  when "quit", "exit", "x", "q"
    puts "Bye!"
    break
  when /^m (\d+) (\d+)$/
    # mark this cell
    x, y = check_input $1, $2
    next unless x
    if $screen[x][y] == "."
      # mark it
      $screen[x][y] = "?"
      if victory?
        show_grid
        puts "You win!"
        break
      end
    elsif $screen[x][y] == "?"
      # unmark it
      $screen[x][y] = "."
    end
    show_grid
  when /^c (\d+) (\d+)$/
    x, y = check_input $1, $2
    next unless x
    $mines ||= create_mines(x, y)
    if $mines[x][y]
      puts "You hit a mine!"
      show_grid true
      break
    else
      clear_space x, y
      show_grid
      if victory?
        puts "You win!"
        break
      end
    end
  when "p"
    show_grid
  end
end


  

You may also check:How to resolve the algorithm Repeat step by step in the Fe programming language
You may also check:How to resolve the algorithm Deceptive numbers step by step in the Python programming language
You may also check:How to resolve the algorithm Variable size/Get step by step in the ooRexx programming language
You may also check:How to resolve the algorithm Determine if a string is numeric step by step in the Batch File programming language
You may also check:How to resolve the algorithm Averages/Arithmetic mean step by step in the Maxima programming language