【python】pygameでマインスイーパ風のゲームを作ってみた

## 背景

pygameでスペースインベーダー作りたい。

## PCスペック

| 項目 | 情報 |
| --- | --- |
| OS | macOS Sonoma 14.4.1  |
| ハードウェア | MacBook Pro 16inc 2023 |
| プロセッサ | Apple M2 pro |
| メモリ | 32GB |


## ソースコード
```py main.py
import pygame
import random
import sys

# Pygameの初期化
pygame.init()

# 定数の設定
WIDTH = 800
HEIGHT = 600
GRID_SIZE = 20
TILE_SIZE = 30
MINE_COUNT = 50

# 色の定義
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREY = (192, 192, 192)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

# フォントの設定
FONT = pygame.font.SysFont('comicsans', 20)

# スクリーンの設定
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Minesweeper')

# タイルクラスの定義
class Tile:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.is_mine = False
        self.is_revealed = False
        self.is_flagged = False
        self.adjacent_mines = 0

    def draw(self):
        if self.is_revealed:
            pygame.draw.rect(screen, GREY, (self.x * TILE_SIZE, self.y * TILE_SIZE, TILE_SIZE, TILE_SIZE))
            if self.is_mine:
                pygame.draw.circle(screen, BLACK, (self.x * TILE_SIZE + TILE_SIZE // 2, self.y * TILE_SIZE + TILE_SIZE // 2), TILE_SIZE // 4)
            elif self.adjacent_mines > 0:
                text = FONT.render(str(self.adjacent_mines), True, BLACK)
                screen.blit(text, (self.x * TILE_SIZE + TILE_SIZE // 2 - text.get_width() // 2, self.y * TILE_SIZE + TILE_SIZE // 2 - text.get_height() // 2))
        else:
            pygame.draw.rect(screen, WHITE, (self.x * TILE_SIZE, self.y * TILE_SIZE, TILE_SIZE, TILE_SIZE))
            if self.is_flagged:
                pygame.draw.circle(screen, RED, (self.x * TILE_SIZE + TILE_SIZE // 2, self.y * TILE_SIZE + TILE_SIZE // 2), TILE_SIZE // 4)
        pygame.draw.rect(screen, BLACK, (self.x * TILE_SIZE, self.y * TILE_SIZE, TILE_SIZE, TILE_SIZE), 1)

# ゲームボードの初期化
def init_board():
    board = [[Tile(x, y) for y in range(HEIGHT // TILE_SIZE)] for x in range(WIDTH // TILE_SIZE)]
    mines = random.sample([tile for row in board for tile in row], MINE_COUNT)
    for mine in mines:
        mine.is_mine = True
    for row in board:
        for tile in row:
            if not tile.is_mine:
                tile.adjacent_mines = sum(1 for nx in range(tile.x - 1, tile.x + 2) for ny in range(tile.y - 1, tile.y + 2) if 0 <= nx < WIDTH // TILE_SIZE and 0 <= ny < HEIGHT // TILE_SIZE and board[nx][ny].is_mine)
    return board

# ゲームオーバーの処理
def game_over(board):
    for row in board:
        for tile in row:
            tile.is_revealed = True
    draw_board(board)
    pygame.display.update()
    pygame.time.wait(2000)
    pygame.quit()
    sys.exit()

# ゲームクリアの確認
def check_win(board):
    for row in board:
        for tile in row:
            if not tile.is_mine and not tile.is_revealed:
                return False
    return True

# タイルを開く処理
def reveal_tile(board, x, y):
    tile = board[x][y]
    if tile.is_revealed or tile.is_flagged:
        return
    tile.is_revealed = True
    if tile.is_mine:
        game_over(board)
    elif tile.adjacent_mines == 0:
        for nx in range(x - 1, x + 2):
            for ny in range(y - 1, y + 2):
                if 0 <= nx < WIDTH // TILE_SIZE and 0 <= ny < HEIGHT // TILE_SIZE:
                    reveal_tile(board, nx, ny)

# ゲームボードの描画
def draw_board(board):
    for row in board:
        for tile in row:
            tile.draw()

# メイン関数
def main():
    board = init_board()
    run = True

    while run:
        screen.fill(GREEN)
        draw_board(board)
        pygame.display.flip()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
            elif event.type == pygame.MOUSEBUTTONDOWN:
                x, y = pygame.mouse.get_pos()
                x //= TILE_SIZE
                y //= TILE_SIZE
                if event.button == 1:
                    reveal_tile(board, x, y)
                    if check_win(board):
                        print("You win!")
                        run = False
                elif event.button == 3:
                    tile = board[x][y]
                    tile.is_flagged = not tile.is_flagged

    pygame.quit()

if __name__ == "__main__":
    main()
```

## 画面

![スクリーンショット 2024-07-19 12.00.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3327773/41671a6a-5011-6c0f-4f68-781ab5e31e8c.png)


## まとめ

できた。