# Arup Guha
# 6/23/2025
# Defines Minesweeper Object for Minesweeper game.

import random
import time
import pygame, sys
import minefunc
from pygame.locals import *

class mineclass:

    # Initializes a minesweeper board with the first click on x,y.
    def __init__(self, x, y):

        # This is an empty board.
        self.board = []
        for i in range(minefunc.ROWS):
            tmp = []
            for j in range(minefunc.COLS):
                tmp.append(' ')
            self.board.append(tmp)

        # Place bombs.
        i = 0
        while i < minefunc.NUMB:

            # Generate a random location.
            loc = random.randint(0, minefunc.ROWS*minefunc.COLS-1)
            myx = loc//minefunc.COLS
            myy = loc%minefunc.COLS

            # Can't place bomb on the first clicked square.
            if myx == x and myy == y:
                continue

            # Forces unique squars.
            if self.board[myx][myy] == '*':
                continue

            # Very helpful for testing.
            if minefunc.DEBUG:
                print(myx, myy, "making bomb")
            
            # Place a bomb here.
            self.board[myx][myy] = '*'
            i += 1

        # Here we generate the hidden board with all numbers and bombs.
        for i in range(minefunc.ROWS):
            for j in range(minefunc.COLS):

                # These squares are done.
                if self.board[i][j] == '*':
                    continue

                # Put the number here by calling the function to count the adjacent bombs.
                self.board[i][j] = chr(ord('0') + self.numAdj(i, j))

        # This will keep track of what we show to the player.
        self.show = []
        for i in range(minefunc.ROWS):
            tmp = []
            for j in range(minefunc.COLS):
                tmp.append(False)
            self.show.append(tmp)

        # Number of squares we must clear.
        self.need = minefunc.ROWS*minefunc.COLS - minefunc.NUMB

    # Returns the number of bombs adjacent to (x, y) on board myb.
    def numAdj(self, x, y):

        res = 0

        # Go in all directions.
        for i in range(len(minefunc.DX)):

            # Adjacent square i.
            nX = x + minefunc.DX[i]
            nY = y + minefunc.DY[i]

            # Out of bounds.
            if nX < 0 or nX >= minefunc.ROWS or nY < 0 or nY >= minefunc.COLS:
                continue

            # It's a bomb, count it.
            if self.board[nX][nY] == '*':
                res += 1

        # Total number of adjacent bombs.
        return res

    # For the end, we show all.
    def showAll(self):
        for i in range(minefunc.ROWS):
            for j in range(minefunc.COLS):
                self.show[i][j] = True

    # Executes a move at row x, column y.
    def move(self, x, y):

        # Check out of bounds first.
        if x < 0 or x >= minefunc.ROWS or y < 0 or y >= minefunc.COLS:
            return minefunc.NO_MOVE

        # Code for no move...because it's been cleared.
        if self.show[x][y]:
            return minefunc.NO_MOVE

        # You hit a bomb.
        if self.board[x][y] == '*':
            self.showAll()
            return minefunc.LOST

        # Mark it.
        self.show[x][y] = True
        self.need -= 1

        # Recursive clear check.
        if self.board[x][y] == '0':

            # Here it is!
            for i in range(len(minefunc.DX)):
                self.move(x + minefunc.DX[i], y + minefunc.DY[i])

        # Here is how we determine our current game state.
        if self.need > 0:
            return minefunc.CONT
        else:
            return minefunc.WIN

    # Draws this object on this surface. (topx, topy) is the top left square.
    # step is the side length of each square in pixels. Only draws visible squares
    # on top of default board.
    def drawVisible(self, surface, topx, topy, step):

        font = pygame.font.SysFont(None, 45)
        
        # Go through the board.
        for i in range(minefunc.ROWS):
            for j in range(minefunc.COLS):

                # Still hidden.
                if not self.show[i][j]:
                    continue

                # Here is what we write on top.
                minefunc.draw(self.board[i][j], font, 'blue', surface, topx+step*j+minefunc.OFF_X, topy+step*i+minefunc.OFF_Y)
