Skip to content

Commit 97a0ff9

Browse files
authored
Refactor Stock::save() load() into operator>> and fix bug (#122)
* Stock: Extract Stock::load() Stock::save() code to operator>> operator<< * File I/O: Fix empty playername hsi.save and printvector() duplicate printing * Add std:: prefix to printvector() prototype to fix error * Continue fixes on hsi.save issue plus few improvements Add one assertion and removed one magic number * Formatting: Run clang-format From Makefile: make fix * Fixed a bug in Stock::next_round() that will make stock split event never appear The event duration was reduced by durationDecreaseMultipler right after the stock split event was added. Fix: Rearrange the order * Formatting: Run clang-format From Makefile: make fix * Formatting: Run clang-format From Makefile: make fix * Formatting: Run clang-format From Makefile: make fix * fix * fix --------- Signed-off-by: Cheng Ho Ming, Eric <eric310@connect.hku.hk>
1 parent c3d3a2c commit 97a0ff9

File tree

7 files changed

+118
-92
lines changed

7 files changed

+118
-92
lines changed

include/events.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,8 @@ bool assertion_check_mutual_exclusivity(void);
230230
*/
231231
void print_map(const std::map<unsigned int, std::vector<unsigned int>> & map);
232232

233+
extern const Stock_event STOCK_SPLIT_EVENT;
234+
233235
/// @todo Understand this constexpr lambda
234236
inline const unsigned int sumOfAllEventsProbability = []() {
235237
unsigned int sum = 0;

include/file_io.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,5 +97,6 @@ std::vector<std::string> get_saves(void);
9797
/**
9898
* @brief Print the vector of saves aka player folders.
9999
*/
100-
void printvector(std::vector<std::string> avector);
100+
void printvector(const std::vector<std::string> & avector);
101+
101102
#endif

include/stock.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ program. If not, see <https://www.gnu.org/licenses/>.
1616
#define STOCK_H
1717

1818
#include "events.h"
19+
#include "names.h"
1920

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

30+
/**
31+
* The upper limit of the stock price.
32+
* @see Stock::next_round for the "stock split" event.
33+
*/
34+
const float STOCK_PRICE_LIMIT = 1000.0f;
35+
2836
/**
2937
* @class Stock stock.h "stock.h"
3038
* @brief A class that represents a stock object in the game.
@@ -246,6 +254,15 @@ class Stock {
246254
*/
247255
float calculateTradingFeesLost(const float & trading_fees_percent) const;
248256

257+
/**
258+
* @brief Set up a STOCK_SPLIT_EVENT with proper values.
259+
*/
260+
Stock_event setup_STOCK_SPLIT_EVENT(void);
261+
262+
friend std::ostream & operator<<(std::ostream & fout, const Stock & stock);
263+
264+
friend std::istream & operator>>(std::istream & fin, Stock & stock);
265+
249266
private:
250267
/** @brief Name of the stock that we assigned to it. */
251268
std::string name;

src/events.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,3 +1384,16 @@ std::vector<Stock_event> pick_events(
13841384
}
13851385
return picked_events;
13861386
}
1387+
1388+
const Stock_event STOCK_SPLIT_EVENT = {
1389+
/* event_id */ 65535,
1390+
/* mutually_exclusive_events */ {},
1391+
/* text */
1392+
" has rised too high and the company has decide a stock split on it.",
1393+
/* duration */ 1,
1394+
/* percentage_permille */ 0,
1395+
/* type_of_event */ pick_random_stock,
1396+
/* category. Assign this to zero first. */ 0,
1397+
/* modifiers*/
1398+
{{standard_deviation, 0}, {mean, 0}, {lower_limit, 0}, {upper_limit, 0}},
1399+
};

src/file_io.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ void createplayer(string & playerName) {
8989
filesystem::create_directory(foldername); // create a empty folder for new save
9090
}
9191

92-
void load_hsi(std::vector<float> hsi_history, const string & playerName) {
92+
void load_hsi(std::vector<float> & hsi_history, const string & playerName) {
9393
std::string filesave =
9494
SAVE_FOLDER_PREFIX + playerName + "/hsi" + SAVE_FILE_EXTENSION_TXT;
9595
std::ifstream fin;
@@ -227,10 +227,11 @@ vector<string> get_saves(void) {
227227
return saves;
228228
}
229229

230-
void printvector(vector<string> avector) {
230+
void printvector(const vector<string> & avector) {
231231
cout << avector[0];
232-
for (const auto & item : avector) {
233-
cout << ", " << item;
232+
// note: start from 1 to avoid printing the first element twice
233+
for (unsigned long i = 1; i < avector.size(); i++) {
234+
cout << ", " << avector[i];
234235
}
235236
cout << endl;
236237
}

src/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,6 @@ int main(void) {
307307
drawLogo(row, col);
308308
time::sleep(sleepMedium);
309309
std::vector<float> hsi_history;
310-
get_hsi(stocks_list, hsi_history);
311310

312311
{
313312
std::string loadsave;
@@ -320,6 +319,7 @@ int main(void) {
320319
if (loadsave.compare(USER_SAVE_OPTION::NEW_GAME) == 0) {
321320
createplayer(playerName);
322321
savestatus(rounds_played, stocks_list, balance, playerName);
322+
get_hsi(stocks_list, hsi_history);
323323
}
324324
if (loadsave.compare(USER_SAVE_OPTION::LOAD_GAME) == 0) {
325325
loadstatus(rounds_played, stocks_list, balance, playerName, hsi_history);

src/stock.cpp

Lines changed: 78 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ program. If not, see <https://www.gnu.org/licenses/>.
2020
#include "random_price.h"
2121

2222
#include <algorithm>
23+
#include <cassert>
2324
#include <fstream>
2425
#include <iostream>
2526

@@ -49,97 +50,92 @@ void Stock::save(const std::string & playerName, int i) {
4950
filesave = SAVE_FOLDER_PREFIX + playerName + "/" + std::to_string(i) + "" +
5051
SAVE_FILE_EXTENSION_TXT; // creating the file path
5152
fout.open(filesave.c_str());
52-
fout << category << std::endl; // literally load everything into class into file
53-
fout << name << std::endl;
54-
for (unsigned int index = 0; index < history.size(); index++) {
55-
fout << history[index] << " ";
56-
}
57-
fout << -1
58-
<< std::endl; // -1 is the stop code for vector<float> history in filesave
59-
fout << quantity << std::endl;
60-
fout << attributes[standard_deviation] << " ";
61-
fout << attributes[mean] << " ";
62-
fout << attributes[lower_limit] << " ";
63-
fout << attributes[upper_limit] << std::endl;
64-
fout << split_count << std::endl << std::endl;
65-
66-
// Save the ongoing events, separated by std::endl
67-
std::list<Stock_event>::iterator event_itr = events.begin();
68-
while (event_itr != events.end()) {
69-
fout << *event_itr << std::endl;
70-
event_itr++;
71-
}
53+
fout << *this; // use operator<< to save the Stock object
7254
fout.close();
7355
}
7456

7557
void Stock::load(const std::string & playerName, int i) {
7658
std::string fileToBeLoaded;
77-
float loadedPrice;
7859
std::ifstream fin;
7960
fileToBeLoaded = SAVE_FOLDER_PREFIX + playerName + "/" + std::to_string(i) + "" +
8061
SAVE_FILE_EXTENSION_TXT;
8162
std::cout << "Loading " << fileToBeLoaded << " ... ";
8263
fin.open(fileToBeLoaded.c_str());
8364
// get the first line, which is category
84-
fin >> category;
85-
// boundary check for category
86-
if (category >= category_list_size) {
87-
std::cerr << "Error: Invalid category loaded" << std::endl;
88-
exit(1);
65+
fin >> *this; // use operator>> to load the Stock object
66+
fin.close();
67+
// @todo Do not hardcode this limit, use a constant
68+
// STOCK_PRICE_LIMIT instead
69+
assert(price <= 1000 && "Price exceed the limit");
70+
std::cout << "done" << std::endl;
71+
}
72+
73+
std::ostream & operator<<(std::ostream & fout, const Stock & stock) {
74+
fout << stock.category
75+
<< std::endl; // literally load everything into class into file
76+
fout << stock.name << std::endl;
77+
for (unsigned int index = 0; index < stock.history.size(); index++) {
78+
fout << stock.history[index] << " ";
8979
}
90-
// the second line is entirely the stock name
91-
std::getline(fin >> std::ws, name);
80+
fout << -1 << std::endl; // -1 is the stop code for vector<float> history in
81+
// filesave
82+
fout << stock.quantity << std::endl;
83+
fout << stock.attributes.at(standard_deviation) << " ";
84+
fout << stock.attributes.at(mean) << " ";
85+
fout << stock.attributes.at(lower_limit) << " ";
86+
fout << stock.attributes.at(upper_limit) << std::endl;
87+
fout << stock.split_count << std::endl << std::endl;
88+
89+
// Save the ongoing events, separated by std::endl
90+
for (Stock_event event : stock.events) {
91+
fout << event << std::endl;
92+
}
93+
return fout;
94+
}
95+
96+
std::istream & operator>>(std::istream & fin, Stock & stock) {
97+
fin >> stock.category; // line 1
98+
assert(stock.category < category_list_size && "Invalid category");
99+
// line 2 is entirely the stock name
100+
std::getline(fin >> std::ws, stock.name);
101+
float loadedPrice;
92102
fin >> loadedPrice;
93-
// Erase the history vector, since we called the constructor already
94-
history.clear();
103+
// Erase the history vector and load the new history
104+
stock.history.clear();
95105
while (loadedPrice != -1) {
96-
history.emplace_back(loadedPrice);
97-
fin >> loadedPrice;
106+
stock.history.emplace_back(loadedPrice);
107+
fin >> loadedPrice; // line 3
98108
}
99109
// Set the price
100-
price = history[history.size() - 1];
101-
fin >> quantity;
102-
fin >> attributes[standard_deviation];
103-
fin >> attributes[mean];
104-
fin >> attributes[lower_limit];
105-
fin >> attributes[upper_limit];
106-
fin >> split_count;
107-
// Manually reposition the file pointer to the sixth line
108-
// by going to the beginning of the file and skipping the first five lines
109-
fin.seekg(0, std::ios::beg);
110-
for (int lineCount = 0; lineCount < 7; lineCount++) {
111-
std::string line;
112-
std::getline(fin, line);
113-
}
114-
115-
// Load the ongoing events, separated by std::endl
110+
stock.price = stock.history.back();
111+
fin >> stock.quantity; // line 4
112+
fin >> stock.attributes[standard_deviation]; // line 5
113+
fin >> stock.attributes[mean];
114+
fin >> stock.attributes[lower_limit];
115+
fin >> stock.attributes[upper_limit];
116+
fin >> stock.split_count; // line 6
117+
// Clear the events list
118+
stock.events.clear();
119+
// Skip 2 empty lines
120+
std::string emptyLine;
121+
std::getline(fin >> std::ws, emptyLine);
122+
std::getline(fin >> std::ws, emptyLine);
116123
std::string loadedEventString;
117124
while (std::getline(fin, loadedEventString)) {
118125
Stock_event loadedEvent;
119126
std::istringstream(loadedEventString) >> loadedEvent;
120127
// Check the loaded event is valid
121-
// Ignore the special case of event_id >= 65535
122-
if (loadedEvent.event_id >= 65535 &&
123-
loadedEvent.event_id < all_stock_events.size()) {
124-
add_event(loadedEvent);
128+
if (loadedEvent.event_id == STOCK_SPLIT_EVENT.event_id) {
125129
continue;
126130
}
131+
assert(
132+
loadedEvent.event_id < all_stock_events.size() && "Invalid event loaded");
127133
Stock_event comparedEvent = all_stock_events[loadedEvent.event_id];
128-
if (loadedEvent == comparedEvent) {
129-
add_event(loadedEvent);
130-
}
131-
else {
132-
std::cerr << "Error: Invalid event loaded" << std::endl;
133-
// Output the difference between the loaded event and the compared event
134-
std::cerr << "Loaded event: " << loadedEvent << std::endl;
135-
std::cerr << "Compared event: " << comparedEvent << std::endl;
136-
exit(1);
137-
}
134+
assert(loadedEvent == comparedEvent && "Invalid event loaded");
135+
stock.add_event(loadedEvent);
138136
}
139-
fin.close();
140-
time::sleep(random_integer(sleepShort)); // optimize this
141-
std::cout << "done" << std::endl;
142-
}
137+
return fin;
138+
};
143139

144140
float Stock::purchase(
145141
float & balance, unsigned int amount, float trading_fees_percent) {
@@ -271,33 +267,20 @@ float Stock::sum_attribute(stock_modifiers attribute) {
271267
return sum;
272268
}
273269

270+
Stock_event Stock::setup_STOCK_SPLIT_EVENT(void) {
271+
Stock_event event_copy = STOCK_SPLIT_EVENT;
272+
event_copy.text = name + event_copy.text;
273+
event_copy.category = category;
274+
return event_copy;
275+
}
276+
274277
void Stock::next_round(void) {
275278
/** Update the price of the stock.
276279
* If the price is less than 1000, the price will increase or decrease by a random
277280
* percentage. If the price is more than 1000, the price will be halved and the
278281
* quantity will be doubled.
279282
*/
280283
float price_diff = percentage_change_price(*this) / 100;
281-
if (!(price * (1 + price_diff) > 999.9)) {
282-
price *= (1 + price_diff);
283-
}
284-
else {
285-
price /= 2;
286-
quantity *= 2;
287-
split_count++;
288-
add_event(Stock_event{// Stock split event
289-
/** event_id */ 65535,
290-
/** mutually_exclusive_events */ {},
291-
/** text */
292-
name +
293-
" has rised too high and the company has decide a stock split on it.",
294-
/** duration */ 1,
295-
/** percentage_permille */ 0,
296-
/** type_of_event */ pick_random_stock,
297-
/** category */ category,
298-
/** modifiers*/
299-
{{standard_deviation, 0}, {mean, 0}, {lower_limit, 0}, {upper_limit, 0}}});
300-
}
301284
// Reduce all events duration by one.
302285
std::list<Stock_event>::iterator event_itr = events.begin();
303286
while (event_itr != events.end()) {
@@ -309,6 +292,15 @@ void Stock::next_round(void) {
309292
}
310293
event_itr++;
311294
}
295+
if (!(price * (1 + price_diff) >= STOCK_PRICE_LIMIT)) {
296+
price *= (1 + price_diff);
297+
}
298+
else {
299+
price /= 2;
300+
quantity *= 2;
301+
split_count++;
302+
add_event(setup_STOCK_SPLIT_EVENT());
303+
}
312304
remove_obselete_event();
313305
update_history();
314306
}

0 commit comments

Comments
 (0)