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

graphics.cpp #150

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Conversation

akshitbansal2005
Copy link

Code Analysis

  1. Modular Design:

    • The code is organized into namespaces (Statistics and Graphics), promoting modularity and separation of concerns. This makes it easier to manage and maintain different parts of the application.
  2. String Construction:

    • Uses std::ostringstream for building strings with rich formatting (e.g., bold text). This approach enhances readability and ensures that formatting is applied consistently.
  3. Constants Usage:

    • The code employs constexpr for defining string literals and arrays. This enables compile-time evaluation and optimization, making the code more efficient and less error-prone.
  4. Tuple Data Handling:

    • Utilizes std::get to access elements from a tuple (total_stats_display_data_t). This is a straightforward way to manage grouped data but requires ensuring that the tuple structure remains consistent.
  5. Dynamic Formatting:

    • The code dynamically adjusts the layout of the statistics display based on the available data. It handles scenarios where no statistics are saved, displaying an appropriate message to the user.
  6. Readability:

    • The code is relatively easy to read, with clear variable names and structured flow. However, the inline comments could be more descriptive to explain complex logic or intentions behind certain design choices.
  7. Potential Improvements:

    • Error Handling: While the code checks if the statistics file is loaded, there could be additional error handling for scenarios like file access issues or malformed data.
    • Unit Testing: Implementing unit tests for the functions, especially those involving file I/O and data formatting, would ensure robustness and help catch bugs early.

Here’s a revised version of your C++ code, including improvements based on the previous analysis, along with a brief analysis of the changes made. This code can be submitted as a pull request (PR) on GitHub.

### Revised C++ Code

```cpp
#include "statistics.hpp"
#include "color.hpp"
#include "scores-graphics.hpp"
#include "scores.hpp"
#include "statistics-graphics.hpp"
#include <algorithm>
#include <array>
#include <fstream>
#include <sstream>

namespace Statistics {

namespace {

// Helper function to receive player name input
std::string receive_input_player_name(std::istream &is) {
    std::string name;
    is >> name;

    // Validate input
    if (is.fail()) {
        throw std::runtime_error("Invalid player name input.");
    }
    
    return name;
}

// Generate total game stats from input data
total_game_stats_t generateStatsFromInputData(std::istream &is) {
    total_game_stats_t stats;
    is >> stats;

    // Validate input
    if (is.fail()) {
        throw std::runtime_error("Invalid statistics input data.");
    }
    
    return stats;
}

// Save statistics data to the output stream
bool generateFilefromStatsData(std::ostream &os, const total_game_stats_t &stats) {
    os << stats;
    return os.good(); // Check if the write was successful
}

// Save end game statistics to a file
bool saveToFileEndGameStatistics(const std::string &filename, const total_game_stats_t &s) {
    std::ofstream filedata(filename);
    if (!filedata) {
        throw std::runtime_error("Could not open file for saving statistics.");
    }
    return generateFilefromStatsData(filedata, s);
}

// Create final score display data
Scoreboard::Graphics::finalscore_display_data_t make_finalscore_display_data(const Scoreboard::Score &finalscore) {
    return std::make_tuple(
        std::to_string(finalscore.score),
        std::to_string(finalscore.largestTile),
        std::to_string(finalscore.moveCount),
        secondsFormat(finalscore.duration)
    );
}

} // namespace

// Load statistics from a file
load_stats_status_t loadFromFileStatistics(const std::string &filename) {
    std::ifstream statistics(filename);
    if (!statistics) {
        return load_stats_status_t{false, total_game_stats_t{}};
    }

    total_game_stats_t stats = generateStatsFromInputData(statistics);
    return load_stats_status_t{true, stats};
}

// Load the best score from game statistics
ull load_game_best_score() {
    total_game_stats_t stats;
    bool stats_file_loaded{};
    ull tempscore{0};

    std::tie(stats_file_loaded, stats) = loadFromFileStatistics("../data/statistics.txt");
    if (stats_file_loaded) {
        tempscore = stats.bestScore;
    }

    return tempscore;
}

// Save end game statistics
void saveEndGameStats(const Scoreboard::Score &finalscore) {
    total_game_stats_t stats;
    std::tie(std::ignore, stats) = loadFromFileStatistics("../data/statistics.txt");
    
    stats.bestScore = std::max(stats.bestScore, finalscore.score);
    stats.gameCount++;
    stats.winCount += finalscore.win ? 1 : 0;
    stats.totalMoveCount += finalscore.moveCount;
    stats.totalDuration += finalscore.duration;

    saveToFileEndGameStatistics("../data/statistics.txt", stats);
}

// Create final score and end game data file
void CreateFinalScoreAndEndGameDataFile(std::ostream &os, std::istream &is, Scoreboard::Score finalscore) {
    const auto finalscore_display_data = make_finalscore_display_data(finalscore);
    DrawAlways(os, DataSuppliment(finalscore_display_data, Scoreboard::Graphics::EndGameStatisticsPrompt));

    DrawAlways(os, Graphics::AskForPlayerNamePrompt);
    finalscore.name = receive_input_player_name(is);
    
    Scoreboard::saveScore(finalscore);
    saveEndGameStats(finalscore);
    DrawAlways(os, Graphics::MessageScoreSavedPrompt);
}

} // namespace Statistics

using namespace Statistics;

// Overload operator to read total game stats
std::istream &operator>>(std::istream &is, total_game_stats_t &s) {
    is >> s.bestScore >> s.gameCount >> s.winCount >> s.totalMoveCount >> s.totalDuration;

    // Validate stream state after reading
    if (is.fail()) {
        throw std::runtime_error("Failed to read total game stats.");
    }

    return is;
}

// Overload operator to write total game stats
std::ostream &operator<<(std::ostream &os, const total_game_stats_t &s) {
    os << s.bestScore << "\n"
       << s.gameCount << "\n"
       << s.winCount << "\n"
       << s.totalMoveCount << "\n"
       << s.totalDuration;

    return os;
}
```

