changes
This commit is contained in:
+5
-7
@@ -1,20 +1,18 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
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)
|
project(tic_tac_toe)
|
||||||
set(CMAKE_C_STANDARD 23)
|
|
||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
|
||||||
include_directories(include)
|
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/input.cpp
|
|
||||||
src/window.cpp
|
src/window.cpp
|
||||||
src/elements.cpp
|
src/elements.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(tic_tac_toe ${SOURCES})
|
add_executable(tic_tac_toe ${SOURCES})
|
||||||
|
|
||||||
|
target_include_directories(tic_tac_toe PRIVATE include)
|
||||||
find_package(Curses REQUIRED)
|
find_package(Curses REQUIRED)
|
||||||
include_directories(${CURSES_INCLUDE_DIR})
|
|
||||||
target_link_libraries(tic_tac_toe ${CURSES_LIBRARIES})
|
target_link_libraries(tic_tac_toe ${CURSES_LIBRARIES})
|
||||||
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|
||||||
|
|||||||
@@ -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
@@ -1,24 +1,34 @@
|
|||||||
#ifndef ELEMENTS_HPP
|
#ifndef ELEMENTS_HPP
|
||||||
#define ELEMENTS_HPP
|
#define ELEMENTS_HPP
|
||||||
|
|
||||||
struct Point;
|
#include <memory>
|
||||||
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);
|
|
||||||
|
|
||||||
struct Point {
|
class Window;
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Player {
|
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;
|
char m_symbol;
|
||||||
int m_id;
|
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
|
#endif
|
||||||
|
|||||||
@@ -2,6 +2,5 @@
|
|||||||
#define GLOBALS_HPP
|
#define GLOBALS_HPP
|
||||||
#define ROW 5
|
#define ROW 5
|
||||||
#define COL 5
|
#define COL 5
|
||||||
#define FMPS 16
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -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
|
|
||||||
+5
-7
@@ -1,16 +1,14 @@
|
|||||||
#ifndef WINDOW_HPP
|
#ifndef WINDOW_HPP
|
||||||
#define WINDOW_HPP
|
#define WINDOW_HPP
|
||||||
|
|
||||||
#include "globals.hpp"
|
#include <globals.hpp>
|
||||||
#include "elements.hpp"
|
#include <array>
|
||||||
|
|
||||||
struct Window {
|
struct Window {
|
||||||
Point* m_board[ROW][COL] = {};
|
Window();
|
||||||
// void terminate(Tile (&tile_list)[9], bool &running);
|
std::array<std::array<char, COL>, ROW> view;
|
||||||
|
|
||||||
void clear_display();
|
void clear_display();
|
||||||
void update_display(Point** buffer);
|
void draw_display();
|
||||||
void draw_display(int fps);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+108
-43
@@ -1,53 +1,118 @@
|
|||||||
#include "elements.hpp"
|
#include <elements.hpp>
|
||||||
#include "globals.hpp"
|
#include <iostream>
|
||||||
|
#include <window.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <ncurses.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <ranges>
|
||||||
|
|
||||||
void cleanup(Point** buffer, int buffer_size) {
|
Engine::Engine() {
|
||||||
for(int i = 0; i < buffer_size; i++) {
|
initscr();
|
||||||
delete buffer[i];
|
cbreak();
|
||||||
|
noecho();
|
||||||
|
curs_set(0);
|
||||||
|
nodelay(stdscr, true);
|
||||||
|
win = std::make_unique<Window>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Engine::~Engine() {
|
||||||
|
endwin();
|
||||||
}
|
}
|
||||||
|
|
||||||
void allocate_points(Point** buffer, char symbol) {
|
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;
|
int index = 0;
|
||||||
for(int i = 0; i < ROW; i += 2) {
|
// if(state.player1.on_line()) {
|
||||||
for(int a = 0; a < ROW; a += 2) {
|
// std::cout << "Game Over! Player 1 wins!" << std::endl;
|
||||||
buffer[index++] = new Point { i, a, symbol };
|
// return false;
|
||||||
}
|
// } else if(state.player2.on_line()) {
|
||||||
}
|
// std::cout << "Game Over! Player 2 wins!" << std::endl;
|
||||||
}
|
// return false;
|
||||||
|
// }
|
||||||
Player* get_current_player(Player* current_player, Player* player_one, Player* player_two) {
|
for(auto &row : win->view) {
|
||||||
if(current_player == nullptr) { current_player = player_one; }
|
for(auto val : row) {
|
||||||
else if(current_player->m_id == 1) { current_player = player_two; }
|
if(val != ' ') index++;
|
||||||
else if(current_player->m_id == 2) { current_player = player_one; }
|
|
||||||
return current_player;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(index == 9) {
|
||||||
|
std::cout << "Game Over! Its a tie!" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
|
} else index = 0;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
// }
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
#include <ncurses.h>
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-50
@@ -1,55 +1,7 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <thread>
|
|
||||||
#include "window.hpp"
|
|
||||||
#include "input.hpp"
|
|
||||||
#include "elements.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() {
|
int main() {
|
||||||
Input input;
|
Engine engine;
|
||||||
Window window;
|
engine.run();
|
||||||
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();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-21
@@ -1,36 +1,27 @@
|
|||||||
#include "window.hpp"
|
#include <window.hpp>
|
||||||
|
#include <ranges>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <chrono>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
void Window::clear_display() {
|
Window::Window() {
|
||||||
for(int i = 0; i < ROW; i++) {
|
for(auto &val : view) { val.fill('.'); }
|
||||||
for(int j = 0; j < COL; j++) {
|
for(int i : std::ranges::views::iota(0, ROW)) {
|
||||||
m_board[i][j] = nullptr;
|
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
|
std::cout << "\033[H" << std::flush; // Clear screen
|
||||||
for(int i = 0; i < ROW; i++) {
|
for(int i : std::ranges::views::iota(0, ROW)) {
|
||||||
for(int j = 0; j < COL; j++) {
|
for(int j : std::ranges::views::iota(0, COL)) {
|
||||||
if(m_board[i][j] == nullptr) {
|
if(view[i][j] != '.') { std::cout << view[i][j] << " "; }
|
||||||
|
if(view[i][j] == '.') {
|
||||||
if(i % 2 == 0) { std::cout << "| "; }
|
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(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::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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user