Skip to main content

SavedGame

Struct SavedGame 

Source
pub struct SavedGame {
    pub title: String,
    pub solved_by: String,
    pub notes: String,
    pub current_move_idx: usize,
    /* private fields */
}
Expand description

Sequence of pusher steps with accompanying metadata (ie. title, author, etc..).

Besides movement direction, SavedGame also represents:

  • forward or reverse SolvingMode
  • pusher jumps (in reverse solving mode)
  • pusher selection steps on boards with multiple pushers (Multiban)

A saved game is tied to the physical board it was recorded against. Mirroring, rotating, resizing, or trimming that board can invalidate movement directions and piece positions. The mismatch is not detected automatically and not enforced anywhere. GameBoard and SavedGame are separate structures, and the client is expected to maintain the relationship between them.

§Examples

let saved_game = SavedGame::parsed("llRRddsssSSSnNN", Tessellation::Hexoban)?;
let saved_game = SavedGame::parsed("nlLNrr", Tessellation::Trioban)?;

assert_eq!(saved_game[3].direction(), Direction::NorthEast);
assert_eq!(saved_game[3], Step::PushOrPull(Direction::NorthEast));

§Editing

SavedGame is not editable in a sense where Steps can be added, removed, inserted or modified. This is by design. This type is meant for archival purposes, as an input of Sokoban data to writers and as output of Sokoban data readers. Game engines and solvers will need to use their own structures to track movement history. At the end of solving session, they may use SavedGame to archive progress of that session.

§Step counts, and the metrics that are not here

moves_count() and pushes_count() are facts about the step sequence, so they live here.

The rest of the standard metric set does not. Box lines and box changes need to know which box each push moved, and no saved game text format encodes box identity, so they can only be had by replaying the steps against a GameBoard. Replaying a game is playing it and that is out of scope for this crate.

§Solutions

Whether a SavedGame solves a board, how far it replays on one, and what its SolvingMode::Reverse solution looks like played forwards are all questions about playing rather than about what is recorded, so again - out of scope for this crate. Nothing here plays, validates or edits a game: game engines and solvers keep their own board state, and are expected to track these as they go rather than replay a SavedGame to recover them.

§Serde

With the serde feature, SavedGame is a map whose steps are the same text a .sok file would carry:

{
  "tessellation": "Sokoban",
  "moves": "[]LuR",
  "title": "",
  "solved_by": "",
  "notes": "",
  "current_move_idx": 0
}

There is deliberately no solving_mode field. SolvingMode::Reverse is always encoded in the text itself as a leading jump section ([] when the game does not open with a jump), so the moves are the single source of truth and no document can declare a mode contradicting its own steps. A solving_mode key is rejected rather than silently ignored, as are all other unknown fields.

Everything except tessellation and moves may be omitted. current_move_idx is special: the parser reads it from a * marker inside the moves text, so omitting the field keeps whatever the text says, while providing it overrides that. An empty moves string is the empty SolvingMode::Forward game (an empty reverse game prints as "[]").

use sokoban_elements::*;

let saved_game = SavedGame::parsed("lurd", Tessellation::Sokoban)?;

let json = serde_json::to_string(&saved_game)?;
assert_eq!(serde_json::from_str::<SavedGame>(&json)?, saved_game);

Fields§

§title: String§solved_by: String§notes: String§current_move_idx: usize

Index of currently played move from steps().

This can be found in some sokoban file formats so SavedGame has a place for it. On the other hand, SavedGame doesn’t use it in any way, so it doesn’t validate it against steps() either. SavedGame clients are advised to maintain and validate this value themselves.

Implementations§

Source§

impl SavedGame

Source

pub fn parsed( data: &str, tessellation: Tessellation, ) -> Result<Self, SavedGameSyntaxError>

Constructs SavedGame from parsed string of moves in context of Tessellation.

§Examples
let saved_game: SavedGame = SavedGame::parsed("lurdLurd", Tessellation::Sokoban)?;
§Grammar

Save game strings use following characters:

char
Move pusher‘l’, ‘u’, ‘r’, ‘d’, ‘w’, ‘e’, ‘n’, ‘s’
Push/pull box‘L’, ‘U’, ‘R’, ‘D’, ‘W’, ‘E’, ‘N’, ‘S’
Begin jump‘[’
End jump‘]’
Begin pusher change‘{’
End pusher change‘{’
Current position‘*’

Mapping between characters and movement direction depends on Tessellation context and is not the same in every Tessellation. Also, not all Tessellations use all movement characters, so this method may return error if it finds legal movement character but in wrong game tessellation context. Ie. Sokoban will return error for character 'W'.

SavedGame strings mostly follow SokobanYASC specification:

::::::::::::::::::::::::::: Moves ::::::::::::::::::::::::::
::                                                        ::
:: Remarks:                                               ::
::                                                        ::
::   Moves may be run-length encoded, e.g., "3r4U" means  ::
::   "rrrUUUU", and "2(3(dr)R)" means "drdrdrRdrdrdrR".   ::
::   Each line must, however, have at least one proper    ::
::   non-digit character. Spaces between moves are        ::
::   allowed.                                             ::
::                                                        ::
::   Jumps and pulls: Only in reverse mode saved games    ::
::   and solutions.                                       ::
::                                                        ::
::   Reverse mode saved games and solutions must begin    ::
::   with a jump, even if it is empty. An example:        ::
::   "[]U[rr]d".                                          ::
::                                                        ::
::   Pusher changes: Only in puzzles with multiple        ::
::   pushers, e.g., Multiban. Moves inside the braces     ::
::   depict the relative movement to get from the         ::
::   currently active pusher to the next active pusher.   ::
::   At game start, a "{...}" sequence activates the      ::
::   pusher relative to the top-left pusher. An example:  ::
::   "{rddd}Urr{uul}uLU". If the top-left pusher is the   ::
::   first active pusher, then the empty "{}" can be      ::
::   omitted.                                             ::
::                                                        ::
::   The current position is optional and defaults to the ::
::   position after the last move.                        ::
::                                                        ::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Source

pub fn from_steps( data: &[Step], tessellation: Tessellation, solving_mode: Option<SolvingMode>, ) -> Result<Self, SavedGameSyntaxError>

Constructs new SavedGame from sequence of Step, validating each of them in context of given game tessellation.

§Examples
use sokoban_elements::*;

let moves = vec![
    Step::Move(Direction::Down),
    Step::PushOrPull(Direction::Down),
    Step::PushOrPull(Direction::Down),
    Step::Jump(Direction::Down),
    Step::Jump(Direction::Down),
    Step::Move(Direction::Down),
];

let saved_game: SavedGame =
    SavedGame::from_steps(&moves, Tessellation::Sokoban, Some(SolvingMode::Forward))?;

assert_eq!(saved_game.solving_mode(), SolvingMode::Reverse);
Source

pub fn printed_steps(&self) -> String

Serializes steps() into saved game string.

Source

pub fn solving_mode(&self) -> SolvingMode

SolvingMode this SavedGame was recorded in.

Source

pub fn tessellation(&self) -> Tessellation

Tessellation this SavedGame was recorded against.

Source

pub fn steps(&self) -> &[Step]

Slice of Steps in this SavedGame.

Source

pub fn len(&self) -> usize

Total number of steps in this SavedGame.

Source

pub fn is_empty(&self) -> bool

True if this SavedGame has no steps.

Source

pub fn moves_count(&self) -> usize

Total number of Step::Move steps in this SavedGame.

Source

pub fn pushes_count(&self) -> usize

Total number of Step::PushOrPull steps in this SavedGame.

Trait Implementations§

Source§

impl Clone for SavedGame

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SavedGame

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for SavedGame

Source§

fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Index<usize> for SavedGame

Source§

type Output = Step

The returned type after indexing.
Source§

fn index(&self, index: usize) -> &Self::Output

Performs the indexing (container[index]) operation. Read more
Source§

impl PartialEq for SavedGame

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for SavedGame

Source§

fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for SavedGame

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Conv for T

Source§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> FmtForward for T

Source§

fn fmt_binary(self) -> FmtBinary<Self>
where Self: Binary,

Causes self to use its Binary implementation when Debug-formatted.
Source§

fn fmt_display(self) -> FmtDisplay<Self>
where Self: Display,

Causes self to use its Display implementation when Debug-formatted.
Source§

fn fmt_lower_exp(self) -> FmtLowerExp<Self>
where Self: LowerExp,

Causes self to use its LowerExp implementation when Debug-formatted.
Source§

fn fmt_lower_hex(self) -> FmtLowerHex<Self>
where Self: LowerHex,

Causes self to use its LowerHex implementation when Debug-formatted.
Source§

fn fmt_octal(self) -> FmtOctal<Self>
where Self: Octal,

Causes self to use its Octal implementation when Debug-formatted.
Source§

fn fmt_pointer(self) -> FmtPointer<Self>
where Self: Pointer,

Causes self to use its Pointer implementation when Debug-formatted.
Source§

fn fmt_upper_exp(self) -> FmtUpperExp<Self>
where Self: UpperExp,

Causes self to use its UpperExp implementation when Debug-formatted.
Source§

fn fmt_upper_hex(self) -> FmtUpperHex<Self>
where Self: UpperHex,

Causes self to use its UpperHex implementation when Debug-formatted.
Source§

fn fmt_list(self) -> FmtList<Self>
where &'a Self: for<'a> IntoIterator,

Formats each item in a sequence. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pipe for T
where T: ?Sized,

Source§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
Source§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
Source§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
Source§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows self, then passes self.as_ref() into the pipe function.
Source§

fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.as_mut() into the pipe function.
Source§

fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
Source§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R, ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Tap for T

Source§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
Source§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
Source§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
Source§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
Source§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
Source§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
Source§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
Source§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
Source§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
Source§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .tap_borrow() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Calls .tap_borrow_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Calls .tap_ref() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Calls .tap_ref_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> TryConv for T

Source§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.