• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

eric15342335 / comp2113-engg1340-group-project / #13

12 Jun 2024 04:31PM UTC coverage: 67.084% (+0.08%) from 67.005%
#13

push

travis-ci

web-flow
Fix 'For loop variable changed in body' issue (#138)

43 of 48 new or added lines in 3 files covered. (89.58%)

17 existing lines in 2 files now uncovered.

1341 of 1999 relevant lines covered (67.08%)

4298418.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.09
/src/main.cpp
1
/// @file main.cpp
2
/// file with the main() function
3
/*
4
This program is free software: you can redistribute it and/or modify it under the
5
terms of the GNU Lesser General Public License as published by the Free Software
6
Foundation, either version 3 of the License, or (at your option) any later version.
7

8
This program is distributed in the hope that it will be useful, but WITHOUT ANY
9
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
10
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
11

12
You should have received a copy of the GNU Lesser General Public License along with this
13
program. If not, see <https://www.gnu.org/licenses/>.
14
*/
15

16
#include "controls.h"
17
#include "draw.h"
18
#include "events.h"
19
#include "file_io.h"
20
#include "format.h"
21
#include "graph.h"
22
#include "random_price.h"
23
#include "stock.h"
24

25
#if defined(__GNUC__) || defined(__clang__)
26
#pragma GCC diagnostic push
27
#pragma GCC diagnostic ignored "-Wold-style-cast"
28
#endif
29
#include "nonstdlibs/VariadicTable.h"
30
#if defined(__GNUC__) || defined(__clang__)
31
#pragma GCC diagnostic pop
32
#endif
33

34
#include <cmath>
35
#include <fstream>
36
#include <numeric>
37

38
#ifdef _WIN32
39
#define NOMINMAX 1          // Prevent Windows.h from defining min and max macros
40
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
41
#include <windows.h>
42
/** @brief Enable Windows VT processing for ANSI escape codes
43
 * @details Without this, ANSI escape codes will not work on Windows 10.
44
 * E.g. text color, cursor position, etc.
45
 */
46
void enableWindowsVTProcessing(void) {
2✔
47
    // Set the console to UTF-8 mode
48
    SetConsoleOutputCP(65001);
2✔
49
    // Get the current console mode
50
    DWORD consoleMode;
51
    GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &consoleMode);
2✔
52
    // Enable virtual terminal processing
53
    consoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
2✔
54
    SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), consoleMode);
2✔
55
    std::cout << "Experimental Windows VT processing enabled." << std::endl;
2✔
56
    // Enable Data Execution Prevention (DEP) for the process
57
    SetProcessDEPPolicy(PROCESS_DEP_ENABLE);
2✔
58
}
2✔
59
#else
60
#define enableWindowsVTProcessing() // Do nothing
61
#endif
62

63
/**
64
 * <value> / 100 means charging <value>% more/portion of the money involved in stock
65
 * operations.
66
 */
67
const float trading_fees_percent = 0.1 / 100;
68

69
/** Player's balance */
70
float balance = 1000.0f;
71
/** Number of rounds played */
72
unsigned int rounds_played = 1;
73

74
/** Player's name */
75
std::string playerName;
76

77
std::string vectorToString(const std::vector<unsigned int> & vec) {
780✔
78
    return std::accumulate(
79
        vec.begin(), vec.end(), std::string(), [](const std::string & s, int v) {
1,560✔
80
            return s.empty() ? std::to_string(v) : s + " " + std::to_string(v);
156✔
81
        });
1,560✔
82
}
83

84
void get_hsi(std::vector<Stock> stocks_list, std::vector<float> & hsi_history) {
39✔
85
    float hsi = 0;
39✔
86
    std::string filesave =
87
        SAVE_FOLDER_PREFIX + playerName + "/hsi" + SAVE_FILE_EXTENSION_TXT;
39✔
88
    std::vector<float> total;
39✔
89
    for (unsigned int i = 0; i < stocks_list.size(); i++) {
819✔
90
        total.emplace_back(
780✔
91
            stocks_list[i].get_price() / stocks_list[i].get_initial_price() * 1000 *
1,560✔
92
            static_cast<float>(std::pow(2, stocks_list[i].get_split_count())));
780✔
93
        // HSI formula = (price/initial price) * 1000 * 2^split count
94
    }
95
    hsi = std::reduce(total.begin(), total.end()) / total.size();
39✔
96
    hsi_history.emplace_back(hsi);
39✔
97
    std::ofstream fout;
39✔
98
    fout.open(filesave.c_str(), std::ios::app);
39✔
99
    fout << hsi << ' ';
39✔
100
    fout.close();
39✔
101
}
39✔
102

