Search code examples
pythonturtle-graphicspython-turtleminesweeper

Python find closest turtle via mouse click


I'm in the process of creating a minesweeper style game using a turtle-based grid setup. I need to find the closest cell within the grid and reveal the icon located under it whether that be a bomb or a number icons. I'm not looking to make it exact, I just need the mouse click to find the nearest cell in the grid even if the click isn't directly on the board. Currently my code only reveals the icon of the last turtle created on the board and then does nothing else with further clicks.

What can I do to make it recognize the real closest click and do it multiple times until the last bomb is found?

import random
import turtle
import cell

class Game:

def __init__(self, size):

    registershapes()
    self.__boardsize = size
    self.__boardlist = []
    self.__bombnum = 0
    self.__probe = 0
    self.__probelist = []
    offset = (size-1) * 17
    for x in range(size):
        for y in range(size):
            t = cell.Cell(x,y)
            t.up()
            t.shape('question.gif')
            t.goto(y*34-offset, offset-x*34)
            self.__boardlist.append(t)
def hideMines(self, num):
    if num > self.__boardsize ** 2:
        return False
    self.__bombnum = num
    self.__rnums = []
    i = 0
    while i < self.__bombnum:
        currentnum = random.randrange(0, (self.__boardsize**2) - 1)
        if currentnum not in self.__rnums:
            self.__rnums.append(currentnum)
            i += 1
    return True

def probe(self, x, y):
    for t in self.__boardlist:
        pos = t.position()
        distx = abs(x - pos[0])
        disty = abs(y - pos[1])
        distfinal = (distx ** 2 + disty ** 2) ** 0.5
        curdist = 0
        if curdist < distfinal:
            curdist = distfinal
            closest = t
    if closest in self.__probelist:
        return (self.__probe, self.__bombnum)
    elif closest in self.__rnums:
        closest.shape("bomb.gif")
        self.__bombnum -= 1
        self.__probe += 1
        self.__probelist.append(closest)
        return (self.__probe, self.__bombnum)

    else:
        closest.shape("0.gif")
        self.__probe += 1
        self.__probelist.append(closest)
        return (self.__probe, self.__bombnum)


def registershapes():
    wn = turtle.Screen()
    wn.register_shape('0.gif')
    wn.register_shape('1.gif')
    wn.register_shape('2.gif')
    wn.register_shape('3.gif')
    wn.register_shape('4.gif')
    wn.register_shape('5.gif')
    wn.register_shape('6.gif')
    wn.register_shape('7.gif')
    wn.register_shape('8.gif')
    wn.register_shape('9.gif')
    wn.register_shape('bomb.gif')
    wn.register_shape('question.gif')

Solution

  • I believe you're approaching this problem the wrong way. You're activating screen.onclick() and trying to map it to a turtle. Instead, activate turtle.onclick() on the individual turtles, deactivating it when a turtle is selected. Then you don't have to search for the turtle in question, and not actively ignore turtles that have already been selected.

    Below is my rework of your code from this and your previous question into an example you can run. I had to guess about the definition of the Cell class:

    from turtle import Turtle, Screen
    import random
    
    class Cell(Turtle):
    
        def __init__(self, number):
            super().__init__("question.gif")
            self.__number = number
            self.penup()
    
        def number(self):
            return self.__number
    
    class Game:
    
        def __init__(self, size):
            registershapes()
            self.__boardsize = size
            self.__boardlist = []
            self.__bombnum = 0
            self.__probe = 0
            self.__rnums = None
    
            offset = (size - 1) * 17
    
            for y in range(size):
                for x in range(size):
                    t = Cell(x + y * size)
                    t.goto(x * 34 - offset, offset - y * 34)
                    t.onclick(lambda x, y, self=t: closure(self))
                    self.__boardlist.append(t)
    
        def hideMines(self, num):
            if num > self.__boardsize ** 2:
                return False
    
            self.__bombnum = num
            self.__rnums = []
            i = 0
    
            while i < self.__bombnum:
                currentnum = random.randrange(0, self.__boardsize ** 2 - 1)
    
                if currentnum not in self.__rnums:
                    self.__rnums.append(currentnum)
                    i += 1
            return True
    
        def probe(self, closest):
            closest.onclick(None)
    
            if closest.number() in self.__rnums:
                closest.shape("bomb.gif")
                self.__bombnum -= 1
            else:
                closest.shape("0.gif")
    
            self.__probe += 1
            return (self.__probe, self.__bombnum)
    
    def registershapes():
        screen.register_shape('0.gif')
        # ...
        screen.register_shape('bomb.gif')
        screen.register_shape('question.gif')
    
    def closure(closest):
        _, rem = mine.probe(closest)
    
        if rem == 0:
    
            over = screen.textinput("Text Input", "Would you like to play again? (Y)es or (N)o")
    
            if over.upper() == 'Y':
                main()
            else:
                screen.bye()
    
    def main():
        global mine
    
        board = screen.numinput("Numeric Input", "Enter desired board size: ")
        mine = Game(int(board))
    
        nummine = screen.numinput("Numeric Input", "Enter desired number of mines: ")
        mine.hideMines(int(nummine))
    
    screen = Screen()
    
    mine = None
    main()
    
    screen.mainloop()
    

    enter image description here