This commit is contained in:
Thomas 2022-01-09 18:30:15 +01:00
parent 15dc67c348
commit a81ec496a6
No known key found for this signature in database
GPG Key ID: E538821A6CDFDAD7
16 changed files with 291 additions and 117 deletions

View File

@ -1,10 +1,5 @@
Questions que je (Thomas Rubini) voudrais poser
- Est-ce que vous préférez l'implémentation orienté objet ou orienté macro pour lire la config ? Pourquoi ?
- Est-ce que traduire les chars A B et C (identifiants des types d'aliens) tirés de la config en valeurs d'enum est une bonne chose, pas important, ou contre-productif ?
- Est-ce que mon implémentation du réseau est bonne ?
- Est-ce mon implémentation du multithreading est bonne ?
- Est-on obligé d'utiliser size_t quand on sait que la taille du vecteur ne dépassera jamais concrètement la taille d'un int (cas précis : taille de 100 maximum, est-on obligé d'utiliser size_t de 8 bytes ?)
* Non
- Que pensez-vous de la sémantique de déplacement, plutot que la référence constante ?

1
README
View File

@ -4,6 +4,7 @@ PascalCase pour les noms de classes/structures
Nommage en anglais
Pas de fonctions de +100 lignes
Les guards sont de cette forme : GUARD_<filename>_H
Lors d'un include, on utilise <> pour la STD, "" pour nos fichiers/MinGL
Concernant les const :
Afin de limiter l'utilisation du mot const (pour garder une certaine lisibilité du code), les cas suivants n'ont pas besoin d'etre déclarés comme const :

View File

@ -0,0 +1,50 @@
/*!
*
* @file configManagement.h
* @author RUBINI Thomas
* @author SIMAILA Djalim
* @date January 2022
* @version 1.0
* @brief config parser
*
*/
#ifndef SPACE_CONFIGMANAGEMENT_H
#define SPACE_CONFIGMANAGEMENT_H
/*
* This header will only be imported once, but Djalim told me he would hurt me
* if I didn't move it into his own header file instead of the cpp file
*/
class ConfigBuilder{
public:
ConfigData collectedData;
void parseFile(const string& fname);
void readConfig();
void dumpInternalValues() const;
private:
map<string, string> internalValues;
const string& getString(const configKey& key, const string& def) const;
const string& getString(const configKey& key) const;
char getChar(const configKey& key, char def) const;
char getChar(const configKey& key) const;
int getInt(const configKey& key) const;
int getInt(const configKey& key, int def) const;
void getColor(const configKey& key, nsGraphics::RGBAcolor& color, nsGraphics::RGBAcolor& def) const;
void getColor(const configKey& key, nsGraphics::RGBAcolor& color) const;
void getList(const configKey& key, vector<string>& vec) const;
void readGrid(const configKey& baseKey);
void readPlayer(const configKey& baseKey, PlayerDef&);
void readInvaderType(const configKey& baseKey, InvaderTypeDef&);
};
void ConfigBuilder::dumpInternalValues() const {
for(const auto& ite : internalValues){
cerr << ite.first << " -> " << ite.second << endl;
}
}
#endif

25
headers/errors.h Normal file
View File

@ -0,0 +1,25 @@
/*!
*
* @file configData.h
* @author RUBINI Thomas
* @date January 2022
* @version 1.0
* @brief Configuration file data storage
*
*/
#ifndef GUARD_ERRORS_H
#define GUARD_ERRORS_H
#include<string>
#include<stdexcept>
using namespace std;
// We keep the same case
class config_error : public runtime_error {
public:
explicit config_error(const string& msg);
};
#endif

View File

@ -202,37 +202,37 @@ private:
void moveTorpedos();
/*!
* @brief
* @return
* @fn
* @brief checks if a missile collides with a player
* @fn checkMissilesAndPlayers();
*/
bool checkMissilesAndPlayers();
void checkMissilesAndPlayers();
/*!
* @brief
* @return
* @fn
* @brief check if a torpedo collides with an invader
* @return true if there is a collision, false elsewise
* @fn bool checkTorpedosAndInvaders();
*/
bool checkTorpedosAndInvaders();
/*!
* @brief
* @return
* @fn
* @brief check if the invaders have reach the players
* @return true if they have reach the player, false elsewise
* @fn bool invadersTouchPlayer() const;
*/
bool invadersTouchPlayer() const;
// god things
/*!
*
* @brief change god's state to awake
* @fn void awakeGod();
*/
void awakeGod();
/*!
* @brief
* @return
* @fn
* @brief make god behave
* @return true if theres no invader left, false elsewise
* @fn bool manageGod();
*/
bool manageGod();
@ -240,28 +240,37 @@ public:
// in case someone wants to mess with the code, here's a minimal API, costs nothing to us
/*!
*
* @brief constructor for the game class
* @fn Game();
*/
Game();
/*!
*
* @brief manages and changes the states of the game
* @fn void managedGames();
*/
void managedGames();
/*!
* @brief
* @return
* @fn
* @brief enter the main gameplay game loop
* @return the value of the winners can be the players, the invaders or god
* @fn WinValue enterGameLoop();
*/
WinValue enterGameLoop();
/*!
* @brief
* @return
* @fn
* @brief reload the configuration file for a new game
* @return false if an error occured, true elsewise
* @fn bool reloadConfig();
*/
bool reloadConfig();
/*!
* @brief tells if all players are dead
* @return true if all player are dead, false otherwise
* @fn bool arePlayersDead();
*/
bool arePlayersDead();
};
#endif

View File

@ -6,6 +6,9 @@
* @version 1.0
* @brief Special entity known as "God"
*
* Well, i (Djalim) discovered that Thomas put God in the game
* I didnt think it was a good idea but no man can stop Thomas
*
*/
#ifndef GUARD_GOD_H
@ -15,7 +18,7 @@
#include "invadersGrid.h"
/*!
* @brief
* @brief list of all known god states
*/
enum class GodState{
NONE,
@ -40,18 +43,18 @@ enum class GodState{
/*!
* @class God
* @brief
* @brief stores all God's data
*/
class God{
public:
/*!
* @brief
* @brief god's current state
*/
GodState state;
/*!
* @brief
* @brief manage all sorts of things, gods secrets remains unknown
*/
unsigned counter;
@ -59,34 +62,34 @@ public:
// we do not use a Position because it is used for pixel X and Y
/*!
* @brief
* @brief x pixel coordinate of the invader thrown by the hand of god
*/
unsigned thrownInvPosX;
/*!
* @brief
* @brief y pixel coordinate of the invader thrown by the hand of god
*/
unsigned thrownInvPosY;
/*!
* @brief
* @brief type of the invader thrown by the hand of god
*/
InvaderType thrownInvType;
/*!
* @brief
* @brief direction of the thrown invader movement
*/
Position thrownVector;
/*!
* @brief
* @brief position of a point for bezier's curve
*/
Position thrownTransition;
/*!
* @brief
* @param[in] screenWidth :
* @return
* @brief give initial the pixel coordinates of god's right hand
* @param[in] screenWidth : width of the screen in pixel
* @return pixel coordinates of the hand
* @fn Position getRightHandPos(unsigned screenWidth) const;
*/
Position getRightHandPos(unsigned screenWidth) const;

View File

@ -53,7 +53,7 @@ public:
* @return
* @fn
*/
unsigned randomValid() const;
unsigned randomValidInv() const;
}; // class InvadersColumn

View File

@ -18,17 +18,62 @@
using namespace std;
/*!
* @struct ScoreLink
* @brief Makes a link between a player username and their score
*/
struct ScoreLink{
/*!
* @brief player username
*/
string name;
/*!
* @brief player score
*/
unsigned score;
/*!
* @brief constructor of the struct
* @param[in] name : player username
* @param[in] score : player score
* @fn ScoreLink(string name, unsigned score);
*/
ScoreLink(string name, unsigned score);
};
/*!
* @class ScoresManager
* @brief manage the score in the game and inside the score file
*/
class ScoresManager {
public:
/*!
* @brief list of pairs of player names and their score
*/
vector<ScoreLink> scores;
/*!
* @brief add player name and their score in the list of scores
* @param[in] name : player name
* @param[in] score : player score
* @fn void inputScore(string name, unsigned score);
*/
void inputScore(string name, unsigned score);
/*!
* @brief read the score file and put all of its data inside the list of score
* @fn void readFile();
*/
void readFile();
/*!
* @brief write the score list into the score file
* @fn void writeFile() const;
*/
void writeFile() const;
};

View File

@ -1,3 +1,13 @@
/*!
*
* @file utils.h
* @author RUBINI Thomas
* @date January 2022
* @version 1.0
* @brief utilies for the game
*
*/
#ifndef GUARD_UTILS_H
#define GUARD_UTILS_H
@ -28,11 +38,14 @@
using namespace std;
using nsGraphics::RGBAcolor;
/*!
* @brief list of win values
*/
enum class WinValue{
NOBODY, // should never be used
PLAYERS,
INVADERS,
GOD,
};
@ -42,9 +55,25 @@ typedef unsigned playerID;
#define PLAYER2 1
// didn't want to use Position because of the semantic with x and y
/*!
* @brief tells if 2 lines are colliding in a 1 dimentionnal space
* @param[in] start1 : position of the first point of the first line
* @param[in] end1: posision of the last point of the first line
* @param[in] start2 : position of the first point of the seconde line
* @param[in] end2: posision of the last point of the second line
* @return true if they are
olliding, false elsewise */
bool areLinesColliding(unsigned start1, unsigned end1, unsigned start2, unsigned end2);
// change draw position for a specified size (keeps the same center)
/*!
* @brief change the size of a Position object
* @param[in,out] pos : Position object
* @param[in] sizeFrom: current size of the objet
* @param[in] sizeTo : new size of the object
*/
void applyTransformation(Position& pos, unsigned sizeFrom, unsigned sizeTo);
#endif

View File

@ -11,33 +11,9 @@
#include <fstream>
#include "game.h"
#include "configManagement.h"
#include "errors.h"
// you will MOVE THIS OUT OF THIS FILE
class ConfigBuilder{
public:
ConfigData collectedData;
void parseFile(const string& fname);
void readConfig();
void dumpInternalValues() const;
private:
map<string, string> internalValues;
const string& getString(const configKey& key) const;
char getChar(const configKey& key) const;
int getInt(const configKey& key) const;
void getColor(const configKey& key, nsGraphics::RGBAcolor&) const;
void getList(const configKey& key, vector<string>&) const;
void readGrid(const configKey& baseKey);
void readPlayer(const configKey& baseKey, PlayerDef&);
void readInvaderType(const configKey& baseKey, InvaderTypeDef&);
};
void ConfigBuilder::dumpInternalValues() const {
for(const auto& ite : internalValues){
cerr << ite.first << " -> " << ite.second << endl;
}
}
void trimSpaces(string& str){
str.erase(0, str.find_first_not_of(' '));
@ -56,12 +32,12 @@ void sanitizeValue(string& val) {
/*
* WARNING : This implementation of YAML is not meant to be complete, but to work with our specific needs
* It also can't detect and report errors in a non YAML-compliant file
* It also can't detect and report errors in a non-YAML-compliant file
*/
void ConfigBuilder::parseFile(const string& fname) {
ifstream file(fname);
if(!file.is_open())throw runtime_error("Error while opening config.yml. Check file location ?");
if(!file.is_open())throw config_error("Error while opening config.yml. Check file location ?");
vector<string> keyParts;
unsigned listIndex;
@ -91,7 +67,7 @@ void ConfigBuilder::parseFile(const string& fname) {
}else{
match = line.find(':');
if (match == string::npos)throw runtime_error("Invalid line : " + line);
if (match == string::npos)throw config_error("Invalid line : " + line);
string key = line.substr(0, match);
string value = line.substr(match + 1);
trimSpaces(key);
@ -148,7 +124,7 @@ void ConfigBuilder::readGrid(const configKey& baseKey) {
break;
}
default:{
throw runtime_error("Invalid invader ID in grid definition : "+ to_string(s[i]));
throw config_error("Invalid invader ID in grid definition : "+ to_string(s[i]));
}
}
}
@ -212,26 +188,60 @@ void ConfigBuilder::readConfig() {
getColor("projectiles.torpedos.color", collectedData.torpedosColor);
}
int ConfigBuilder::getInt(const configKey& key) const {
return stoi(getString(key));
}
char ConfigBuilder::getChar(const configKey& key) const {
return getString(key)[0];
const string& ConfigBuilder::getString(const configKey& key, const string& def) const {
try{
return getString(key);
}catch(config_error& e){
cerr << e.what() << endl;
return def;
}
}
const string& ConfigBuilder::getString(const configKey& key) const {
if(internalValues.contains(key)){
return internalValues.at(key);
}else{
throw runtime_error("Non-existent key requested : "+key);
throw config_error("Non-existent key requested : "+key);
}
}
int ConfigBuilder::getInt(const configKey& key) const {
try{
return stoi(getString(key));
}catch(invalid_argument& e){
cerr << e.what() << endl;
throw config_error("Invalid int data for key "+key+" : |"+getString(key)+"|");
}
}
int ConfigBuilder::getInt(const configKey& key, int def) const {
try{
return getInt(key);
}catch(config_error& e){
cerr << e.what() << endl;
return def;
}
}
char ConfigBuilder::getChar(const configKey& key) const {
string s = getString(key);
if(s.size()!=1)throw config_error("Invalid char data for key "+key+" : |"+s+"|");
return s[0];
}
char ConfigBuilder::getChar(const configKey& key, char def) const {
try{
return getChar(key);
}catch(config_error& e){
cerr << e.what() << endl;
return def;
}
}
void ConfigBuilder::getList(const configKey& key, vector<string>& toPopulate) const {
size_t i=0;
string fullKey = key+".0";
if(!internalValues.contains(fullKey))throw runtime_error("Non-existent list key requested : "+key);
if(!internalValues.contains(fullKey))throw config_error("Non-existent list key requested : "+key);
do{
toPopulate.push_back(internalValues.at(fullKey));
@ -259,14 +269,10 @@ void ConfigBuilder::getColor(const configKey& key, nsGraphics::RGBAcolor& color)
else if (colorStr == "purple")color = nsGraphics::KPurple;
else if (colorStr == "teal")color = nsGraphics::KTeal;
else if (colorStr == "navy")color = nsGraphics::KNavy;
else throw runtime_error("Invalid color string : "+colorStr);
else throw config_error("Invalid color string : "+colorStr);
}
/**
*
* @return false if there was an error
*/
bool Game::reloadConfig() {
map<string, string> strValues;
ConfigBuilder builder;
@ -275,7 +281,7 @@ bool Game::reloadConfig() {
builder.parseFile("config.yml");
parsed = true;
builder.readConfig();
}catch(runtime_error& e){
}catch(config_error& e){
if(parsed)cerr << "An error occured while reading the configuration :" << endl;
else cerr << "An error occured while parsing the configuration :" << endl;
cerr << e.what() << endl;

5
src/errors.cpp Normal file
View File

@ -0,0 +1,5 @@
#include "errors.h"
config_error::config_error(const string& msg) : runtime_error(msg) {
}

View File

@ -60,7 +60,6 @@ void Game::managedGames() {
// we assume the game has been played before, and so we need to clean used members
void Game::initGame(){
grid = confData.grid; // will copy the grid
updateColumns(); // Would have liked to to that in the "config grid", but.. I'm lazy
// we re-construct players objects, we don't have to clear all members and can rely on the construction value (set in .h file)
players.clear();
@ -114,16 +113,19 @@ WinValue Game::enterGameLoop(){ // returns when game is finished
}
}
manageGod();
if(manageGod())return WinValue::PLAYERS;
if(arePlayersDead())return WinValue::GOD;
moveMissiles();
remCollidingProjectiles();
moveTorpedos();
remCollidingProjectiles();
if(checkMissilesAndPlayers())return WinValue::INVADERS;
checkMissilesAndPlayers();
if(checkTorpedosAndInvaders())return WinValue::PLAYERS;
if(arePlayersDead())return WinValue::INVADERS;
displayAll(fps);
pm.endFrame();
@ -146,3 +148,10 @@ WinValue Game::enterGameLoop(){ // returns when game is finished
Position Game::invIndexToPos(unsigned x, unsigned y) const {
return basePos+Position(INV_GET_POS(x), INV_GET_POS(y));
}
bool Game::arePlayersDead() {
for(Player& p : players){
if(!p.isEliminated())return false;
}
return true;
}

View File

@ -48,10 +48,6 @@ void Game::managePlayers(){
for(unsigned i=0;i<players.size();++i)manageOnePlayer(i);
}
/** Makes the invaders play once, and check lower bounds
*
* @return true if the invaders went down from one line (and we should check lower boundary), else false
*/
bool Game::manageInvaders(){
if(grid.empty())return false; // If there are no more invaders we don't need to manage them
// shoot
@ -94,10 +90,6 @@ bool Game::manageInvaders(){
}
/** makes projectiles move, and manage collisions between them and everything
*
* @return 1 if the invaders are all dead, 2 if the player is dead, else 0
*/
void Game::remCollidingProjectiles(){
auto miss = missiles.begin();
@ -150,7 +142,7 @@ void Game::moveTorpedos() {
}
}
bool Game::checkMissilesAndPlayers() {
void Game::checkMissilesAndPlayers() {
auto miss_ite = missiles.begin();
while(miss_ite!=missiles.end()){
bool wasColliding = false;
@ -171,7 +163,6 @@ bool Game::checkMissilesAndPlayers() {
if(wasColliding)missiles.erase(miss_ite);
else ++miss_ite;
}
return false; // TODO manage death animation
}
bool Game::checkTorpedosAndInvaders() {
@ -202,7 +193,7 @@ bool Game::checkTorpedosAndInvaders() {
grid[i][alienIndex] = InvaderType::NONE;
if(updateColumns()) return true;
if(!areThereInvadersLeft()) return true;
break;
}
}

View File

@ -18,6 +18,11 @@ void Game::awakeGod() {
/**
* @returns true if we can finish the game
*/
/*
* This is a really long function, but I feel like it's still readable because of the switch, and..
* Honestly I think splitting it into multiple small functions would be ugly
*/
bool Game::manageGod() {
switch (god.state) {
case GodState::NONE: {
@ -35,18 +40,17 @@ bool Game::manageGod() {
++god.counter;
return false;
}
if (areThereInvadersLeft()) {
// init throw
god.counter = 0;
god.state = GodState::RETRIEVE1;
god.thrownInvPosX = grid.randomValidCol();
god.thrownInvPosY = grid[god.thrownInvPosX].randomValid();
god.thrownInvType = InvaderType::NONE;
god.thrownTransition.setX(pm.getScreenWidth() - GOD_HAND_DISTANCE - GOD_HAND_SIZE);
god.thrownTransition.setY(basePos.getY() + INV_GET_POS(god.thrownInvPosY));
return false;
}
// init throw
god.counter = 0;
god.state = GodState::RETRIEVE1;
god.thrownInvPosX = grid.randomValidCol();
god.thrownInvPosY = grid[god.thrownInvPosX].randomValidInv();
god.thrownInvType = InvaderType::NONE;
god.thrownTransition.setX(pm.getScreenWidth() - GOD_HAND_DISTANCE - GOD_HAND_SIZE);
god.thrownTransition.setY(basePos.getY() + INV_GET_POS(god.thrownInvPosY));
return false;
}
case GodState::RETRIEVE1: {
if (god.counter < 100) {
@ -132,14 +136,19 @@ bool Game::manageGod() {
* we do not need to reset other members, they'll be treated as non-initialized
* When we cycle back between states
*/
return false;
// We could have optimized that, but it's more readable like this and hopefully the compiler will optimize it itself
if(areThereInvadersLeft())return false;
else return true;
}
case GodState::YOLO: {
}
}
throw runtime_error("SHOULD NOT HAPPEN : invalid action for god : ID="+ to_string(static_cast<int>(god.state)));
}
Position God::getRightHandPos(unsigned screenWidth) const {
return Position(screenWidth - GOD_HAND_DISTANCE - GOD_HAND_SIZE, 0);
return {screenWidth - GOD_HAND_DISTANCE - GOD_HAND_SIZE, 0};
}

View File

@ -27,7 +27,7 @@ unsigned InvadersColumn::getOutterInvader() const {
// these are used to invoke rand() as less as possible
unsigned InvadersColumn::randomValid() const {
unsigned InvadersColumn::randomValidInv() const {
unsigned validTotal = 0;

View File

@ -1,6 +1,3 @@
#include <utils.h>
#include "utils.h"
bool areLinesColliding(unsigned start1, unsigned end1, unsigned start2, unsigned end2){