How good is AI minesweeping? Other countries have come to seek teachers to learn skills (with a complete course)

Posted by dc_jt on Sat, 08 Jan 2022 03:19:27 +0100

Above

hello everyone! I'm classmate pear!

I hope you can support me! ha-ha

To thank everyone who cares about me: 💓 The project source code of each article is shared free of charge 💓 See the end of the text!

Many functions of csdn are still under research, and it's no wonder that Xiaobian's writing is not good. He will slowly improve and learn from you

Xiaobian has also been learning programming. If there are errors in the code applet, please leave a message in the comment area!

Finally - if the article helps you, remember to "pay attention", "like" and "comment"~

Foreword

Minesweeper is a popular puzzle game, which was released in 1992.

The goal of the game is to find out all non thunder grids according to the numbers in the click grid in the shortest time, and avoid stepping on thunder and stepping on one

Ray lost everything.

It's the first game many people come into contact with. It's probably the best game for office workers and net free students when they are bored, isn't it

Every time I see a villain wearing sunglasses, I have a sense of achievement? In those years when there was no net (cut-off net), minesweeping accompanied countless people

After their childhood. What's your best record? Xiaobian brushed a game for 30 seconds between work 😳).

So? How can I get through customs? This may not be possible in our lifetime. BUT we can rely on our Python

Automatic Minesweeper! Don't panic ~ let's officially start!

Automatic minesweeping

Environment configuration:

Python 3, pychar, Pygame and some built-in modules.

Installation of third-party library: pip  install pygame


Effect display:

Game start:

 

Automatic minesweeping:

 

 

game over:

 

Code demonstration:

1) Main program

The code is relatively simple. There are comments on each line. You can read it yourself. If you don't understand it, you can talk to me and learn together!

# -*- coding: utf-8 -*-  
import pygame
from pygame.locals import *
import numpy as np
import random
import sys
import time
import copy


# Screen size
Screen_Size = (1200, 600)
# Number of rows
Rows = 20
# Number of columns
Colums = 40
# Number of Mines
numOfMines = 80
# winning probability
VictoryRate=0

