Crie um Jogo 2048 com Python Tkinter

PythonBeginner
Pratique Agora

Introdução

Neste projeto, você aprenderá como criar um jogo 2048 simples usando Python e a biblioteca Tkinter para a interface gráfica do usuário. 2048 é um jogo de quebra-cabeça deslizante popular, onde você combina peças para alcançar a peça com o valor de 2048. Embora este projeto não crie a interface do usuário mais moderna e bonita, ele fornecerá uma base sólida sobre a qual você pode construir para aprimorar a estética.

👀 Pré-visualização

2048 game

🎯 Tarefas

Neste projeto, você aprenderá:

  • Como importar as bibliotecas necessárias para o jogo
  • Como criar a classe Game2048 para lidar com a lógica do jogo e a interface do usuário
  • Como desenhar a grade do jogo usando Tkinter
  • Como gerar as peças iniciais na grade
  • Como atualizar a interface do usuário para refletir o estado atual da grade do jogo
  • Como definir as cores das peças com base em seus valores
  • Como lidar com as teclas pressionadas para mover as peças
  • Como definir métodos para mover as peças em diferentes direções
  • Como verificar se o jogo acabou

🏆 Conquistas

Após concluir este projeto, você será capaz de:

  • Usar a biblioteca Tkinter para criar uma interface gráfica do usuário
  • Lidar com as teclas pressionadas e acionar as ações correspondentes
  • Atualizar a interface do usuário com base no estado do jogo
  • Implementar a lógica do jogo para movimentos e fusão de peças
  • Verificar se um jogo acabou

Criar os arquivos do projeto

Primeiro, crie um novo arquivo chamado 2048_game.py e abra-o no seu editor de código preferido.

cd ~/project
touch 2048_game.py
✨ Verificar Solução e Praticar

Importar as bibliotecas necessárias

Para que seu jogo funcione, você precisa importar as bibliotecas essenciais. No arquivo 2048_game.py, inclua a biblioteca random do Python e Tkinter para construir a interface gráfica do usuário.

import random
import tkinter as tk

Essas bibliotecas serão usadas para criar a funcionalidade e a interface do usuário do jogo.

✨ Verificar Solução e Praticar

Criar a classe Game2048

Defina a classe Game2048 no seu arquivo 2048_game.py. Essa classe irá lidar com a lógica do jogo e a interface do usuário.

class Game2048:
    def __init__(self, root):
        self.root = root
        self.root.title("2048 Game")
        self.root.geometry("400x400")

        ## Initialize the game grid and score
        self.grid = [[0 for _ in range(4)] for _ in range(4)]
        self.score = 0

        ## Create an empty list to store tile labels
        self.tiles = []

Aqui, você está criando um construtor de classe que inicializa a janela raiz do jogo, define seu título e tamanho, inicializa a grade do jogo e cria uma lista para armazenar os blocos do jogo.

✨ Verificar Solução e Praticar

Desenhar a grade do jogo

Na classe Game2048, adicione um método para desenhar a grade do jogo usando Tkinter. Este método cria a grade inicial da interface do usuário.

    def draw_grid(self):
        for i in range(4):
            row = []
            for j in range(4):
                cell = tk.Label(self.root, text="", font=("Helvetica", 24), width=5, height=2, borderwidth=4, relief="ridge")
                cell.grid(row=i, column=j, padx=5, pady=5)
                row.append(cell)
            self.tiles.append(row)

Este método cria uma grade 4x4 de rótulos que exibirão os blocos do jogo.

✨ Verificar Solução e Praticar

Gerar as peças iniciais

Agora, você precisa implementar um método que gera os blocos iniciais quando o jogo começa. Este método irá colocar um ou dois blocos com o valor 2 ou 4 na grade.

    def spawn_tile(self):
        empty_cells = [(i, j) for i in range(4) for j in range(4) if self.grid[i][j] == 0]
        if empty_cells:
            i, j = random.choice(empty_cells)
            self.grid[i][j] = 2 if random.random() < 0.9 else 4
            self.update_tiles()

Este método localiza células vazias na grade e coloca aleatoriamente um novo bloco em uma delas.

✨ Verificar Solução e Praticar

Atualizar a interface do usuário

Crie um método para atualizar a interface do usuário para refletir o estado atual da grade do jogo.

    def update_tiles(self):
        for i in range(4):
            for j in range(4):
                tile_value = self.grid[i][j]
                self.tiles[i][j]["text"] = str(tile_value) if tile_value > 0 else ""
                self.tiles[i][j]["background"] = self.get_tile_color(tile_value)

Este método itera pela grade, atualizando os rótulos na interface gráfica do usuário para corresponder ao estado da grade.

✨ Verificar Solução e Praticar

Definir as cores das peças