103
/**
104
 * @brief hiding mean/sd/uplim/lowlim/event_id columns in the table
105
 */
106
enum mode { normal, dev };
107

108
/** Print the table of stocks. We put it in a function so we can call it multiple times.
109
 * @param stocks_list A vector of stocks. The stocks to be printed.
110
 * @param _playerBal How much money the player has.
111
 * @param m mode to hide mean/sd/uplim/lowlim/event_id columns in the table
112
 */
113
void print_table(std::vector<Stock> stocks_list, float _playerBal, mode m = dev) {
39✔
114
    std::vector<std::string> defaultColumns = {
115
        "#", "Category", "Name", "$Price", "Change", R"(%Change)", "#Has", "#Max"};
78✔
116
    VariadicTable<unsigned int, std::string, std::string, float, float, float,
117
        unsigned int, unsigned int>
118
        defaultTable(defaultColumns);
39✔
119
    if (m == dev) {
39✔
120
        defaultColumns.emplace_back(" Mean ");
39✔
121
        defaultColumns.emplace_back(" SD ");
39✔
122
        defaultColumns.emplace_back(" up ");
39✔
123
        defaultColumns.emplace_back(" low ");
39✔
124
        defaultColumns.emplace_back("event_id");
39✔
125
        // Create a table, note that R"(% Change)" is a raw string literal (C++11
126
        // feature).
127
        VariadicTable<unsigned int, std::string, std::string, float, float, float,
128
            unsigned int, unsigned int, float, float, float, float, std::string>
129
            devTable({defaultColumns});
39✔
130
        /* Set the precision and format of the columns.
131
         * Note: Precision and Format is ignored for std::string columns. */
132
        devTable.setColumnPrecision({0, 0, 0, 2, 2, 2, 0, 0, 1, 0, 0, 0, 0});
78✔
133
        devTable.setColumnFormat({VariadicTableColumnFormat::AUTO,
78✔
134
            VariadicTableColumnFormat::AUTO, VariadicTableColumnFormat::AUTO,
135
            VariadicTableColumnFormat::FIXED, VariadicTableColumnFormat::FIXED,
136
            VariadicTableColumnFormat::FIXED, VariadicTableColumnFormat::FIXED,
137
            VariadicTableColumnFormat::FIXED, VariadicTableColumnFormat::FIXED,
138
            VariadicTableColumnFormat::FIXED, VariadicTableColumnFormat::FIXED,
139
            VariadicTableColumnFormat::FIXED, VariadicTableColumnFormat::AUTO});
140
        for (unsigned int i = 0; i < stocks_list.size(); i++) {
819✔
141
            std::map<stock_modifiers, float> modifiers =
142
                getProcessedModifiers(stocks_list[i]);
780✔
143
            devTable.addRow(i + 1, stocks_list[i].category_name(),
3,900✔
144
                stocks_list[i].get_name(), stocks_list[i].get_price(),
2,340✔
145
                stocks_list[i].delta_price(),
780✔
146
                stocks_list[i].delta_price_percentage() * 100,
780✔
147
                stocks_list[i].get_quantity(),
780✔
148
                stocks_list[i].num_stocks_affordable(_playerBal, trading_fees_percent),
780✔
149
                modifiers[mean], modifiers[standard_deviation], modifiers[upper_limit],
780✔
150
                modifiers[lower_limit], vectorToString(stocks_list[i].get_event_ids()));
1,560✔
151
        }
780✔
152
        devTable.print(std::cout);
39✔
153
    }
39✔
154
    else {
155
        /* Set the precision and format of the columns.
156
         * Note: Precision and Format is ignored for std::string columns. */
157
        defaultTable.setColumnPrecision({0, 0, 0, 2, 2, 2, 0, 0});
×
158
        defaultTable.setColumnFormat(
×
159
            {VariadicTableColumnFormat::AUTO, VariadicTableColumnFormat::AUTO,
160
                VariadicTableColumnFormat::AUTO, VariadicTableColumnFormat::FIXED,
161
                VariadicTableColumnFormat::FIXED, VariadicTableColumnFormat::FIXED,
162
                VariadicTableColumnFormat::FIXED, VariadicTableColumnFormat::FIXED});
163
        for (unsigned int i = 0; i < stocks_list.size(); i++) {
×
164
            defaultTable.addRow(i + 1, stocks_list[i].category_name(),
×
165
                stocks_list[i].get_name(), stocks_list[i].get_price(),
×
166
                stocks_list[i].delta_price(),
×
167
                stocks_list[i].delta_price_percentage() * 100,
×
168
                stocks_list[i].get_quantity(),
×
169
                stocks_list[i].num_stocks_affordable(_playerBal, trading_fees_percent));
×
170
        }
171
        defaultTable.print(std::cout);
×
172
    }
173
    // Modify the stringstream so that for the column "Change", the text
174
    // "Increase" is green and "Decrease" is red.
175
    // @note This is a workaround because VariadicTable does not support
176
    // modifying the text color of a specific cell.
177
    // Warning: This is a hack and may not work in the future!
178
    for (unsigned int i = 0; i < stocks_list.size(); i++) {
819✔
179
        std::string index = std::to_string(i + 1);
780✔
180
        if (i < 10 - 1) {
780✔
181
            index = " " + index;
351✔
182
        }
183
        if (stocks_list[i].delta_price() > 0) {
780✔
184
            std::cout << setCursorPosition(i + 9, 3) << textGreen << index;
397✔
185
        }
186
        else if (stocks_list[i].delta_price() < 0) {
383✔
187
            std::cout << setCursorPosition(i + 9, 3) << textRed << index;
363✔
188
        }
189
    }
780✔
190
    std::cout << textWhite;
39✔
191
    /* Display 2 decimal places for balance.
192
     * This line reverts the precision back to default after the table is printed.
193
     * Since the table uses std::auto (VariadicTableColumnFormat::AUTO), we need to
194
     * revert it back to default.
195
     */
196
    std::cout << std::fixed << std::setprecision(2);
39✔
197
}
39✔
198