class Sweep(object):
    """docstring for Sweep"""

    def __init__(self):
        # Initialize a page
        self.Screen = pygame.display.set_mode(Screen_Size)
        # typeface
        self.myfont = pygame.font.SysFont('Immature', 25)
        # Lattice size
        self.gwide = int(Screen_Size[0] / Colums)
        self.gheight = int(Screen_Size[1] / Rows)
        self.board = np.zeros((Rows, Colums))
        # Store the optional location for the next step
        self.NBS = []
        # NBS auxiliary container is used to judge whether NBS has changed
        self.NBSTool = []
        # Judge whether to conduct probability selection minesweeping
        self.GO = False
        # Traversing storage containers
        self.container = []
        # Label mine storage container
        self.mineContainer = []
        # Actual mine location storage container
        self.Mines = []
        # Digital storage container
        self.numbers = []
        # Load picture
        self.LoadImg()
        # Draw grid
        self.DrawGrid()
        # Buried mine
        self.HideMines()

    def LoadImg(self):
        # Load mine picture
        self.mine = pygame.image.load('image/mine.jpg').convert_alpha()
        self.mine = pygame.transform.scale(
            self.mine, (self.gwide, self.gheight))
        # Load flag picture
        self.flag = pygame.image.load('image/flag.jpg').convert_alpha()
        self.flag = pygame.transform.scale(
            self.flag, (self.gwide, self.gheight))
        # Load mine explosion picture
        self.boom = pygame.image.load('image/boom.png').convert_alpha()
        self.boom = pygame.transform.scale(
            self.boom, (self.gwide, self.gheight))
        # Load digital picture
        self.num1 = pygame.image.load('image/1.png')
        self.num1 = pygame.transform.scale(
            self.num1, (self.gwide, self.gheight))
        self.num2 = pygame.image.load('image/2.png')
        self.num2 = pygame.transform.scale(
            self.num2, (self.gwide, self.gheight))
        self.num3 = pygame.image.load('image/3.png')
        self.num3 = pygame.transform.scale(
            self.num3, (self.gwide, self.gheight))
        self.num4 = pygame.image.load('image/4.png')
        self.num4 = pygame.transform.scale(
            self.num4, (self.gwide, self.gheight))
        self.num5 = pygame.image.load('image/5.png')
        self.num5 = pygame.transform.scale(
            self.num5, (self.gwide, self.gheight))
        self.num6 = pygame.image.load('image/6.png')
        self.num6 = pygame.transform.scale(
            self.num6, (self.gwide, self.gheight))
        self.num7 = pygame.image.load('image/7.png')
        self.num7 = pygame.transform.scale(
            self.num7, (self.gwide, self.gheight))
        self.num8 = pygame.image.load('image/8.png')
        self.num8 = pygame.transform.scale(
            self.num8, (self.gwide, self.gheight))
        # Setting background image after loading access
        self.back = pygame.image.load('image/back.jpg')
        self.back = pygame.transform.scale(
            self.back, (self.gwide, self.gheight))
        # Failed to load game background
        self.gameOver=pygame.image.load('image/gameover.jpg')
        self.gameOver=pygame.transform.scale(self.gameOver,Screen_Size)
        # Load game victory background
        self.victoryOver=pygame.image.load('image/victory.jpg')
        self.victoryOver=pygame.transform.scale(self.victoryOver,Screen_Size)

    def HideMines(self):
        """Buried mine"""
        for i in range(numOfMines):
            while True:
                y = random.randint(0, Colums - 1)
                x = random.randint(0, Rows - 1)
                if self.board[x][y] == 0:
                    self.board[x][y] = -1
                    self.Mines.append((x, y))
                    break

    def ShowAllMines(self):
        """
        Show the location of all mines
        """
        for i in range(Rows):
            for j in range(Colums):
                if self.board[i][j] == -1:
                    self.Screen.blit(
                        self.mine, (self.gwide * j, self.gheight * i))

    def DrawGrid(self):
        """
        Draw background interface
        """
        self.Screen.fill((191, 251, 255))
        # Draw a horizontal line
        for i in range(1, Rows):
            pygame.draw.line(
                self.Screen, (0, 0, 0), (0, self.gheight * i), (Screen_Size[0], self.gheight * i))
        # Draw a vertical line
        for i in range(1, Colums):
            pygame.draw.line(self.Screen, (0, 0, 0),
                             (self.gwide * i, 0), (self.gwide * i, Screen_Size[1]))

    def NumOfPos(self, pos):
        """
        Returns the number of mines around a point
        pos Map coordinates
        At the same time, set the number of mines at the corresponding position of the two-dimensional array
        """
        n = 0
        y, x = pos[0], pos[1]
        if x - 1 >= 0:
            if self.board[x - 1][y] == -1:
                n += 1
            if y - 1 >= 0 and self.board[x - 1][y - 1] == -1:
                n += 1
            if y + 1 <= Colums - 1 and self.board[x - 1][y + 1] == -1:
                n += 1
        if x + 1 <= Rows - 1:
            if self.board[x + 1][y] == -1:
                n += 1
            if y - 1 >= 0 and self.board[x + 1][y - 1] == -1:
                n += 1
            if y + 1 <= Colums - 1 and self.board[x + 1][y + 1] == -1:
                n += 1
        if y - 1 >= 0 and self.board[x][y - 1] == -1:
            n += 1
        if y + 1 <= Colums - 1 and self.board[x][y + 1] == -1:
            n += 1
        # self.board[x][y] = n
        return n

    def SetNumOfPos(self, pos):
        """
        Set the number of mines around a safety point
        pos Is the map coordinates
        """
        n = self.NumOfPos(pos)
        if n == 0:
            self.Screen.blit(
                self.back, (self.gwide * pos[0], self.gheight * pos[1]))
        if n == 1:
            self.Screen.blit(
                self.num1, (self.gwide * pos[0], self.gheight * pos[1]))
        if n == 2:
            self.Screen.blit(
                self.num2, (self.gwide * pos[0], self.gheight * pos[1]))
        if n == 3:
            self.Screen.blit(
                self.num3, (self.gwide * pos[0], self.gheight * pos[1]))
        if n == 4:
            self.Screen.blit(
                self.num4, (self.gwide * pos[0], self.gheight * pos[1]))
        if n == 5:
            self.Screen.blit(
                self.num5, (self.gwide * pos[0], self.gheight * pos[1]))
        if n == 6:
            self.Screen.blit(
                self.num6, (self.gwide * pos[0], self.gheight * pos[1]))
        if n == 7:
            self.Screen.blit(
                self.num7, (self.gwide * pos[0], self.gheight * pos[1]))
        if n == 8:
            self.Screen.blit(
                self.num8, (self.gwide * pos[0], self.gheight * pos[1]))
        return n

    def NeighborsOf(self, pos):
        """
        Get the neighbor coordinates of a point
        pos Is the coordinates of a two-dimensional array
        """
        x, y = pos[0], pos[1]
        neibors = []
        if x - 1 >= 0:
            if y - 1 >= 0:
                neibors.append((x - 1, y - 1))
            if y + 1 <= Colums - 1:
                neibors.append((x - 1, y + 1))
            neibors.append((x - 1, y))
        if x + 1 <= Rows - 1:
            if y - 1 >= 0:
                neibors.append((x + 1, y - 1))
            if y + 1 <= Colums - 1:
                neibors.append((x + 1, y + 1))
            neibors.append((x + 1, y))
        if y - 1 >= 0:
            neibors.append((x, y - 1))
        if y + 1 <= Colums - 1:
            neibors.append((x, y + 1))
        return neibors

    def Boom(self, pos):
        """
        pos Is a 2D array position
        """
        self.Screen.blit(
            self.boom, (self.gwide * pos[1], self.gheight * pos[0]))
        # pygame.display.update()

    def Ergodic(self, pos):
        """
        Diverge from one position to all around until it meets the position with thunder,Is a recursive function
        pos Is the coordinates of a two-dimensional array
        Convert 2D array pos The coordinates and the number of surrounding mines are stored in the container self.container in
        """
        x, y = pos[0], pos[1]
        # If the number of mines around the location is not 0, stop
        if self.NumOfPos((y, x)) > 0 and self.board[x][y] != -1:
            self.numbers.append(pos)
            return
        # Store the two-dimensional array pos coordinates and the number of mines around in the container self In container
        if self.board[x][y] != -1:
            self.container.append(pos)
        # Upward traversal
        if x - 1 >= 0 and (x - 1, y) not in self.container:
            self.Ergodic((x - 1, y))
        # Traversal down
        if x + 1 <= Rows - 1 and (x + 1, y) not in self.container:
            self.Ergodic((x + 1, y))
        # Want to traverse left
        if y - 1 >= 0 and (x, y - 1) not in self.container:
            self.Ergodic((x, y - 1))
        # Box right traversal
        if y + 1 <= Colums - 1 and (x, y + 1) not in self.container:
            self.Ergodic((x, y + 1))

    def DrawContainer(self):
        # self.ShowAllMines()
        for pos in self.container:
            x, y = pos[0], pos[1]
            self.SetNumOfPos((y, x))

    def DrawNumbers(self):
        for pos in self.numbers:
            self.SetNumOfPos((pos[1], pos[0]))

    def DrawFlags(self):
        for pos in self.mineContainer:
            self.Screen.blit(
                self.flag, (pos[1] * self.gwide, pos[0] * self.gheight))

    def Removed(self):
        n = 0
        for pos in self.mineContainer:
            if pos in self.Mines:
                n += 1
        return n

    def AutoPlay(self):
        # Random position of two-dimensional array
        x = random.randint(0, Rows - 1)
        y = random.randint(0, Colums - 1)
        print("Step 1:",self.board[x][y])
        if self.board[x][y]==-1:
            self.Boom((x,y))
            pygame.display.update()
            self.GameOver()
        print(x, y)
        while True:
            for ev in pygame.event.get():
                if ev.type == pygame.QUIT:
                    sys.exit(0)
            if self.board[x][y] == -1:
                self.Boom((x, y))
                time.sleep(3)
                sys.exit(0)
            # Draw grid
            self.DrawGrid()
            # Divergent traversal
            self.Ergodic((x, y))
            # Draw the position that has been traversed and there is no thunder around (with white background)
            self.DrawContainer()
            # Draw the position that has been traversed but surrounded by thunder (in numbers)
            self.DrawNumbers()
            # Find out all possible positions for the next step
            self.NextSteps()
            # Draw the location of the marked mine (with a flag)
            self.DrawFlags()
            # Find out where the mine must be and mark it (with a flag)
            self.SetFlags()
            # Find out the location mark (number or blank) where there must be no thunder
            self.NoMines()
            # When NBS is 0, probability selection
            self.ChooseWithBigProbability()
            # Refresh
            pygame.display.update()
            # Print the number of marked mines
            print("Number of Mines marked:", len(self.mineContainer))
            print("Number of Mines removed:", self.Removed())
            if self.Removed()==numOfMines:
                time.sleep(3)
                self.Victory()

    def NextSteps(self):
        """
        Find out the optional position for the next minesweeping
        Algorithm idea:
            Eight neighborhoods have been visited to find the location of each mine that has been marked with the number of surrounding mines
            These positions are the possible positions for the next step
        """
        self.NBS.clear()
        for pos in self.numbers:
            for n in self.NeighborsOf(pos):
                if n not in self.NBS + self.container + self.numbers:
                    self.NBS.append(n)
        if self.NBSTool == self.NBS:
            print(self.GO)
            self.GO = True
        self.NBSTool = copy.deepcopy(self.NBS)
        # print(self.NBS)

    def SetFlags(self):
        """
        Find out where there must be thunder and mark it with a flag
        Algorithm idea:
            For each location marked with the number of mines, find out the location that has not been visited in the eight neighborhoods of the location,
            Find out the number of mines not found. If the number of mines not found around the location is greater than or equal to the allowable number
            The number of walking positions, then the remaining eight neighborhood walkable positions of the position must be all mines, and mark the position
            For the mine and put it in self.mineContainer container
        """
        for pos in self.numbers:
            s = list(set(self.NeighborsOf(pos)) -
                     set(self.container) - set(self.numbers) - set(self.mineContainer))
            s1 = list(set(self.NeighborsOf(pos)) -
                      set(self.container) - set(self.numbers))
            # if self.NumOfPos((pos[1], pos[0])) == 1 and len(s) == 1:
            if self.NumOfPos((pos[1], pos[0])) - len(set(s1) & set(self.mineContainer)) >= len(s):
                if self.board[pos[0]][pos[1]] == -1:
                    self.Boom(pos)
                    time.sleep(3)
                    sys.exit(0)
                for pos1 in s:
                    self.mineContainer.append(pos1)
                    self.Screen.blit(
                        self.flag, ((self.gwide * pos1[1], self.gheight * pos1[0])))
                    # time.sleep(1)
                    # pygame.display.update()

    def NoMines(self):
        """
        Find a location where there are no mines
        Algorithm idea:
            For each location marked with the number of surrounding mines, find the remaining mines in the eight neighborhoods of the location
            Check if all the mines around the current location have been marked,
            If all of them have been marked, there are still unreachable locations in the eight neighborhoods of the location
            ,If the number of mines around the location is 0, use Ergodic()Function traversal, if
            If the number of mines around the location is not 0, indicate the number of mines at the location
        """
        for pos in self.numbers:
            s = list(set(self.NeighborsOf(pos)) -
                     set(self.container) - set(self.numbers) - set(self.mineContainer))
            if self.NumOfPos((pos[1], pos[0])) == len(set(self.NeighborsOf(pos)) & set(self.mineContainer)):
                for pos1 in s:
                    if self.board[pos1[0]][pos1[1]] == -1:
                        self.mineContainer.append(pos)
                        continue
                    if self.NumOfPos((pos1[1], pos1[0])) == 0:
                        self.container.append(pos1)
                        self.Ergodic(pos1)
                        self.Screen.blit(
                            self.back, (pos1[1] * self.gwide, pos1[0] * self.gheight))
                        continue
                    if self.NumOfPos((pos1[1], pos1[0])) > 0:
                        self.SetNumOfPos((pos1[1], pos1[0]))
                        self.numbers.append(pos1)
                    # self.DrawContainer()
                    # pygame.display.update()

    def ChooseWithBigProbability(self):
        """
        Probability selection function
        Algorithm idea:
            When encountering the situation that it is uncertain whether there is thunder or no thunder, find the location that has not been visited
            Find out the sum of the number of mines at the locations marked with the number of mines in the eight neighborhoods of these locations, and use
            And divided by 8, these locations and their corresponding probabilities are stored in the container, and the probabilities are calculated from large to small
            Sequential sorting, if the probability of the position with the highest probability is greater than or equal to 0.75,Then the location
            Mark as mine if the probability is less than 0.75,Then find out if you haven't visited and are not in the next step
            The planned position is the position of the container. Select a position at random. If you step on the thunder, the game ends,
            If there are no mines around the location, call Ergodic()If there are mines around
            Then mark the number of mines at that location
        """
        if self.GO == True:
            print("Enter probability selection environment")
            noSeen = []
            # Quantity to be cleared
            leftmines=numOfMines-self.Removed()
            # Find the location that has not been traversed
            for i in range(Rows):
                for j in range(Colums):
                    if (i, j) not in self.container + self.mineContainer + self.numbers:
                        s = list(set(self.NeighborsOf((i, j)))
                                 & set(self.numbers))
                        numerator = 0
                        for s1 in s:
                            numerator += self.NumOfPos((s1[1], s1[0]))
                        # Calculation probability
                        noSeen.append([(i, j), numerator / 8])
            sorted(noSeen, key=lambda proba: proba[1], reverse=True)
            if noSeen != []:
                if noSeen[0][1] >= 0.75:
                    pos = noSeen[0][0]
                    self.mineContainer.append(pos)
                else:
                    nos = [p[0] for p in noSeen]
                    s = list(set(nos) - set(self.NBS))
                    index = 0
                    pos = (-1, -1)
                    if len(s) > 1:
                        index = random.randint(0, len(s) - 1)
                        pos = s[index]
                    elif len(s) == 1:
                        pos = s[0]
                    elif s == []:
                        pos = noSeen[0][0]
                    if self.board[pos[0]][pos[1]] == -1:
                        self.Boom(pos)
                        time.sleep(2)
                        self.GameOver()
                    elif self.NumOfPos((pos[1], pos[0])) > 0:
                        self.numbers.append(pos)
                    elif self.NumOfPos((pos[1], pos[0])) == 0:
                        self.Ergodic(pos)

            noSeen.clear()
            self.GO = False

    def GameOver(self):
        for pos in self.Mines:
            self.Boom(pos)
            pygame.display.update()
            time.sleep(0.1)
        self.Screen.blit(self.gameOver,(0,0))
        VictoryRate=content="Demining rate:%d"%((self.Removed()/numOfMines)*100)
        text=self.myfont.render(content+'%',True,(0,0,0),(255,255,255))
        self.Screen.blit(text,(500,150))
        pygame.display.update()
        time.sleep(5)
        sys.exit(0)

    def Victory(self):
        # if self.Removed()==numOfMines:
        VictoryRate=100
        self.Screen.blit(self.victoryOver,(0,0))
        pygame.display.update()
        time.sleep(5)
        sys.exit(0)

    def Show(self):
        self.DrawGrid()
        self.ShowAllMines()
        pygame.display.update()
        time.sleep(5)
        self.DrawGrid()
        pygame.display.update()
        time.sleep(3)
        while True:
            for ev in pygame.event.get():
                if ev.type == pygame.QUIT:
                    sys.exit(0)
                self.AutoPlay()
                pygame.display.update()


if __name__ == '__main__':
    pygame.display.init()
    pygame.font.init()
    app = Sweep()
    app.Show()

ending

What's the saying - are there any people who can pass the customs manually without relying on AI mine clearance?

Automatic mine sweeping, there is a mine sweeping, these two source codes can find me to play by myself, ha!

Follow Xiaobian for more wonderful content!

It's not easy to make. Remember to click three times!! If you need packaged source code + materials, share them for free!! Portal

 

Topics: Python Programmer Game Development pygame