1

The draw Board functions has an if statement to see which colour circle it should draw. But when the mouse button is clicked it draws the circle in the right x coordinate but in the opposite y coordinate. Also, the end is a little bit weird I want to display the winner and then wait using time.wait but it waits and then prints the statement for a second. If you could run it, you would understand. I am also looking for general improvements that I could make.

import pygame, sys
import numpy as np
import math

pygame.init()
clock = pygame.time.Clock()

# Colours
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)

ROW_COUNT = 3
COLUMN_COUNT = 3
SQUARE_SIZE = 100
RADIUS = int(SQUARE_SIZE/2 - 5)

WIDTH = COLUMN_COUNT * SQUARE_SIZE
HEIGHT = ROW_COUNT * SQUARE_SIZE

myfont = pygame.font.SysFont("monospace", 20)

PLAYER_COUNT = 2
screen = pygame.display.set_mode((WIDTH, HEIGHT))
screen.fill(WHITE)


class Player:
    def __init__(self, number):
        self.number = number


players = [Player(i) for i in range(1, PLAYER_COUNT + 1)]


class Board:
    def __init__(self):
        self.board_body = np.zeros((ROW_COUNT, COLUMN_COUNT), dtype=np.uint8)
        self.remaining_spaces = 9
        self.print_board()
        self.draw_board()

    def print_board(self):
        print(np.flip(self.board_body, 0))

    def draw_board(self):
        one_x, one_y, one_end_x, one_end_y, two_x, two_y, two_end_x, two_end_y = 100, 0, 100, 300, 0, 100, 300, 100
        for r in range(ROW_COUNT):
            for c in range(COLUMN_COUNT):
                pygame.draw.line(screen, BLACK, (one_x, one_y), (one_end_x, one_end_y), 3)
                one_x += 100
                one_end_x += 100
            pygame.draw.line(screen, BLACK, (two_x, two_y), (two_end_x, two_end_y), 3)
            two_y += 100
            two_end_y += 100

        for c in range(COLUMN_COUNT):
            for r in range(ROW_COUNT):
                if self.board_body[r][c] == 1:
                    pygame.draw.circle(screen, RED, (int(c * SQUARE_SIZE + SQUARE_SIZE / 2), HEIGHT - int(r * SQUARE_SIZE + SQUARE_SIZE / 2)), RADIUS)
                elif self.board_body[r][c] == 2:
                    pygame.draw.circle(screen, GREEN, (int(c * SQUARE_SIZE + SQUARE_SIZE / 2), HEIGHT - int(r * SQUARE_SIZE + SQUARE_SIZE / 2)), RADIUS)
    pygame.display.update()

    def check_empty_space_and_place(self, row, column, number):
        if self.board_body[row][column] == 0:
            self.board_body[row][column] = number
            self.remaining_spaces -= 1

    def check_winning_move(self, board, piece):
        # Horizontal
        for c in range(COLUMN_COUNT - 1):
            for r in range(ROW_COUNT - 1):
                if board[r - 1][c] == piece and board[r][c] == piece and board[r + 1][c] == piece:
                    return True

        # Vertical
        for c in range(0, COLUMN_COUNT - 1):
            for r in range(0, ROW_COUNT - 1):
                if board[r][c - 1] == piece and board[r][c] == piece and board[r][c + 1] == piece:
                    return True
        # Positively sloped diagonals
        for c in range(0, COLUMN_COUNT - 1):
            for r in range(0, ROW_COUNT - 1):
                if board[r][c] == piece and board[r + 1][c + 1] == piece and board[r + 2][c + 2] == piece:
                    return True

        # Negatively sloped diagonals
        for c in range(COLUMN_COUNT - 1):
            for r in range(ROW_COUNT - 1):
                if board[r + 1][c + 1] == piece and board[r][c] == piece and board[r - 1][c - 1] == piece:
                    return True


def next_player():
    while True:
        for player in players:
            yield player


player_generator = next_player()

board = Board()

run = True