### Brief Analysis of Changes Made

1. **Error Handling:**
   - **Change:** Added exceptions for error scenarios in file operations and input validations.
   - **Rationale:** This enhances robustness by ensuring that failures are explicitly communicated, preventing undefined behavior.

2. **Const-correctness:**
   - **Change:** Function parameters for references (e.g., `total_game_stats_t &s`) are made `const` where modifications are not needed.
   - **Rationale:** Improves clarity and ensures that functions do not modify the inputs unintentionally.

3. **Stream State Validation:**
   - **Change:** Added checks after reading from streams to ensure that the read operation was successful.
   - **Rationale:** Helps catch issues early if input data is malformed or unexpected.

4. **Simplified Logic:**
   - **Change:** Used `std::max` to simplify the logic for updating the `bestScore`.
   - **Rationale:** Enhances readability and maintains the same functionality.

5. **Commenting:**
   - **Change:** Improved comments throughout the code for clarity.
   - **Rationale:** Helps future developers (and yourself) understand the purpose and functionality of the code.
### Code Analysis

1. **Modular Design:**
   - The code is organized into namespaces (`Statistics` and `Graphics`), promoting modularity and separation of concerns. This makes it easier to manage and maintain different parts of the application.

2. **String Construction:**
   - Uses `std::ostringstream` for building strings with rich formatting (e.g., bold text). This approach enhances readability and ensures that formatting is applied consistently.

3. **Constants Usage:**
   - The code employs `constexpr` for defining string literals and arrays. This enables compile-time evaluation and optimization, making the code more efficient and less error-prone.

4. **Tuple Data Handling:**
   - Utilizes `std::get` to access elements from a tuple (`total_stats_display_data_t`). This is a straightforward way to manage grouped data but requires ensuring that the tuple structure remains consistent.

5. **Dynamic Formatting:**
   - The code dynamically adjusts the layout of the statistics display based on the available data. It handles scenarios where no statistics are saved, displaying an appropriate message to the user.

6. **Readability:**
   - The code is relatively easy to read, with clear variable names and structured flow. However, the inline comments could be more descriptive to explain complex logic or intentions behind certain design choices.

7. **Potential Improvements:**
   - **Error Handling:** While the code checks if the statistics file is loaded, there could be additional error handling for scenarios like file access issues or malformed data.
   - **Unit Testing:** Implementing unit tests for the functions, especially those involving file I/O and data formatting, would ensure robustness and help catch bugs early.

### Conclusion

Overall, the code effectively accomplishes its goals while adhering to good C++ practices. It is well-structured, readable, and handles basic functionality appropriately. Minor improvements in error handling and documentation could enhance its reliability and maintainability further.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant