Estrutura do Programa
A arquitetura do prograam baseia-se em classes que representam os componentes do jogo:
ChessBoard: Responsável por desenhar o tabuleiro do jogo, incluindo suas dimensões pesronalizáveis.Player: Uma classe base abstrata para os jogadores. Dela derivam classes específicas para cada cor de peça (WhitePlayer,BlackPlayer,YellowPlayer). Cada jogador mantém uma lista de suas peças e possui um método para verificar se venceu.ChessPiece: Representa uma única peça no tabuleiro, com atributos de posição (x, y) e cor.Mouse: Captura e gerencia as interações do mouse, como cliques e suas coordenadas.Menu: Encapsula a lógica principal do jogo, gerenciando a interface do usuário, o fluxo do jogo e as interações entre os jogadores e o tabuleiro.
A organização dos arquivos segue um padrão de separação entre declaração (.h) e implementação (.cpp) para cada classe principal.
Implementação do Código
main.cpp
O ponto de entrada do programa. Inicializa o objeto Menu e gerencia o loop principal da interface, direcionando para as funções de iniciar o jogo, exibir introdução ou sair.
#include <iostream>
#include <graphics.h>
#include "ChessBoard.h"
#include "Menu.h"
#include "Mouse.h"
#include "Player.h"
int main() {
Menu mainMenu;
while (true) {
int choice = mainMenu.displayMainMenu();
switch (choice) {
case '1': // Iniciar Jogo
mainMenu.startGame();
break;
case '2': // Introdução
mainMenu.showIntroduction();
break;
case '0': // Sair
return 0;
}
}
return 0;
}
Player.h
Define a classe base Player e suas classes derivadas WhitePlayer, BlackPlayer e YellowPlayer.
#pragma once
#include <vector>
#include <algorithm>
#include "ChessPiece.h"
#include "Mouse.h"
#include "ChessBoard.h"
// Classe base abstrata para Jogadores
class Player {
private:
std::vector<ChessPiece> playerPieces;
public:
virtual ~Player() = default; // Destrutor virtual
std::vector<ChessPiece>& getPieces();
virtual void placePiece(Mouse& mouse, Player& other1, Player& other2) = 0; // Método puro para colocar peça
bool checkWinCondition(const ChessBoard& board); // Verifica condição de vitória
};
// Jogador de Peças Brancas
class WhitePlayer : public Player {
public:
void placePiece(Mouse& mouse, Player& other1, Player& other2) override;
};
// Jogador de Peças Pretas
class BlackPlayer : public Player {
public:
void placePiece(Mouse& mouse, Player& other1, Player& other2) override;
};
// Jogador de Peças Amarelas
class YellowPlayer : public Player {
public:
void placePiece(Mouse& mouse, Player& other1, Player& other2) override;
};
Player.cpp
Implementa os métodos da classe Player e suas derivadas, incluindo a lógica para colocar peças e verificar a condição de vitória (horizontal, vertical e diagonal).
#include "Player.h"
std::vector<ChessPiece>& Player::getPieces() {
return this->playerPieces;
}
bool Player::checkWinCondition(const ChessBoard& board) {
if (playerPieces.empty()) {
return false;
}
const ChessPiece& lastPiece = playerPieces.back();
int pieceX = lastPiece.getX();
int pieceY = lastPiece.getY();
int pieceColor = lastPiece.getColor();
// Verifica direções horizontal, vertical e diagonais
int directions[4][2] = {{1, 0}, {0, 1}, {1, 1}, {1, -1}}; // H, V, D\, D/
for (auto& dir : directions) {
int count = 1;
// Verifica para frente
for (int i = 1; i < 5; ++i) {
int checkX = pieceX + dir[0] * i * BOARD_INTERVAL;
int checkY = pieceY + dir[1] * i * BOARD_INTERVAL;
ChessPiece target(checkX, checkY, pieceColor);
if (std::find(playerPieces.begin(), playerPieces.end(), target) != playerPieces.end()) {
count++;
} else {
break;
}
}
// Verifica para trás
for (int i = 1; i < 5; ++i) {
int checkX = pieceX - dir[0] * i * BOARD_INTERVAL;
int checkY = pieceY - dir[1] * i * BOARD_INTERVAL;
ChessPiece target(checkX, checkY, pieceColor);
if (std::find(playerPieces.begin(), playerPieces.end(), target) != playerPieces.end()) {
count++;
} else {
break;
}
}
if (count >= 5) {
return true;
}
}
return false;
}
// Função auxiliar para adicionar peça sem duplicatas e desenhar
void addUniquePiece(Player& currentPlayer, Player& other1, Player& other2, const ChessPiece& newPiece, Mouse& mouse) {
auto isDuplicate = [&](const Player& p, const ChessPiece& piece) {
return std::find(p.getPieces().begin(), p.getPieces().end(), piece) != p.getPieces().end();
};
if (!isDuplicate(currentPlayer, newPiece) && !isDuplicate(other1, newPiece) && !isDuplicate(other2, newPiece)) {
currentPlayer.getPieces().push_back(newPiece);
setfillcolor(newPiece.getColor());
fillcircle(newPiece.getX(), newPiece.getY(), PIECE_RADIUS);
mouse.incrementClickCount();
}
}
void WhitePlayer::placePiece(Mouse& mouse, Player& other1, Player& other2) {
if (mouse.isClicked()) {
ChessPiece piece(mouse.getClickCoords().x, mouse.getClickCoords().y, WHITE);
piece.alignToGrid();
addUniquePiece(*this, other1, other2, piece, mouse);
}
}
void BlackPlayer::placePiece(Mouse& mouse, Player& other1, Player& other2) {
if (mouse.isClicked()) {
ChessPiece piece(mouse.getClickCoords().x, mouse.getClickCoords().y, BLACK);
piece.alignToGrid();
addUniquePiece(*this, other1, other2, piece, mouse);
}
}
void YellowPlayer::placePiece(Mouse& mouse, Player& other1, Player& other2) {
if (mouse.isClicked()) {
ChessPiece piece(mouse.getClickCoords().x, mouse.getClickCoords().y, YELLOW);
piece.alignToGrid();
addUniquePiece(*this, other1, other2, piece, mouse);
}
}
ChessPiece.h
Define a classe ChessPiece, com métodos para obter e definir coordenadas e cor, além de um operador de igualdade para comparação.
#pragma once
#include <graphics.h>
#define BOARD_GRID_SIZE 20 // Tamanho do intervalo da grade
#define PIECE_RADIUS 5 // Raio da peça
// Classe para representar uma peça
class ChessPiece {
private:
int posX;
int posY;
int pieceColor;
public:
ChessPiece(int x = 0, int y = 0, int color = WHITE) : posX(x), posY(y), pieceColor(color) {}
bool operator==(const ChessPiece& other) const; // Sobrecarga do operador ==
void setColor(int color);
void setPosition(int x, int y);
int getX() const { return posX; }
int getY() const { return posY; }
int getColor() const { return pieceColor; }
void alignToGrid(); // Alinha a peça à grade mais próxima e a desenha
};
ChessPiece.cpp
Implementa os métodos de ChessPiece, incluindo o alinhamento da peça às coordenadas da grade.
#include "ChessPiece.h"
bool ChessPiece::operator==(const ChessPiece& other) const {
return this->posX == other.posX && this->posY == other.posY;
}
void ChessPiece::setColor(int color) {
this->pieceColor = color;
}
void ChessPiece::setPosition(int x, int y) {
this->posX = x;
this->posY = y;
}
void ChessPiece::alignToGrid() {
// Ajusta X à grade mais próxima
if (posX % BOARD_GRID_SIZE > BOARD_GRID_SIZE / 2) {
posX = (posX / BOARD_GRID_SIZE + 1) * BOARD_GRID_SIZE;
} else {
posX = (posX / BOARD_GRID_SIZE) * BOARD_GRID_SIZE;
}
// Ajusta Y à grade mais próxima
if (posY % BOARD_GRID_SIZE > BOARD_GRID_SIZE / 2) {
posY = (posY / BOARD_GRID_SIZE + 1) * BOARD_GRID_SIZE;
} else {
posY = (posY / BOARD_GRID_SIZE) * BOARD_GRID_SIZE;
}
// Desenha a peça após o alinhamento
setfillcolor(pieceColor);
fillcircle(posX, posY, PIECE_RADIUS);
}
ChessBoard.h
Define a classe ChessBoard, com métodos para obter e definir dimensões e para desenhar o tabuleiro.
#pragma once
#include <graphics.h>
#define BOARD_GRID_SIZE 20 // Intervalo da grade
// Classe para representar o tabuleiro
class ChessBoard {
private:
int boardWidth;
int boardHeight;
public:
ChessBoard(int w = 400, int h = 400) : boardWidth(w), boardHeight(h) {}
void setDimensions(int w, int h);
int getWidth() const;
int getHeight() const;
void drawBoard(); // Desenha o tabuleiro
};
ChessBoard.cpp
Implementa os métodos de ChessBoard, incluindo a lógica de desenho das linhas da grade.
#include "ChessBoard.h"
void ChessBoard::setDimensions(int w, int h) {
this->boardWidth = w;
this->boardHeight = h;
}
int ChessBoard::getWidth() const {
return boardWidth;
}
int ChessBoard::getHeight() const {
return boardHeight;
}
void ChessBoard::drawBoard() {
for (int i = 0; i <= boardWidth; i += BOARD_GRID_SIZE) {
line(0, i, boardWidth, i); // Linhas horizontais
line(i, 0, i, boardHeight); // Linhas verticais
}
}
Mouse.h
Define a classe Mouse para capturar eventos do mouse.
#pragma once
#include <graphics.h>
// Classe para gerenciar eventos do mouse
class Mouse {
private:
ExMessage mouseEvent;
int clickCount;
public:
Mouse() : clickCount(0) {}
void incrementClickCount();
int getClickCount() const;
ExMessage& getMouseEvent();
bool isClicked(); // Verifica se o botão esquerdo do mouse foi clicado
POINT getClickCoords() const; // Retorna as coordenadas do clique
};
Mouse.cpp
Implementa os métodos de Mouse, verificando cliques e capturando coordenadas.
#include "Mouse.h"
void Mouse::incrementClickCount() {
this->clickCount++;
}
int Mouse::getClickCount() const {
return this->clickCount;
}
ExMessage& Mouse::getMouseEvent() {
return this->mouseEvent;
}
bool Mouse::isClicked() {
if (peekmessage(&this->mouseEvent) && this->mouseEvent.message == WM_LBUTTONDOWN) {
return true;
}
return false;
}
POINT Mouse::getClickCoords() const {
return {mouseEvent.x, mouseEvent.y};
}
Menu.h
Define a classe Menu, responsável pelas interfaces e pelo fluxo principal do jogo.
#pragma once
#include <graphics.h>
#include <conio.h>
#include "ChessBoard.h"
#include "Mouse.h"
#include "Player.h"
// Classe que gerencia as interfaces e o fluxo do jogo
class Menu {
private:
void handlePlayerTurns(Mouse& mouse, WhitePlayer& wp, BlackPlayer& bp, YellowPlayer& yp);
bool checkGameEnd(const ChessBoard& board, WhitePlayer& wp, BlackPlayer& bp, YellowPlayer& yp);
public:
int displayMainMenu(); // Exibe o menu principal
void showIntroduction(); // Exibe a tela de introdução
void startGame(); // Inicia o loop do jogo
};
Menu.cpp
Implementa os métodos de Menu, incluindo a inicialização do gráfico, desenho das interfaces e gerenciamento dos turnos dos jogadores.
#include "Menu.h"
int Menu::displayMainMenu() {
initgraph(800, 600);
setbkcolor(WHITE);
cleardevice();
settextstyle(70, 0, "Arial");
outtextxy(150, 120, "Gomoku 3 Jogadores");
settextstyle(50, 0, "Arial");
outtextxy(280, 250, "1. Jogar");
outtextxy(280, 350, "2. Instruções");
outtextxy(280, 450, "0. Sair");
int key;
while (true) {
if (_kbhit()) {
key = _getch();
if (key == '1' || key == '2' || key == '0') {
closegraph();
return key;
}
}
}
}
void Menu::showIntroduction() {
initgraph(800, 600, 0);
cleardevice();
settextstyle(50, 0, "Arial");
outtextxy(0, 100, "Instruções:");
outtextxy(0, 200, "- Jogo para 3 jogadores.");
outtextxy(0, 300, "- Ordem: Branco, Preto, Amarelo.");
outtextxy(0, 400, "- Alcance 5 peças em linha para vencer.");
outtextxy(450, 500, "Pressione 0 para voltar.");
while (!_kbhit() || _getch() != '0');
closegraph();
}
void Menu::startGame() {
ChessBoard board(600, 600); // Define um tabuleiro 600x600
Mouse mouse;
WhitePlayer whitePlayer;
BlackPlayer blackPlayer;
YellowPlayer yellowPlayer;
initgraph(board.getWidth(), board.getHeight());
setbkcolor(RGB(245, 222, 181)); // Cor de fundo
cleardevice();
board.drawBoard();
while (true) {
// Verifica se o jogador pode jogar
if (mouse.isClicked()) {
handlePlayerTurns(mouse, whitePlayer, blackPlayer, yellowPlayer);
}
// Verifica condição de vitória
if (checkGameEnd(board, whitePlayer, blackPlayer, yellowPlayer)) {
// Espera o jogador pressionar '0' para retornar ao menu
while (!_kbhit() || _getch() != '0');
break;
}
// Adiciona um pequeno delay para não sobrecarregar a CPU
Sleep(10);
}
closegraph();
}
void Menu::handlePlayerTurns(Mouse& mouse, WhitePlayer& wp, BlackPlayer& bp, YellowPlayer& yp) {
int turn = mouse.getClickCount() % 3;
switch (turn) {
case 0: wp.placePiece(mouse, bp, yp); break;
case 1: bp.placePiece(mouse, wp, yp); break;
case 2: yp.placePiece(mouse, wp, bp); break;
}
}
bool Menu::checkGameEnd(const ChessBoard& board, WhitePlayer& wp, BlackPlayer& bp, YellowPlayer& yp) {
if (wp.checkWinCondition(board)) {
cleardevice();
settextstyle(30, 0, "Arial");
outtextxy(board.getWidth() / 2 - 100, board.getHeight() / 2, "Branco Venceu! Pressione 0 para voltar.");
return true;
} else if (bp.checkWinCondition(board)) {
cleardevice();
settextstyle(30, 0, "Arial");
outtextxy(board.getWidth() / 2 - 100, board.getHeight() / 2, "Preto Venceu! Pressione 0 para voltar.");
return true;
} else if (yp.checkWinCondition(board)) {
cleardevice();
settextstyle(30, 0, "Arial");
outtextxy(board.getWidth() / 2 - 100, board.getHeight() / 2, "Amarelo Venceu! Pressione 0 para voltar.");
return true;
}
return false;
}
Efeitos de Implementação
O jogo exibe uma interface gráfica onde os jogadores podem clicar para colocar suas peças. O tabuleiro é desenhado com linhas de grade, e as peças são centralizadas nas interseções. A detecção de vitória é realizada após cada jogada.
Bugs Conhecidos
- A detecção de vitória pode não cobrir todas as combinações em casos extremos.
- A interface de usuário pode ser aprimorada para melhor feedback visual.