Skip to content

Commit

Permalink
Merge pull request #238 from rustyscreeps/ord-for-local-types
Browse files Browse the repository at this point in the history
Implement Ord for Position, RoomName, ObjectId and RawObjectId
  • Loading branch information
daboross authored Aug 23, 2019
2 parents f36fad4 + 9451ad1 commit 56ec5ec
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 5 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
39 changes: 36 additions & 3 deletions src/local/object_id.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::{
cmp::{Eq, PartialEq},
cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd},
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
Expand Down Expand Up @@ -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<T>` 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<T> {
Expand Down Expand Up @@ -74,6 +95,18 @@ impl<T> Hash for ObjectId<T> {
self.raw.hash(state)
}
}
impl<T> PartialOrd<ObjectId<T>> for ObjectId<T> {
#[inline]
fn partial_cmp(&self, other: &ObjectId<T>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T> Ord for ObjectId<T> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.raw.cmp(&other.raw)
}
}

impl<T> FromStr for ObjectId<T> {
type Err = RawObjectIdParseError;
Expand Down
15 changes: 14 additions & 1 deletion src/local/object_id/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
34 changes: 34 additions & 0 deletions src/local/room_name.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::{
cmp::{Ord, Ordering, PartialOrd},
error,
fmt::{self, Write},
ops,
Expand All @@ -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:
Expand All @@ -23,6 +40,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,
}

Expand Down Expand Up @@ -361,6 +380,21 @@ impl PartialEq<RoomName> for &String {
}
}

impl PartialOrd for RoomName {
#[inline]
fn partial_cmp(&self, other: &RoomName) -> Option<Ordering> {
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;

Expand Down
39 changes: 38 additions & 1 deletion src/local/room_position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -236,6 +258,21 @@ impl Position {
}
}

impl PartialOrd for Position {
#[inline]
fn partial_cmp(&self, other: &Position) -> Option<Ordering> {
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};

Expand Down

0 comments on commit 56ec5ec

Please sign in to comment.