Search code examples
pythonminesweeper

how would i check all adjacent cells for my minesweeper game?


I have been trying to figure out a way to check each adjacent cell for my minesweeper game and am coming up short. I am a beginner in python and would also like to start using OOP. however before I can even get there, I need to rectify this. all the tutorials I have seen don't use basic python, but different versions to the IDLE i use, so I am struggling. can anyone help me? I need to be able to go around each adjacent cell and check if there is a bomb there. The value to check if there is a bomb there is 1 and will also turn red. Thank you all so much! also if you could dumb it down a little for me, that would be lovely.

import random
import pygame

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0) 
WIDTH = 20
HEIGHT = 20 
MARGIN = 5
bombnum = 10
grid = []
for row in range(10):
    grid.append([])
    for column in range(10):
        grid[row].append(0)
print(grid)

pygame.init()
 
WINDOW_SIZE = [255, 315]
screen = pygame.display.set_mode(WINDOW_SIZE)
 
pygame.display.set_caption("Array Backed Grid")
 
done = False
 
clock = pygame.time.Clock()

#class bomb:
    #def revealed(self,pick):#this is the grid thats been picked
     #   self.shown = shown
def placebomb():
    for i in range(bombnum):
        while True:
            row  = random.randint(0,8)
            column = random.randint(0,8)
            if grid[row][column] == 0:
                grid[row][column] = 1
                break
placebomb()  
        
                
# -------- Main Program Loop -----------
while not done:
    for event in pygame.event.get():  
        if event.type == pygame.QUIT:  
            done = True 
        elif event.type == pygame.MOUSEBUTTONDOWN:
            pos = pygame.mouse.get_pos()
            column = pos[0] // (WIDTH + MARGIN)
            row = (pos[1]-50) // (HEIGHT + MARGIN)
            grid[row][column] = 1
            print("Click ", pos, "Grid coordinates: ", row, column)


    screen.fill(BLACK)
 
    for row in range(10):
        for column in range(10):
            color = WHITE
            if grid[row][column] == 1:
                color = RED
            pygame.draw.rect(screen,
                             color,
                             [(MARGIN + WIDTH) * (column) + MARGIN,
                              50+(MARGIN + HEIGHT) * row + MARGIN,
                              WIDTH,
                              HEIGHT])
 

    clock.tick(60)
 

    pygame.display.flip()
 

pygame.quit()


Solution

  • First up, you definitely want to make an OOP project out of this. Minesweeper's probably near the complexity limit of what you can reasonably do without object-oriented programming, but if you want to take the basic Minesweeper concept and make it more interesting / complex, you're going to need better structure.

    And even if you're not considering making it more complex, thinking about what sorts of complexities you could add in is helpful in planning your classes. (Perhaps you didn't realize there's a "planning your classes" step? I'll get to that.) My steps here should work for pretty much any beginning OOP project, since it seems Minesweeper's just a convenient example.

    I realize this answer will be a giant diversion from OP's how-do-I-check-the-nearest-neighbors question, but OP was also asking about OOP and answering their question in an OOP context means getting a class model in place. Trying to retrofit OOP onto non-OOP code is possible, but is usually harder than doing OOP from scratch.

    1. If you're new to OOP in Python, check these two tutorials. Both of them run through the basics, neither requires an IDE, and neither introduces complexity / features beyond what you need to start. The rest of my answer will assume you're familiar with how to write classes. I highly recommend typing out both tutorials on your own terminal and trying a couple variants on them before you go further.
    2. Now that you know roughly what a class is, make a list of all the operations you're going to want in your Minesweeper program, and what sorts of things come up in it. The point here isn't to write code, and natural language will serve as pseudocode here. Things you'll probably want to include: "Add a mine to a cell on the grid," "check a cell on the grid," "display the number of spots near a given cell on the grid." You might also include some "future plans" like "resize the grid in the middle of a game", "limit number of moves," "get hint," "allow mines to move," "have people that move through minefield," multiple types of checks / mines / cells, hex grid as an alternative to a square grid, strangely-shaped and 3D grids, etc. Don't worry if you don't know how to code those things yet. If you've got an interesting idea, include it on the list.
    3. Go through that list and make some notes about the main words / concepts that keep coming up. These will be your classes. For Minesweeper, I'd list "Cell," "Grid," "Game," "Check," and "Mine," but your mileage may vary. These are not necessarily the final class list. They're just a step toward organizing your thoughts so you can turn the high-level "Minesweeper game" into something concrete & extensible. You can add / remove classes later once you get a better feel for which ones you'll actually use.
    4. Note the relationships between objects of the classes, including which ones need to be described for the others to make sense / be instantiated. This step is analogous to how you might plan a relational database: A Game has one and only one Grid, a Grid is a collection of Cells arranged in a fixed pattern, a Cell may or may not contain a single Mine, a Check reveals whether a Mine is in a given Cell and if not, reveals the number of Mines in Cells adjacent to the Checked Cell, etc.
    5. Open up your IDE, and start blocking out classes in a file. The point here isn't so much to write code as to get all your classes and notes into the code for easy reference. If this file starts to get big, think about how the classes group together and split that one file into several, with import statements connecting them. By "blocking out classes," I mean "writing the simplest classes which will compile, but don't try to make them run." At this stage, you can / should have classes that look like this:
    class Grid:
        """A Game has one and only one Grid, a Grid is a collection of Cells arranged in a fixed pattern"""
        def __init__(self, game, **setup_params):
            """Initialize the grid given a game and possibly other parameters for shape and size"""
            raise NotImplementedError('This code has not yet been written.')
    
        def resize_grid(self, new_size):
            raise NotImplementedError('This code has not yet been written.')
    
        def check_cell_at(self, position):
            raise NotImplementedError('This code has not yet been written.')
    

    Things to check: This is completely legal Python and compiles fine. All of your notes from steps 2-4 should end up in docstrings. All of the target functionality you described in your notes corresponds to particular methods on classes that have something to do with those functions. Every class you've described is present, and has a docstring describing its purpose & structure. Every class's __init__() method takes the arguments that are necessary to instantiate the class. Every method takes some parameters that will likely be helpful. You can always edit the parameter lists later, but again, the point is to organize your code before you get too far into writing it, so that relationships and functionality are easy to track. If you're using Git or another version-tracking tool, make your first commit as soon as you're done with this step.

    1. Now that you've got all the structure blocked out, figure out what the "main entry point" is going to be, instantiate that class (probably Game in my approach), and check that your code compiles, runs, and exits with a NotImplementedError coming with the line and message you expect. (If so, that's success!) If the project feels big, or if you're going to be sharing code with other developers, you may also want to add unit tests at this stage. The expectation is that every test will fail, but writing the tests helps to document the planned functionality.
    2. Fill in the methods one by one, add unit tests to go with new or updated methods, and try running the code a bit at a time. You don't have to write the methods in any particular order, but it will be easier to test once all the __init__() methods are complete, and those are generally straightforward.

    Now, how do you check the neighbors of a cell?

    • You should have methods on a Cell for "get list of my neighbors," and "get whether this cell contains a mine." You probably also have a method on Grid to "get Cell at position." You might have multiple types of Checks, but the base Minesweeper game only has the one, so Cell has a method like
       def check(self):
           """Returns "BOMB" if this cell has a Bomb. Otherwise, returns the number of neighbors with bombs as an integer."""
           if self.has_bomb:
               return "BOMB"
           neighboring_mines = 0
           for cell in self.grid.get_neighbors_of(self.position):
                if cell.has_bomb:
                    neighboring_mines += 1
           return neighboring_mines