GUI de Tic Tac Toe en Python usando PyGame

Este artículo lo guiará y le dará una idea básica para diseñar un juego Tic Tac Toe usando la biblioteca pygame de Python. Pygame es un conjunto multiplataforma de módulos Python diseñados para escribir videojuegos. Incluye gráficos por computadora y bibliotecas de sonido diseñadas para usarse con el lenguaje de programación Python. Dividamos la tarea en cinco partes:

  1. Importación de las bibliotecas requeridas y configuración de las variables globales requeridas.
  2. Diseñar la función de visualización del juego, que establecerá una plataforma para que otros componentes se muestren en la pantalla.
  3. Algoritmo principal de ganar y empate
  4. Obtener la entrada del usuario y mostrar la «X» o la «O» en la posición adecuada donde el usuario ha hecho clic con el mouse.
  5. Ejecutar un bucle infinito e incluir en él los métodos definidos.

Nota: Los archivos PNG necesarios se pueden descargar a continuación:

portada_modificada.png

X_modificado.png

o_modificado.png

Importación de las bibliotecas requeridas y configuración de las variables globales requeridas

Vamos a usar la biblioteca pygame, time y sys de Python. La biblioteca de tiempo se usa para realizar un seguimiento del tiempo y el método sleep() que vamos a usar dentro de nuestro código. Eche un vistazo al código a continuación. 

Python3

# importing the required libraries
import pygame as pg
import sys
import time
from pygame.locals import *
  
# declaring the global variables
 
# for storing the 'x' or 'o'
# value as character
XO = 'x'
 
# storing the winner's value at
# any instant of code
winner = None
 
# to check if the game is a draw
draw = None
 
# to set width of the game window
width = 400
 
# to set height of the game window
height = 400
 
# to set background color of the
# game window
white = (255, 255, 255)
 
# color of the straightlines on that
# white game board, dividing board
# into 9 parts
line_color = (0, 0, 0)
  
# setting up a 3 * 3 board in canvas
board = [[None]*3, [None]*3, [None]*3]

Diseño de la pantalla del juego

Esta es la parte más complicada, que tiene la mayor importancia en el desarrollo del juego. Podemos usar el método display.set_mode() para configurar nuestra ventana de visualización. Esto toma tres argumentos, el primero es una tupla que tiene (ancho, alto) de la pantalla que queremos que sea, los otros dos argumentos son profundidad y fps respectivamente. display.set_caption() , establece un título en la etiqueta de nombre de nuestra pantalla. pg.image.load() es un método útil para cargar las imágenes de fondo para personalizar la visualización. Este método toma el nombre del archivo como argumento junto con la extensión. Hay un pequeño problema con image.load(), carga la imagen como un objeto de Python en su tamaño nativo, que puede no estar optimizado junto con la pantalla. Así que usamos otro método en pygame conocido comopg.transform.escala() . Este método toma dos argumentos, uno es el nombre del objeto de la imagen y el otro es una tupla que tiene (ancho, alto), a la que queremos escalar nuestra imagen. Finalmente nos dirigimos a la primera función, game_initiating_window() . En la primera línea hay una función screen.blit() . La pantalla es la función de Python y blit es el método que permite que pygame muestre algo sobre otra cosa. Aquí, el objeto de imagen se ha mostrado sobre la pantalla, que inicialmente se configuró en blanco. pg.display.update() es otra función importante en el desarrollo de juegos. Actualiza la visualización de nuestra ventana cuando se llama. Pygame también nos permite dibujar objetos geométricos como líneas, círculos, etc. En este proyecto hemos utilizadopg.draw.line() método que toma cinco argumentos, a saber: (pantalla, color de línea, punto de inicio, punto final, ancho) . Esto implica un poco de geometría de coordenadas para dibujar las líneas correctamente. Esto no es suficiente. En cada actualización de la pantalla, necesitamos saber el estado del juego, ya sea que gane o pierda. draw_status() nos ayuda a mostrar otra ventana de 100 piezas en la parte inferior de la ventana principal, que actualiza el estado con cada clic del usuario. 

Python3

# initializing the pygame window
pg.init()
 
# setting fps manually
fps = 30
 
# this is used to track time
CLOCK = pg.time.Clock()
 
# this method is used to build the
# infrastructure of the display
screen = pg.display.set_mode((width, height + 100), 0, 32)
 
# setting up a nametag for the
# game window
pg.display.set_caption("My Tic Tac Toe")
  
# loading the images as python object
initiating_window = pg.image.load("modified_cover.png")
x_img = pg.image.load("X_modified.png")
y_img = pg.image.load("o_modified.png")
  
# resizing images
initiating_window = pg.transform.scale(initiating_window, (width, height + 100))
x_img = pg.transform.scale(x_img, (80, 80))
o_img = pg.transform.scale(y_img, (80, 80))
  
