Merge pull request #14 from jorenchik/kristoferssolo/refactor/root

This commit is contained in:
Kristofers Solo 2024-03-14 03:46:49 +02:00 committed by GitHub
commit 0e74bc30d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 477 additions and 445 deletions

View File

@ -1,7 +1,5 @@
cmake_minimum_required(VERSION 3.1...3.28) cmake_minimum_required(VERSION 3.1...3.28)
enable_testing()
project( project(
Template Template
VERSION 0.1.0 VERSION 0.1.0
@ -9,28 +7,37 @@ project(
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_subdirectory(src/cppunit) # GoogleTest requires at least C++14
add_subdirectory(src/prep) set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
enable_testing()
add_subdirectory(src/modules)
add_executable(main src/main.cc)
add_executable(test_validation src/test_validation.cc)
add_library(validation src/validation.cc src/validation.hh)
target_link_libraries(main PUBLIC validation)
target_link_libraries(validation PRIVATE modules)
add_executable(main src/main.cpp)
add_library(lib src/lib.cpp src/lib.hh)
target_link_libraries(main PUBLIC lib)
target_link_libraries(lib PUBLIC prepLib)
target_include_directories(main PUBLIC "${PROJECT_BINARY_DIR}" target_include_directories(main PUBLIC "${PROJECT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}/prep") "${PROJECT_SOURCE_DIR}/modules")
target_include_directories(lib PUBLIC "${PROJECT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}/prep") target_include_directories(
test_validation PUBLIC "${PROJECT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}/modules")
include(FetchContent) include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
googletest googletest
URL https://github.com/google/googletest/archive/5376968f6948923e2411081fd9372e71a59d8e77.zip URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
) )
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt
ON
CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest) FetchContent_MakeAvailable(googletest)
add_executable(runtests src/test.cpp)
target_link_libraries(runtests PUBLIC lib gtest gtest_main)
target_include_directories(runtests PUBLIC "${PROJECT_BINARY_DIR}")
include(GoogleTest) include(GoogleTest)
gtest_discover_tests(runtests) target_link_libraries(test_validation PRIVATE GTest::gtest_main)
gtest_discover_tests(test_validation)

View File

