diff --git a/src/calendar.cpp b/src/calendar.cpp index c80399781cc32..aec69d6235200 100644 --- a/src/calendar.cpp +++ b/src/calendar.cpp @@ -75,6 +75,7 @@ moon_phase get_moon_phase( const time_point &p ) } // TODO: Refactor sunrise / sunset +// The only difference between them is the start_hours array time_point sunrise( const time_point &p ) { static_assert( static_cast( SPRING ) == 0, @@ -95,7 +96,6 @@ time_point sunrise( const time_point &p ) return midnight + time_duration::from_minutes( static_cast( time * 60 ) ); } -// TODO: Refactor sunrise / sunset time_point sunset( const time_point &p ) { static_assert( static_cast( SPRING ) == 0, @@ -127,8 +127,6 @@ time_point daylight_time( const time_point &p ) return sunrise( p ) + 15_minutes; } -// TODO: bool is_day( const time_point &p ) ? - bool is_night( const time_point &p ) { const time_duration now = time_past_midnight( p ); @@ -138,6 +136,15 @@ bool is_night( const time_point &p ) return now >= sunset + twilight_duration || now <= sunrise; } +bool is_day( const time_point &p ) +{ + const time_duration now = time_past_midnight( p ); + const time_duration sunrise = time_past_midnight( ::sunrise( p ) ); + const time_duration sunset = time_past_midnight( ::sunset( p ) ); + + return now >= sunrise + twilight_duration && now <= sunset; +} + bool is_dusk( const time_point &p ) { const time_duration now = time_past_midnight( p ); diff --git a/src/calendar.h b/src/calendar.h index 27cc08f43572b..4d6df98ab316c 100644 --- a/src/calendar.h +++ b/src/calendar.h @@ -542,6 +542,8 @@ time_point daylight_time( const time_point &p ); time_point night_time( const time_point &p ); /** Returns true if it's currently night time - after dusk and before dawn. */ bool is_night( const time_point &p ); +/** Returns true if it's currently day time - after dawn and before dusk. */ +bool is_day( const time_point &p ); /** Returns true if it's currently dusk - between sunset and and twilight_duration after sunset. */ bool is_dusk( const time_point &p ); /** Returns true if it's currently dawn - between sunrise and twilight_duration after sunrise. */ diff --git a/tests/sun_test.cpp b/tests/sun_test.cpp index 564f708160152..bb2a024e1780a 100644 --- a/tests/sun_test.cpp +++ b/tests/sun_test.cpp @@ -4,30 +4,51 @@ // SUN TESTS -// is_night, is_dawn, is_dusk +// The 24-hour solar cycle is divided into four parts, as returned by four calendar.cpp functions: +// +// is_night : from the end of dusk, until the following sunrise (start of dawn) +// is_dawn : begins at sunrise, lasts twilight_duration (1 hour) +// is_day : from the end of dawn, until sunset (start of dusk) +// is_dusk : begins at sunset, lasts twilight_duration (1 hour) +// +// These are inclusive at their endpoints; in other words, each overlaps with the next like so: +// +// 00:00 is_night +// : is_night +// 06:00 is_night && is_dawn ( sunrise ) +// : is_dawn ( sunrise + twilight ) +// 07:00 is_dawn && is_day +// : is_day +// 19:00 is_day && is_dusk ( sunset ) +// : is_dusk ( sunset + twilight ) +// 20:00 is_dusk && is_night +// : is_night +// 23:59 is_night +// +// The times of sunrise and sunset will naturally depend on the current time of year; this aspect is +// covered by the "sunrise and sunset" and solstice/equinox tests later in this file. Here we simply +// use the first day of spring as a baseline. +// +// This test covers is_night, is_dawn, is_day, is_dusk, and their behavior in relation to time of day. TEST_CASE( "daily solar cycle", "[sun][night][dawn][day][dusk]" ) { // Use sunrise/sunset on the first day (spring equinox) static const time_point midnight = calendar::turn_zero; + static const time_point noon = calendar::turn_zero + 12_hours; static const time_point today_sunrise = sunrise( midnight ); static const time_point today_sunset = sunset( midnight ); REQUIRE( "Year 1, Spring, day 1 6:00:00 AM" == to_string( today_sunrise ) ); REQUIRE( "Year 1, Spring, day 1 7:00:00 PM" == to_string( today_sunset ) ); - // 00:00 is_night - // : is_night - // 06:00 is_night && is_dawn (sunrise time) - // : is_dawn (sunrise + twilight ) - // 07:00 is_dawn && is_day - // : is_day - // 19:00 is_day && is_dusk (sunset time) - // : is_dusk (sunset + twilight ) - // 20:00 is_dusk && is_night - // : is_night - // 23:59 is_night - + // First, at the risk of stating the obvious CHECK( is_night( midnight ) ); + // And while we're at it + CHECK_FALSE( is_day( midnight ) ); + CHECK_FALSE( is_dawn( midnight ) ); + CHECK_FALSE( is_dusk( midnight ) ); + + // Yep, still dark CHECK( is_night( midnight + 1_seconds ) ); CHECK( is_night( midnight + 2_hours ) ); CHECK( is_night( midnight + 3_hours ) ); @@ -38,24 +59,56 @@ TEST_CASE( "daily solar cycle", "[sun][night][dawn][day][dusk]" ) CHECK( is_dawn( today_sunrise ) ); // Dawn + CHECK_FALSE( is_night( today_sunrise + 1_seconds ) ); CHECK( is_dawn( today_sunrise + 1_seconds ) ); CHECK( is_dawn( today_sunrise + 30_minutes ) ); - CHECK( is_dawn( today_sunrise + 59_minutes ) ); + CHECK( is_dawn( today_sunrise + 1_hours - 1_seconds ) ); + CHECK_FALSE( is_day( today_sunrise + 1_hours - 1_seconds ) ); // The endpoint of dawn is both "dawn" and "day" CHECK( is_dawn( today_sunrise + 1_hours ) ); - //CHECK( is_day( today_sunrise + 1_hours ) ); - - // FIXME: No function to tell when it is daytime + CHECK( is_day( today_sunrise + 1_hours ) ); + + // Breakfast + CHECK_FALSE( is_dawn( today_sunrise + 1_hours + 1_seconds ) ); + CHECK( is_day( today_sunrise + 1_hours + 1_seconds ) ); + CHECK( is_day( today_sunrise + 2_hours ) ); + // Second breakfast + CHECK( is_day( today_sunrise + 3_hours ) ); + CHECK( is_day( today_sunrise + 4_hours ) ); + // Luncheon + CHECK( is_day( noon - 3_hours ) ); + CHECK( is_day( noon - 2_hours ) ); + // Elevenses + CHECK( is_day( noon - 1_hours ) ); + + // Noon + CHECK( is_day( noon ) ); + CHECK_FALSE( is_dawn( noon ) ); + CHECK_FALSE( is_dusk( noon ) ); + CHECK_FALSE( is_night( noon ) ); + + // Afternoon tea + CHECK( is_day( noon + 1_hours ) ); + CHECK( is_day( noon + 2_hours ) ); + // Dinner + CHECK( is_day( noon + 3_hours ) ); + CHECK( is_day( today_sunset - 2_hours ) ); + // Supper + CHECK( is_day( today_sunset - 1_hours ) ); + CHECK( is_day( today_sunset - 1_seconds ) ); + CHECK_FALSE( is_dusk( today_sunset - 1_seconds ) ); // The beginning of sunset is both "day" and "dusk" - //CHECK( is_day( today_sunset ) ); + CHECK( is_day( today_sunset ) ); CHECK( is_dusk( today_sunset ) ); // Dusk + CHECK_FALSE( is_day( today_sunset + 1_seconds ) ); CHECK( is_dusk( today_sunset + 1_seconds ) ); CHECK( is_dusk( today_sunset + 30_minutes ) ); - CHECK( is_dusk( today_sunset + 59_minutes ) ); + CHECK( is_dusk( today_sunset + 1_hours - 1_seconds ) ); + CHECK_FALSE( is_night( today_sunset + 1_hours - 1_seconds ) ); // The point when dusk ends is both "dusk" and "night" CHECK( is_dusk( today_sunset + 1_hours ) ); @@ -68,22 +121,6 @@ TEST_CASE( "daily solar cycle", "[sun][night][dawn][day][dusk]" ) CHECK( is_night( today_sunset + 4_hours ) ); } -// current_daylight_level returns seasonally-adjusted maximum daylight level -TEST_CASE( "current daylight level", "[sun][daylight]" ) -{ - static const time_duration one_season = calendar::season_length(); - static const time_point spring = calendar::turn_zero; - static const time_point summer = spring + one_season; - static const time_point autumn = summer + one_season; - static const time_point winter = autumn + one_season; - - // For ~Boston: solstices are +/- 25% sunlight intensity from equinoxes - CHECK( 100.0f == current_daylight_level( spring ) ); - CHECK( 125.0f == current_daylight_level( summer ) ); - CHECK( 100.0f == current_daylight_level( autumn ) ); - CHECK( 75.0f == current_daylight_level( winter ) ); -} - // The calendar `sunlight` function returns light level for both sun and moon. TEST_CASE( "sunlight and moonlight", "[sun][sunlight][moonlight]" ) { @@ -92,43 +129,43 @@ TEST_CASE( "sunlight and moonlight", "[sun][sunlight][moonlight]" ) static const time_point today_sunrise = sunrise( midnight ); static const time_point today_sunset = sunset( midnight ); - // Tests assume 100.0f maximum - REQUIRE( default_daylight_level() == 100.0f ); + // Expected numbers below assume 100.0f maximum daylight level + // (maximum daylight is different at other times of year - see [daylight] tests) + REQUIRE( 100.0f == default_daylight_level() ); SECTION( "sunlight" ) { // Before dawn - CHECK( sunlight( midnight ) == 1.0f ); - CHECK( sunlight( today_sunrise ) == 1.0f ); + CHECK( 1.0f == sunlight( midnight ) ); + CHECK( 1.0f == sunlight( today_sunrise ) ); // Dawn - CHECK( sunlight( today_sunrise + 1_seconds ) == Approx( 1.0275f ) ); - CHECK( sunlight( today_sunrise + 1_minutes ) == Approx( 2.65f ) ); - CHECK( sunlight( today_sunrise + 15_minutes ) == 25.75f ); - CHECK( sunlight( today_sunrise + 15_minutes ) == 25.75f ); - CHECK( sunlight( today_sunrise + 30_minutes ) == 50.50f ); - CHECK( sunlight( today_sunrise + 45_minutes ) == 75.25f ); + CHECK( 1.0275f == sunlight( today_sunrise + 1_seconds ) ); + CHECK( 2.65f == sunlight( today_sunrise + 1_minutes ) ); + CHECK( 25.75f == sunlight( today_sunrise + 15_minutes ) ); + CHECK( 50.50f == sunlight( today_sunrise + 30_minutes ) ); + CHECK( 75.25f == sunlight( today_sunrise + 45_minutes ) ); // 1 second before full daylight - CHECK( sunlight( today_sunrise + 1_hours - 1_seconds ) == Approx( 99.9725f ) ); - CHECK( sunlight( today_sunrise + 1_hours ) == 100.0f ); + CHECK( 99.9725f == sunlight( today_sunrise + 1_hours - 1_seconds ) ); + CHECK( 100.0f == sunlight( today_sunrise + 1_hours ) ); // End of dawn, full light all day - CHECK( sunlight( today_sunrise + 2_hours ) == 100.0f ); - CHECK( sunlight( today_sunrise + 3_hours ) == 100.0f ); + CHECK( 100.0f == sunlight( today_sunrise + 2_hours ) ); + CHECK( 100.0f == sunlight( today_sunrise + 3_hours ) ); // Noon - CHECK( sunlight( midnight + 12_hours ) == 100.0f ); - CHECK( sunlight( midnight + 13_hours ) == 100.0f ); - CHECK( sunlight( midnight + 14_hours ) == 100.0f ); + CHECK( 100.0f == sunlight( midnight + 12_hours ) ); + CHECK( 100.0f == sunlight( midnight + 13_hours ) ); + CHECK( 100.0f == sunlight( midnight + 14_hours ) ); // Dusk begins - CHECK( sunlight( today_sunset ) == 100.0f ); + CHECK( 100.0f == sunlight( today_sunset ) ); // 1 second after dusk begins - CHECK( sunlight( today_sunset + 1_seconds ) == Approx( 99.9725f ) ); - CHECK( sunlight( today_sunset + 15_minutes ) == 75.25f ); - CHECK( sunlight( today_sunset + 30_minutes ) == 50.50f ); - CHECK( sunlight( today_sunset + 45_minutes ) == 25.75f ); + CHECK( 99.9725f == sunlight( today_sunset + 1_seconds ) ); + CHECK( 75.25f == sunlight( today_sunset + 15_minutes ) ); + CHECK( 50.50f == sunlight( today_sunset + 30_minutes ) ); + CHECK( 25.75f == sunlight( today_sunset + 45_minutes ) ); // 1 second before full night - CHECK( sunlight( today_sunset + 1_hours - 1_seconds ) == Approx( 1.0275f ) ); - CHECK( sunlight( today_sunset + 1_hours ) == 1.0f ); + CHECK( 1.0275f == sunlight( today_sunset + 1_hours - 1_seconds ) ); + CHECK( 1.0f == sunlight( today_sunset + 1_hours ) ); // After dusk - CHECK( sunlight( today_sunset + 2_hours ) == 1.0f ); - CHECK( sunlight( today_sunset + 3_hours ) == 1.0f ); + CHECK( 1.0f == sunlight( today_sunset + 2_hours ) ); + CHECK( 1.0f == sunlight( today_sunset + 3_hours ) ); } // This moonlight test is intentionally simple, only checking new moon (minimal light) and full @@ -142,19 +179,50 @@ TEST_CASE( "sunlight and moonlight", "[sun][sunlight][moonlight]" ) WHEN( "the moon is new" ) { REQUIRE( get_moon_phase( new_moon ) == MOON_NEW ); THEN( "moonlight is 1.0" ) { - CHECK( sunlight( new_moon ) == 1.0f ); + CHECK( 1.0f == sunlight( new_moon ) ); } } WHEN( "the moon is full" ) { REQUIRE( get_moon_phase( full_moon ) == MOON_FULL ); THEN( "moonlight is 10.0" ) { - CHECK( sunlight( full_moon ) == 10.0f ); + CHECK( 10.0f == sunlight( full_moon ) ); } } } } +// current_daylight_level returns seasonally-adjusted maximum daylight level +TEST_CASE( "current daylight level", "[sun][daylight][equinox][solstice]" ) +{ + static const time_duration one_season = calendar::season_length(); + static const time_point spring = calendar::turn_zero; + static const time_point summer = spring + one_season; + static const time_point autumn = summer + one_season; + static const time_point winter = autumn + one_season; + + SECTION( "baseline 100 daylight on the spring and autumn equinoxes" ) { + CHECK( 100.0f == current_daylight_level( spring ) ); + CHECK( 100.0f == current_daylight_level( autumn ) ); + } + + SECTION( "25 percent more daylight on the summer solstice" ) { + CHECK( 125.0f == current_daylight_level( summer ) ); + } + + SECTION( "25 percent less daylight on the winter solstice" ) { + CHECK( 75.0f == current_daylight_level( winter ) ); + } + + // Many other times of day have peak daylight level, but noon is for sure + SECTION( "noon is peak daylight level" ) { + CHECK( 100.0f == sunlight( spring + 12_hours ) ); + CHECK( 125.0f == sunlight( summer + 12_hours ) ); + CHECK( 100.0f == sunlight( autumn + 12_hours ) ); + CHECK( 75.0f == sunlight( winter + 12_hours ) ); + } +} + // The times of sunrise and sunset vary throughout the year. For simplicity, equinoxes occur on the // first day of spring and autumn, and solstices occur on the first day of summer and winter. TEST_CASE( "sunrise and sunset", "[sun][sunrise][sunset][equinox][solstice]" )