Initial commit
| 
						 | 
					@ -0,0 +1,157 @@
 | 
				
			||||||
 | 
					import pygame
 | 
				
			||||||
 | 
					import random
 | 
				
			||||||
 | 
					from square import Square
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Board:
 | 
				
			||||||
 | 
					    def __init__(self, size, square_size, surface):
 | 
				
			||||||
 | 
					        self.surface = surface
 | 
				
			||||||
 | 
					        self.size = size
 | 
				
			||||||
 | 
					        self.square_size = square_size
 | 
				
			||||||
 | 
					        self.game_lost = False
 | 
				
			||||||
 | 
					        self.game_won = False
 | 
				
			||||||
 | 
					        self.total_flags = 0
 | 
				
			||||||
 | 
					        self.coords = (0,50)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.unclicked_squares = size[0] * size[1]
 | 
				
			||||||
 | 
					        self.total_mines = round(.2*self.unclicked_squares)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Makes a 2D list of Square objects
 | 
				
			||||||
 | 
					        start_x = self.coords[0]
 | 
				
			||||||
 | 
					        start_y = self.coords[1]
 | 
				
			||||||
 | 
					        end_x = start_x + self.square_size*size[1]
 | 
				
			||||||
 | 
					        end_y = start_y + self.square_size*size[0]
 | 
				
			||||||
 | 
					        self.squares = [[Square(self, i, j)
 | 
				
			||||||
 | 
					            for i in range(start_x, end_x, self.square_size)]
 | 
				
			||||||
 | 
					            for j in range(start_y, end_y, self.square_size)] 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Font object for mine counter and timer
 | 
				
			||||||
 | 
					        self.game_font = pygame.font.Font('freesansbold.ttf', 48)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update_mine_counter(self):
 | 
				
			||||||
 | 
					        # Makes the mine counter the right size
 | 
				
			||||||
 | 
					        self.mine_counter = self.game_font.render("99", True, (255,0,0))
 | 
				
			||||||
 | 
					        self.counter_loc = self.mine_counter.get_rect()
 | 
				
			||||||
 | 
					        self.counter_loc.bottomleft = self.coords
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Makes the background of the counter black
 | 
				
			||||||
 | 
					        self.mine_counter.fill((0,0,0))
 | 
				
			||||||
 | 
					        self.surface.blit(self.mine_counter, self.counter_loc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mines_left = str(self.total_mines - self.total_flags)
 | 
				
			||||||
 | 
					        if len(mines_left) == 1:
 | 
				
			||||||
 | 
					            mines_left = "0" + mines_left
 | 
				
			||||||
 | 
					        self.mine_counter = self.game_font.render(mines_left, True, (255,0,0))
 | 
				
			||||||
 | 
					        self.counter_loc = self.mine_counter.get_rect()
 | 
				
			||||||
 | 
					        self.counter_loc.bottomleft = self.coords
 | 
				
			||||||
 | 
					        self.surface.blit(self.mine_counter, self.counter_loc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check_for_win(self):
 | 
				
			||||||
 | 
					        only_mines_left = self.total_mines == self.unclicked_squares
 | 
				
			||||||
 | 
					        if not self.game_lost and only_mines_left:
 | 
				
			||||||
 | 
					            self.game_won = True
 | 
				
			||||||
 | 
					            self.flag_remaining_mines()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def show_unflagged_mines(self):
 | 
				
			||||||
 | 
					        for row in self.squares:
 | 
				
			||||||
 | 
					            for s in row:
 | 
				
			||||||
 | 
					                if s.is_mine and not s.is_flagged:
 | 
				
			||||||
 | 
					                    s.reveal()
 | 
				
			||||||
 | 
					                elif not s.is_mine and s.is_flagged:
 | 
				
			||||||
 | 
					                    s.draw_line()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def flag_remaining_mines(self):
 | 
				
			||||||
 | 
					        for row in self.squares:
 | 
				
			||||||
 | 
					            for s in row:
 | 
				
			||||||
 | 
					                if s.is_mine and s.is_questioned:
 | 
				
			||||||
 | 
					                    s.cycle_flag()
 | 
				
			||||||
 | 
					                if s.is_mine and not s.is_flagged:
 | 
				
			||||||
 | 
					                    s.cycle_flag()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_clicked_square(self, mousepos):
 | 
				
			||||||
 | 
					        mousex, mousey = mousepos
 | 
				
			||||||
 | 
					        for row in self.squares:
 | 
				
			||||||
 | 
					            for s in row:
 | 
				
			||||||
 | 
					                sx, sy = s.coords
 | 
				
			||||||
 | 
					                too_far_right = sx+self.square_size < mousex
 | 
				
			||||||
 | 
					                too_far_up = sy+self.square_size < mousey
 | 
				
			||||||
 | 
					                too_far_down = sy > mousey
 | 
				
			||||||
 | 
					                if not (too_far_right or too_far_up or too_far_down):
 | 
				
			||||||
 | 
					                    return s
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_index(self, s):
 | 
				
			||||||
 | 
					        for row in self.squares:
 | 
				
			||||||
 | 
					            if s in row:
 | 
				
			||||||
 | 
					                return (self.squares.index(row), row.index(s))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Places mines in such a way that the first space clicked will be a 0 space
 | 
				
			||||||
 | 
					    # Returns False if a mine was clicked, returns True otherwise
 | 
				
			||||||
 | 
					    def place_mines(self, mousepos):
 | 
				
			||||||
 | 
					        square = self.get_clicked_square(mousepos)
 | 
				
			||||||
 | 
					        if square is None:
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        adj_squares = self.squares_adjacent_to(square)
 | 
				
			||||||
 | 
					        free_spaces = self.unclicked_squares-self.total_mines-1-len(adj_squares)
 | 
				
			||||||
 | 
					        is_mine = ([True]*self.total_mines + [False]*free_spaces)
 | 
				
			||||||
 | 
					        random.shuffle(is_mine)
 | 
				
			||||||
 | 
					        for row in self.squares:
 | 
				
			||||||
 | 
					            for s in row:
 | 
				
			||||||
 | 
					                if s is not square and s not in adj_squares:
 | 
				
			||||||
 | 
					                    s.is_mine = is_mine.pop()
 | 
				
			||||||
 | 
					        for row in self.squares:
 | 
				
			||||||
 | 
					            for s in row:
 | 
				
			||||||
 | 
					                adj_squares = self.squares_adjacent_to(s)
 | 
				
			||||||
 | 
					                total_adj_mines = sum([i.is_mine for i in adj_squares])
 | 
				
			||||||
 | 
					                s.mines_touching = total_adj_mines
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def squares_adjacent_to(self, s):
 | 
				
			||||||
 | 
					        x, y = self.get_index(s)
 | 
				
			||||||
 | 
					        adj_indices = [(x-1,y-1), (x,y-1), (x+1,y-1),
 | 
				
			||||||
 | 
					                (x-1,y), (x+1,y),
 | 
				
			||||||
 | 
					                (x+1,y+1), (x,y+1), (x-1,y+1)]
 | 
				
			||||||
 | 
					        adj_squares = [self.squares[i[0]][i[1]] for i in adj_indices 
 | 
				
			||||||
 | 
					            if 0 <= i[0] < self.size[0] and 0 <= i[1] < self.size[1]]
 | 
				
			||||||
 | 
					        return adj_squares
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def open(self, s):
 | 
				
			||||||
 | 
					        if s.is_clicked or s.is_flagged:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        s.reveal()
 | 
				
			||||||
 | 
					        if s.is_clicked:
 | 
				
			||||||
 | 
					            self.unclicked_squares -= 1
 | 
				
			||||||
 | 
					        if s.is_mine and s.is_clicked:
 | 
				
			||||||
 | 
					            self.game_lost = True
 | 
				
			||||||
 | 
					            self.show_unflagged_mines()
 | 
				
			||||||
 | 
					        elif not s.is_mine and s.is_clicked and s.mines_touching == 0:
 | 
				
			||||||
 | 
					            self.open_squares_adjacent_to(s)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def open_squares_adjacent_to(self, square):
 | 
				
			||||||
 | 
					        adj_squares = self.squares_adjacent_to(square)
 | 
				
			||||||
 | 
					        counter = 0
 | 
				
			||||||
 | 
					        for s in adj_squares:
 | 
				
			||||||
 | 
					            if s.is_flagged:
 | 
				
			||||||
 | 
					                counter += 1
 | 
				
			||||||
 | 
					        if counter != square.mines_touching:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        for s in adj_squares:
 | 
				
			||||||
 | 
					            self.open(s)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def left_click(self, mousepos):
 | 
				
			||||||
 | 
					        square = self.get_clicked_square(mousepos)
 | 
				
			||||||
 | 
					        if square is None:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        elif square.is_clicked:
 | 
				
			||||||
 | 
					            self.open_squares_adjacent_to(square)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        self.open(square)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def right_click(self, mousepos):
 | 
				
			||||||
 | 
					        square = self.get_clicked_square(mousepos)
 | 
				
			||||||
 | 
					        if square is None:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        square.cycle_flag()
 | 
				
			||||||
 | 
					        if square.is_flagged:
 | 
				
			||||||
 | 
					            self.total_flags += 1
 | 
				
			||||||
 | 
					        elif square.is_questioned:
 | 
				
			||||||
 | 
					            self.total_flags -= 1
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,48 @@
 | 
				
			||||||
 | 
					import pygame
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					from pygame.locals import *
 | 
				
			||||||
 | 
					from square import Square
 | 
				
			||||||
 | 
					from board import Board
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pygame.init()
 | 
				
			||||||
 | 
					# Pygame/ALSA has a bug that results in high CPU usage. This line reduces the
 | 
				
			||||||
 | 
					# CPU usage
 | 
				
			||||||
 | 
					pygame.mixer.quit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DISPLAYSURF = pygame.display.set_mode((1280, 720))
 | 
				
			||||||
 | 
					DISPLAYSURF.fill((185,185,185))
 | 
				
			||||||
 | 
					pygame.display.set_caption("Minesweeper")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					board = Board((16, 31), 40, DISPLAYSURF)
 | 
				
			||||||
 | 
					# board = Board((9, 10), 48, DISPLAYSURF)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Prevents mouse movement from counting as an event, reducing CPU usage
 | 
				
			||||||
 | 
					pygame.event.set_blocked(MOUSEMOTION)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					first_left_click = True
 | 
				
			||||||
 | 
					while True:
 | 
				
			||||||
 | 
					    board.update_mine_counter()
 | 
				
			||||||
 | 
					    board.check_for_win()
 | 
				
			||||||
 | 
					    pygame.display.update()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # TODO: Add options to play again
 | 
				
			||||||
 | 
					    if board.game_lost:
 | 
				
			||||||
 | 
					        print("You lose")
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					    elif board.game_won:
 | 
				
			||||||
 | 
					        print("You win")
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Program waits here until a non-mouse movement event is read
 | 
				
			||||||
 | 
					    event = pygame.event.wait()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if event.type == QUIT:
 | 
				
			||||||
 | 
					        pygame.quit()
 | 
				
			||||||
 | 
					        sys.exit()
 | 
				
			||||||
 | 
					    elif event.type == MOUSEBUTTONUP and event.button == 1:
 | 
				
			||||||
 | 
					        # FIXME: Need a check to make sure a square was actually clicked
 | 
				
			||||||
 | 
					        if first_left_click:
 | 
				
			||||||
 | 
					            first_left_click = board.place_mines(event.pos)
 | 
				
			||||||
 | 
					        board.left_click(event.pos)
 | 
				
			||||||
 | 
					    elif event.type == MOUSEBUTTONUP and event.button == 3:
 | 
				
			||||||
 | 
					        board.right_click(event.pos)
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 262 B  | 
| 
		 After Width: | Height: | Size: 518 B  | 
| 
		 After Width: | Height: | Size: 1.5 KiB  | 
| 
		 After Width: | Height: | Size: 921 B  | 
| 
		 After Width: | Height: | Size: 954 B  | 
| 
		 After Width: | Height: | Size: 709 B  | 
| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 After Width: | Height: | Size: 1.1 KiB  | 
| 
		 After Width: | Height: | Size: 798 B  | 
| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 After Width: | Height: | Size: 328 B  | 
| 
		 After Width: | Height: | Size: 9.5 KiB  | 
| 
						 | 
					@ -0,0 +1,69 @@
 | 
				
			||||||
 | 
					import pygame
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Square:
 | 
				
			||||||
 | 
					    def __init__(self, board, x, y):
 | 
				
			||||||
 | 
					        self.board = board
 | 
				
			||||||
 | 
					        self.surface = board.surface
 | 
				
			||||||
 | 
					        self.size = board.square_size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.is_clicked = False
 | 
				
			||||||
 | 
					        self.is_mine = False
 | 
				
			||||||
 | 
					        self.is_flagged = False
 | 
				
			||||||
 | 
					        self.is_questioned = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.mines_touching = 0
 | 
				
			||||||
 | 
					        self.coords = (x, y)
 | 
				
			||||||
 | 
					        self.set_img("png/Minesweeper_unopened_square.png")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def draw_line(self):
 | 
				
			||||||
 | 
					        RED = (255,0,0)
 | 
				
			||||||
 | 
					        top_left = self.coords
 | 
				
			||||||
 | 
					        bottom_right = (self.coords[0]+self.size, self.coords[1]+self.size)
 | 
				
			||||||
 | 
					        pygame.draw.line(self.surface, RED, top_left, bottom_right, 10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update_img(self):
 | 
				
			||||||
 | 
					        if self.is_mine:
 | 
				
			||||||
 | 
					            self.set_img("png/bomb.png")
 | 
				
			||||||
 | 
					        elif self.mines_touching == 0:
 | 
				
			||||||
 | 
					            self.set_img("png/Minesweeper_0.png")
 | 
				
			||||||
 | 
					        elif self.mines_touching == 1:
 | 
				
			||||||
 | 
					            self.set_img("png/Minesweeper_1.png")
 | 
				
			||||||
 | 
					        elif self.mines_touching == 2:
 | 
				
			||||||
 | 
					            self.set_img("png/Minesweeper_2.png")
 | 
				
			||||||
 | 
					        elif self.mines_touching == 3:
 | 
				
			||||||
 | 
					            self.set_img("png/Minesweeper_3.png")
 | 
				
			||||||
 | 
					        elif self.mines_touching == 4:
 | 
				
			||||||
 | 
					            self.set_img("png/Minesweeper_4.png")
 | 
				
			||||||
 | 
					        elif self.mines_touching == 5:
 | 
				
			||||||
 | 
					            self.set_img("png/Minesweeper_5.png")
 | 
				
			||||||
 | 
					        elif self.mines_touching == 6:
 | 
				
			||||||
 | 
					            self.set_img("png/Minesweeper_6.png")
 | 
				
			||||||
 | 
					        elif self.mines_touching == 7:
 | 
				
			||||||
 | 
					            self.set_img("png/Minesweeper_7.png")
 | 
				
			||||||
 | 
					        elif self.mines_touching == 8:
 | 
				
			||||||
 | 
					            self.set_img("png/Minesweeper_8.png")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def set_img(self, img):
 | 
				
			||||||
 | 
					        self.img = pygame.image.load(img)
 | 
				
			||||||
 | 
					        self.img = pygame.transform.scale(self.img, (self.size, self.size))
 | 
				
			||||||
 | 
					        self.surface.blit(self.img, self.coords)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def reveal(self):
 | 
				
			||||||
 | 
					        if not self.is_flagged and not self.is_clicked:
 | 
				
			||||||
 | 
					            self.is_clicked = True
 | 
				
			||||||
 | 
					            self.is_questioned = False
 | 
				
			||||||
 | 
					            self.update_img()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def cycle_flag(self):
 | 
				
			||||||
 | 
					        if self.is_clicked:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        elif self.is_flagged:
 | 
				
			||||||
 | 
					            self.set_img("png/Minesweeper_questionmark.png")
 | 
				
			||||||
 | 
					            self.is_flagged = False
 | 
				
			||||||
 | 
					            self.is_questioned = True
 | 
				
			||||||
 | 
					        elif self.is_questioned:
 | 
				
			||||||
 | 
					            self.set_img("png/Minesweeper_unopened_square.png")
 | 
				
			||||||
 | 
					            self.is_questioned = False
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            self.set_img("png/Minesweeper_flag.png")
 | 
				
			||||||
 | 
					            self.is_flagged = True
 | 
				
			||||||