2

I am recreating minesweeper in pygame for my programming class, and I am having trouble with registering when the player clicks. I have a function called get_move(board) that takes in the game board as a parameter and does the following:

def get_move(board): click = None for event in pygame.event.get(): if event.type == pygame.MOUSEBUTTONDOWN: mouse_pos = pygame.mouse.get_pos() if event.button == 1: #Left click = 0 click = 0 if event.button == 3: #Right click = 1 click = 1 if click != None: for row in range(len(board.cells)): for col in range(len(board.cells[row])): if board.cells[row][col].rect.collidepoint(mouse_pos): return ((row, col), click) 

This function is being called at the beginning of my game loop as so:

while in_game: move = get_move(board) ... 

However, the clicks very rarely register, and only register once in a while when I rapidly press the mouse button. Any ideas as to what might be going on? Here is the full code (setup is a separate file I have that handles classes and basic setup functions). I replaced the event loop with pygame.mouse.get_pressed(), and it appears to be working:

import setup import pygame from pygame.locals import * pygame.init() def main(): zero = pygame.image.load("Pictures/0.png") one = pygame.image.load("Pictures/1.png") two = pygame.image.load("Pictures/2.png") three = pygame.image.load("Pictures/3.png") four = pygame.image.load("Pictures/4.png") five = pygame.image.load("Pictures/5.png") six = pygame.image.load("Pictures/6.png") seven = pygame.image.load("Pictures/7.png") eight = pygame.image.load("Pictures/8.png") nums = [zero, one, two, three, four, five, six, seven, eight] unopened = pygame.image.load("Pictures/Unopened.png") flag = pygame.image.load("Pictures/Flag.png") in_game = True difficulty = int(input("Choose a difficulty (1-3): ")) if difficulty == 1: n = 10 size = 9 if difficulty == 2: n = 40 size = 16 if difficulty == 3: n = 99 size = 22 SURFACE = pygame.display.set_mode((40 * size, 40 * size)) mines = setup.generate_mines(size, n) board = setup.Board(mines) setup.print_matrix(mines) ## Fill the board's cells x = 0 y = 0 for row in range(len(board.mine_matrix)): for col in range(len(board.mine_matrix[row])): num = board.mine_matrix[row][col] width = 40 height = 40 visible = False flagged = False board.cells[row][col] = setup.Cell(num, x, y, width, height, visible, flagged) x += 40 x = 0 y += 40 while in_game: move = get_move(board) board = update_board(board, move) for row in range(len(board.mine_matrix)): for col in range(len(board.mine_matrix[row])): cell = board.cells[row][col] if not cell.visible and cell.flagged: pic = flag elif not cell.visible and not cell.flagged: pic = unopened elif cell.visible and not cell.flagged: pic = nums[cell.num] SURFACE.blit(pic, cell.rect) pygame.display.update() for event in pygame.event.get(): if event.type == QUIT: pygame.quit() in_game = False def get_move(board, old_click=None): click = None """ for event in pygame.event.get(): if event.type == pygame.MOUSEBUTTONDOWN: mouse_pos = pygame.mouse.get_pos() if event.button == 1: #Left click = 0 click = 0 if event.button == 3: #Right click = 1 click = 1 """ click = pygame.mouse.get_pressed()[0] if click == 1 or click == 3: mouse_pos = pygame.mouse.get_pos() for row in range(len(board.cells)): for col in range(len(board.cells[row])): if board.cells[row][col].rect.collidepoint(mouse_pos): return ((row, col), click) def update_board(board, move): if move != None: row = move[0][0] col = move[0][1] click_type = move[1] cell = board.cells[row][col] if click_type == 1 and cell.flagged == False: if cell.num == 9: print("game over") else: open_cell(row, col, board) print(board.cells[row][col].visible) return board def open_cell(row, col, board): board.cells[row][col].visible = True if board.cells[row][col].num == 0: if row - 1 >= 0: if board.cells[row - 1][col].num == 0 and board.cells[row-1][col].visible == False: open_cell(row - 1, col, board) elif board.cells[row - 1][col].num != 9 and board.cells[row-1][col].visible == False: board.cells[row-1][col].visible = True if row + 1 < len(board.cells): if board.cells[row + 1][col].num == 0 and board.cells[row+1][col].visible == False: open_cell(row + 1, col, board) elif board.cells[row + 1][col].num != 9 and board.cells[row+1][col].visible == False: board.cells[row+1][col].visible = True if col - 1 >= 0: if board.cells[row][col-1].num == 0 and board.cells[row][col - 1].visible == False: open_cell(row, col-1, board) elif board.cells[row][col - 1].num != 9 and board.cells[row][col-1].visible == False: board.cells[row][col-1].visible = True if col + 1 < len(board.cells[row]): if board.cells[row][col+1].num == 0 and board.cells[row][col+1].visible == False: open_cell(row, col + 1, board) elif board.cells[row][col + 1].num != 9 and board.cells[row][col+1].visible == False: board.cells[row][col+1].visible = True if row + 1 < len(board.cells) and col + 1 < len(board.cells) and board.cells[row + 1][col + 1].num != 0: board.cells[row+1][col+1].visible = True if row + 1 < len(board.cells) and col - 1 >= 0 and board.cells[row + 1][col - 1].num != 0: board.cells[row+1][col-1].visible = True if row - 1 >= 0 and col + 1 < len(board.cells) and board.cells[row - 1][col + 1].num != 0: board.cells[row-1][col+1].visible = True if row - 1 >= 0 and col - 1 >= 0 and board.cells[row - 1][col - 1].num != 0: board.cells[row-1][col-1].visible = True main() 
2
  • 1
    Do you have any other event loop in your code? (I've noticed a similar problem when I had two different event loops.) In any case, it may be helpful to post your entire code. Commented May 9, 2019 at 19:25
  • I just added the rest of my code. Yes, I do have more than one event loop. I updated the code to use pygame.mouse.get_pressed() instead, and it seems to be working. Commented May 9, 2019 at 19:55

2 Answers 2

2

I've tried the pygame.mouse.get_pressed() approach, but it wasn't as versatile (e.g. just having whether the button is pressed down doesn't tell you if the button was clicked---pressed down and released). I would recommend using a class with a method which handles pygame events. It might look like this:

class get_move_class(): def __init__(self): self.click = None def handle_event(self, event, board): self.click = None if event.type == pygame.MOUSEBUTTONDOWN: mouse_pos = pygame.mouse.get_pos() if event.button == 1: #Left click = 0 self.click = 0 if event.button == 3: #Right click = 1 self.click = 1 if self.click != None: for row in range(len(board.cells)): for col in range(len(board.cells[row])): if board.cells[row][col].rect.collidepoint(mouse_pos): return ((row, col), click) # Return something return None 

And then the loop in main would have:

# Instantiate the class obj_get_move = get_move_class() while in_game: for event in pygame.event.get(): move = obj_get_move.handle_event(event, board) ... 

To me, this approach seems less intuitive than your solution, but I think it handles the event loop better, can more easily be adapted for other events, and doesn't freeze up the loop.

Sign up to request clarification or add additional context in comments.

1 Comment

it can be done even as function get_move() or handle_event() in Player class but this is not important in problem. More important thing is - which you show in code - it has to be only one for event and it has to be outside handle_event() or get_move(). +1
-1

Tl;dr - Ensure that you never have parallel event loops capturing user input competing to process the inputs.

This was somewhat addressed in some comments above, but I wanted to provide an actual answer reply to the question for visibility for those learning to work with pygame that come across this as a top Google result, and then forget you found a potential answer and debug obsessively for four hours on your own. I'm going through the ceremonial 40x tab closing session after a marathon weekend, and not ashamed to admit that it took me an embarrassing amount of time relative to my experience working as a developer to realize that "dropped" inputs aren't the result of clock ticks, etc, and it was having parallel event loops running.

I didn't 100% confirm it, but I believe that this causes a race condition where the competing event loops starve each other. I had pretty much the same issue as OP, and after refactoring the main loop and event loops to ensure everything is sequential and properly queued into a single place, all of my issues immediately resolved. It was an annoying process 24hrs into a dev prototype, but well worth it, since it runs bug free after the overhaul. For reference, my issue was a super simple action in the prototype to consistently move an object from one grid point to another following a mousedown event.

Also, I'd like to reiterate another point in one of the replies for anyone learning, a mousedown event != a full click (button down && button up). Lastly, that a mousedown event has other uses, like dragging the mouse by checking the mouse position after a mouse down event and then again after a subsequent mouseup, etc.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.