pygame realizes tic tac toe chess - 3 Logical optimization

Posted by wisewood on Fri, 18 Feb 2022 15:47:47 +0100

preface

Let's talk about the goal of this period first.
First of all, when we judge the win or loss and draw, there is only one console output, which obviously does not conform to the doge of our game
In addition, there is a loophole in the previous logic, that is, the player must be the first player (that is, the first person to play chess, which also needs to be modified).
In addition, there is a short pop-up window to show who it is, and the playability of the game will be improved.
Finally, if there is a problem with the click position, we need to display it.

Rendering problem

The car overturned. The previous logic was to write the Jiugong grid and background color to death, and the previous rendering was directly based on the previous one, but our pop-up window needs to be removed in time after the end, so we need to constantly re render the interface.

I have to change it.

First, we don't need to use draw The rect method creates a rectangle, but uses another pyGame Rect function.
At the same time, in order to save nine lines of basically the same code, I created a tuple to store.
To create the latest version:

# Represents nine squares
rect = [0]*9
rect_wh = [
    (1,1), (single+3,1), (single*2+5,1), 
    (1,single+3), (single+3,single+3), (single*2+5,single+3),
    (1,single*2+5), (single+3,single*2+5), (single*2+5,single*2+5)
    ]
for i in range(len(rect)):
    rect[i] = pygame.Rect(*rect_wh[i],single,single)
    rect[i] = Lattice(rect[i],screen)

Rect function:
By passing in x, y coordinates and height and width, you can create a rect object.
What needs to be explained here is * rect_wh[i], the latter part is to find a specified element in the tuple list, and the asterisk is used to unpack the tuple.

Encapsulation and unpacking

a, b = 1, 2
First, we encapsulate the following two variables into a tuple and assign them to the part before the equal sign;
There are two variables in the front, so we also need to unpack, that is, split a tuple into variables one by one.

Notes recorded at that time:

Back to the previous grid problem, another detail is how we should display the division lines around the grid. Previously, we used the ones that came with drawing rectangles. Now they can't be used anymore because of the problem of refreshing (otherwise, who can stand to create 9 rectangles and draw them every time they are refreshed).
My method is to reduce the size of single:

single = width/3 - 1

It should be noted that I modified the value of (x,y) in the tuple list, so that we can draw such a graph:

(original is not easy. The whole eye is going blind, but it is still not perfect -|)
bg_color = (0, 0, 0), black.
We also have a draw Rect method, pass in screen, (255, 255, 255), rect object, and we can display a white rectangle.
Because the width is a little smaller, we can see the great boundary (much better than what I drew myself)

(ah, why is it yellow, because I changed bg_color..., no problem)

Current update method:

def update():
    screen.fill((255,228,181))
    for i in rect:
        pygame.draw.rect(screen,(255, 255, 255),i.rect)
        i.draw()

Pop up display

Pop ups added:
Win or lose, draw pop-up window, exit the program after 3s;
If there is a problem with which side plays chess and the position of playing chess, quit after 0.3s.

After reading the alien series, I should know that there is a great regret that the given button class is a little special, so I can only add the button class myself later.
This time, the pop-up class I wrote will be more applicable.

popup.py

"""During the game, add various pop-up windows"""
import pygame
class Popup():
    def __init__(self, screen,msg):
        self.msg = msg
        self.screen = screen
        self.bg_color = (0, 0, 0)
        self.text_color = (230, 230, 230)
        self.font = pygame.font.SysFont(None,48)
        self.msg_image = self.font.render(msg,True,self.text_color,self.bg_color)
        self.msg_rect = self.msg_image.get_rect()
        self.screen_rect = self.screen.get_rect()
        self.msg_rect.center = self.screen_rect.center
        self.screen.blit(self.msg_image,self.msg_rect)

Pass in a message to be displayed, and then you can render it to the screen.
The methods are the same as before. Read this blog.
(so is it because there is basically no content...)

