From 0b8e13619fb4ed4c06710dbaec57c39eb5ac10cd Mon Sep 17 00:00:00 2001 From: David Ross Date: Fri, 23 Aug 2019 10:47:32 -0700 Subject: [PATCH 1/2] Fix a missing link in RoomName internal docs --- src/local/room_name.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/local/room_name.rs b/src/local/room_name.rs index daf93ae7..114abcbd 100644 --- a/src/local/room_name.rs +++ b/src/local/room_name.rs @@ -23,6 +23,8 @@ pub struct RoomName { /// /// This is the same representation of the upper 16 bits of [`Position`]'s /// packed representation. + /// + /// [`Position`]: crate::local::Position packed: u16, } From 9451ad12937bcbb16eec6cad64602d7d20b60dcf Mon Sep 17 00:00:00 2001 From: David Ross Date: Fri, 23 Aug 2019 10:47:51 -0700 Subject: [PATCH 2/2] Implement PartialOrd, Ord for local::* types --- CHANGELOG.md | 2 ++ src/local/object_id.rs | 39 +++++++++++++++++++++++++++++++++++--- src/local/object_id/raw.rs | 15 ++++++++++++++- src/local/room_name.rs | 32 +++++++++++++++++++++++++++++++ src/local/room_position.rs | 39 +++++++++++++++++++++++++++++++++++++- 5 files changed, 122 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af3c6354..cb386937 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ Unreleased - Fix typos in JavaScript code for `game::market::get_order` and `Nuke::launch_room_name` - Add support for accessing intershard resource amounts, which currently only includes subscription tokens, under `game::resources`. +- Implement `PartialOrd` and `Ord` for `Position`, `RoomName`, `RawObjectId` and `ObjectId`. See + documentation for ordering specifications. - Remove remaining usages of internal `get_from_js!` macro, as it was minimally useful - Improve syntax and consistency of some internal macros diff --git a/src/local/object_id.rs b/src/local/object_id.rs index 4604219a..f6880257 100644 --- a/src/local/object_id.rs +++ b/src/local/object_id.rs @@ -1,5 +1,5 @@ use std::{ - cmp::{Eq, PartialEq}, + cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}, fmt, hash::{Hash, Hasher}, marker::PhantomData, @@ -35,11 +35,32 @@ pub use raw::*; /// With that said, using this can provide nice type inference, and should have /// few disadvantages to the lower-level alternative, [`RawObjectId`]. /// -/// --- +/// # Conversion /// /// Use `into` to convert between `ObjectId` and [`RawObjectId`], and /// [`ObjectId::into_type`] to change the type this `ObjectId` points to freely. -// Copy, Clone, Debug, PartialEq, Eq, Hash implemented manually below +/// +/// # Ordering +/// +/// To facilitate use as a key in a [`BTreeMap`] or other similar data +/// structures, `ObjectId` implements [`PartialOrd`] and [`Ord`]. +/// +/// `ObjectId`'s are ordered by the corresponding order of their underlying +/// byte values. This agrees with: +/// +/// - lexicographical ordering of the object id strings +/// - JavaScript's ordering of object id strings +/// - ordering of [`RawObjectId`]s +/// +/// **Note:** when running on the official screeps server, or on a private +/// server backed by a MongoDB database, this ordering roughly corresponds to +/// creation order. The first four bytes of a MongoDB-created `ObjectId` [are +/// seconds since the epoch when the id was created][1], so up to a second +/// accuracy, these ids will be sorted by object creation time. +/// +/// [`BTreeMap`]: std::collections::BTreeMap +/// [1]: https://docs.mongodb.com/manual/reference/method/ObjectId/ +// Copy, Clone, Debug, PartialEq, Eq, Hash, PartialEq, Eq implemented manually below #[derive(Serialize, Deserialize)] #[serde(transparent, bound = "")] pub struct ObjectId { @@ -74,6 +95,18 @@ impl Hash for ObjectId { self.raw.hash(state) } } +impl PartialOrd> for ObjectId { + #[inline] + fn partial_cmp(&self, other: &ObjectId) -> Option { + Some(self.cmp(other)) + } +} +impl Ord for ObjectId { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.raw.cmp(&other.raw) + } +} impl FromStr for ObjectId { type Err = RawObjectIdParseError; diff --git a/src/local/object_id/raw.rs b/src/local/object_id/raw.rs index 57beb121..2f056593 100644 --- a/src/local/object_id/raw.rs +++ b/src/local/object_id/raw.rs @@ -25,7 +25,20 @@ const MAX_PACKED_VAL: u128 = (1 << (32 * 3)) - 1; /// To convert to a String in JavaScript, either use /// [`RawObjectId::to_array_string`], or [`RawObjectId::unsafe_as_uploaded`]. /// See method documentation for more information. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +/// +/// # Ordering +/// +/// To facilitate use as a key in a [`BTreeMap`] or other similar data +/// structures, `ObjectId` implements [`PartialOrd`] and [`Ord`]. +/// +/// `RawObjectId`'s are ordered by the corresponding order of their underlying +/// byte values. See [`ObjectId`] documentation for more information. +/// +/// [`BTreeMap`]: std::collections::BTreeMap +/// [`Ord`]: std::cmp::Ord +/// [`PartialOrd`]: std::cmp::PartialOrd +/// [`ObjectId`]: super::ObjectId +#[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)] #[serde(transparent)] pub struct RawObjectId { packed: [u32; 3], diff --git a/src/local/room_name.rs b/src/local/room_name.rs index 114abcbd..564a8976 100644 --- a/src/local/room_name.rs +++ b/src/local/room_name.rs @@ -1,4 +1,5 @@ use std::{ + cmp::{Ord, Ordering, PartialOrd}, error, fmt::{self, Write}, ops, @@ -10,6 +11,22 @@ use arrayvec::ArrayString; use super::{HALF_WORLD_SIZE, VALID_ROOM_NAME_COORDINATES}; /// A structure representing a room name. +/// +/// # Ordering +/// +/// To facilitate use as a key in a [`BTreeMap`] or other similar data +/// structures, `RoomName` implements [`PartialOrd`] and [`Ord`]. +/// +/// `RoomName`s are ordered first by y position, then by x position. North is +/// considered less than south, and west less than east. +/// +/// The total ordering is `N127W127`, `N127W126`, `N127W125`, ..., `N127W0`, +/// `N127E0`, ..., `N127E127`, `N126W127`, ..., `S127E126`, `S127E127`. +/// +/// This follows left-to-right reading order when looking at the Screeps map +/// from above. +/// +/// [`BTreeMap`]: std::collections::BTreeMap #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct RoomName { /// A bit-packed integer, containing, from highest-order to lowest: @@ -363,6 +380,21 @@ impl PartialEq for &String { } } +impl PartialOrd for RoomName { + #[inline] + fn partial_cmp(&self, other: &RoomName) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for RoomName { + fn cmp(&self, other: &Self) -> Ordering { + self.y_coord() + .cmp(&other.y_coord()) + .then_with(|| self.x_coord().cmp(&other.x_coord())) + } +} + mod serde { use std::fmt; diff --git a/src/local/room_position.rs b/src/local/room_position.rs index fa4210c7..b2ab2709 100644 --- a/src/local/room_position.rs +++ b/src/local/room_position.rs @@ -3,7 +3,10 @@ //! This is a reimplementation/translation of the `RoomPosition` code originally //! written in JavaScript. All RoomPosition to RoomPosition operations in this //! file stay within Rust. -use std::fmt; +use std::{ + cmp::{Ord, Ordering, PartialOrd}, + fmt, +}; use super::{RoomName, HALF_WORLD_SIZE}; @@ -75,8 +78,27 @@ mod world_utils; /// }; /// ``` /// +/// # Ordering +/// +/// To facilitate use as a key in a [`BTreeMap`] or other similar data +/// structures, `Position` implements [`PartialOrd`] and [`Ord`]. +/// +/// `Position`s are ordered first by ascending world `y` position, then by +/// ascending world `x` position. World `x` and `y` here simply extend the x,y +/// coords within the room `E0S0` throughout the map. +/// +/// Looking at positions as tuples `(world_x, world_y)`, the sorting obeys rules +/// such as: +/// +/// - `(a, 0) < (b, 1)` for any `a`, `b` +/// - `(0, c) < (1, c)` for any `c` +/// +/// This follows left-to-right reading order when looking at the Screeps map +/// from above. +/// /// [`bincode`]: https://github.com/servo/bincode /// [`HasPosition::pos`]: crate::HasPosition::pos +/// [`BTreeMap`]: std::collections::BTreeMap #[derive(Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] pub struct Position { @@ -236,6 +258,21 @@ impl Position { } } +impl PartialOrd for Position { + #[inline] + fn partial_cmp(&self, other: &Position) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Position { + fn cmp(&self, other: &Self) -> Ordering { + self.world_y() + .cmp(&other.world_y()) + .then_with(|| self.world_x().cmp(&other.world_x())) + } +} + mod stdweb { use stdweb::{Reference, Value};