def game_initiating_window():
     
    # displaying over the screen
    screen.blit(initiating_window, (0, 0))
     
    # updating the display
    pg.display.update()
    time.sleep(3)                   
    screen.fill(white)
  
    # drawing vertical lines
    pg.draw.line(screen, line_color, (width / 3, 0), (width / 3, height), 7)
    pg.draw.line(screen, line_color, (width / 3 * 2, 0), (width / 3 * 2, height), 7)
  
    # drawing horizontal lines
    pg.draw.line(screen, line_color, (0, height / 3), (width, height / 3), 7)
    pg.draw.line(screen, line_color, (0, height / 3 * 2), (width, height / 3 * 2), 7)
    draw_status()
  
def draw_status():
     
    # getting the global variable draw
    # into action
    global draw
     
    if winner is None:
        message = XO.upper() + "'s Turn"
    else:
        message = winner.upper() + " won !"
    if draw:
        message = "Game Draw !"
  
    # setting a font object
    font = pg.font.Font(None, 30)
     
    # setting the font properties like
    # color and width of the text
    text = font.render(message, 1, (255, 255, 255))
  
    # copy the rendered message onto the board
    # creating a small block at the bottom of the main display
    screen.fill ((0, 0, 0), (0, 400, 500, 100))
    text_rect = text.get_rect(center =(width / 2, 500-50))
    screen.blit(text, text_rect)
    pg.display.update()

Algoritmo principal

El algoritmo principal tiene un enfoque sencillo. Un usuario puede ganar por filas, por columnas y en diagonal. Entonces, al usar una array multidimensional, podemos configurar las condiciones fácilmente. 

Python3

def check_win():
    global board, winner, draw
  
    # checking for winning rows
    for row in range(0, 3):
        if((board[row][0] == board[row][1] == board[row][2]) and (board [row][0] is not None)):
            winner = board[row][0]
            pg.draw.line(screen, (250, 0, 0),
                         (0, (row + 1)*height / 3 -height / 6),
                         (width, (row + 1)*height / 3 - height / 6 ),
                         4)
            break
  
    # checking for winning columns
    for col in range(0, 3):
        if((board[0][col] == board[1][col] == board[2][col]) and (board[0][col] is not None)):
            winner = board[0][col]
            pg.draw.line (screen, (250, 0, 0), ((col + 1)* width / 3 - width / 6, 0), \
                          ((col + 1)* width / 3 - width / 6, height), 4)
            break
  
    # check for diagonal winners
    if (board[0][0] == board[1][1] == board[2][2]) and (board[0][0] is not None):
         
        # game won diagonally left to right
        winner = board[0][0]
        pg.draw.line (screen, (250, 70, 70), (50, 50), (350, 350), 4)
         
    if (board[0][2] == board[1][1] == board[2][0]) and (board[0][2] is not None):
         
        # game won diagonally right to left
        winner = board[0][2]
        pg.draw.line (screen, (250, 70, 70), (350, 50), (50, 350), 4)
  
    if(all([all(row) for row in board]) and winner is None ):
        draw = True
 
    draw_status()

Obtener la entrada del usuario y mostrar la «X» o la «O»

Esta parte trata de una visualización del tablero y un poco de geometría de coordenadas. drawXO() toma dos argumentos fila y columna. En primer lugar, debemos configurar la posición geométrica correcta para colocar la imagen de X y la imagen de O que hemos almacenado como dos objetos de Python «x_img» e «y_img» respectivamente. Eche un vistazo al código para una comprensión adecuada. user_click() es una función que hemos diseñado para obtener la entrada de un clic del mouse del usuario. Imagina que has hecho clic en una de las nueve partes (cuadros divididos por las líneas que hemos dibujado en horizontal y en vertical), esta función definirá la coordenada de la posición en la que has hecho clic. pg.mouse.get_pos()obtiene la coordenada x y la coordenada y del clic del mouse del usuario y devuelve una tupla. Dependiendo de (x, y) podemos definir la fila exacta y la columna exacta donde el usuario ha hecho clic. Finalmente, cuando tenemos la fila y la columna, pasamos estos dos como argumentos a la función dibujarXO(fila, columna) para dibujar la imagen de ‘X’ o la imagen de ‘O’ en la posición deseada del usuario en el juego. pantalla. 

Python3

def drawXO(row, col):
    global board, XO
     
    # for the first row, the image
    # should be pasted at a x coordinate
    # of 30 from the left margin
    if row == 1:
        posx = 30
         
    # for the second row, the image
    # should be pasted at a x coordinate
    # of 30 from the game line    
    if row == 2:
 
        # margin or width / 3 + 30 from
        # the left margin of the window
        posx = width / 3 + 30
         
    if row == 3:
        posx = width / 3 * 2 + 30
  
    if col == 1:
        posy = 30
         
    if col == 2:
        posy = height / 3 + 30
     
    if col == 3:
        posy = height / 3 * 2 + 30
         
    # setting up the required board
    # value to display
    board[row-1][col-1] = XO
     
    if(XO == 'x'):
         
        # pasting x_img over the screen
        # at a coordinate position of
        # (pos_y, posx) defined in the
        # above code
        screen.blit(x_img, (posy, posx))
        XO = 'o'
     
    else:
        screen.blit(o_img, (posy, posx))
        XO = 'x'
    pg.display.update()
  
def user_click():
    # get coordinates of mouse click
    x, y = pg.mouse.get_pos()
  
    # get column of mouse click (1-3)
    if(x<width / 3):
        col = 1
     
    elif (x<width / 3 * 2):
        col = 2
     
    elif(x<width):
        col = 3
     
    else:
        col = None
  
    # get row of mouse click (1-3)
    if(y<height / 3):
        row = 1
     
    elif (y<height / 3 * 2):
        row = 2
     
    elif(y<height):
        row = 3
     
    else:
        row = None
       
    # after getting the row and col,
    # we need to draw the images at
    # the desired positions
    if(row and col and board[row-1][col-1] is None):
        global XO
 
        drawXO(row, col)
        check_win()

Ejecutar un bucle infinito

Este es el último paso importante para ejecutar nuestro juego infinitamente hasta que el usuario haga clic en salir . Antes de ejecutar un ciclo infinito, debemos configurar una función que pueda restablecer todos los valores y parámetros globales a los valores iniciales para un nuevo comienzo del juego. reset_game() se usa para este propósito. Restablece el valor de la placa a 3 * 3 Ninguno de nuevo e inicializa los parámetros globales. En el desarrollo del juego, cada acción del jugador es un evento . Ya sea que haga clic en la ventana o haga clic en el icono de salir/cerrar. Para obtener estos eventos como un objeto, pygame tiene un método integrado que se usa como pg.event.get() . Si el tipo de evento es «QUIT», usamos la biblioteca sys de Python para salir del juego. Pero si se presiona el mouse, el event.get()devolverá «MOUSEBUTTONDOWN» y nuestra llamada a user_click() conoce la coordenada exacta del tablero donde el usuario ha hecho clic. En todo el código, hemos utilizado el método .sleep() para pausar nuestro juego a veces y hacerlo fácil de usar y fluido. 

Python3

def reset_game():
    global board, winner, XO, draw
    time.sleep(3)
    XO = 'x'
    draw = False
    game_initiating_window()
    winner = None
    board = [[None]*3, [None]*3, [None]*3]
  
game_initiating_window()
  
while(True):
    for event in pg.event.get():
 
        if event.type == QUIT:
            pg.quit()
            sys.exit()
 
        elif event.type is MOUSEBUTTONDOWN:
            user_click()
 
            if(winner or draw):
                reset_game()
 
    pg.display.update()
    CLOCK.tick(fps)

El código completo: 

Python3

# importing the required libraries
import pygame as pg
import sys
import time
from pygame.locals import *
  
# declaring the global variables
 
# for storing the 'x' or 'o'
# value as character
XO = 'x'
 
# storing the winner's value at
# any instant of code
winner = None
 
# to check if the game is a draw
draw = None
 
# to set width of the game window
width = 400
 
# to set height of the game window
height = 400
 
# to set background color of the
# game window
white = (255, 255, 255)
 
# color of the straightlines on that
# white game board, dividing board
# into 9 parts
line_color = (0, 0, 0)
  
# setting up a 3 * 3 board in canvas
board = [[None]*3, [None]*3, [None]*3]
 
 
# initializing the pygame window
pg.init()
 
# setting fps manually
fps = 30
 
# this is used to track time
CLOCK = pg.time.Clock()
 
# this method is used to build the
# infrastructure of the display
screen = pg.display.set_mode((width, height + 100), 0, 32)
 
# setting up a nametag for the
# game window
pg.display.set_caption("My Tic Tac Toe")
  
# loading the images as python object
initiating_window = pg.image.load("modified_cover.png")
x_img = pg.image.load("X_modified.png")
y_img = pg.image.load("o_modified.png")
  
# resizing images
initiating_window = pg.transform.scale(initiating_window, (width, height + 100))
x_img = pg.transform.scale(x_img, (80, 80))
o_img = pg.transform.scale(y_img, (80, 80))
  
def game_initiating_window():
     
    # displaying over the screen
    screen.blit(initiating_window, (0, 0))
     
    # updating the display
    pg.display.update()
    time.sleep(3)                   
    screen.fill(white)
  
    # drawing vertical lines
    pg.draw.line(screen, line_color, (width / 3, 0), (width / 3, height), 7)
    pg.draw.line(screen, line_color, (width / 3 * 2, 0), (width / 3 * 2, height), 7)
  
    # drawing horizontal lines
    pg.draw.line(screen, line_color, (0, height / 3), (width, height / 3), 7)
    pg.draw.line(screen, line_color, (0, height / 3 * 2), (width, height / 3 * 2), 7)
    draw_status()
  
