Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Stock::save() load() into operator>> and fix bug #122

Merged
merged 14 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/events.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@ bool assertion_check_mutual_exclusivity(void);
*/
void print_map(const std::map<unsigned int, std::vector<unsigned int>> & map);

extern const Stock_event STOCK_SPLIT_EVENT;

/// @todo Understand this constexpr lambda
inline const unsigned int sumOfAllEventsProbability = []() {
unsigned int sum = 0;
Expand Down
3 changes: 2 additions & 1 deletion include/file_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,6 @@ std::vector<std::string> get_saves(void);
/**
* @brief Print the vector of saves aka player folders.
*/
void printvector(std::vector<std::string> avector);
void printvector(const std::vector<std::string> & avector);

#endif
17 changes: 17 additions & 0 deletions include/stock.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ program. If not, see <https://www.gnu.org/licenses/>.
#define STOCK_H

#include "events.h"
#include "names.h"

#include <istream>
#include <list>
#include <map>
#include <string>
Expand All @@ -25,6 +27,12 @@ program. If not, see <https://www.gnu.org/licenses/>.
/** @brief Initial stock count */
const int initial_stock_count = 20;

/**
* The upper limit of the stock price.
* @see Stock::next_round for the "stock split" event.
*/
const float STOCK_PRICE_LIMIT = 1000.0f;

/**
* @class Stock stock.h "stock.h"
* @brief A class that represents a stock object in the game.
Expand Down Expand Up @@ -246,6 +254,15 @@ class Stock {
*/
float calculateTradingFeesLost(const float & trading_fees_percent) const;

/**
* @brief Set up a STOCK_SPLIT_EVENT with proper values.
*/
Stock_event setup_STOCK_SPLIT_EVENT(void);

friend std::ostream & operator<<(std::ostream & fout, const Stock & stock);

friend std::istream & operator>>(std::istream & fin, Stock & stock);

private:
/** @brief Name of the stock that we assigned to it. */
std::string name;
Expand Down
13 changes: 13 additions & 0 deletions src/events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1384,3 +1384,16 @@ std::vector<Stock_event> pick_events(
}
return picked_events;
}

const Stock_event STOCK_SPLIT_EVENT = {
/* event_id */ 65535,
/* mutually_exclusive_events */ {},
/* text */
" has rised too high and the company has decide a stock split on it.",
/* duration */ 1,
/* percentage_permille */ 0,
/* type_of_event */ pick_random_stock,
/* category. Assign this to zero first. */ 0,
/* modifiers*/
{{standard_deviation, 0}, {mean, 0}, {lower_limit, 0}, {upper_limit, 0}},
};
9 changes: 5 additions & 4 deletions src/file_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void createplayer(string & playerName) {
filesystem::create_directory(foldername); // create a empty folder for new save
}

