From ea925b10fe312f13e6d655115193e70561f46301 Mon Sep 17 00:00:00 2001 From: Shane Madden Date: Tue, 27 Aug 2024 14:41:07 -0600 Subject: [PATCH] Add constants and functions for season 1 score cycle mechanic (#535) --- CHANGELOG.md | 1 + src/constants/extra.rs | 9 ++- src/constants/seasonal.rs | 108 ++++++++++++++++++++++++++++++++ src/game/cpu.rs | 19 +++--- src/game/market.rs | 7 +-- src/local/room_xy/extra_math.rs | 2 + 6 files changed, 131 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4afec1e..3bf834df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Unreleased ### Additions: - Make `RoomName::from_packed` and `RoomName::packed_repr` public +- Add constants to `season_1` module representing the Season 1 score cycle mechanic 0.21.3 (2024-08-14) =================== diff --git a/src/constants/extra.rs b/src/constants/extra.rs index a3e1a513..10d9e9b3 100644 --- a/src/constants/extra.rs +++ b/src/constants/extra.rs @@ -89,9 +89,12 @@ pub const CREEP_SAY_MAX_LENGTH: u32 = 10; /// [`Flag`]: crate::objects::Flag pub const FLAG_NAME_MAX_LENGTH: u32 = 100; -/// The cost of a single 'intent' (in milliseconds), a CPU penalty charged for -/// most successful API calls which change the game state ([`Creep::pull`], -/// [`Creep::say`], and [`PowerCreep::say`] are excepted) +/// The cost of a single 'intent' in CPU time, charged for all successful +/// changes to game state. +/// +/// API calls which change the game state ([`Creep::pull`], [`Creep::say`], and +/// [`PowerCreep::say`] are excepted) will add this amount to your used CPU for +/// the tick each time they're successful. /// /// [Code reference](https://github.com/screeps/driver/blob/97a9e51d124c7170429caa1621096f0f4d888d72/lib/runtime/runtime.js#L52) /// diff --git a/src/constants/seasonal.rs b/src/constants/seasonal.rs index 9ebef685..2e5c1962 100644 --- a/src/constants/seasonal.rs +++ b/src/constants/seasonal.rs @@ -39,6 +39,114 @@ pub mod season_1 { /// /// [`ScoreCollector`]: crate::objects::ScoreCollector pub const SCORE_COLLECTOR_MAX_CAPACITY: u32 = 20_000; + + // extra constants for s1 related to the score spawn bonus/crisis cycle + // https://github.com/screeps/mod-season1/blob/7ca3c7ddb47bf9dfbdfb4e72b666a3159fde8780/src/scoreContainer.roomObject.js + /// The duration of a full score cycle. + pub const SCORE_CYCLE_DURATION: u32 = 50_000; + + /// The point of the score cycle where bonus time begins, multiplying + /// spawned score by [`SCORE_CYCLE_BONUS_MULTIPLIER`]. + pub const SCORE_CYCLE_BONUS_START: u32 = 45_000; + + /// The point of the score cycle where bonus time ends. + pub const SCORE_CYCLE_BONUS_END: u32 = 50_000; + + /// The multiplier for score spawned during bonus time. + pub const SCORE_CYCLE_BONUS_MULTIPLIER: u8 = 2; + + /// The point of the score cycle where crisis time begins, during which no + /// new [`ScoreContainer`]s will be spawned. + /// + /// [`ScoreContainer`]: crate::objects::ScoreContainer + pub const SCORE_CYCLE_CRISIS_START: u32 = 10_000; + + /// The point of the score cycle where crisis time ends, allowing + /// [`ScoreContainer`]s to be spawned once again. + /// + /// [`ScoreContainer`]: crate::objects::ScoreContainer + pub const SCORE_CYCLE_CRISIS_END: u32 = 15_000; + + /// The multiplier for score spawned during crisis time. + pub const SCORE_CYCLE_CRISIS_MULTIPLIER: u8 = 0; + + /// The minimum amount of ticks a [`ScoreContainer`] can exist before + /// despawning. + /// + /// [`ScoreContainer`]: crate::objects::ScoreContainer + // https://github.com/screeps/mod-season1/blob/7ca3c7ddb47bf9dfbdfb4e72b666a3159fde8780/src/scoreContainer.roomObject.js#L93 + pub const SCORE_MIN_DECAY: u16 = 500; + + /// The maximum amount of ticks a [`ScoreContainer`] can exist before + /// despawning. + /// + /// [`ScoreContainer`]: crate::objects::ScoreContainer + pub const SCORE_MAX_DECAY: u16 = 5_000; + + /// The different periods in the score cycle. + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub enum ScoreCycleState { + /// Normal score spawns during the normal parts of the cycle + Normal, + /// No score spawns during the crisis period + Crisis, + /// Double score in each container during the bonus period + Bonus, + } + + impl ScoreCycleState { + /// Gets the multiplier of score associated with the represented part of + /// the score cycle. + pub fn multiplier(&self) -> u8 { + match self { + ScoreCycleState::Normal => 1, + ScoreCycleState::Crisis => SCORE_CYCLE_CRISIS_MULTIPLIER, + ScoreCycleState::Bonus => SCORE_CYCLE_BONUS_MULTIPLIER, + } + } + } + + pub const fn score_cycle_at_tick(tick: u32) -> ScoreCycleState { + match tick % SCORE_CYCLE_DURATION { + // the bonus/crisis periods are exclusive of their boundaries + // https://github.com/screeps/mod-season1/blob/7ca3c7ddb47bf9dfbdfb4e72b666a3159fde8780/src/scoreContainer.roomObject.js#L77-L81 + // match on those exact values first + SCORE_CYCLE_CRISIS_START => ScoreCycleState::Normal, + SCORE_CYCLE_BONUS_START => ScoreCycleState::Normal, + // then on the remaining ranges + SCORE_CYCLE_CRISIS_START..SCORE_CYCLE_CRISIS_END => ScoreCycleState::Crisis, + SCORE_CYCLE_BONUS_START..SCORE_CYCLE_BONUS_END => ScoreCycleState::Bonus, + _ => ScoreCycleState::Normal, + } + } + + #[cfg(test)] + mod test { + use super::{score_cycle_at_tick, ScoreCycleState}; + + #[test] + fn score_cycle() { + assert_eq!(score_cycle_at_tick(0), ScoreCycleState::Normal); + assert_eq!(score_cycle_at_tick(10_000), ScoreCycleState::Normal); + assert_eq!(score_cycle_at_tick(10_001), ScoreCycleState::Crisis); + assert_eq!(score_cycle_at_tick(14_999), ScoreCycleState::Crisis); + assert_eq!(score_cycle_at_tick(15_000), ScoreCycleState::Normal); + assert_eq!(score_cycle_at_tick(45_000), ScoreCycleState::Normal); + assert_eq!(score_cycle_at_tick(45_001), ScoreCycleState::Bonus); + assert_eq!(score_cycle_at_tick(49_999), ScoreCycleState::Bonus); + assert_eq!(score_cycle_at_tick(50_000), ScoreCycleState::Normal); + + assert_eq!(score_cycle_at_tick(200_000), ScoreCycleState::Normal); + assert_eq!(score_cycle_at_tick(210_000), ScoreCycleState::Normal); + assert_eq!(score_cycle_at_tick(210_001), ScoreCycleState::Crisis); + assert_eq!(score_cycle_at_tick(214_999), ScoreCycleState::Crisis); + assert_eq!(score_cycle_at_tick(215_000), ScoreCycleState::Normal); + assert_eq!(score_cycle_at_tick(245_000), ScoreCycleState::Normal); + assert_eq!(score_cycle_at_tick(245_001), ScoreCycleState::Bonus); + assert_eq!(score_cycle_at_tick(249_999), ScoreCycleState::Bonus); + assert_eq!(score_cycle_at_tick(250_000), ScoreCycleState::Normal); + } + } } /// Constants for Screeps seasonal, season 2 diff --git a/src/game/cpu.rs b/src/game/cpu.rs index 80e6b4c2..5120e354 100644 --- a/src/game/cpu.rs +++ b/src/game/cpu.rs @@ -61,9 +61,10 @@ pub fn limit() -> u32 { Cpu::limit() } -/// The amount of CPU available for execution this tick, which consists of -/// your per-tick CPU [`limit`] plus your accrued [`bucket`], up to a maximum of -/// 500 ([`CPU_TICK_LIMIT_MAX`]); [`f64::INFINITY`] on sim. +/// The amount of CPU available for execution in a given tick. +/// +/// Consists of your per-tick CPU [`limit`] plus your accrued [`bucket`], up to +/// a maximum of 500 ([`CPU_TICK_LIMIT_MAX`]); [`f64::INFINITY`] on sim. /// /// [`CPU_TICK_LIMIT_MAX`]: crate::constants::extra::CPU_TICK_LIMIT_MAX pub fn tick_limit() -> f64 { @@ -123,11 +124,13 @@ pub fn halt() { Cpu::halt() } -/// Sets new shard limits for your script in an [`Object`], with shard names -/// in [`JsString`] form as keys and numbers as values. This is the same -/// format accepted by [`shard_limits`]. Total amount of CPU should -/// remain equal to the sum of the values of [`shard_limits`]. This method -/// can be used only once per 12 hours ([`CPU_SET_SHARD_LIMITS_COOLDOWN`]). +/// Set the allocation of your CPU among the server shards. +/// +/// Limits should be in an [`Object`], with shard names in [`JsString`] form as +/// keys and numbers as values. This is the same format returned by +/// [`shard_limits`]. Total amount of CPU should remain equal to the sum of the +/// values of [`shard_limits`]. This method can be used only once per 12 hours +/// ([`CPU_SET_SHARD_LIMITS_COOLDOWN`]). /// /// [Screeps documentation](https://docs.screeps.com/api/#Game.cpu.setShardLimits) /// diff --git a/src/game/market.rs b/src/game/market.rs index f57d1bef..3f100d9c 100644 --- a/src/game/market.rs +++ b/src/game/market.rs @@ -176,10 +176,9 @@ pub fn get_all_orders(filter: Option<&LodashFilter>) -> Vec { } /// Get information about the price history on the market for the last 14 -/// days for a given resource as an [`Array`] of [`OrderHistoryRecord`]s, or -/// for all resources if `None`. Warning: returns an empty [`Object`] -/// instead of an array if there is no history for the resource, verifying -/// the type is recommended before use if the market might be empty. +/// days. +/// +/// Gets information for a given resource, or for all resources if `None`. /// /// [Screeps documentation](https://docs.screeps.com/api/#Game.market.getHistory) pub fn get_history(resource: Option) -> Vec { diff --git a/src/local/room_xy/extra_math.rs b/src/local/room_xy/extra_math.rs index 49b75b39..4f8f933e 100644 --- a/src/local/room_xy/extra_math.rs +++ b/src/local/room_xy/extra_math.rs @@ -34,6 +34,8 @@ impl RoomXY { /// pos.offset(-5, 5); /// assert_eq!(pos, RoomXY::checked_new(16, 26).unwrap()); /// ``` + /// + /// [`Position::offset`]: crate::local::Position::offset #[inline] #[track_caller] pub fn offset(&mut self, x: i8, y: i8) {