@ -5,20 +5,21 @@
## Projekts ## Projekts
Šis ir vienkāršs C++ projekts, kas ietver funkciju mūsu kolēģiem Šis ir vienkāršs C++ projekts, kas ietver funkciju mūsu kolēģiem
testēšanai. Funkcija (t.i., galvenā funkcija un tajā izmantotās apakšfunkcijas) testēšanai. Funkcija, t.i., apakšfunkcijas, kuru nepieciešams testēt
atrodas `lib.cpp`. atrodas `validation.cc`.
## Papildu bibliotēkas un izpildāmā programma ## Papildu bibliotēkas un izpildāmā programma
Mūsu bibliotēka `prep` ir saistīta ar datu sagatavošanu pirms funkcijas Direktorija `modules` ir saistīta ar datu sagatavošanu pirms funkcijas
izsaukuma. `test.cpp` ir viens testa fails, kurā būs mūsu kolēģu testi izsaukuma. `test_validation.cc` ir viens testa fails, kas satur testus.
un mūsu pašu izstrādes testi. GTest tiek izmantots testiem. [GTest](https://github.com/google/googletest) tiek izmantots testiem.
## Lietojums ## Lietojums
## Kompilācija uz Linux un MacOS ## Kompilācija uz Linux un MacOS
Kompilācijai operētājsistēmā Linux ir nepieciešams cmake un CXX kompilators (e.g., g++). Kompilācijai operētājsistēmā Linux ir nepieciešams cmake un
CXX kompilators (e.g., g++).
### MacOS ### MacOS
@ -59,10 +60,15 @@ make
### Windows un/vai VSCode ### Windows un/vai VSCode
Uzstādiet [CMake](https://cmake.org/download/) un VSCode [CMake Tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools) paplašinājumu. Uzstādiet [CMake](https://cmake.org/download/) un VSCode
[CMake Tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools)
paplašinājumu.
- Atveriet projektu kā saknes direktoriju VSCode. - Atveriet projektu kā saknes direktoriju
- Izvēlaties **View->Command palete...** vai `Ctrl+Shift+P` un palaidiet `CMake: Build`. VSCode.
- Izvēlaties **View->Command palete...** vai
`Ctrl+Shift+P` un palaidiet `CMake: Build`.
Rezultātā iegūtie binārie faili ir `build/main` - programma un `build/runtests`, lai Rezultātā iegūtie binārie faili ir `build/main` -- programma un
palaistu punktos norādītos testus iekš `test.cpp`. `build/test_validation`, lai palaistu norādītos testus
no `test_validation.cc` faila.

View File

@ -7,14 +7,14 @@
## Project ## Project
This is a simple C++ project that includes the function for our colleagues to This is a simple C++ project that includes the function for our colleagues to
test. The function (i.e., the main function and the subfunctions used in it) is test. The function (i.e., the subfunctions that are used in main function) is
located in `lib.cpp`. located in `validation.cc`.
## Additional libraries and executable ## Additional libraries and executable
Our library `prep` is concerned with preparation of data prior to the function Directory `modules` is concerned with preparation of data prior to the function
call. `test.cpp` is a single test file that will contain the tests of our colleagues call. `test_validation.cc` is a single test file that contain the tests.
and our own development tests. GTest is used for tests. [GTest](https://github.com/google/googletest) is used for tests.
## Usage ## Usage
@ -61,9 +61,12 @@ make
### Windows and/or VSCode ### Windows and/or VSCode
Install [CMake](https://cmake.org/download/) and VSCode [CMake Tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools) extension. Install [CMake](https://cmake.org/download/) and VSCode
[CMake Tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools)
extension.
- Open project as root in VSCode. - Open project as root in VSCode.
- Press **View->Command palette...** or `Ctrl+Shift+P` and run `CMake: Build`. - Press **View->Command palette...** or `Ctrl+Shift+P` and run `CMake: Build`.
Resulting binaries are `build/main` -- the program and `build/runtests` to run tests specified in `test.cpp`. Resulting binaries are `build/main` -- the program and `build/test_validation`
to run tests specified in `test_validation.cc`.

View File

@ -1 +0,0 @@
add_library(cppunit cppunit.cpp cppunit.hh)

View File

@ -1,83 +0,0 @@
#ifndef CPPUNIT_H
#define CPPUNIT_H
// Required headers, or just use #include <bits/stdc++.h>
#include <cstring>
#include <ctime>
#include <iostream>
#include <sstream>
#include <string>
// CPlusPlusUnit - C++ Unit testing TDD framework (github.com/cppunit/cppunit)
class Cppunit {
public:
#define CHECK(a, b) check<long long>(a, b, #a, #b, __FILE__, __LINE__, __FUNCTION__);
#define CHECKT(a) check<bool>(a, true, #a, "true", __FILE__, __LINE__, __FUNCTION__);
#define CHECKS(a, b) check<cs>(a, b, #a, #b, __FILE__, __LINE__, __FUNCTION__);
typedef const std::string &cs;
int checks, fails;
std::ostringstream serr;
std::istringstream *in;
Cppunit() {
checks = fails = 0;
}
void test_cin(cs s) {
in = new std::istringstream(s);
std::cin.rdbuf(in->rdbuf());
}
void fail_hdr(cs stra, cs strb, cs file, int line, cs func) {
serr << "==================================================" << std::endl;
serr << "FAIL: " << func << std::endl;
serr << "--------------------------------------------------" << std::endl;
serr << "File \"" << file << "\", line " << line << " in " << func << std::endl;
serr << " Checking " << stra << " == " << strb << std::endl;
}
template <typename T> void check(T a, T b, cs stra, cs strb, cs file, int line, cs func) {
checks++;
if (a == b) {
std::cout << ".";
return;
}
fails++;
std::cout << "F";
fail_hdr(stra, strb, file, line, func);
serr << " Error: \"" << a << "\" ! = \"" << b << "\"" << std::endl << std::endl;
}
virtual void single_test() {
}
virtual void test_list() {
single_test();
}
double dclock() {
return double(clock()) / CLOCKS_PER_SEC;
}
int status() {
std::cout << std::endl;
if (fails) std::cout << serr.str();
std::cout << "--------------------------------------------------" << std::endl;
std::cout << "Ran " << checks << " checks in " << dclock() << "s" << std::endl << std::endl;
if (fails)
std::cout << "FAILED (failures=" << fails << ")";
else
std::cout << "OK" << std::endl;
return fails > 0;
}
int run() {
std::streambuf *ocin = std::cin.rdbuf();
test_list();
std::cin.rdbuf(ocin);
return status();
}
};
#endif // CPPUNIT_H

View File

@ -1,98 +0,0 @@
#include "lib.hh"
#include "prep/prep.hh"
#include <algorithm>
#include <cstdio>
#include <vector>
enum VALIDATION_STATUS {
PLAYER_NOT_IN_ROOM,
NO_TARGET_PLAYER_SPECIFIED,
ROOM_NOT_IN_PROGRESS,
ACTION_DOES_NOT_BELONG_TO_ROLE,
ACTION_PROHIBITED,
NO_ACTOR,
NO_ACTION,
NO_ROLE,
NO_ROOM,
NO_RELATED_EVENTS,
ACTION_VALID,
};
void run() {
const Action kill = Action("kill", true);
const Action heal = Action("heal", true);
const Action vote = Action("vote", true);
Role role1({vote, kill, heal});
Role role2({heal});
Event event1 = Event("Event 1", 1710087355, 1, true, {}, {});
Event event2 = Event("Event 2", 1710087363, 1, true, {kill}, {});
Event event3 = Event("Event 3", 1710087369, 1, true, {}, {kill});
std::vector<Event> relatedEvents({event2, event3});
Player player1 = Player(69, "player1", role1, PlayerStatus::ALIVE);
Player player2 = Player(420, "player2", role1, PlayerStatus::ALIVE);
Room room1(1, "Room 1", 1710087364, RoomStatus::IN_PROGRESS, {player1, player2});
Room room2(2, "Room 2", 1710087384, RoomStatus::ENDED, {});
int actionValidated = validateAction(&player1, &kill, &room1, &relatedEvents, &player2);
printf("The action validation result is %u\n", actionValidated);
}
int validateAction(
Player *actor, const Action *action, Room *room, std::vector<Event> *relatedEvents, Player *target = nullptr) {
if (!actor) {
return NO_ACTOR;
}
if (!action) {
return NO_ACTION;
}
if (!room) {
return NO_ROOM;
}
if (!relatedEvents) {
return NO_RELATED_EVENTS;
}
if (!playerBelongsToRoom(actor, room)) {
return PLAYER_NOT_IN_ROOM;
}
if (action->hasTarget && !target) {
return NO_TARGET_PLAYER_SPECIFIED;
}
if (room->status != RoomStatus::IN_PROGRESS) {
return ROOM_NOT_IN_PROGRESS;
}
Role *role = &actor->role;
if (!role) {
return NO_ROLE;
}
if (!actionBelongsToRole(role, action)) {
return ACTION_DOES_NOT_BELONG_TO_ROLE;
}
if (!isActionAllowed(action, relatedEvents)) {
return ACTION_PROHIBITED;
}
return ACTION_VALID;
}
bool playerBelongsToRoom(Player *player, Room *room) {
return std::find(room->players.begin(), room->players.end(), *player) != room->players.end();
}
bool actionBelongsToRole(Role *role, const Action *action) {
return std::find(role->actions.begin(), role->actions.end(), *action) != role->actions.end();
}
bool isActionAllowed(const Action *action, std::vector<Event> *relevantEvents) {
// actions are disabled by default
bool allowed = false;
std::sort(relevantEvents->begin(), relevantEvents->end());
for (auto &event : *relevantEvents) {
if (std::find(event.prohibits.begin(), event.prohibits.end(), *action) != event.prohibits.end()) {
allowed = false;
}
if (std::find(event.allows.begin(), event.allows.end(), *action) != event.allows.end()) {
allowed = true;
}
}
return allowed;
}

View File

@ -1,8 +0,0 @@
#include "prep/prep.hh"
void run();
bool playerBelongsToRoom(Player *player, Room *room);
bool actionBelongsToRole(Role *role, const Action *action);
bool isActionAllowed(const Action *action, std::vector<Event> *relevantEvents);
int validateAction(Player *actor, const Action *action, Room *room, std::vector<Event> *relatedEvents, Player *target);

24
src/main.cc Normal file
View File

@ -0,0 +1,24 @@
#include "./validation.hh"
#include <stdlib.h>
#include <string>
int main(int argc, char *argv[]) {
const Action kill = Action("kill", true);
const Action heal = Action("heal", true);
const Action vote = Action("vote", true);
Role role1({vote, kill, heal});
Role role2({heal});
Event event1 = Event("Event 1", 1710087355, 1, true, {}, {});
Event event2 = Event("Event 2", 1710087363, 1, true, {kill}, {});
Event event3 = Event("Event 3", 1710087369, 1, true, {}, {kill});
std::vector<Event> relatedEvents({event2, event3});
Player player1 = Player(69, "player1", role1, PlayerStatus::Alive);
Player player2 = Player(420, "player2", role1, PlayerStatus::Alive);
Room room1(1, "Room 1", 1710087364, RoomStatus::InProgress, {player1, player2});
Room room2(2, "Room 2", 1710087384, RoomStatus::Ended, {});
ValidationStatus validated_action = validate_action(&player1, &kill, &room1, &relatedEvents, &player2);
std::string validated_action_str = ValidationStatusUtils::to_string(validated_action);
printf("The validation result is \"%s\"\n", validated_action_str.c_str());
return EXIT_SUCCESS;
}

View File

@ -1,8 +0,0 @@
#include "lib.hh"
#include <stdlib.h>
int main(int argc, char *argv[]) {
run();
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,14 @@
add_library(
modules
action.cc
action.hh
event.cc
event.hh
player.cc
player.hh
role.cc
role.hh
room.cc
room.hh
time.cc
time.hh)

12
src/modules/action.cc Normal file
View File

@ -0,0 +1,12 @@
#include "./action.hh"
#include <string>
Action::Action(std::string name, bool has_target) {
this->name = name;
this->has_target = has_target;
}
bool Action::operator==(const Action &other) const {
return this->name == other.name;
}

10
src/modules/action.hh Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <string>
struct Action {
std::string name;
bool has_target;
Action(std::string name, bool has_target);
bool operator==(const Action &other) const;
};

48
src/modules/event.cc Normal file
View File

@ -0,0 +1,48 @@
#include "./event.hh"
#include "./action.hh"
#include "./time.hh"
#include <initializer_list>
#include <string>
#include <vector>
Event::Event(std::string title,
uint32_t created_at,
uint32_t night_number,
bool is_visible,
std::vector<Action> prohibits,
std::vector<Action> allows):
title(title),
created_at(create_utc_timestamp(created_at)),
night_number(night_number),
is_visible(is_visible),
prohibits(prohibits),
allows(allows) {
}
Event::Event(std::string title,
uint32_t created_at,
uint32_t night_number,
bool is_visible,
std::initializer_list<Action> prohibits,
std::initializer_list<Action> allows):
title(title),
created_at(create_utc_timestamp(created_at)),
night_number(night_number),
is_visible(is_visible),
prohibits(prohibits),
allows(allows) {
}
bool Event::operator<(const Event &right) const {
return this->created_at < right.created_at;
}
bool Event::operator==(const Event &right) const {
return this->created_at == right.created_at;
}
bool Event::operator>(const Event &right) const {
return this->created_at > right.created_at;
}

41
src/modules/event.hh Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#include "./action.hh"
#include <cstdint>
#include <ctime>
#include <initializer_list>
#include <string>
#include <vector>
enum class EventType {
PhaseChange,
EventAction,
RoomStateChange,
PlayerStateChange,
};
struct Event {
std::string title;
std::tm *created_at;
uint32_t night_number;
bool is_visible;
std::vector<Action> prohibits;
std::vector<Action> allows;
bool operator<(const Event &other) const;
bool operator==(const Event &other) const;
bool operator>(const Event &other) const;
Event(std::string title,
uint32_t created_at,
uint32_t night_number,
bool is_visible,
std::vector<Action> prohibits,
std::vector<Action> allows);
Event(std::string title,
uint32_t created_at,
uint32_t night_number,
bool is_visible,
std::initializer_list<Action> prohibits,
std::initializer_list<Action> allows);
};

16
src/modules/player.cc Normal file
View File

@ -0,0 +1,16 @@
#include "./player.hh"
#include "./role.hh"
#include <string>
Player::Player(uint32_t id, std::string username, Role role, PlayerStatus status):
id(id),
username(username),
role(role),
status(status) {
}
bool Player::operator==(const Player &other) const {
return this->id == other.id;
}

22
src/modules/player.hh Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include "./role.hh"
#include <cstdint>
#include <string>
enum class PlayerStatus {
Kicked,
Alive,
Dead,
VotedOut,
};
struct Player {
uint32_t id;
std::string username;
Role role;
PlayerStatus status;
Player(uint32_t id, std::string username, Role role, PlayerStatus status);
bool operator==(const Player &other) const;
};

10
src/modules/role.cc Normal file
View File

@ -0,0 +1,10 @@
#include "./role.hh"
#include <initializer_list>
#include <vector>
Role::Role(std::initializer_list<Action> actions): Role(std::vector<Action>(actions)) {
}
Role::Role(std::vector<Action> actions): actions(actions) {
}

12
src/modules/role.hh Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include "./action.hh"
#include <initializer_list>
#include <vector>
struct Role {
std::vector<Action> actions;
explicit Role(std::vector<Action> actions);
Role(std::initializer_list<Action> actions);
};

25
src/modules/room.cc Normal file
View File

@ -0,0 +1,25 @@
#include "./room.hh"
#include "./time.hh"
#include <cstdint>
#include <initializer_list>
#include <string>
#include <vector>
Room::Room(uint32_t id, std::string title, uint32_t created_at, RoomStatus status, std::vector<Player> players):
id(id),
title(title),
status(status),
players(players) {
this->created_at = create_utc_timestamp(created_at);
}
Room::Room(
uint32_t id, std::string title, uint32_t created_at, RoomStatus status, std::initializer_list<Player> players):
id(id),
title(title),
status(status),
players(players) {
this->created_at = create_utc_timestamp(created_at);
}

27
src/modules/room.hh Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include "./player.hh"
#include <cstdint>
#include <ctime>
#include <initializer_list>
#include <string>
#include <vector>
enum class RoomStatus {
AwaitingStart,
InProgress,
Stopped,
Ended,
};
struct Room {
uint32_t id;
std::string title;
std::tm *created_at;
RoomStatus status;
std::vector<Player> players;
Room(uint32_t id, std::string title, uint32_t created_at, RoomStatus status, std::vector<Player> players);
Room(uint32_t id, std::string title, uint32_t created_at, RoomStatus status, std::initializer_list<Player> players);
};

View File

@ -1,10 +1,10 @@
#include "timeUtils.hh" #include "./time.hh"
#include <chrono> #include <chrono>
#include <cstdint> #include <cstdint>
#include <ctime> #include <ctime>
std::tm *createUTCTimestamp(uint32_t timestamp) { std::tm *create_utc_timestamp(uint32_t timestamp) {
// Convert the timestamp into a time_point object // Convert the timestamp into a time_point object
std::chrono::seconds sec(timestamp); std::chrono::seconds sec(timestamp);
std::chrono::time_point<std::chrono::system_clock> tp(sec); std::chrono::time_point<std::chrono::system_clock> tp(sec);

6
src/modules/time.hh Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include <cstdint>
#include <ctime>
std::tm *create_utc_timestamp(uint32_t timestamp);

View File

@ -1 +0,0 @@
add_library(prepLib prep.cpp prep.hh timeUtils.cpp timeUtils.hh)

View File

@ -1,92 +0,0 @@
#include "prep.hh"
#include "timeUtils.hh"
#include <cstdint>
#include <initializer_list>
Action::Action(std::string name, bool hasTarget) {
this->name = name;
this->hasTarget = hasTarget;
}
bool Action::operator==(const Action &other) const {
return this->name == other.name;
}
Role::Role(std::initializer_list<Action> actions): Role(std::vector<Action>(actions)) {
}
Role::Role(std::vector<Action> actions): actions(actions) {
}
Player::Player(uint32_t id, std::string username, Role role, PlayerStatus playerStatus):
id(id),
username(username),
role(role),
playerStatus(playerStatus) {
}
bool Player::operator==(const Player &other) const {
return this->id == other.id;
}
Room::Room(
uint32_t id, std::string title, uint32_t utcTimestampCreatedAt, RoomStatus status, std::vector<Player> players):
id(id),
title(title),
status(status),
players(players) {
this->utcTimestampCreatedAt = createUTCTimestamp(utcTimestampCreatedAt);
}
Room::Room(uint32_t id,
std::string title,
uint32_t utcTimestampCreatedAt,
RoomStatus status,
std::initializer_list<Player> players):
Room(id, title, utcTimestampCreatedAt, status, std::vector<Player>(players)) {
this->utcTimestampCreatedAt = createUTCTimestamp(utcTimestampCreatedAt);
}
Event::Event(std::string title,
uint32_t utcTimestampCreatedAt,
uint32_t numberNight,
bool isVisible,
std::vector<Action> prohibits,
std::vector<Action> allows):
title(title),
utcTimestampCreatedAt(createUTCTimestamp(utcTimestampCreatedAt)),
numberNight(numberNight),
isVisible(isVisible),
prohibits(prohibits),
allows(allows) {
this->utcTimestampCreatedAt = createUTCTimestamp(utcTimestampCreatedAt);
}
Event::Event(std::string title,
uint32_t utcTimestampCreatedAt,
uint32_t numberNight,
bool isVisible,
std::initializer_list<Action> prohibits,
std::initializer_list<Action> allows):
Event(title,
utcTimestampCreatedAt,
numberNight,
isVisible,
std::vector<Action>(prohibits),
std::vector<Action>(allows)) {
this->utcTimestampCreatedAt = createUTCTimestamp(utcTimestampCreatedAt);
}
bool Event::operator<(const Event &right) const {
return this->utcTimestampCreatedAt < right.utcTimestampCreatedAt;
}
bool Event::operator==(const Event &right) const {
return this->utcTimestampCreatedAt == right.utcTimestampCreatedAt;
}
bool Event::operator>(const Event &right) const {
return this->utcTimestampCreatedAt > right.utcTimestampCreatedAt;
}

View File

@ -1,103 +0,0 @@
#ifndef PREP_H
#define PREP_H
#include <cstdint>
#include <ctime>
#include <string>
#include <vector>
// All IDs are uint32_t
enum EventType {
PHASE_CHANGE,
ACTION,
ROOM_STATE_CHANGE,
PLAYER_STATE_CHANGE,
};
enum RoomStatus {
AWAITING_START,
IN_PROGRESS,
STOPPED,
ENDED,
};
enum PlayerStatus {
KICKED,
ALIVE,
DEAD,
VOTED_OUT,
};
struct Role;
struct Player;
struct Room;
struct Event;
struct Action {
std::string name;
bool hasTarget;
Action(std::string name, bool hasTarget);
bool operator==(const Action &other) const;
};
struct Role {
std::vector<Action> actions;
Role(std::initializer_list<Action> actions);
Role(std::vector<Action> actions);
};
struct Player {
uint32_t id;
std::string username;
Role role;
PlayerStatus playerStatus;
Player(uint32_t id, std::string username, Role role, PlayerStatus playerStatus);
bool operator==(const Player &other) const;
};
struct Room {
uint32_t id;
std::string title;
std::tm *utcTimestampCreatedAt;
RoomStatus status;
std::vector<Player> players;
Room(
uint32_t id, std::string title, uint32_t utcTimestampCreatedAt, RoomStatus status, std::vector<Player> players);
Room(uint32_t id,
std::string title,
uint32_t utcTimestampCreatedAt,
RoomStatus status,
std::initializer_list<Player> players);
};
struct Event {
std::string title;
std::tm *utcTimestampCreatedAt;
uint32_t numberNight;
bool isVisible;
std::vector<Action> prohibits;
std::vector<Action> allows;
bool operator<(const Event &other) const;
bool operator==(const Event &other) const;
bool operator>(const Event &other) const;
Event(std::string title,
uint32_t utcTimestampCreatedAt,
uint32_t numberNight,
bool isVisible,
std::vector<Action> prohibits,
std::vector<Action> allows);
Event(std::string title,
uint32_t utcTimestampCreatedAt,
uint32_t numberNight,
bool isVisible,
std::initializer_list<Action> prohibits,
std::initializer_list<Action> allows);
};
#endif

View File

@ -1,9 +0,0 @@
#include <cstdint>
#include <ctime>
#ifndef TIMEUTILS_H
# define TIMEUTILS_H
std::tm *createUTCTimestamp(uint32_t timestamp);
#endif

View File

@ -1,5 +0,0 @@
#include <gtest/gtest.h>
TEST(ProgramTest, testFunction) {
EXPECT_EQ(8, 8);
}

10
src/test_validation.cc Normal file
View File

@ -0,0 +1,10 @@
#include "gtest/gtest.h"
TEST(ExampleTest, Example) {
EXPECT_EQ(1, 1);
}
int main(int argc, char *argv[]) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

116
src/validation.cc Normal file
View File

@ -0,0 +1,116 @@
#include "./validation.hh"
#include "modules/action.hh"
#include "modules/event.hh"
#include "modules/player.hh"
#include "modules/role.hh"
#include "modules/room.hh"
#include <algorithm>
#include <string>
/**
* Check if a player belongs to a given room.
*
* @param player Pointer to the player object.
* @param room Pointer to the room object.
* @return `true` if the player belongs to the room, otherwise `false`.
*/
bool player_belongs_to_room(const Player *player, const Room *room) {
return std::find(room->players.begin(), room->players.end(), *player) != room->players.end();
}
/**
* Check if an action belongs to a given role.
*
* @param role Pointer to the role object.
* @param action Pointer to the action object.
* @return `true` if the action belongs to the role, otherwise `false`.
*/
bool action_belongs_to_role(const Role *role, const Action *action) {
return std::find(role->actions.begin(), role->actions.end(), *action) != role->actions.end();
}
/**
* Check if an action is allowed based on relevant events.
*
* @param action Pointer to the action object.
* @param relevantEvents Pointer to the vector of relevant events.
* @return `true` if the action is allowed, otherwise `false`.
*/
bool is_action_allowed(const Action *action, std::vector<Event> *relevant_events) {
bool allowed = false; // Actions are disabled by default
std::sort(relevant_events->begin(), relevant_events->end());
for (const auto &event : *relevant_events) {
if (std::find(event.prohibits.begin(), event.prohibits.end(), *action) != event.prohibits.end()) {
allowed = false;
}
if (std::find(event.allows.begin(), event.allows.end(), *action) != event.allows.end()) {
allowed = true;
}
}
return allowed;
}
/**
* Validate if an action is valid for a player in a room based on related events.
*
* @param actor Pointer to the player performing the action.
* @param action Pointer to the action to validate.
* @param room Pointer to the room where the action is taking place.
* @param related_events Pointer to the vector of related events.
* @param target Pointer to the target player (optional, defaults to `nullptr`).
* @return An integer representing the validation status.
*/
ValidationStatus validate_action(
Player *actor, const Action *action, Room *room, std::vector<Event> *related_events, Player *target = nullptr) {
if (!actor) {
return ValidationStatus::NoActor;
}
if (!action) {
return ValidationStatus::NoAction;
}
if (!room) {
return ValidationStatus::NoRoom;
}
if (!related_events) {
return ValidationStatus::NoRelatedEvents;
}
if (!player_belongs_to_room(actor, room)) {
return ValidationStatus::PlayerNotInRoom;
}
if (action->has_target && !target) {
return ValidationStatus::NoTargetPlayerSpecified;
}
if (room->status != RoomStatus::InProgress) {
return ValidationStatus::RoomNotInProgress;
}
Role *role = &actor->role;
if (!role) {
return ValidationStatus::NoRole;
}
if (!action_belongs_to_role(role, action)) {
return ValidationStatus::ActionDoesNotBelongToRole;
}
if (!is_action_allowed(action, related_events)) {
return ValidationStatus::ActionProhibited;
}
return ValidationStatus::ActionValid;
}
std::string ValidationStatusUtils::to_string(ValidationStatus status) {
switch (status) {
case ValidationStatus::PlayerNotInRoom: return "player not in room";
case ValidationStatus::NoTargetPlayerSpecified: return "no target player specified";
case ValidationStatus::RoomNotInProgress: return "room not in progress";
case ValidationStatus::ActionDoesNotBelongToRole: return "action does not belong to role";
case ValidationStatus::ActionProhibited: return "action prohibited";
case ValidationStatus::NoActor: return "no actor";
case ValidationStatus::NoAction: return "no action";
case ValidationStatus::NoRole: return "no role";
case ValidationStatus::NoRoom: return "no room";
case ValidationStatus::NoRelatedEvents: return "no relevant events";
case ValidationStatus::ActionValid: return "action valid";
default: return "unknown validation status";
}
}

31
src/validation.hh Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include "modules/event.hh"
#include "modules/player.hh"
#include "modules/room.hh"
#include <string>
#include <vector>
enum class ValidationStatus {
PlayerNotInRoom,
NoTargetPlayerSpecified,
RoomNotInProgress,
ActionDoesNotBelongToRole,
ActionProhibited,
NoActor,
NoAction,
NoRole,
NoRoom,
NoRelatedEvents,
ActionValid,
};
std::string validation_status_to_string(ValidationStatus status);
ValidationStatus
validate_action(Player *actor, const Action *action, Room *room, std::vector<Event> *relatedEvents, Player *target);
class ValidationStatusUtils {
public:
static std::string to_string(ValidationStatus status);
};