The first case (taking the draw as an example):

Popup(screen,"draw")
pygame.display.flip()
time.sleep(3)
exit()

Call class (one call is complete, and there is no need to use an instance)
display screen
Hang for three seconds
Exit program

In the second case, I choose "computer choice" as an example, that is, it's my turn to operate the computer:

Popup(srceeen,"computer choice")
pygame.display.filp()
time.sleep(0.3)

But in this way, the pop-up window will not disappear by itself, so we need to follow an update method.

Oh, by the way, there's another one that doesn't say how to realize it

To judge whether the click is valid
elif event.type == pygame.MOUSEBUTTONDOWN:
            mouse_x, mouse_y = pygame.mouse.get_pos()
            # Judge whether the player clicks successfully
            success = 0
            for i in rect:
                if  not i.stats and i.rect.collidepoint(mouse_x,mouse_y):
                	# If the click is valid, set the variable to 1

            if not success:
                update(0.3,"you can't choose here!")

update optimization

After a look, there are basically only two situations. One is normal update, and the other is pop-up and delay.

def update(time_sleep=0,msg=""):
    screen.fill((255,228,181))
    for i in rect:
        pygame.draw.rect(screen,(255, 255, 255),i.rect)
        i.draw()
    if msg:
        Popup(screen,msg)
    pygame.display.flip()
    if time_sleep:
        time.sleep(time_sleep)

For normal refresh, you only need to call update(). If you need pop-up and delay, you can add variables yourself.

Main circulation part:

while not judge:
    update()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.MOUSEBUTTONDOWN:
            mouse_x, mouse_y = pygame.mouse.get_pos()
            # Judge whether the player clicks successfully
            success = 0
            for i in rect:
                # Make sure the player takes the next step
                if  not i.stats and i.rect.collidepoint(mouse_x,mouse_y):
                    success = 1
                    # Players play chess
                    i.stats = -1
                    update()
                    win_or_lose()
                    # Computer chess
                    update(0.3,"Computer choice!")
                    computer()
                    update()
                    win_or_lose()
                    update(0.3,"your choice!")

            if not success:
                update(0.3,"you can't choose here!")

computer function part:

def computer():
    """The round of the computer randomly generates a position"""
    global judge
    random_num = [i for i in range(len(rect)) if not rect[i].stats]
    # There's no seat. It's a draw
    if not random_num:
        update(3,"draw")
        exit()
    rect[random.choice(random_num)].stats = 1

Judging the winning part:

def win_or_lose():
    global judge
    stats1 = [i for i in range(len(rect)) if rect[i].stats == 1]
    stats2 = [i for i in range(len(rect)) if rect[i].stats == -1]
    win_list = [
        [0, 1, 2], [3, 4, 5], [6, 7, 8],
        [0, 3, 6], [1, 4, 7], [2, 5, 8],
        [0, 4, 8], [2, 4, 6]
    ]
    for i in win_list:
        if i == [j for j in i if j in stats1]:
            update(3,"Computer win!")
            exit()
        elif i == [j for j in i if j in stats2]:
            update(3,"You win!")
            exit()

First hand problem

Well, let's leave it to random numbers.
Use random RandInt (0,1) generates a 0 / 1 to judge the first hand,
If you start with a computer, use (0,8) to select one and set its stats to 1
code:

def first_hand():
    """Judge first. If the random number is 1, the computer first"""
    x = random.randint(0,1)
    if x:
        x = random.randint(0,8)
        rect[x].stats = 1

Just call it before the loop starts.

epilogue

The whole game is realized. A very simple one can deepen the use of pygame module.
I uploaded the whole project to GitHub. If you like, you can get it yourself.
website
In addition, can I refuse white whoring

Previous Blogs:
pygame to realize tic tac toe chess - 1 Draw Jiugong grid
pygame to realize tic tac toe chess - 2 Logical implementation

Topics: pygame