Anna Tol
Anna Tol

Reputation: 157

How can I generate the position of a second click in Pygame?

I'm making the Towers of Hanoi.

It should work like this: you click on the first tower, from where you want a disk to move, and then on the second where you want the disk to move to. The disk should move from the first tower (a list) to the second tower (another list).

My problem is that when you first click, the code generates the position, and soon after that you should click again to decide where the disk should go, but the code automatically takes the position of the first click.

Here's a sample of my code:

import pygame, sys
from pygame.locals import *

pygame.init()

DISPLAYSURF = pygame.display.set_mode((500, 400))
pygame.display.set_caption("Tower of Hanoi")

block_red = pygame.image.load('red.png')
block_blue = pygame.image.load('blue.png')
block_green = pygame.image.load('green.png')

rod1 = [block_red, block_blue, block_green]
rod2 = []
rod3 = []

WHITE = (255, 255, 255)

while True:

    DISPLAYSURF.fill(WHITE)

    # get the position of the mouse click
    for event in pygame.event.get():
        if event.type == MOUSEBUTTONDOWN:
            mousex, mousey = pygame.mouse.get_pos()
            click1 = mousex, mousey

            # first in the left part of the screen
            if (mousex > 0) and (mousex < 166) and (mousey > 0) and (mousey < 400):

                if len(rod1) == 0:
                    print "not valid"

                elif len(rod1) == 1 or 2 or 3:
                    disk1 = rod1[-1]
                    rod1.remove(disk1)

                    # click again in a other part of the screen
                    if event.type == MOUSEBUTTONDOWN:
                        mousex, mousey = pygame.mouse.get_pos()   
                        click2 = mousex, mousey

                        if (mousex > 166) and (mousex < 333) and (mousey > 0) and (mousey < 400):
                            rod2.append(disk1)
                        elif (mousex > 333) and (mousex < 500) and (mousey > 0) and (mousey < 400):
                            rod3.append(disk1)
                        else: 
                            rod1.append(disk1)

    # if statement fot the middle part
    # if statement for the right part                        

    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
    pygame.display.update()

Upvotes: 2

Views: 1278

Answers (3)

Roy Prins
Roy Prins

Reputation: 3080

I think this line will not do what you expect it to:

elif len(rod1) == 1 or 2 or 3:

Test in the interpreter that it does not evaluate to True but rather always to the value of 2.

You probably meant to do something like:

elif len(rod1) in (1, 2, 3):

Or even:

elif len(rod1) > 0:

Additionally, you could still go for a series of "or" statements to cover your needs:

[not recommended]

elif len(rod1) == 1 or len(rod1) == 2 or len(rod1) == 3:

If any one of the statements evaluates to True, the conditional statement will also be True.

Upvotes: 1

sloth
sloth

Reputation: 101162

Keep in mind that your code runs in a loop, so you have to do keep track of the state of your game.

Clicking on the first tower changes the state of your game: Now one tower is selected, and clicking on a tower now does something different (it moves a block from the first to the second tower).

In your example, you just need to keep track of the fact if a tower is currently selected or not (and the blocks each tower has, of course). Don't be afraid of the word state, a simple variable is enough.

Take a look at the following code (note the comments). It just keeps track of the selected rod in the variable selected, then later checks if it is set to decide if a block needs to be moved.

import pygame
from collections import namedtuple

pygame.init()
screen = pygame.display.set_mode((500, 400))

# create a named tuple to keep track of the size/location of the rods and their blocks
Rod = namedtuple('Rod', ['rect', 'items'])

# first rod has 4 items. The just use a number to keep track of the size of the blocks 
rods = (Rod(pygame.rect.Rect((100, 150, 25, 250)), [6, 5, 4, 3, 2, 1]),
        Rod(pygame.rect.Rect((225, 150, 25, 250)), []),
        Rod(pygame.rect.Rect((350, 150, 25, 250)), []))

# keep track of the currently selected rod
selected = None

while True:
    if pygame.event.get(pygame.QUIT): break

    screen.fill(pygame.color.Color('white'))

    # draw the rods. It's easy since every rod has a rect which we can use with pygame.draw.rect
    for rod in rods:
        # if a rod is selected, we draw it yellow instead of black
        pygame.draw.rect(screen, pygame.color.Color('yellow' if selected == rod else 'black'), rod.rect)    
        # draw each block of each rod
        for i, item in enumerate(rod.items):
            r = pygame.rect.Rect(rod.rect.x - item * 8, 375 - 25 * i, 25 + item * 16, 25) 
            pygame.draw.rect(screen, pygame.color.Color('green' if selected == rod else 'darkgreen'), r)    

    for e in pygame.event.get():
        if e.type == pygame.MOUSEBUTTONDOWN:
            # check if we clicked a rod. It's easy since every rod has a rect
            rod = next((r for r in rods if r.rect.collidepoint(pygame.mouse.get_pos())), None)
            if rod:
                if selected:
                    # if there's already a rod selected, move block from one the selected
                    # rod to the clicked rod
                    rod.items.append(selected.items.pop())
                    selected = None
                elif rod.items:
                    # if no rod is selected, selected the currently clicked one (if it has blocks)
                    selected = rod
            else:
                selected = None

    pygame.display.flip()   

Result:

enter image description here

Upvotes: 1

rwflash
rwflash

Reputation: 188

What I would personally do is put a loop around your code...

clicklocations = []
for clicknumber in range(0,1):
    # get the position of the mouse click
    for event in pygame.event.get():
        if event.type == MOUSEBUTTONDOWN:
            mousex, mousey = pygame.mouse.get_pos()
            clicklocations.append([mousex, mousey])

 # code for left part of the screen
 if clicklocations[0] in left part of screen, do stuff etc. etc.
 # code for middle part of the screen
 # code for right part of the screen

Something like this.

Then, you'll have both click locations stored and won't have to rewrite your clicking code twice.

Upvotes: 0

Related Questions