Crie um método para atribuir cores de fundo aos blocos com base em seus valores.

    def get_tile_color(self, value):
        colors = {
            0: "#CDC1B4",
            2: "#EEE4DA",
            4: "#EDE0C8",
            8: "#F2B179",
            16: "#F59563",
            32: "#F67C5F",
            64: "#F65E3B",
            128: "#EDCF72",
            256: "#EDCC61",
            512: "#EDC850",
            1024: "#EDC53F",
            2048: "#EDC22E"
        }
        return colors.get(value, "#FF0000")

Este método retorna um código de cor com base no valor do bloco.

✨ Verificar Solução e Praticar

Gerenciar as teclas pressionadas

Implemente um método para lidar com as teclas pressionadas. Nesta etapa, você capturará as teclas de seta pressionadas e responderá de acordo.

    def key_pressed(self, event):
        if self.is_game_over():
            return

        if event.keysym == "Up":
            self.move("up")
        elif event.keysym == "Down":
            self.move("down")
        elif event.keysym == "Left":
            self.move("left")
        elif event.keysym == "Right":
            self.move("right")

        self.spawn_tile()
        self.update_tiles()

Este método escuta as teclas de seta pressionadas e aciona a função de movimento correspondente.

✨ Verificar Solução e Praticar

Mover as peças

Implemente métodos para mover os blocos na direção desejada: para cima (up), para baixo (down), para a esquerda (left) ou para a direita (right).

    def move(self, direction):
        if direction == "up":
            self.grid = list(map(list, zip(*self.grid)))
            self.move_left()
            self.grid = list(map(list, zip(*self.grid)))
        elif direction == "down":
            self.grid = list(map(list, zip(*self.grid)))
            self.move_right()
            self.grid = list(map(list, zip(*self.grid)))
        elif direction == "left":
            self.move_left()
        elif direction == "right":
            self.move_right()

Este método prepara a grade para o movimento, invoca a função de movimento apropriada e, em seguida, redefine a grade.

✨ Verificar Solução e Praticar

Mover as peças para a esquerda

Defina um método para mover os blocos para a esquerda dentro de uma linha.

    def move_left(self):
        for i in range(4):
            row = self.grid[i]
            row = [value for value in row if value != 0]
            while len(row) < 4:
                row.append(0)

            for j in range(3):
                if row[j] == row[j + 1] and row[j] != 0:
                    row[j] *= 2
                    row[j + 1] = 0
                    self.score += row[j]

            row = [value for value in row if value != 0]
            while len(row) < 4:
                row.append(0)
            self.grid[i] = row

Este método processa os movimentos dos blocos para a esquerda, fundindo blocos adjacentes quando possível e preenchendo as células vazias.

✨ Verificar Solução e Praticar

Mover as peças para a direita

Defina um método para mover os blocos para a direita dentro de uma linha.

    def move_right(self):
        for i in range(4):
            row = self.grid[i]
            row = [value for value in row if value != 0]
            while len(row) < 4:
                row.insert(0, 0)

            for j in range(3, 0, -1):
                if row[j] == row[j - 1] and row[j] != 0:
                    row[j] *= 2
                    row[j - 1] = 0
                    self.score += row[j]

            row = [value for value in row if value != 0]
            while len(row) < 4:
                row.insert(0, 0)
            self.grid[i] = row

Este método é responsável por mover os blocos para a direita, fundindo-os quando apropriado e preenchendo os espaços vazios.

✨ Verificar Solução e Praticar

Verificar se o jogo acabou

Crie um método para verificar se o jogo acabou. O jogo termina quando não são mais possíveis movimentos.

    def is_game_over(self):
        for i in range(4):
            for j in range(4):
                if self.grid[i][j] == 0:
                    return False

        for i in range(4):
            for j in range(3):
                if self.grid[i][j] == self.grid[i][j + 1]:
                    return False

        for j in range(4):
            for i in range(3):
                if self.grid[i][j] == self.grid[i + 1][j]:
                    return False

        return True

Este método verifica se ainda são possíveis movimentos, examinando se a grade (grid) contém células vazias ou células adjacentes com o mesmo valor. Se não forem mais possíveis movimentos, o jogo termina.

✨ Verificar Solução e Praticar

Executar o projeto

Finalmente, mude para a Área de Trabalho (Desktop) e execute o projeto usando o seguinte comando:

python 2048_game.py
2048 game
✨ Verificar Solução e Praticar

Resumo

Neste projeto, você aprendeu como criar um jogo 2048 simples em Python usando a biblioteca Tkinter. Você começou configurando o projeto, criando o arquivo Python principal e definindo a classe Game2048. Em seguida, implementou as teclas de atalho (key presses), os movimentos das peças (tile movements) e verificou as condições de fim de jogo. A interface do jogo pode não ser a mais moderna e bonita, mas agora você tem uma base que pode aprimorar e personalizar para criar um jogo 2048 mais refinado. Para executar o projeto, você pode seguir os passos abaixo.