Search code examples
pythonuser-interfacepygamebacktrackingknights-tour

Pygame Becomes Unresponsive after running for a few seconds


I'm trying to write a GUI to display the Knights Tour backtracking algorithm. I have the algorithm working but when running the GUI, it becomes unresponsive after a few seconds.

import pygame
import time

# Init Gui

pygame.init()


# Window Size
WIN_DIMENSION = 800

win = pygame.display.set_mode((WIN_DIMENSION, WIN_DIMENSION))
pygame.display.set_caption("Knights Tour")


# Board Size
N = 8

TARGETMOVES = N**2

# Colors
white = (255, 255, 255)
black = (0, 0, 0)
red = (180, 0, 0)

def DisplayGui(board, final=False):

    win.fill(black)

    for i in range(N):
        ydraw = i * (WIN_DIMENSION/N)
        for n in range(N):
            xdraw = n * (WIN_DIMENSION / N)
            pygame.draw.rect(win, red, (xdraw, ydraw, WIN_DIMENSION/N, WIN_DIMENSION/N), 3)
            displayText(board[i][n], xdraw, ydraw)

    while final:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                final = False
                print("CLOSING")

    pygame.display.update()
    #time.sleep(1)


def text_objects(text, font):
    textSurface = font.render(str(text), True, white)
    return textSurface, textSurface.get_rect()


def displayText(text, xdraw, ydraw):
    font = pygame.font.Font('freesansbold.ttf', 7 * N)
    TextSurf, TextRect = text_objects(text, font)
    TextRect.center = ((xdraw + (WIN_DIMENSION / N) / 2), (ydraw + (WIN_DIMENSION / N) / 2))
    win.blit(TextSurf, TextRect)


def checkValid(board, movx, movy):
    '''
        params:
        movx => next x move
        movy => next y move

        checks if move is valid
    '''
    if (movx >= 0 and movy >= 0 and movx < N and movy < N and board[movx][movy] == " "):
        return True
    return False


def printBoard(board):
    '''
        Prints Board
    '''
    for i in range(len(board)):
        print(board[i])


def KnightsTour():
    currx = 0
    curry = 0

    # Init board
    board = [[" " for i in range(N)] for i in range(N)]

    xmoves = [-2, -2, -1, -1, 1, 1, 2, 2]
    ymoves = [1, -1, 2, -2, 2, -2, 1, -1]

    totalmoves = 1

    board[0][0] = 0
    DisplayGui(board)

    if generateMove(board, currx, curry, totalmoves, xmoves, ymoves):
        printBoard(board)
        DisplayGui(board, True)
    else: print("Invalid")





def generateMove(board, currx, curry, totalmoves, xmoves, ymoves):
    if totalmoves == TARGETMOVES:
        return True

    print("X: {} <> Y: {}".format(currx, curry)) # draw  board here

    DisplayGui(board)        


    for i in range(8):

        nextx = currx + xmoves[i]
        nexty = curry + ymoves[i]

        if checkValid(board, nextx, nexty):
            board[nextx][nexty] = totalmoves


            if generateMove(board, nextx, nexty, totalmoves+1, xmoves, ymoves):

                return True
            # backtracking
            board[nextx][nexty] = " "

    return False


if __name__ == "__main__":
    KnightsTour()

I tried to slow it down using sys.sleep() but that didn't help it. I have no idea whats causing the GUI to freeze. The algo still runs in the background.


Solution

  • If you want to overcome this problem without changing your code significantly you can call pygame.event.pump() in your DisplayGui() function. This will make your operating system / window manager believe that your program hasn't frozen.

    def DisplayGui(board, final=False):
    
        win.fill(black)
        if not final:
            pygame.event.pump()
        for i in range(N):
            ...
    

    It would be more helpful to actually handle QUIT events so you could end the program prematurely without ending the python process.

    If you're considering reorganising your code, perhaps this openbook would be useful. This section describes a standard game loop which should look something like:

    event handling → update state → draw new state → update display