//! Exhaustive v5 action timing policy. Admission and completion are separate: //! this table prices time, and never applies effects or advances a State. use crate::action::CommittedAction; use crate::services::{sleep_ticks, Service}; use std::num::NonZeroU64; pub const WORLD_TICK_MS: u64 = 100; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ActionTiming { Observation, World(NonZeroU64), Unattended(NonZeroU64), } impl ActionTiming { pub fn ticks(self) -> u64 { match self { Self::Observation => 0, Self::World(ticks) | Self::Unattended(ticks) => ticks.get(), } } } /// No wildcard/default arm: new committed operations must choose their timing. /// InspectBoard will move out of the committed vocabulary during v5 integration. pub fn action_timing(action: &CommittedAction) -> ActionTiming { let world = |ticks| ActionTiming::World(NonZeroU64::new(ticks).expect("positive action duration")); match action { CommittedAction::Move(_) | CommittedAction::Wait => world(1), CommittedAction::InspectBoard { .. } => ActionTiming::Observation, CommittedAction::AcceptContract { .. } | CommittedAction::UseTransition { .. } | CommittedAction::ResolveAttack { .. } | CommittedAction::Pickup { .. } | CommittedAction::Drop { .. } | CommittedAction::Wield { .. } | CommittedAction::TurnIn { .. } => world(10), CommittedAction::TinkerIdentify { .. } => world(20), CommittedAction::UseService { service } => match service { Service::Inn => ActionTiming::Unattended( NonZeroU64::new(sleep_ticks()).expect("positive authored inn duration"), ), Service::Tinker | Service::Lampwright | Service::Warden | Service::Mender | Service::Fence | Service::Auction => world(10), }, } } #[cfg(test)] mod tests { use super::*; use crate::action::Direction; #[test] fn all_actions_duration_table() { let cases = [ (CommittedAction::Move(Direction::North), 1), (CommittedAction::Wait, 1), ( CommittedAction::InspectBoard { town: "town".into(), }, 0, ), ( CommittedAction::AcceptContract { id: "contract".into(), }, 10, ), (CommittedAction::UseTransition { transition_id: 7 }, 10), (CommittedAction::ResolveAttack { target_actor: 9 }, 10), (CommittedAction::Pickup { item_id: 11 }, 10), (CommittedAction::Drop { item_id: 11 }, 10), (CommittedAction::Wield { item_id: 11 }, 10), (CommittedAction::TinkerIdentify { item_id: 11 }, 20), ( CommittedAction::TurnIn { contract_id: "contract".into(), }, 10, ), ( CommittedAction::UseService { service: Service::Inn, }, 8640, ), ]; for (action, expected) in cases { assert_eq!(action_timing(&action).ticks(), expected, "{action:?}"); } for service in [ Service::Tinker, Service::Lampwright, Service::Warden, Service::Mender, Service::Fence, Service::Auction, ] { assert_eq!( action_timing(&CommittedAction::UseService { service }).ticks(), 10 ); } assert!(matches!( action_timing(&CommittedAction::UseService { service: Service::Inn }), ActionTiming::Unattended(_) )); } }