From 226bebc04f41bebbc3c80f7f0b5c7ff5e7a412b1 Mon Sep 17 00:00:00 2001 From: actual-nh <74678550+actual-nh@users.noreply.github.com> Date: Tue, 8 Jun 2021 02:55:04 -0400 Subject: [PATCH] Fix float-keying activity level problem (#49179) * Activity diary test now detects float-keying problem avatar::log_activity_level is now getting fed (floating-point) numbers that don't match the exact activity levels, due to #45316 making the activity level per-turn then averaging activity over each 5-minute period. The activity diary test (in tests/char_biometrics_test.cpp) was not spotting this since it was still using times in multiples of 5 minutes. This commit moves around 1 minute of NO_EXERCISE, keeping the same total number of minutes of each activity level. Currently, this results in a test failure in which 1 5-minute interval is missing from each of the activity levels in the diary, since those 5-minute intervals were at intermediate (averaged) levels of activity. * Process intermediate activity levels for debug output This "buckets" activity levels that aren't exactly the predefined set of levels (due to averaging over 5 minutes) into the closest predefined level in avatar::total_daily_calories_string. Note that saving of levels will not be bucketed. This, while useful for close inspection of intermediate levels via looking at the save file, will make the file slightly larger, slower to input/output, and (even) harder to read manually. However, this has been true since #45316 was merged and nobody seems to have noticed it. * Correct logic error in attempted fix. I forgot to add the number of 5-minute intervals and was instead adding just one. It still doesn't quite work right (and I'll need to adjust the expected gained/spent/total caloric values), but it's a lot closer. * Adjust expected numbers for mixed intervals The new results in terms of minutes are actually now correct; the difference is because the last 5 minutes are 4 minutes of EXTRA_EXERCISE and 1 minute of NO_EXERCISE, which averages out to ACTIVE_EXERCISE (410 + 11 = 41; /5 = 8.2, and ACTIVE_EXERCISE = 8.0). The rest is likely roundoff error, although it still bothers me a bit. Co-authored-by: actual-nh --- src/avatar.cpp | 43 +++++++++++++++++++++++++++++----- tests/char_biometrics_test.cpp | 11 +++++---- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/avatar.cpp b/src/avatar.cpp index 4fe74ab391b7c..ad261c4b715e6 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -1759,19 +1759,50 @@ std::string avatar::total_daily_calories_string() const const std::string format_string = " %4d %4d %4d %4d %4d %4d %4d %6d %6d"; + const float light_ex_thresh = ( NO_EXERCISE + LIGHT_EXERCISE ) / 2.0f; + const float mod_ex_thresh = ( LIGHT_EXERCISE + MODERATE_EXERCISE ) / 2.0f; + const float brisk_ex_thresh = ( MODERATE_EXERCISE + BRISK_EXERCISE ) / 2.0f; + const float active_ex_thresh = ( BRISK_EXERCISE + ACTIVE_EXERCISE ) / 2.0f; + const float extra_ex_thresh = ( ACTIVE_EXERCISE + EXTRA_EXERCISE ) / 2.0f; + std::string ret = header_string; // Start with today in the first row, day number from start of cataclysm int today = day_of_season( calendar::turn ) + 1; int day_offset = 0; for( const daily_calories &day : calorie_diary ) { + // Yes, this is clunky. + // Perhaps it should be done in log_activity_level? But that's called a lot more often. + int no_exercise = 0; + int light_exercise = 0; + int moderate_exercise = 0; + int brisk_exercise = 0; + int active_exercise = 0; + int extra_exercise = 0; + for( const std::pair &level : day.activity_levels ) { + if( level.second > 0 ) { + if( level.first < light_ex_thresh ) { + no_exercise += level.second; + } else if( level.first < mod_ex_thresh ) { + light_exercise += level.second; + } else if( level.first < brisk_ex_thresh ) { + moderate_exercise += level.second; + } else if( level.first < active_ex_thresh ) { + brisk_exercise += level.second; + } else if( level.first < extra_ex_thresh ) { + active_exercise += level.second; + } else { + extra_exercise += level.second; + } + } + } std::string row_data = string_format( format_string, today + day_offset--, - 5 * day.activity_levels.at( NO_EXERCISE ), - 5 * day.activity_levels.at( LIGHT_EXERCISE ), - 5 * day.activity_levels.at( MODERATE_EXERCISE ), - 5 * day.activity_levels.at( BRISK_EXERCISE ), - 5 * day.activity_levels.at( ACTIVE_EXERCISE ), - 5 * day.activity_levels.at( EXTRA_EXERCISE ), + 5 * no_exercise, + 5 * light_exercise, + 5 * moderate_exercise, + 5 * brisk_exercise, + 5 * active_exercise, + 5 * extra_exercise, day.gained, day.spent ); // Alternate gray and white text for row data if( day_offset % 2 == 0 ) { diff --git a/tests/char_biometrics_test.cpp b/tests/char_biometrics_test.cpp index 5e9a395d91658..c68dd22bf88ce 100644 --- a/tests/char_biometrics_test.cpp +++ b/tests/char_biometrics_test.cpp @@ -453,21 +453,22 @@ TEST_CASE( "activity levels and calories in daily diary", "[avatar][biometrics][ SECTION( "shows time at each activity level for the current day" ) { dummy.reset_activity_level(); - test_activity_duration( dummy, NO_EXERCISE, 1_hours ); + test_activity_duration( dummy, NO_EXERCISE, 59_minutes ); test_activity_duration( dummy, LIGHT_EXERCISE, 45_minutes ); test_activity_duration( dummy, MODERATE_EXERCISE, 30_minutes ); test_activity_duration( dummy, BRISK_EXERCISE, 20_minutes ); test_activity_duration( dummy, ACTIVE_EXERCISE, 15_minutes ); test_activity_duration( dummy, EXTRA_EXERCISE, 10_minutes ); + test_activity_duration( dummy, NO_EXERCISE, 1_minutes ); - int expect_gained_kcal = 1283; - int expect_net_kcal = 551; - int expect_spent_kcal = 732; + int expect_gained_kcal = 1282; + int expect_net_kcal = 552; + int expect_spent_kcal = 730; CHECK( condensed_spaces( dummy.total_daily_calories_string() ) == string_format( " Minutes at each exercise level Calories per day\n" " Day None Light Moderate Brisk Active Extra Gained Spent Total\n" - " 61 60 45 30 20 15 10 %d %d %d\n", + " 61 60 45 30 20 20 5 %d %d %d\n", expect_gained_kcal, expect_spent_kcal, expect_net_kcal ) ); } }