Use Python to implement minesweeper games

Text | Yeke

Source: Python technology "ID: pythonall"

Minesweeper is a puzzle game that was first released by Microsoft on Windows in 1992. The game is suitable for all ages. The rules are simple, that is, find all non-thunder grids in the shortest time and you can’t step on them in the middle. When you reach the thunder, you will fail if you step on the thunder and you need to start again.

In this article, we use Python to implement minesweeping games, and the main Python library used is pygame.

achieve

The game composition is relatively simple, mainly including: small squares, timers, mines, etc.

First, we initialize some constants, such as the number of horizontal and vertical squares, the number of mines, and mouse clicks, as shown below:

BLOCK_WIDTH = 30
BLOCK_HEIGHT = 16
# 块大小
SIZE = 20
# 地雷数
MINE_COUNT = 66
# 未点击
normal = 1
# 已点击
opened = 2
# 地雷
mine = 3
# 标记为地雷
flag = 4
# 标记为问号
ask = 5
# 踩中地雷
bomb = 6
# 被双击的周围
hint = 7
# 正被鼠标左右键双击
double = 8
readied = 1,
started = 2,
over = 3,
win = 4

Then define a mine class, which defines some basic attributes (such as coordinates, status, etc.) and get and set methods. The code is implemented as follows:

class Mine:
    def __init__(self, x, y, value=0):
        self._x = x
        self._y = y
        self._value = 0
        self._around_mine_count = -1
        self._status = normal
        self.set_value(value)
    def __repr__(self):
        return str(self._value)
    def get_x(self):
        return self._x
    def set_x(self, x):
        self._x = x
    x = property(fget=get_x, fset=set_x)
    def get_y(self):
        return self._y
    def set_y(self, y):
        self._y = y
    y = property(fget=get_y, fset=set_y)
    def get_value(self):
        return self._value
    def set_value(self, value):
        if value:
            self._value = 1
        else:
            self._value = 0
    value = property(fget=get_value, fset=set_value, doc='0:非地雷 1:雷')
    def get_around_mine_count(self):
        return self._around_mine_count
    def set_around_mine_count(self, around_mine_count):
        self._around_mine_count = around_mine_count
    around_mine_count = property(fget=get_around_mine_count, fset=set_around_mine_count, doc='四周地雷数量')
    def get_status(self):
        return self._status
    def set_status(self, value):
        self._status = value
    status = property(fget=get_status, fset=set_status, doc='BlockStatus')

Then define a MineBlock class to handle the basic logic of mine sweeping. The code is implemented as follows:

