Add base game
This commit is contained in:
parent
85e4a03a90
commit
716ae24600
12
.gitignore
vendored
12
.gitignore
vendored
@ -1,11 +1 @@
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Testing
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
build
|
||||
|
6
CMakeLists.txt
Normal file
6
CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(CppConsoleGame VERSION 1.0.0)
|
||||
|
||||
add_subdirectory(conio)
|
||||
add_subdirectory(random-utils)
|
||||
add_subdirectory(cpp-console-game)
|
11
conio/CMakeLists.txt
Normal file
11
conio/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
add_library(
|
||||
conio
|
||||
conio.hpp
|
||||
conio.cpp
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
conio
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
30
conio/conio.cpp
Normal file
30
conio/conio.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include "conio.hpp"
|
||||
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
|
||||
/**
|
||||
* Get a character from the console without needing to press enter
|
||||
*/
|
||||
char conio::getch()
|
||||
{
|
||||
char c;
|
||||
struct termios oldattr, newattr;
|
||||
|
||||
// Get the terminal settings
|
||||
tcgetattr(STDIN_FILENO, &oldattr);
|
||||
newattr = oldattr;
|
||||
|
||||
// Disable canonical mode and echo
|
||||
newattr.c_lflag &= ~(ICANON | ECHO);
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &newattr);
|
||||
|
||||
// Get the character, will not wait for enter because of the new settings
|
||||
std::cin.get(c);
|
||||
|
||||
// Reset the terminal settings back to normal behaviour
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldattr);
|
||||
|
||||
return c;
|
||||
}
|
9
conio/conio.hpp
Normal file
9
conio/conio.hpp
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef CONIO_HPP
|
||||
#define CONIO_HPP
|
||||
|
||||
namespace conio
|
||||
{
|
||||
char getch();
|
||||
}
|
||||
|
||||
#endif // CONIO_HPP
|
55
cpp-console-game/Board.cpp
Normal file
55
cpp-console-game/Board.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
#include "Board.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace ConsoleGame
|
||||
{
|
||||
Board::Board(int width, int height, std::string fill) : fill(fill)
|
||||
{
|
||||
cells.resize(height);
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
cells[i].resize(width, fill);
|
||||
}
|
||||
}
|
||||
|
||||
void Board::draw()
|
||||
{
|
||||
std::cout << "\033[H"; // Move cursor to the top left corner
|
||||
std::cout << "\033[2J"; // Clear the screen
|
||||
|
||||
for (auto row : cells)
|
||||
{
|
||||
for (auto cell : row)
|
||||
{
|
||||
std::cout << cell;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void Board::setCell(int x, int y, std::string value)
|
||||
{
|
||||
cells[y][x] = value;
|
||||
}
|
||||
|
||||
std::string Board::getCell(int x, int y)
|
||||
{
|
||||
return cells[y][x];
|
||||
}
|
||||
|
||||
int Board::getWidth()
|
||||
{
|
||||
return cells[0].size();
|
||||
}
|
||||
|
||||
int Board::getHeight()
|
||||
{
|
||||
return cells.size();
|
||||
}
|
||||
|
||||
std::string Board::getFill()
|
||||
{
|
||||
return fill;
|
||||
}
|
||||
}
|
32
cpp-console-game/Board.hpp
Normal file
32
cpp-console-game/Board.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef BOARD_HPP
|
||||
#define BOARD_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace ConsoleGame
|
||||
{
|
||||
class Board
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Construct a new Board object
|
||||
* @param width The width of the board
|
||||
* @param height The height of the board
|
||||
* @param fill The string to fill the board with
|
||||
*/
|
||||
Board(int width, int height, std::string fill = "#");
|
||||
void draw();
|
||||
void setCell(int x, int y, std::string value);
|
||||
std::string getCell(int x, int y);
|
||||
int getWidth();
|
||||
int getHeight();
|
||||
std::string getFill();
|
||||
|
||||
private:
|
||||
std::vector<std::vector<std::string>> cells;
|
||||
std::string fill;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // BOARD_HPP
|
40
cpp-console-game/CMakeLists.txt
Normal file
40
cpp-console-game/CMakeLists.txt
Normal file
@ -0,0 +1,40 @@
|
||||
add_library(
|
||||
Board
|
||||
Board.hpp
|
||||
Board.cpp
|
||||
)
|
||||
|
||||
add_library(
|
||||
GameState
|
||||
GameState.hpp
|
||||
GameState.cpp
|
||||
)
|
||||
|
||||
add_library(
|
||||
Player
|
||||
Player.hpp
|
||||
Player.cpp
|
||||
)
|
||||
|
||||
add_executable(
|
||||
cpp-console-game
|
||||
main.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
cpp-console-game PRIVATE
|
||||
GameState
|
||||
conio
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
GameState PRIVATE
|
||||
Board
|
||||
Player
|
||||
RandomUtils
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
Player PRIVATE
|
||||
RandomUtils
|
||||
)
|
91
cpp-console-game/GameState.cpp
Normal file
91
cpp-console-game/GameState.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
#include "GameState.hpp"
|
||||
|
||||
#include <RandomUtils.hpp>
|
||||
|
||||
int idCounter = 0;
|
||||
|
||||
namespace ConsoleGame
|
||||
{
|
||||
GameState::GameState(int width, int height, std::string fill)
|
||||
: board(width, height, fill),
|
||||
running(true),
|
||||
players() {}
|
||||
|
||||
int GameState::addPlayer()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
// Add a player to the game at a random position
|
||||
int x, y;
|
||||
do
|
||||
{
|
||||
x = RandomUtils::rand(0, board.getWidth() - 1);
|
||||
y = RandomUtils::rand(0, board.getHeight() - 1);
|
||||
} while (board.getCell(x, y) != board.getFill());
|
||||
|
||||
// Increment the id counter and add the player to the map
|
||||
int id = idCounter++;
|
||||
players[id] = Player({x, y});
|
||||
board.setCell(x, y, players[id].getFill());
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void GameState::removePlayer(int id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
// Replace the player's position with a regular board cell
|
||||
auto pos = players[id].getPosition();
|
||||
board.setCell(pos.first, pos.second, board.getFill());
|
||||
|
||||
players.erase(id);
|
||||
}
|
||||
|
||||
void GameState::movePlayer(int id, std::pair<int, int> newPos)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
// Check if the new position is out of bounds
|
||||
if (newPos.first < 0 || newPos.first >= board.getWidth() || newPos.second < 0 || newPos.second >= board.getHeight())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Replace the player's old position with a regular board cell
|
||||
auto oldPos = players[id].getPosition();
|
||||
board.setCell(oldPos.first, oldPos.second, board.getFill());
|
||||
|
||||
// Update the player's position and set the new position on the board
|
||||
players[id].setPosition(newPos);
|
||||
board.setCell(newPos.first, newPos.second, players[id].getFill());
|
||||
}
|
||||
|
||||
std::pair<int, int> GameState::getPlayerPosition(int id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
return players[id].getPosition();
|
||||
}
|
||||
|
||||
void GameState::draw()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
board.draw();
|
||||
}
|
||||
|
||||
bool GameState::isRunning()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
return running;
|
||||
}
|
||||
|
||||
void GameState::stop()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
running = false;
|
||||
}
|
||||
}
|
68
cpp-console-game/GameState.hpp
Normal file
68
cpp-console-game/GameState.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
#ifndef GAME_STATE_HPP
|
||||
#define GAME_STATE_HPP
|
||||
|
||||
#include "Board.hpp"
|
||||
#include "Player.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
namespace ConsoleGame
|
||||
{
|
||||
class GameState
|
||||
{
|
||||
public:
|
||||
GameState(int width, int heigh, std::string fill = "#");
|
||||
|
||||
/**
|
||||
* Add a player to the game
|
||||
* @return New player id
|
||||
*/
|
||||
int addPlayer();
|
||||
|
||||
/**
|
||||
* Remove a player from the game
|
||||
* @param id Player id
|
||||
*/
|
||||
void removePlayer(int id);
|
||||
|
||||
/**
|
||||
* Move a player to a new position
|
||||
* @param id Player id
|
||||
* @param newPos New position of the player
|
||||
*/
|
||||
void movePlayer(int id, std::pair<int, int> newPos);
|
||||
|
||||
/**
|
||||
* Get the position of a player
|
||||
* @param id Player id
|
||||
* @return Player position
|
||||
*/
|
||||
std::pair<int, int> getPlayerPosition(int id);
|
||||
|
||||
/**
|
||||
* Draw the game board
|
||||
*/
|
||||
void draw();
|
||||
|
||||
/**
|
||||
* Check if the game is running
|
||||
* @return True if the game is running, false otherwise
|
||||
*/
|
||||
bool isRunning();
|
||||
|
||||
/**
|
||||
* Stop the game
|
||||
*/
|
||||
void stop();
|
||||
|
||||
private:
|
||||
Board board;
|
||||
std::unordered_map<int, Player> players;
|
||||
bool running = true;
|
||||
std::mutex mutex;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // GAME_STATE_HPP
|
41
cpp-console-game/Player.cpp
Normal file
41
cpp-console-game/Player.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
#include "Player.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <RandomUtils.hpp>
|
||||
|
||||
namespace ConsoleGame
|
||||
{
|
||||
int colors[] = {
|
||||
31, // Red
|
||||
32, // Green
|
||||
33, // Yellow
|
||||
36, // Cyan
|
||||
};
|
||||
|
||||
Player::Player() : pos({0, 0})
|
||||
{
|
||||
fill = "\033[32m@\033[0m";
|
||||
}
|
||||
|
||||
Player::Player(std::pair<int, int> pos) : pos(pos)
|
||||
{
|
||||
// Set the fill color to a random color
|
||||
int color = colors[RandomUtils::rand(0, sizeof(colors) / sizeof(colors[0]) - 1)];
|
||||
fill = "\033[" + std::to_string(color) + "m@\033[0m";
|
||||
}
|
||||
|
||||
std::pair<int, int> Player::getPosition()
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
std::string Player::getFill()
|
||||
{
|
||||
return fill;
|
||||
}
|
||||
|
||||
void Player::setPosition(std::pair<int, int> newPos)
|
||||
{
|
||||
pos = newPos;
|
||||
}
|
||||
}
|
25
cpp-console-game/Player.hpp
Normal file
25
cpp-console-game/Player.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef PLAYER_HPP
|
||||
#define PLAYER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ConsoleGame
|
||||
{
|
||||
class Player
|
||||
{
|
||||
public:
|
||||
Player(); // TODO - Get rid of this constructor while keeping the compiler happy
|
||||
Player(std::pair<int, int> pos);
|
||||
|
||||
std::pair<int, int> getPosition();
|
||||
std::string getFill();
|
||||
|
||||
void setPosition(std::pair<int, int> newPos);
|
||||
|
||||
private:
|
||||
std::pair<int, int> pos;
|
||||
std::string fill;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // PLAYER_HPP
|
60
cpp-console-game/main.cpp
Normal file
60
cpp-console-game/main.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include "GameState.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <thread>
|
||||
#include <conio.hpp>
|
||||
|
||||
ConsoleGame::GameState gameState(20, 10);
|
||||
|
||||
void inputLoop(int playerId)
|
||||
{
|
||||
while (gameState.isRunning())
|
||||
{
|
||||
char c = conio::getch();
|
||||
auto position = gameState.getPlayerPosition(playerId);
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 'w':
|
||||
gameState.movePlayer(playerId, {position.first, position.second - 1});
|
||||
break;
|
||||
case 'a':
|
||||
gameState.movePlayer(playerId, {position.first - 1, position.second});
|
||||
break;
|
||||
case 's':
|
||||
gameState.movePlayer(playerId, {position.first, position.second + 1});
|
||||
break;
|
||||
case 'd':
|
||||
gameState.movePlayer(playerId, {position.first + 1, position.second});
|
||||
break;
|
||||
case 'q':
|
||||
gameState.stop();
|
||||
break;
|
||||
case 'p':
|
||||
gameState.addPlayer();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void renderLoop()
|
||||
{
|
||||
while (gameState.isRunning())
|
||||
{
|
||||
gameState.draw();
|
||||
usleep(100000); // Sleep for 100ms
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int playerId = gameState.addPlayer();
|
||||
|
||||
std::thread inputThread(inputLoop, playerId);
|
||||
std::thread renderThread(renderLoop);
|
||||
|
||||
inputThread.join();
|
||||
renderThread.join();
|
||||
|
||||
return 0;
|
||||
}
|
11
random-utils/CMakeLists.txt
Normal file
11
random-utils/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
add_library(
|
||||
RandomUtils
|
||||
RandomUtils.hpp
|
||||
RandomUtils.cpp
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
RandomUtils
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
26
random-utils/RandomUtils.cpp
Normal file
26
random-utils/RandomUtils.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include "RandomUtils.hpp"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
namespace RandomUtils
|
||||
{
|
||||
bool seeded = false;
|
||||
|
||||
void seed()
|
||||
{
|
||||
if (seeded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
srand(time(NULL));
|
||||
seeded = true;
|
||||
}
|
||||
|
||||
int rand(int min, int max)
|
||||
{
|
||||
seed();
|
||||
|
||||
return min + std::rand() % (max - min + 1);
|
||||
}
|
||||
}
|
14
random-utils/RandomUtils.hpp
Normal file
14
random-utils/RandomUtils.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef RANDOM_HPP
|
||||
#define RANDOM_HPP
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
namespace RandomUtils
|
||||
{
|
||||
/**
|
||||
* Generates a random number between min and max (inclusive).
|
||||
*/
|
||||
int rand(int min = 0, int max = RAND_MAX);
|
||||
}
|
||||
|
||||
#endif // RANDOM_HPP
|
Loading…
Reference in New Issue
Block a user