while run:
    if board.remaining_spaces == 0:
        run = False
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

        if event.type == pygame.MOUSEBUTTONDOWN:
            pos_of_mouse = pygame.mouse.get_pos()
            posx = pos_of_mouse[0]
            posy = pos_of_mouse[1]
            column = int(posx // SQUARE_SIZE)
            row = int(posy // SQUARE_SIZE)
            p = player_generator.__next__()
            board.check_empty_space_and_place(row, column, p.number)
            board.draw_board()
            if board.check_winning_move(board.board_body, p.number):
                label = myfont.render("Player {} wins!!!!".format(p.number), False, WHITE)
                screen.fill(BLACK)
                screen.blit(label, (20, 50))
                pygame.time.wait(3000)
                run = False

    board.print_board()
    pygame.display.update()
    clock.tick(60)```
Ahmad
  • 274
  • 2
  • 15
Coding
  • 59
  • 6

1 Answers1

0

In the PyGame coordinate system the top left is (0, 0). You don't have to flip the y-axis or subtract the y-coordinate from the height. Remove the term HEIGHT - in the method draw_board of the class Board:

class Board:
    # [...]

    def draw_board(self):
        # [...]

        for c in range(COLUMN_COUNT):
            for r in range(ROW_COUNT):
                if self.board_body[r][c] == 1:
                    pygame.draw.circle(screen, RED, (int(c * SQUARE_SIZE + SQUARE_SIZE / 2), int(r * SQUARE_SIZE + SQUARE_SIZE / 2)), RADIUS)
                elif self.board_body[r][c] == 2:
                    pygame.draw.circle(screen, GREEN, (int(c * SQUARE_SIZE + SQUARE_SIZE / 2), int(r * SQUARE_SIZE + SQUARE_SIZE / 2)), RADIUS)
        
        # [...]

If you just wait for some time, you can use pygame.time.wait or pygame.time.delay. However, if you want to display a message and then wait some time, you need to update the display beforehand. The display is updated only if either pygame.display.update() or pygame.display.flip() is called. Further you've to handles the events by pygame.event.pump(), before the update of the display becomes visible in the window:
(See How to wait some time in pygame?)

screen.fill(BLACK)
screen.blit(label, (20, 50))
pygame.display.flip()
pygame.event.pump()
pygame.time.wait(3000)

Also, there are some mistakes when looking for a winner. Yo don't need nestes loops at all. There are just 2 diagonals. So you don't need any loops for the diagonals:

class Board:
    # [...]

    def check_winning_move(self, board, piece):
        # Horizontal
        for c in range(COLUMN_COUNT):
            if board[0][c] == piece and board[1][c] == piece and board[2][c] == piece:
                return True

        # Vertical
        for r in range(ROW_COUNT):
            if board[r][0] == piece and board[r][1] == piece and board[r][2] == piece:
                return True

        # Positively sloped diagonals
        if board[0][0] == piece and board[1][1] == piece and board[2][2] == piece:
            return True

        # Negatively sloped diagonals
        if board[0][2] == piece and board[1][1] == piece and board[2][0] == piece:
            return True

See also Pygame Tic Tak Toe Logic? How Would I Do It.


Complete code:

import pygame, sys
import numpy as np
import math

pygame.init()
clock = pygame.time.Clock()

# Colours
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)

ROW_COUNT = 3
COLUMN_COUNT = 3
SQUARE_SIZE = 100
RADIUS = int(SQUARE_SIZE/2 - 5)

WIDTH = COLUMN_COUNT * SQUARE_SIZE
HEIGHT = ROW_COUNT * SQUARE_SIZE

myfont = pygame.font.SysFont("monospace", 20)

PLAYER_COUNT = 2
screen = pygame.display.set_mode((WIDTH, HEIGHT))
screen.fill(WHITE)


class Player:
    def __init__(self, number):
        self.number = number


players = [Player(i) for i in range(1, PLAYER_COUNT + 1)]


class Board:
    def __init__(self):
        self.board_body = np.zeros((ROW_COUNT, COLUMN_COUNT), dtype=np.uint8)
        self.remaining_spaces = 9
        self.print_board()
        self.draw_board()

    def print_board(self):
        print(np.flip(self.board_body, 0))

    def draw_board(self):
        one_x, one_y, one_end_x, one_end_y, two_x, two_y, two_end_x, two_end_y = 100, 0, 100, 300, 0, 100, 300, 100
        for r in range(ROW_COUNT):
            for c in range(COLUMN_COUNT):
                pygame.draw.line(screen, BLACK, (one_x, one_y), (one_end_x, one_end_y), 3)
                one_x += 100
                one_end_x += 100
            pygame.draw.line(screen, BLACK, (two_x, two_y), (two_end_x, two_end_y), 3)
            two_y += 100
            two_end_y += 100

        for c in range(COLUMN_COUNT):
            for r in range(ROW_COUNT):
                if self.board_body[r][c] == 1:
                    pygame.draw.circle(screen, RED, (int(c * SQUARE_SIZE + SQUARE_SIZE / 2), int(r * SQUARE_SIZE + SQUARE_SIZE / 2)), RADIUS)
                elif self.board_body[r][c] == 2:
                    pygame.draw.circle(screen, GREEN, (int(c * SQUARE_SIZE + SQUARE_SIZE / 2), int(r * SQUARE_SIZE + SQUARE_SIZE / 2)), RADIUS)
    pygame.display.update()

    def check_empty_space_and_place(self, row, column, number):
        if self.board_body[row][column] == 0:
            self.board_body[row][column] = number
            self.remaining_spaces -= 1

    def check_winning_move(self, board, piece):
        # Horizontal
        for c in range(COLUMN_COUNT):
            if board[0][c] == piece and board[1][c] == piece and board[2][c] == piece:
                return True

        # Vertical
        for r in range(ROW_COUNT):
            if board[r][0] == piece and board[r][1] == piece and board[r][2] == piece:
                return True

        # Positively sloped diagonals
        if board[0][0] == piece and board[1][1] == piece and board[2][2] == piece:
            return True

        # Negatively sloped diagonals
        if board[0][2] == piece and board[1][1] == piece and board[2][0] == piece:
            return True


def next_player():
    while True:
        for player in players:
            yield player


player_generator = next_player()

board = Board()

run = True

while run:
    if board.remaining_spaces == 0:
        run = False
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

        if event.type == pygame.MOUSEBUTTONDOWN:
            pos_of_mouse = pygame.mouse.get_pos()
            posx = pos_of_mouse[0]
            posy = pos_of_mouse[1]
            column = int(posx // SQUARE_SIZE)
            row = int(posy // SQUARE_SIZE)
            p = player_generator.__next__()
            board.check_empty_space_and_place(row, column, p.number)
            board.draw_board()
            if board.check_winning_move(board.board_body, p.number):
                label = myfont.render("Player {} wins!!!!".format(p.number), False, WHITE)
                screen.fill(BLACK)
                screen.blit(label, (20, 50))
                pygame.display.flip()
                pygame.event.pump()
                pygame.time.wait(3000)

                run = False

    board.print_board()
    pygame.display.update()
    clock.tick(60)
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • thanks, can you help with some of the other suff – Coding Dec 31 '20 at 15:00
  • if I take away the minus 1 wherever I press it says index out of range – Coding Dec 31 '20 at 15:05
  • You fixed the screen at the end issue but the bigger issue is wherever I press the internal representation and the graphical representation has a opposite y coordinate for example if I were to place a circle In the bottom row middle column it would be placed on the top row middle column internally and graphically – Coding Dec 31 '20 at 15:08
  • internally it happens because I flip it vertically using lumpy.flip in the print_board function – Coding Dec 31 '20 at 15:09
  • ok I saw the answer now how should the check_winning_move method be – Coding Dec 31 '20 at 15:12
  • @Coding I've answered all your questions. Please read the complete answer. I've added the complete code at the end of the answer. – Rabbid76 Dec 31 '20 at 15:13