def draw_status():
     
    # getting the global variable draw
    # into action
    global draw
     
    if winner is None:
        message = XO.upper() + "'s Turn"
    else:
        message = winner.upper() + " won !"
    if draw:
        message = "Game Draw !"
  
    # setting a font object
    font = pg.font.Font(None, 30)
     
    # setting the font properties like
    # color and width of the text
    text = font.render(message, 1, (255, 255, 255))
  
    # copy the rendered message onto the board
    # creating a small block at the bottom of the main display
    screen.fill ((0, 0, 0), (0, 400, 500, 100))
    text_rect = text.get_rect(center =(width / 2, 500-50))
    screen.blit(text, text_rect)
    pg.display.update()
     
def check_win():
    global board, winner, draw
  
    # checking for winning rows
    for row in range(0, 3):
        if((board[row][0] == board[row][1] == board[row][2]) and (board [row][0] is not None)):
            winner = board[row][0]
            pg.draw.line(screen, (250, 0, 0),
                         (0, (row + 1)*height / 3 -height / 6),
                         (width, (row + 1)*height / 3 - height / 6 ),
                         4)
            break
  
    # checking for winning columns
    for col in range(0, 3):
        if((board[0][col] == board[1][col] == board[2][col]) and (board[0][col] is not None)):
            winner = board[0][col]
            pg.draw.line (screen, (250, 0, 0), ((col + 1)* width / 3 - width / 6, 0), \
                          ((col + 1)* width / 3 - width / 6, height), 4)
            break
  
    # check for diagonal winners
    if (board[0][0] == board[1][1] == board[2][2]) and (board[0][0] is not None):
         
        # game won diagonally left to right
        winner = board[0][0]
        pg.draw.line (screen, (250, 70, 70), (50, 50), (350, 350), 4)
         
    if (board[0][2] == board[1][1] == board[2][0]) and (board[0][2] is not None):
         
        # game won diagonally right to left
        winner = board[0][2]
        pg.draw.line (screen, (250, 70, 70), (350, 50), (50, 350), 4)
  
    if(all([all(row) for row in board]) and winner is None ):
        draw = True
    draw_status()
     
def drawXO(row, col):
    global board, XO
     
    # for the first row, the image
    # should be pasted at a x coordinate
    # of 30 from the left margin
    if row == 1:
        posx = 30
         
    # for the second row, the image
    # should be pasted at a x coordinate
    # of 30 from the game line    
    if row == 2:
 
        # margin or width / 3 + 30 from
        # the left margin of the window
        posx = width / 3 + 30
         
    if row == 3:
        posx = width / 3 * 2 + 30
  
    if col == 1:
        posy = 30
         
    if col == 2:
        posy = height / 3 + 30
     
    if col == 3:
        posy = height / 3 * 2 + 30
         
    # setting up the required board
    # value to display
    board[row-1][col-1] = XO
     
    if(XO == 'x'):
         
        # pasting x_img over the screen
        # at a coordinate position of
        # (pos_y, posx) defined in the
        # above code
        screen.blit(x_img, (posy, posx))
        XO = 'o'
     
    else:
        screen.blit(o_img, (posy, posx))
        XO = 'x'
    pg.display.update()
  
def user_click():
    # get coordinates of mouse click
    x, y = pg.mouse.get_pos()
  
    # get column of mouse click (1-3)
    if(x<width / 3):
        col = 1
     
    elif (x<width / 3 * 2):
        col = 2
     
    elif(x<width):
        col = 3
     
    else:
        col = None
  
    # get row of mouse click (1-3)
    if(y<height / 3):
        row = 1
     
    elif (y<height / 3 * 2):
        row = 2
     
    elif(y<height):
        row = 3
     
    else:
        row = None
       
    # after getting the row and col,
    # we need to draw the images at
    # the desired positions
    if(row and col and board[row-1][col-1] is None):
        global XO
        drawXO(row, col)
        check_win()
         
def reset_game():
    global board, winner, XO, draw
    time.sleep(3)
    XO = 'x'
    draw = False
    game_initiating_window()
    winner = None
    board = [[None]*3, [None]*3, [None]*3]
  
game_initiating_window()
  
while(True):
    for event in pg.event.get():
        if event.type == QUIT:
            pg.quit()
            sys.exit()
        elif event.type is MOUSEBUTTONDOWN:
            user_click()
            if(winner or draw):
                reset_game()
    pg.display.update()
    CLOCK.tick(fps)

Producción:

Publicación traducida automáticamente

Artículo escrito por AbhijitTripathy y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *