This commit is contained in:
DarthMooMancer
2025-11-06 17:34:42 -05:00
parent 0a07d776ce
commit fccc07600a
11 changed files with 193 additions and 205 deletions
Vendored
BIN
View File
Binary file not shown.
+5 -7
View File
@@ -1,20 +1,18 @@
cmake_minimum_required(VERSION 3.16)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(tic_tac_toe)
set(CMAKE_C_STANDARD 23)
set(CMAKE_C_STANDARD_REQUIRED ON)
include_directories(include)
set(SOURCES
src/main.cpp
src/input.cpp
src/window.cpp
src/elements.cpp
)
add_executable(tic_tac_toe ${SOURCES})
target_include_directories(tic_tac_toe PRIVATE include)
find_package(Curses REQUIRED)
include_directories(${CURSES_INCLUDE_DIR})
target_link_libraries(tic_tac_toe ${CURSES_LIBRARIES})
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+39
View File
@@ -0,0 +1,39 @@
# Gemini Code Assistant Context
This document provides context for the Gemini code assistant to understand and assist with the development of this project.
## Project Overview
This is a simple, terminal-based Tic-Tac-Toe game written in C++. It uses the `ncurses` library to create the game interface in the terminal. The game is played on a 5x5 grid.
The project is structured into three main components:
* **`Engine`**: The main game engine that manages the game loop, player turns, and input processing.
* **`Window`**: Handles the display of the game board in the terminal.
* **`Player`**: Represents a player in the game.
## Building and Running
The project uses CMake for building.
**To build the project:**
```bash
mkdir build
cd build
cmake ..
make
```
**To run the game:**
```bash
./tic_tac_toe
```
## Development Conventions
* **Language**: C++23
* **Build System**: CMake
* **Code Style**: The code follows a consistent style with the use of header guards, and clear naming conventions.
* **Testing**: There are no tests in the project.
+23 -13
View File
@@ -1,24 +1,34 @@
#ifndef ELEMENTS_HPP
#define ELEMENTS_HPP
struct Point;
struct Player;
void cleanup(Point** buffer, int buffer_size);
void allocate_points(Point** buffer, char symbol);
Player* get_current_player(Player* current_player, Player* player_one, Player* player_two);
#include <memory>
struct Point {
Point(int row, int col, char symbol) : m_row(row), m_col(col), m_symbol(symbol) {};
int m_row;
int m_col;
char m_symbol;
};
class Window;
struct Player {
Player(char symbol, bool turn, int id) : m_symbol(symbol), m_id(id) {};
Player(char symbol, int id) : m_symbol(symbol), m_id(id) {};
char m_symbol;
int m_id;
bool on_line(Point** buffer);
bool on_line();
};
class Engine {
public:
Engine();
~Engine();
void run();
private:
std::unique_ptr<Window> win;
struct GameState {
Player player1;
Player player2;
Player *temp;
GameState() : player1('x', 1), player2('o', 2), temp { &player1 } {};
} state;
void get_current_player();
bool process_input();
bool exit_code();
};
#endif
-1
View File
@@ -2,6 +2,5 @@
#define GLOBALS_HPP
#define ROW 5
#define COL 5
#define FMPS 16
#endif
-10
View File
@@ -1,10 +0,0 @@
#ifndef INPUT_HPP
#define INPUT_HPP
#include "elements.hpp"
struct Input {
int _getch = 0;
void get_input(bool &running, Point** buffer, Player *&current_player, Player *player_one, Player *player_two);
};
#endif
+5 -7
View File
@@ -1,16 +1,14 @@
#ifndef WINDOW_HPP
#define WINDOW_HPP
#include "globals.hpp"
#include "elements.hpp"
#include <globals.hpp>
#include <array>
struct Window {
Point* m_board[ROW][COL] = {};
// void terminate(Tile (&tile_list)[9], bool &running);
Window();
std::array<std::array<char, COL>, ROW> view;
void clear_display();
void update_display(Point** buffer);
void draw_display(int fps);
void draw_display();
};
#endif
+107 -42
View File
@@ -1,53 +1,118 @@
#include "elements.hpp"
#include "globals.hpp"
void cleanup(Point** buffer, int buffer_size) {
for(int i = 0; i < buffer_size; i++) {
delete buffer[i];
}
#include <elements.hpp>
#include <iostream>
#include <window.hpp>
#include <memory>
#include <ncurses.h>
#include <thread>
#include <ranges>
Engine::Engine() {
initscr();
cbreak();
noecho();
curs_set(0);
nodelay(stdscr, true);
win = std::make_unique<Window>();
}
void allocate_points(Point** buffer, char symbol) {
Engine::~Engine() {
endwin();
}
void Engine::run() {
while (exit_code() && process_input()) {
win->draw_display();
std::cout << "Current Player: " << state.temp->m_symbol << "\n";
std::this_thread::sleep_for(std::chrono::milliseconds((int) (150))); // 1000 / fps; 200ms = 5fps
}
}
bool Engine::exit_code() {
int index = 0;
for(int i = 0; i < ROW; i += 2) {
for(int a = 0; a < ROW; a += 2) {
buffer[index++] = new Point { i, a, symbol };
// if(state.player1.on_line()) {
// std::cout << "Game Over! Player 1 wins!" << std::endl;
// return false;
// } else if(state.player2.on_line()) {
// std::cout << "Game Over! Player 2 wins!" << std::endl;
// return false;
// }
for(auto &row : win->view) {
for(auto val : row) {
if(val != ' ') index++;
}
}
if(index == 9) {
std::cout << "Game Over! Its a tie!" << std::endl;
return false;
} else index = 0;
return true;
}
Player* get_current_player(Player* current_player, Player* player_one, Player* player_two) {
if(current_player == nullptr) { current_player = player_one; }
else if(current_player->m_id == 1) { current_player = player_two; }
else if(current_player->m_id == 2) { current_player = player_one; }
return current_player;
bool Engine::process_input() {
int ch;
while((ch = getch()) != ERR) {
if(ch == 113) { return false; }
if(ch == 49) {
win->view[0][0] = state.temp->m_symbol;
get_current_player();
} if(ch == 50) {
win->view[0][2] = state.temp->m_symbol;
get_current_player();
} if(ch == 51) {
win->view[0][4] = state.temp->m_symbol;
get_current_player();
} if(ch == 52) {
win->view[2][0] = state.temp->m_symbol;
get_current_player();
} if(ch == 53) {
win->view[2][2] = state.temp->m_symbol;
get_current_player();
} if(ch == 54) {
win->view[2][4] = state.temp->m_symbol;
get_current_player();
} if(ch == 55) {
win->view[4][0] = state.temp->m_symbol;
get_current_player();
} if(ch == 56) {
win->view[4][2] = state.temp->m_symbol;
get_current_player();
} if(ch == 57) {
win->view[4][4] = state.temp->m_symbol;
get_current_player();
}
}
return true;
}
bool Player::on_line(Point** buffer) {
for(int i = 0; i <= 6; i += 3) {
if(buffer[i]->m_symbol == buffer[i+1]->m_symbol && buffer[i+1]->m_symbol == buffer[i+2]->m_symbol) {
if(buffer[i]->m_symbol == m_symbol) {
return true;
}
}
}
for(int i = 0; i < 3; i++) {
if(buffer[i]->m_symbol == buffer[i+3]->m_symbol && buffer[i+3]->m_symbol == buffer[i+6]->m_symbol) {
if(buffer[i]->m_symbol == m_symbol) {
return true;
}
}
}
if(buffer[0]->m_symbol == buffer[4]->m_symbol && buffer[4]->m_symbol == buffer[8]->m_symbol) {
if(buffer[0]->m_symbol == m_symbol) {
return true;
}
}
if(buffer[2]->m_symbol == buffer[4]->m_symbol && buffer[4]->m_symbol == buffer[6]->m_symbol) {
if(buffer[2]->m_symbol == m_symbol) {
return true;
}
}
return false;
void Engine::get_current_player() {
if(state.temp->m_symbol == 'x') { state.temp = &state.player2; }
else if(state.temp->m_symbol == 'o') { state.temp = &state.player1; }
}
// bool Player::on_line(vec2** buffer) {
// for(int i = 0; i <= 6; i += 3) {
// if(buffer[i]->m_symbol == buffer[i+1]->m_symbol && buffer[i+1]->m_symbol == buffer[i+2]->m_symbol) {
// if(buffer[i]->m_symbol == m_symbol) {
// return true;
// }
// }
// }
// for(int i = 0; i < 3; i++) {
// if(buffer[i]->m_symbol == buffer[i+3]->m_symbol && buffer[i+3]->m_symbol == buffer[i+6]->m_symbol) {
// if(buffer[i]->m_symbol == m_symbol) {
// return true;
// }
// }
// }
// if(buffer[0]->m_symbol == buffer[4]->m_symbol && buffer[4]->m_symbol == buffer[8]->m_symbol) {
// if(buffer[0]->m_symbol == m_symbol) {
// return true;
// }
// }
// if(buffer[2]->m_symbol == buffer[4]->m_symbol && buffer[4]->m_symbol == buffer[6]->m_symbol) {
// if(buffer[2]->m_symbol == m_symbol) {
// return true;
// }
// }
// return false;
// }
-54
View File
@@ -1,54 +0,0 @@
#include <ncurses.h>
#include "input.hpp"
#include "elements.hpp"
void Input::get_input(bool &running, Point** buffer, Player *&current_player, Player *player_one, Player *player_two) {
while(running) {
initscr();
cbreak();
noecho();
_getch = getch();
endwin();
if(_getch >= 49 && _getch <= 57) {
if(_getch == 49 && buffer[0]->m_symbol == ' ') {
buffer[0]->m_symbol = current_player->m_symbol;
current_player = get_current_player(current_player, player_one, player_two);
}
else if(_getch == 50 && buffer[1]->m_symbol == ' ') {
buffer[1]->m_symbol = current_player->m_symbol;
current_player = get_current_player(current_player, player_one, player_two);
}
else if(_getch == 51 && buffer[2]->m_symbol == ' ') {
buffer[2]->m_symbol = current_player->m_symbol;
current_player = get_current_player(current_player, player_one, player_two);
}
else if(_getch == 52 && buffer[3]->m_symbol == ' ') {
buffer[3]->m_symbol = current_player->m_symbol;
current_player = get_current_player(current_player, player_one, player_two);
}
else if(_getch == 53 && buffer[4]->m_symbol == ' ') {
buffer[4]->m_symbol = current_player->m_symbol;
current_player = get_current_player(current_player, player_one, player_two);
}
else if(_getch == 54 && buffer[5]->m_symbol == ' ') {
buffer[5]->m_symbol = current_player->m_symbol;
current_player = get_current_player(current_player, player_one, player_two);
}
else if(_getch == 55 && buffer[6]->m_symbol == ' ') {
buffer[6]->m_symbol = current_player->m_symbol;
current_player = get_current_player(current_player, player_one, player_two);
}
else if(_getch == 56 && buffer[7]->m_symbol == ' ') {
buffer[7]->m_symbol = current_player->m_symbol;
current_player = get_current_player(current_player, player_one, player_two);
}
else if(_getch == 57 && buffer[8]->m_symbol == ' ') {
buffer[8]->m_symbol = current_player->m_symbol;
current_player = get_current_player(current_player, player_one, player_two);
}
}
if(_getch == 113) {
running = false;
}
}
}
+2 -50
View File
@@ -1,55 +1,7 @@
#include <iostream>
#include <thread>
#include "window.hpp"
#include "input.hpp"
#include "elements.hpp"
bool should_exit(Point** buffer, Player &one, Player &two, bool &running) {
int i, index = 0;
if(one.on_line(buffer)) {
std::cout << "Game Over! Player 1 wins!" << std::endl;
return true;
} else if(two.on_line(buffer)) {
std::cout << "Game Over! Player 2 wins!" << std::endl;
return true;
}
for(i = 0; i < 9; i++) {
if(buffer[i]->m_symbol != ' ' && !one.on_line(buffer) && !one.on_line(buffer)) {
index++;
}
}
if(index == 9) {
std::cout << "Game Over! Its a tie!" << std::endl;
return true;
} else {
index = 0;
}
return false;
}
int main() {
Input input;
Window window;
Player player_one('x', true, 1);
Player player_two('o', false, 2);
Player* default_player = get_current_player(nullptr, &player_one, &player_two);
bool running = true;
int count = 0;
Point* buffer[9] = {};
allocate_points(buffer, ' ');
std::thread input_thread([&] { input.get_input(running, buffer, default_player, &player_one, &player_two); } );
while (running) {
if(should_exit(buffer, player_one, player_two, running)) {
running = false;
}
window.clear_display();
window.update_display(buffer);
window.draw_display(150);
std::cout << "Current Player: " << default_player->m_symbol << std::endl;
}
cleanup(buffer, 9);
input_thread.join();
Engine engine;
engine.run();
return 0;
}
+12 -21
View File
@@ -1,36 +1,27 @@
#include "window.hpp"
#include <window.hpp>
#include <ranges>
#include <iostream>
#include <chrono>
#include <thread>
void Window::clear_display() {
for(int i = 0; i < ROW; i++) {
for(int j = 0; j < COL; j++) {
m_board[i][j] = nullptr;
Window::Window() {
for(auto &val : view) { val.fill('.'); }
for(int i : std::ranges::views::iota(0, ROW)) {
for(int j : std::ranges::views::iota(0, COL)) {
if(i % 2 == 0 && j % 2 == 0) { view[i][j] = ' '; }
}
}
}
void Window::draw_display(int milliseconds) {
void Window::draw_display() {
std::cout << "\033[H" << std::flush; // Clear screen
for(int i = 0; i < ROW; i++) {
for(int j = 0; j < COL; j++) {
if(m_board[i][j] == nullptr) {
for(int i : std::ranges::views::iota(0, ROW)) {
for(int j : std::ranges::views::iota(0, COL)) {
if(view[i][j] != '.') { std::cout << view[i][j] << " "; }
if(view[i][j] == '.') {
if(i % 2 == 0) { std::cout << "| "; }
if(i % 2 != 0 && j % 2 == 0) { std::cout << "- "; }
if(i % 2 != 0 && j % 2 != 0) { std::cout << "+ "; }
}
if(m_board[i][j] != nullptr) { std::cout << m_board[i][j]->m_symbol << " "; }
}
std::cout << "\r\n";
}
std::this_thread::sleep_for(std::chrono::milliseconds((int) (milliseconds))); // 1000 / fps; 200ms = 5fps
}
void Window::update_display(Point** buffer) {
for(int i = 0; i < 9; i++) {
if(buffer[i] != nullptr) {
m_board[buffer[i]->m_row][buffer[i]->m_col] = buffer[i];
}
}
}