Este projeto é uma aplicação acadêmica dos conceitos de programação de rede, implementando um jogo de tabuleiro multiplayer (2 jogadores) em tempo real usando sockets TCP em Python.
O jogo é um "Quiz de TI", onde os jogadores avançam em um tabuleiro de 20 casas. A cada rodada, o servidor "rola um dado" e faz uma pergunta sobre conceitos de tecnologia. Acertos concedem casas bônus, e erros penalizam o jogador. Vence quem chegar à casa 20 primeiro.
Este projeto foi desenvolvido para aplicar e demonstrar os seguintes conceitos:
- Programação de Rede (Sockets): Utilização da biblioteca
socketdo Python para estabelecer comunicação TCP persistente entre um servidor e múltiplos clientes. - Arquitetura Cliente-Servidor: Separação clara de responsabilidades, onde o Servidor (Mestre do Jogo) centraliza toda a lógica e estado, e os Clientes (Jogadores) atuam apenas como interfaces de entrada e saída.
- Multithreading: Uso da biblioteca
threadingno servidor para gerenciar múltiplos clientes concorrentemente. O servidor possui:- Uma thread principal para aceitar novas conexões (Lobby).
- Uma thread de lógica de jogo para gerenciar os turnos e regras.
- Uma thread de escuta dedicada para cada cliente, permitindo E/S (I/O) não-bloqueante.
- Sincronização de Threads: Uso de
queue.Queuepara comunicação segura entre a thread de escuta e a thread de jogo, ethreading.Eventpara sinalizar o encerramento do jogo para todas as threads ativas. - Protocolo de Comunicação: Definição de um protocolo simples baseado em JSON para serializar e desserializar mensagens entre cliente e servidor.
- Python
- Bibliotecas Nativas do Python:
socket(para a comunicação de rede)threading(para concorrência no servidor)json(para serialização do protocolo)queue(para sincronização entre threads)random(para rolar dados e selecionar perguntas)time(para pausas entre os turnos)Tkinter(para desenvolvimento da GUI do cliente)
.
├── server.py # O servidor (Mestre do Jogo)
├── client.py # O cliente (Interface do Jogador)
├── questions.json # O banco de dados de perguntas e respostas
└── README.md # Este arquivo
Para rodar este projeto, você precisará de três janelas de terminal.
Primeiro, inicie o servidor. Ele ficará aguardando no "lobby" por dois jogadores.
python server.pySaída esperada:
Servidor iniciado em localhost:8788. Aguardando jogadores...
Em uma segunda janela de terminal, inicie o primeiro cliente.
python client.pySaída esperada:
Conectado ao servidor do jogo em localhost:8788
... Você é o Jogador 1. Aguardando oponente... ...
Em uma terceira janela de terminal, inicie o segundo cliente.
python client.pySaída esperada:
Conectado ao servidor do jogo em localhost:8788
... Você é o Jogador 2. Aguardando oponente... ...
Assim que o Jogador 2 se conectar, o jogo começará automaticamente em todas as três janelas.
O servidor é o "cérebro" do jogo e a fonte da verdade. Ele gerencia o estado do jogo (posição de cada jogador, de quem é o turno) e lida com toda a lógica.
- Lobby: A thread principal espera por conexões
sock.accept(). - Início do Jogo: Quando 2 jogadores estão conectados, a thread principal lança 3 novas threads:
t_game(Thread de Lógica do Jogo): Controla o loop principal (while game_is_running).t1_listen(Thread de Escuta P1): Fica em um loopconn.recv()para o Jogador 1.t2_listen(Thread de Escuta P2): Fica em um loopconn.recv()para o Jogador 2.
- Fluxo de Turno:
- A
t_gamerola o dado. - Verifica a vitória (>= 20).
- Se não venceu, seleciona uma pergunta de
questions.json. - Envia a pergunta (
{'tipo': 'PERGUNTA', ...}) ao jogador ativo. - Espera por uma resposta da
player.incoming_queue(com um timeout de 30s). - As threads de escuta (
t_listen) recebem os dados, os decodificam e os colocam naincoming_queuedo jogador correspondente. - A
t_gamecalcula a nova posição (baseado no acerto/erro). - Verifica a vitória novamente (com bônus).
- Envia a atualização (
{'tipo': 'STATUS', ...}) para ambos os jogadores. - Troca o turno e repete.
- A
- Conexão: Conecta-se ao servidor.
- Loop Principal: Entra em um
while True. - Buffer de Leitura: O cliente mantém um
bufferde bytes. Ele lê do socket (sock.recv()) e adiciona ao buffer. - Processamento de Mensagens: Ele continuamente verifica se o buffer contém o delimitador
\n.- Se sim, ele extrai a mensagem, a decodifica de JSON e a processa (exibe na tela).
- Se a mensagem for do tipo
PERGUNTA, ele pausa, solicita uminput()do usuário e envia a resposta de volta ao servidor (também com um\n). - Se for
STATUSouAGUARDE, apenas exibe a mensagem. - Se for
FIM, exibe o vencedor e encerra.