diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..7d8d2c7 Binary files /dev/null and b/.DS_Store differ diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f8bb36..93bf249 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..6e04356 --- /dev/null +++ b/GEMINI.md @@ -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. diff --git a/include/elements.hpp b/include/elements.hpp index fc7a8ee..61c1ef2 100644 --- a/include/elements.hpp +++ b/include/elements.hpp @@ -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 -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 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 diff --git a/include/globals.hpp b/include/globals.hpp index b1e15d1..cecf868 100644 --- a/include/globals.hpp +++ b/include/globals.hpp @@ -2,6 +2,5 @@ #define GLOBALS_HPP #define ROW 5 #define COL 5 -#define FMPS 16 #endif diff --git a/include/input.hpp b/include/input.hpp deleted file mode 100644 index 05a935b..0000000 --- a/include/input.hpp +++ /dev/null @@ -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 *¤t_player, Player *player_one, Player *player_two); -}; - -#endif diff --git a/include/window.hpp b/include/window.hpp index ec11205..5593689 100644 --- a/include/window.hpp +++ b/include/window.hpp @@ -1,16 +1,14 @@ #ifndef WINDOW_HPP #define WINDOW_HPP -#include "globals.hpp" -#include "elements.hpp" +#include +#include struct Window { - Point* m_board[ROW][COL] = {}; - // void terminate(Tile (&tile_list)[9], bool &running); - + Window(); + std::array, ROW> view; void clear_display(); - void update_display(Point** buffer); - void draw_display(int fps); + void draw_display(); }; #endif diff --git a/src/elements.cpp b/src/elements.cpp index d1c36b9..2838ce3 100644 --- a/src/elements.cpp +++ b/src/elements.cpp @@ -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 +#include +#include +#include +#include +#include +#include +Engine::Engine() { + initscr(); + cbreak(); + noecho(); + curs_set(0); + nodelay(stdscr, true); + win = std::make_unique(); } -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; +// } diff --git a/src/input.cpp b/src/input.cpp deleted file mode 100644 index c1c9ba5..0000000 --- a/src/input.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include "input.hpp" -#include "elements.hpp" - -void Input::get_input(bool &running, Point** buffer, Player *¤t_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; - } - } -} diff --git a/src/main.cpp b/src/main.cpp index 2632d3c..42618ae 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,55 +1,7 @@ -#include -#include -#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; } diff --git a/src/window.cpp b/src/window.cpp index 10f5eba..d4cd707 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -1,36 +1,27 @@ -#include "window.hpp" +#include +#include #include -#include -#include -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]; - } - } }