void load_hsi(std::vector<float> hsi_history, const string & playerName) {
void load_hsi(std::vector<float> & hsi_history, const string & playerName) {
std::string filesave =
SAVE_FOLDER_PREFIX + playerName + "/hsi" + SAVE_FILE_EXTENSION_TXT;
std::ifstream fin;
Expand Down Expand Up @@ -227,10 +227,11 @@ vector<string> get_saves(void) {
return saves;
}

void printvector(vector<string> avector) {
void printvector(const vector<string> & avector) {
cout << avector[0];
for (const auto & item : avector) {
cout << ", " << item;
// note: start from 1 to avoid printing the first element twice
for (unsigned long i = 1; i < avector.size(); i++) {
cout << ", " << avector[i];
}
cout << endl;
}
2 changes: 1 addition & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,6 @@ int main(void) {
drawLogo(row, col);
time::sleep(sleepMedium);
std::vector<float> hsi_history;
get_hsi(stocks_list, hsi_history);

{
std::string loadsave;
Expand All @@ -320,6 +319,7 @@ int main(void) {
if (loadsave.compare(USER_SAVE_OPTION::NEW_GAME) == 0) {
createplayer(playerName);
savestatus(rounds_played, stocks_list, balance, playerName);
get_hsi(stocks_list, hsi_history);
}
if (loadsave.compare(USER_SAVE_OPTION::LOAD_GAME) == 0) {
loadstatus(rounds_played, stocks_list, balance, playerName, hsi_history);
Expand Down
164 changes: 78 additions & 86 deletions src/stock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ program. If not, see <https://www.gnu.org/licenses/>.
#include "random_price.h"

#include <algorithm>
#include <cassert>
#include <fstream>
#include <iostream>

Expand Down Expand Up @@ -49,97 +50,92 @@ void Stock::save(const std::string & playerName, int i) {
filesave = SAVE_FOLDER_PREFIX + playerName + "/" + std::to_string(i) + "" +
SAVE_FILE_EXTENSION_TXT; // creating the file path
fout.open(filesave.c_str());
fout << category << std::endl; // literally load everything into class into file
fout << name << std::endl;
for (unsigned int index = 0; index < history.size(); index++) {
fout << history[index] << " ";
}
fout << -1
<< std::endl; // -1 is the stop code for vector<float> history in filesave
fout << quantity << std::endl;
fout << attributes[standard_deviation] << " ";
fout << attributes[mean] << " ";
fout << attributes[lower_limit] << " ";
fout << attributes[upper_limit] << std::endl;
fout << split_count << std::endl << std::endl;

// Save the ongoing events, separated by std::endl
std::list<Stock_event>::iterator event_itr = events.begin();
while (event_itr != events.end()) {
fout << *event_itr << std::endl;
event_itr++;
}
fout << *this; // use operator<< to save the Stock object
fout.close();
}

void Stock::load(const std::string & playerName, int i) {
std::string fileToBeLoaded;
float loadedPrice;
std::ifstream fin;
fileToBeLoaded = SAVE_FOLDER_PREFIX + playerName + "/" + std::to_string(i) + "" +
SAVE_FILE_EXTENSION_TXT;
std::cout << "Loading " << fileToBeLoaded << " ... ";
fin.open(fileToBeLoaded.c_str());
// get the first line, which is category
fin >> category;
// boundary check for category
if (category >= category_list_size) {
std::cerr << "Error: Invalid category loaded" << std::endl;
exit(1);
fin >> *this; // use operator>> to load the Stock object
fin.close();
// @todo Do not hardcode this limit, use a constant
// STOCK_PRICE_LIMIT instead
assert(price <= 1000 && "Price exceed the limit");
std::cout << "done" << std::endl;
}

std::ostream & operator<<(std::ostream & fout, const Stock & stock) {
fout << stock.category
<< std::endl; // literally load everything into class into file
fout << stock.name << std::endl;
for (unsigned int index = 0; index < stock.history.size(); index++) {
fout << stock.history[index] << " ";
}
// the second line is entirely the stock name
std::getline(fin >> std::ws, name);
fout << -1 << std::endl; // -1 is the stop code for vector<float> history in
// filesave
fout << stock.quantity << std::endl;
fout << stock.attributes.at(standard_deviation) << " ";
fout << stock.attributes.at(mean) << " ";
fout << stock.attributes.at(lower_limit) << " ";
fout << stock.attributes.at(upper_limit) << std::endl;
fout << stock.split_count << std::endl << std::endl;

// Save the ongoing events, separated by std::endl
for (Stock_event event : stock.events) {
fout << event << std::endl;
}
return fout;
}

std::istream & operator>>(std::istream & fin, Stock & stock) {
fin >> stock.category; // line 1
assert(stock.category < category_list_size && "Invalid category");
// line 2 is entirely the stock name
std::getline(fin >> std::ws, stock.name);
float loadedPrice;
fin >> loadedPrice;
// Erase the history vector, since we called the constructor already
history.clear();
// Erase the history vector and load the new history
stock.history.clear();
while (loadedPrice != -1) {
history.emplace_back(loadedPrice);
fin >> loadedPrice;
stock.history.emplace_back(loadedPrice);
fin >> loadedPrice; // line 3
}
// Set the price
price = history[history.size() - 1];
fin >> quantity;
fin >> attributes[standard_deviation];
fin >> attributes[mean];
fin >> attributes[lower_limit];
fin >> attributes[upper_limit];
fin >> split_count;
// Manually reposition the file pointer to the sixth line
// by going to the beginning of the file and skipping the first five lines
fin.seekg(0, std::ios::beg);
for (int lineCount = 0; lineCount < 7; lineCount++) {
std::string line;
std::getline(fin, line);
}

// Load the ongoing events, separated by std::endl
stock.price = stock.history.back();
fin >> stock.quantity; // line 4
fin >> stock.attributes[standard_deviation]; // line 5
fin >> stock.attributes[mean];
fin >> stock.attributes[lower_limit];
fin >> stock.attributes[upper_limit];
fin >> stock.split_count; // line 6
// Clear the events list
stock.events.clear();
// Skip 2 empty lines
std::string emptyLine;
std::getline(fin >> std::ws, emptyLine);
std::getline(fin >> std::ws, emptyLine);
std::string loadedEventString;
while (std::getline(fin, loadedEventString)) {
Stock_event loadedEvent;
std::istringstream(loadedEventString) >> loadedEvent;
// Check the loaded event is valid
// Ignore the special case of event_id >= 65535
if (loadedEvent.event_id >= 65535 &&
loadedEvent.event_id < all_stock_events.size()) {
add_event(loadedEvent);
if (loadedEvent.event_id == STOCK_SPLIT_EVENT.event_id) {
continue;
}
assert(
loadedEvent.event_id < all_stock_events.size() && "Invalid event loaded");
Stock_event comparedEvent = all_stock_events[loadedEvent.event_id];
if (loadedEvent == comparedEvent) {
add_event(loadedEvent);
}
else {
std::cerr << "Error: Invalid event loaded" << std::endl;
// Output the difference between the loaded event and the compared event
std::cerr << "Loaded event: " << loadedEvent << std::endl;
std::cerr << "Compared event: " << comparedEvent << std::endl;
exit(1);
}
assert(loadedEvent == comparedEvent && "Invalid event loaded");
stock.add_event(loadedEvent);
}
fin.close();
time::sleep(random_integer(sleepShort)); // optimize this
std::cout << "done" << std::endl;
}
return fin;
};

float Stock::purchase(
float & balance, unsigned int amount, float trading_fees_percent) {
Expand Down Expand Up @@ -271,33 +267,20 @@ float Stock::sum_attribute(stock_modifiers attribute) {
return sum;
}

Stock_event Stock::setup_STOCK_SPLIT_EVENT(void) {
Stock_event event_copy = STOCK_SPLIT_EVENT;
event_copy.text = name + event_copy.text;
event_copy.category = category;
return event_copy;
}

void Stock::next_round(void) {
/** Update the price of the stock.
* If the price is less than 1000, the price will increase or decrease by a random
* percentage. If the price is more than 1000, the price will be halved and the
* quantity will be doubled.
*/
float price_diff = percentage_change_price(*this) / 100;
if (!(price * (1 + price_diff) > 999.9)) {
price *= (1 + price_diff);
}
else {
price /= 2;
quantity *= 2;
split_count++;
add_event(Stock_event{// Stock split event
/** event_id */ 65535,
/** mutually_exclusive_events */ {},
/** text */
name +
" has rised too high and the company has decide a stock split on it.",
/** duration */ 1,
/** percentage_permille */ 0,
/** type_of_event */ pick_random_stock,
/** category */ category,
/** modifiers*/
{{standard_deviation, 0}, {mean, 0}, {lower_limit, 0}, {upper_limit, 0}}});
}
// Reduce all events duration by one.
std::list<Stock_event>::iterator event_itr = events.begin();
while (event_itr != events.end()) {
Expand All @@ -309,6 +292,15 @@ void Stock::next_round(void) {
}
event_itr++;
}
if (!(price * (1 + price_diff) >= STOCK_PRICE_LIMIT)) {
price *= (1 + price_diff);
}
else {
price /= 2;
quantity *= 2;
split_count++;
add_event(setup_STOCK_SPLIT_EVENT());
}
remove_obselete_event();
update_history();
}
Expand Down
Loading