199
/**
200
 * Get all the ongoing events.
201
 * @param stocks_list A vector of stocks.
202
 * @return A vector of Stock_event
203
 */
204
std::vector<Stock_event> get_ongoing_events(std::vector<Stock> stocks_list) {
77✔
205
    // Return a vector of ongoing events without duplicates
206
    std::vector<Stock_event> ongoing_events = {};
77✔
207
    for (unsigned int i = 0; i < stocks_list.size(); i++) {
1,617✔
208
        std::list<Stock_event> events = stocks_list[i].get_events();
1,540✔
209
        for (const Stock_event & event : events) {
1,845✔
210
            // Side note: Events with duration <= 0 are automatically removed from the
211
            // stock's event list. By stock.cpp Stock::next_round() which uses
212
            // Stock::remove_obselete_event()
213
            if (event.duration > 0) {
305✔
214
                // If the event is not in the ongoing_events, add it.
215
                if (std::find(ongoing_events.begin(), ongoing_events.end(), event) ==
305✔
216
                    ongoing_events.end()) {
610✔
217
                    ongoing_events.emplace_back(event);
147✔
218
                }
219
            }
220
        }
221
    }
1,540✔
222
    return ongoing_events;
77✔
223
}
×
224

225
/**
226
 * @brief Generate new events and apply them to the stocks. Should be called at the
227
 * beginning of each round.
228
 * @param stocks_list A vector of stocks. Pass by reference to modify the stocks.
229
 */
230
void new_events_next_round(std::vector<Stock> & stocks_list) {
37✔
231
    /** @note numEvents is the sum of these three values:
232
     * - 1
233
     * - A random integer between 0 and 1 (uniform distribution)
234
     * - 1 if more than 10 rounds have been played
235
     * If there was already more than 5 events, we will not generate more events.
236
     */
237
    unsigned int numEvents = 1 + random_integer(1) + (rounds_played / 5 > 2) * 1;
37✔
238
    if (get_ongoing_events(stocks_list).size() > 5) {
37✔
UNCOV
239
        return;
×
240
    }
241
    std::vector<Stock_event> picked_events = pick_events(all_stock_events, numEvents);
37✔
242
    for (const Stock_event & event : picked_events) {
98✔
243
        switch (event.type_of_event) {
61✔
UNCOV
244
            case all_stocks:
×
UNCOV
245
                for (unsigned int i = 0; i < stocks_list.size(); i++) {
×
UNCOV
246
                    stocks_list[i].add_event(event);
×
247
                }
UNCOV
248
                break;
×
249
            case category:
59✔
250
                for (unsigned int i = 0; i < stocks_list.size(); i++) {
1,239✔
251
                    if (stocks_list[i].get_category() == event.category) {
1,180✔
252
                        stocks_list[i].add_event(event);
68✔
253
                    }
254
                }
255
                break;
59✔
256
            case pick_random_stock: {
2✔
257
                std::vector<unsigned int> stocks_indices_not_suitable = {};
2✔
258
                while (!stocks_list.empty() &&
4✔
259
                       stocks_list.size() < stocks_indices_not_suitable.size()) {
2✔
260
                    // Pick a random stock
261
                    unsigned int choice = random_integer(stocks_list.size());
×
262
                    Stock lucky_stock = stocks_list[choice];
×
263
                    if (!lucky_stock.can_add_event(event)) {
×
264
                        stocks_indices_not_suitable.emplace_back(choice);
×
265
                    }
266
                    else {
267
                        Stock_event modified_event = event;
×
268
                        modified_event.text = lucky_stock.get_name() + " " + event.text;
×
269
                        lucky_stock.add_event(modified_event);
×
270
                        break;
×
271
                    }
272
                }
273
                break;
2✔
274
            }
2✔
275
            default:
×
276
                // Should not reach here, but if it does, break the loop
277
                // so that the player can continue playing the game.
278
                break;
×
279
        }
280
    }
281
}
37✔
282

283
void next_round_routine(unsigned int & _rounds, std::vector<Stock> & stocks_list) {
37✔
284
    _rounds++; // Increment the round number
37✔
285
    new_events_next_round(
37✔
286
        stocks_list); // Generate new events and apply them to the stocks
287
    for (unsigned int i = 0; i < stocks_list.size(); i++) {
777✔
288
        stocks_list[i].next_round(); // Update the stock price
740✔
289
    }
290
}
37✔
291

292
void initializePlayerSaves(
2✔
293
    std::vector<Stock> & stocks_list, std::vector<float> & hsi_history) {
294
    std::string EMPTY_INPUT = "";
2✔
295
    std::string loadsave = EMPTY_INPUT;
2✔
296
    while (loadsave.compare(EMPTY_INPUT) == 0) {
4✔
297
        std::cout << USER_SAVE_OPTION_PROMPT;
2✔
298
        std::cin >> loadsave;
2✔
299
        while (!checkValidInput(loadsave)) {
2✔
300
            std::cout << "Invalid input.\n" << USER_SAVE_OPTION_PROMPT;
×
301
            std::cin >> loadsave; // choose new file or load previous file
×
302
        }
303
        if (loadsave.compare(USER_SAVE_OPTION::NEW_GAME) == 0) {
2✔
304
            createplayer(playerName);
1✔
305
            savestatus(rounds_played, stocks_list, balance, playerName);
1✔
306
        }
307
        if (loadsave.compare(USER_SAVE_OPTION::LOAD_GAME) == 0) {
2✔
308
            loadstatus(rounds_played, stocks_list, balance, playerName, hsi_history);
1✔
309
        }
310
        if (loadsave.compare(USER_SAVE_OPTION::DELETE_GAME) == 0) {
2✔
311
            delsave(loadsave);
×
312
            loadsave = EMPTY_INPUT;
×
313
        }
314
        if (loadsave.compare(USER_SAVE_OPTION::EXIT_GAME) == 0) {
2✔
315
            std::cout << "Goodbye! Hope you had a good luck in the stock market!"
×
316
                      << std::endl;
×
317
            exit(EXIT_SUCCESS);
×
318
        }
319
    }
320
}
2✔
321

322
int main(void) {
2✔
323
    enableWindowsVTProcessing();
2✔
324
    std::cout << "The game was compiled on " << __DATE__ << " at " << __TIME__
2✔
325
              << std::endl;
2✔
326

327
    bool advance;          // Whether to advance to the next round
328
    bool gameQuit = false; // Whether the player wants to quit the game
2✔
329
    bool viewMode = false; // 0 to view table, 1 to view graph
2✔
330
    bool overlayEvent;     // Whether the event bar is being shown
331
    bool flush;            // Whether the screen needs updating
332
    int row;               // Number of characters to fit in a column
333
    int col;               // Number of characters to fit in a row
334
    fetchConsoleDimensions(row, col);
2✔
335

336
    std::vector<Stock> stocks_list;
2✔
337
    stocks_list.reserve(initial_stock_count);
2✔
338
    for (int i = 0; i < initial_stock_count; i++) {
42✔
339
        stocks_list.emplace_back();
40✔
340
    }
341

342
    sortStocksList(stocks_list, by_category, ascending);
2✔
343

344
    assertion_check_uniq_events();
2✔
345
    if (assertion_check_mutual_exclusivity()) {
2✔
346
        exit(1);
×
347
    }
348

349
    drawLogo(row, col);
2✔
350
    time::sleep(sleepMedium);
2✔
351
    std::vector<float> hsi_history;
2✔
352

353
    initializePlayerSaves(stocks_list, hsi_history);
2✔
354

355
    get_hsi(stocks_list, hsi_history);
2✔
356
    // Done loading/creating a new file.
357
    std::cout << "Current trading fees are charged at " << trading_fees_percent * 100
2✔
358
              << " %" << std::endl;
2✔
359
    time::sleep(sleepMedium * 2);
2✔
360

361
    while (!gameQuit) {
44✔
362
        advance = false;
40✔
363
        overlayEvent = false;
40✔
364
        flush = false;
40✔
365
        if (viewMode) {
40✔
366
            int indexGraph =
367
                integerInput(row, col, "Select stock index to display (0 for HSI): ");
1✔
368
            while (
1✔
369
                indexGraph < 0 || indexGraph > static_cast<int>(stocks_list.size())) {
1✔
370
                std::cout << setCursorPosition(row, 3) << "\x1b[2K";
×
371
                std::cout << "Index out of range!";
×
372
                time::sleep(sleepMedium);
×
373
                indexGraph = integerInput(
×
374
                    row, col, "Select stock index to display (0 for HSI): ");
375
            }
376
            std::cout << textClear << setCursorPosition(6, 0);
1✔
377
            graph_plotting(playerName, indexGraph - 1, col * 2 / 3, row - 10);
1✔
378
        }
379
        else {
380
            std::cout << textClear << setCursorPosition(6, 0);
39✔
381
            print_table(stocks_list, balance); // Print the table of stocks
39✔
382
        }
383
        drawRoundInfo(row, col, rounds_played, balance, playerName,
40✔
384
            hsi_history[hsi_history.size() - 1]);
40✔
385
        drawEventBar(row, col);
40✔
386
        drawButton(row, col);
40✔
387
        while (!flush) {
80✔
388
            optionsInput(row, col, balance, trading_fees_percent, stocks_list,
40✔
389
                get_ongoing_events(stocks_list), viewMode, advance, overlayEvent, flush,
80✔
390
                gameQuit);
391
        }
392

393
        if (advance) {
40✔
394
            next_round_routine(rounds_played, stocks_list);
37✔
395
            get_hsi(stocks_list, hsi_history);
37✔
396
            savestatus(rounds_played, stocks_list, balance, playerName);
37✔
397
            viewMode = false;
37✔
398
            time::sleep(sleepLong);
37✔
399
        }
400
    }
401
    return EXIT_SUCCESS;
2✔
402
}
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc