From: Skullheadx Date: Fri, 10 Mar 2023 01:09:08 +0000 (-0500) Subject: minesweeper X-Git-Url: http://git.skullheadx.com/nixos/projects.html?a=commitdiff_plain;h=830d66f00503b5b09d8ca31aa3e6e1e4dde9830e;p=Minesweeper.git minesweeper --- diff --git a/.gitignore b/.gitignore index d9005f2..3eb56cb 100644 --- a/.gitignore +++ b/.gitignore @@ -149,4 +149,4 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +.idea/ diff --git a/game.py b/game.py new file mode 100644 index 0000000..d59d3f0 --- /dev/null +++ b/game.py @@ -0,0 +1,30 @@ +import pygame.event + +from setup import * +from grid import Grid + + +class Game: + + def __init__(self): + self.grid = Grid(20, 20, 50) + + self.game_over = False + + def update(self, delta): + for event in pygame.event.get(): + if event.type == pygame.QUIT: + return COMMAND_EXIT + + mouse_pos = pygame.mouse.get_pos() + mouse_button = pygame.mouse.get_pressed() + if self.grid.update(delta, mouse_pos, mouse_button): + return COMMAND_LOSE + if self.grid.win(): + return COMMAND_WIN + + return COMMAND_NONE + + def draw(self, screen): + screen.fill(BLACK) + self.grid.draw(screen) diff --git a/grid.py b/grid.py new file mode 100644 index 0000000..5a43186 --- /dev/null +++ b/grid.py @@ -0,0 +1,117 @@ +import random +from setup import * + + +class Grid: + + def __init__(self, length, height, bomb_count): + self.length, self.height = length, height + self.grid = [[Cell(j, i) for j in range(self.length)] for i in range(self.height)] + + self.bomb_count = bomb_count + self.place_bombs(self.bomb_count) + + self.mouse_cooldown = 0 + + for row in self.grid: + for cell in row: + neighbors = [] + for i in range(-1, 2): + for j in range(-1, 2): + x = int(cell.position.x // Cell.cell_size + i) + y = int(cell.position.y // Cell.cell_size + j) + if 0 <= y < self.height and 0 <= x < self.length and not (i == 0 and j == 0): + neighbors.append(self.grid[y][x]) + cell.give_number(neighbors) + + def update(self, delta, mouse_pos, mouse_button): + if self.mouse_cooldown > 0: + self.mouse_cooldown -= delta + return False + + for row in self.grid: + for cell in row: + if cell.position.x <= mouse_pos[0] <= cell.position.x + Cell.cell_size and cell.position.y <= mouse_pos[1] <= cell.position.y + Cell.cell_size: + if mouse_button[0] == 1: + self.mouse_cooldown = 0.2 + if cell.update(self.grid, self.height, self.length): + return True + elif mouse_button[2] == 1: + if not cell.discovered: + cell.flagged = not cell.flagged + self.mouse_cooldown = 0.2 + return False + + def win(self): + for row in self.grid: + for cell in row: + if not cell.is_bomb and not cell.discovered: + return False + return True + + def draw(self, screen): + for row in self.grid: + for cell in row: + cell.draw(screen) + + def place_bombs(self, bomb_count): + while bomb_count > 0: + x = random.randint(0, self.length - 1) + y = random.randint(0, self.height - 1) + if not self.grid[y][x].is_bomb: + self.grid[y][x].is_bomb = True + bomb_count -= 1 + + +class Cell: + cell_size = 25 + font = pygame.font.SysFont("Arial", 20) + + def __init__(self, x, y): + self.position = pygame.Vector2(x * self.cell_size, y * self.cell_size) + self.is_bomb = False + self.flagged = False + self.number = 0 + self.discovered = False + self.color = GRAY + self.text = None + + def update(self, grid, height, length): + if self.discovered or self.flagged: + return False + self.discovered = True + self.color = GREEN + if self.is_bomb: + self.color = RED + return True + + if self.number == 0: + for i in range(-1, 2): + for j in range(-1, 2): + x = int(self.position.x // self.cell_size + i) + y = int(self.position.y // self.cell_size + j) + if 0 <= y < height and 0 <= x < length and not (i == 0 and j == 0): + if not grid[y][x].discovered and not grid[y][x].flagged: + grid[y][x].update(grid, height, length) + + return False + + def give_number(self, neighbors): + if self.is_bomb: + return + for neighbor in neighbors: + if neighbor.is_bomb: + self.number += 1 + + self.text = self.font.render(str(self.number), True, BLACK) + + def draw(self, screen): + display_color = self.color + if self.flagged: + display_color = BLUE + + pygame.draw.rect(screen, display_color, (self.position.x, self.position.y, self.cell_size, self.cell_size)) + pygame.draw.rect(screen, BLACK, (self.position.x, self.position.y, self.cell_size, self.cell_size), 1) + if self.discovered and self.number > 0: + screen.blit(self.text, self.text.get_rect( + center=(self.position.x + self.cell_size / 2, self.position.y + self.cell_size / 2))) diff --git a/main.py b/main.py new file mode 100644 index 0000000..af94832 --- /dev/null +++ b/main.py @@ -0,0 +1,26 @@ +from setup import * +from game import Game +from menus import Lose, Win + +def main(): + scene = Game() + is_running = True + delta = 0 + while is_running: + status = scene.update(delta) + scene.draw(screen) + + if status == COMMAND_EXIT: + is_running = False + elif status == COMMAND_RESTART: + scene = Game() + elif status == COMMAND_WIN: + scene = Win(screen) + elif status == COMMAND_LOSE: + scene = Lose(screen) + + pygame.display.update() + delta = clock.tick(60)/1000 + +if __name__ == "__main__": + main() diff --git a/menus.py b/menus.py new file mode 100644 index 0000000..67a8aec --- /dev/null +++ b/menus.py @@ -0,0 +1,46 @@ +from setup import * + + +class Menu: + title = pygame.font.SysFont("Arial", 30) + subtitle = pygame.font.SysFont("Arial", 20) + padding = 10 + + def __init__(self, screen, title, subtitle, text_color=RED): + self.bg = screen.copy() + self.titleText = self.title.render(title, True, text_color) + self.subtitleText = self.subtitle.render(subtitle, True, text_color) + + def update(self, delta): + for event in pygame.event.get(): + if event.type == pygame.QUIT: + return COMMAND_EXIT + elif event.type == pygame.KEYDOWN: + if event.key == pygame.K_r: + return COMMAND_RESTART + return COMMAND_NONE + + def draw(self, screen): + screen.blit(self.bg, (0, 0)) + titleRect = self.titleText.get_rect(center=(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2)) + subtitleRect = self.subtitleText.get_rect(center=(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 50)) + boxRect = pygame.Rect(min(titleRect.x, subtitleRect.x) - self.padding, + min(titleRect.y, subtitleRect.y) - self.padding, + max(titleRect.width, subtitleRect.width) + self.padding * 2, + titleRect.height / 2 + subtitleRect.height / 2 + abs( + titleRect.y - subtitleRect.y) + self.padding * 2) + pygame.draw.rect(screen, BLACK, boxRect) + pygame.draw.rect(screen, GRAY, boxRect, 4) + screen.blit(self.titleText, titleRect) + screen.blit(self.subtitleText, subtitleRect) + + +class Lose(Menu): + + def __init__(self, screen): + super().__init__(screen, "You Lose!", "Press R to restart") + + +class Win(Menu): + def __init__(self, screen): + super().__init__(screen, "You Win!", "Press R to play again", GREEN) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..79d580b --- /dev/null +++ b/setup.py @@ -0,0 +1,22 @@ +import pygame + +pygame.init() +SCREEN_WIDTH = 800 +SCREEN_HEIGHT = 600 +screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) +pygame.display.set_caption("Minesweeper") +clock = pygame.time.Clock() + +RED = (255, 0, 0) +GREEN = (0, 255, 0) +BLUE = (0, 0, 255) +BLACK = (0, 0, 0) +WHITE = (255, 255, 255) +GRAY = (128, 128, 128) + + +COMMAND_EXIT = 0 +COMMAND_RESTART = 1 +COMMAND_WIN = 2 +COMMAND_LOSE = 3 +COMMAND_NONE = 4 \ No newline at end of file