class MineBlock:
    def __init__(self):
        self._block = [[Mine(i, j) for i in range(BLOCK_WIDTH)] for j in range(BLOCK_HEIGHT)]
        # 埋雷
        for i in random.sample(range(BLOCK_WIDTH * BLOCK_HEIGHT), MINE_COUNT):
            self._block[i // BLOCK_WIDTH][i % BLOCK_WIDTH].value = 1
    def get_block(self):
        return self._block
    block = property(fget=get_block)
    def getmine(self, x, y):
        return self._block[y][x]
    def open_mine(self, x, y):
        # 踩到雷了
        if self._block[y][x].value:
            self._block[y][x].status = bomb
            return False
        # 先把状态改为 opened
        self._block[y][x].status = opened
        around = _get_around(x, y)
        _sum = 0
        for i, j in around:
            if self._block[j][i].value:
                _sum += 1
        self._block[y][x].around_mine_count = _sum
        # 如果周围没有雷,那么将周围 8 个未中未点开的递归算一遍
        if _sum == 0:
            for i, j in around:
                if self._block[j][i].around_mine_count == -1:
                    self.open_mine(i, j)
        return True
    def double_mouse_button_down(self, x, y):
        if self._block[y][x].around_mine_count == 0:
            return True
        self._block[y][x].status = double
        around = _get_around(x, y)
        # 周围被标记的雷数量
        sumflag = 0
        for i, j in _get_around(x, y):
            if self._block[j][i].status == flag:
                sumflag += 1
        # 周边的雷已经全部被标记
        result = True
        if sumflag == self._block[y][x].around_mine_count:
            for i, j in around:
                if self._block[j][i].status == normal:
                    if not self.open_mine(i, j):
                        result = False
        else:
            for i, j in around:
                if self._block[j][i].status == normal:
                    self._block[j][i].status = hint
        return result
    def double_mouse_button_up(self, x, y):
        self._block[y][x].status = opened
        for i, j in _get_around(x, y):
            if self._block[j][i].status == hint:
                self._block[j][i].status = normal

We then initialize the interface, first generate a panel composed of small squares, the main code implementation is as follows:

for row in block.block:
 for mine in row:
  pos = (mine.x * SIZE, (mine.y + 2) * SIZE)
  if mine.status == opened:
   screen.blit(img_dict[mine.around_mine_count], pos)
   opened_count += 1
  elif mine.status == double:
   screen.blit(img_dict[mine.around_mine_count], pos)
  elif mine.status == bomb:
   screen.blit(img_blood, pos)
  elif mine.status == flag:
   screen.blit(img_flag, pos)
   flag_count += 1
  elif mine.status == ask:
   screen.blit(img_ask, pos)
  elif mine.status == hint:
   screen.blit(img0, pos)
  elif game_status == over and mine.value:
   screen.blit(img_mine, pos)
  elif mine.value == 0 and mine.status == flag:
   screen.blit(img_error, pos)
  elif mine.status == normal:
   screen.blit(img_blank, pos)

Take a look at the effect:

Then add the head part of the panel, including: display the number of thunder, restart button (smiley), display time-consuming, the main code implementation is as follows:

print_text(screen, font1, 30, (SIZE * 2 - fheight) // 2 - 2, '%02d' % (MINE_COUNT - flag_count), red)
if game_status == started:
 elapsed_time = int(time.time() - start_time)
print_text(screen, font1, SCREEN_WIDTH - fwidth - 30, (SIZE * 2 - fheight) // 2 - 2, '%03d' % elapsed_time, red)
if flag_count + opened_count == BLOCK_WIDTH * BLOCK_HEIGHT:
 game_status = win
if game_status == over:
 screen.blit(img_face_fail, (face_pos_x, face_pos_y))
elif game_status == win:
 screen.blit(img_face_success, (face_pos_x, face_pos_y))
else:
 screen.blit(img_face_normal, (face_pos_x, face_pos_y))

Take a look at the effect:

Then add various click events, the code implementation is as follows:

for event in pygame.event.get():
 if event.type == QUIT:
  sys.exit()
 elif event.type == MOUSEBUTTONDOWN:
  mouse_x, mouse_y = event.pos
  x = mouse_x // SIZE
  y = mouse_y // SIZE - 2
  b1, b2, b3 = pygame.mouse.get_pressed()
  if game_status == started:
   # 鼠标左右键同时按下,如果已经标记了所有雷,则打开周围一圈;如果还未标记完所有雷,则有一个周围一圈被同时按下的效果
   if b1 and b3:
    mine = block.getmine(x, y)
    if mine.status == opened:
     if not block.double_mouse_button_down(x, y):
      game_status = over
 elif event.type == MOUSEBUTTONUP:
  if y < 0:
   if face_pos_x <= mouse_x <= face_pos_x + face_size \
     and face_pos_y <= mouse_y <= face_pos_y + face_size:
    game_status = readied
    block = MineBlock()
    start_time = time.time()
    elapsed_time = 0
    continue
  if game_status == readied:
   game_status = started
   start_time = time.time()
   elapsed_time = 0
  if game_status == started:
   mine = block.getmine(x, y)
   # 按鼠标左键
   if b1 and not b3:
    if mine.status == normal:
     if not block.open_mine(x, y):
      game_status = over
   # 按鼠标右键
   elif not b1 and b3:
    if mine.status == normal:
     mine.status = flag
    elif mine.status == flag:
     mine.status = ask
    elif mine.status == ask:
     mine.status = normal
   elif b1 and b3:
    if mine.status == double:
     block.double_mouse_button_up(x, y)

Let's take a look at the final effect:

to sum up

In this article, we simply implemented the minesweeping game through Python. If you are interested, you can actually do it and see if you can eliminate all the mines.

PS: Reply to "Python" in the public account to enter the Python novice learning exchange group, and plan for 100 days together !

Do you remember the old rules , brothers, click on the "Looking" in the lower right corner . If you think the content of the article is good, remember to share the circle of friends to let more people know!

[ Code acquisition method ]

Identify the QR code at the end of the text, reply: 201021

Guess you like

Origin blog.csdn.net/weixin_48923393/article/details/109213602