Skip to main content

GameBoard

Struct GameBoard 

Source
pub struct GameBoard { /* private fields */ }
Expand description

Parsed game board for some Tessellation.

Encapsulates game board as 2D matrix of TileKind.

  • coordinate system origin (0, 0) is at the top left of board
  • X axis points from left to right
  • Y axis points from top to down

GameBoard is end result of parsing and editing game puzzles. As such it is not optimized data structure playing the game, implementing game engines or game solvers. GameBoard is meant to be used only as input for these uses and will have to be converted into more suitable data structure.

GameBoard doesn’t place any restrictions on it’s width, height or on number of pushers, boxes and goals. When implementing game engines or game solvers, you’ll want to validate parsed GameBoard against concrete engine or solver requirements.

BoxID, GoalID, PusherID and PlusID ** do restrict** numeric IDs that they can represent. Having GameBoard with more pushers, boxes or goals than these can represent is possible, but you will not be able to represent ids of it’s pieces. Again and by design, there is no mechanism in GameBoard to prevent this situation.

GameBoard supports Index and IndexMut for (x, y), Point2D and linear usize.

GameBoard supports iter() yielding row-major ((x, y), TileKind) pairs.

Depending on Tessellation, a board position has related TileShape that can be calculated via Tessellation::tile_shape_from_pos(x, y) or via Point2D::tile_shape(tessellation)

§Examples

use sokoban_elements::*;

let board_text = [
    "    #####",
    "    #   #",
    "    #$  #",
    "  ###  $##",
    "  #  $ $ #",
    "### # ## #   ######",
    "#   # ## #####  ..#",
    "# $  $          ..#",
    "##### ### #@##  ..#",
    "    #     #########",
    "    #######",
]
.join("\n");

let mut board = GameBoard::parsed(&board_text, Tessellation::Sokoban)?;

assert_eq!(board.width(), 19);
assert_eq!(board.height(), 11);

// tiles are read by (x, y)...
assert_eq!(board[(4, 0)], TileKind::Wall);

// ... or by linear board position: y * width + x
let position: usize = 4;
assert_eq!(board[position], TileKind::Wall);

// short rows are padded with floor tiles
assert_eq!(board[(18, 0)], TileKind::Floor);

// tiles are writable
board[(4, 0)] = TileKind::Floor;
assert_eq!(board[(4, 0)], TileKind::Floor);

// iteration is row major over ((x, y), tile) pairs
assert_eq!(board.iter().count(), board.width() * board.height());
assert_eq!(board.iter().next(), Some(((0, 0), TileKind::Floor)));

§Board size

Board data is always width-normalized: all rows have exactly width() tiles. Rows shorter than board width are padded with TileKind::Floor. Parsing, resizing, rotation and mirroring ensure that this always holds. Index<(usize, usize)> and Index<Point2D> would’ve panicked for perfectly valid board coordinates, if the rows were ragged. Index<usize> wouldn’t even panic - in case of ragged rows it would silently return wrong board tile.

A board with no columns and or no rows is empty: width() is zero if and only if height() is zero. Constructing or editing a board in a way that would leave it with rows of zero length silently produces the empty board instead. There is no error for this.

These rules is what makes indexing board position well defined.

Resizing edits one edge at a time, so callers control operation order. Additions to a non-empty board apply count exactly, and methods return how many lines were applied.

Removals saturate. remove_rows(Top, 42) on a 11-row board removes 11, it is not an error. In that case we’d removed all the rows from the board, so there are no columns left either.

The result of chaining board edge edits depends on order of calls. Any individual step may collapse the board to size zero (because removals saturate).

Once edge editing empties a board, the one-edge-at-a-time methods cannot grow it back: there are no rows to add columns to, and rows added to a board with no columns are of zero length, which is the empty board again. Use grow() for that.

Top and Left resizing shifts existing content. To compensate what would otherwise be a break in tile geometry, GameBoard adds or removes one padding line in perpendicular axis. This means that Top and Left resizing may have side effect of changing both board dimensions. This depends on concrete Tessellation:

  • add_rows(Top, 1) on a Trioban or Octoban board also adds a left column; add_columns(Left,1) also adds a top row.
  • Hexoban needs it on odd Top counts when the retained board spans both row parities. Even counts need nothing

Bottom and Right resizing never adds perpendicular padding.

Each resizing count is one batched shift. Adding two Top rows to Trioban adds no padding, while two separate one-row calls each add a column.

If you need to tighten the resulting board after this padding, call trim() explicitly.

§Transformations

rotate() applies one tessellation-specific step; see Tessellation::rotation_step_degrees(). A transform may add one row or column because its result must be re-anchored with a rigid motion of the tiling; integer translation periods are exposed by Tessellation::translation_period(). A full turn likewise takes four steps for Sokoban and Octoban, and six for Hexoban and Trioban. Rotation keeps its complete bounding box; call trim() separately when a tight board is wanted.

Mirroring twice restores the original content, but may retain padding added by re-anchoring.

§Normalization and enclosing

grow() and enclose() are the two resizing methods that are not per-edge, because neither takes an edge to work on: grow() is told a size, and enclose() derives the edges it must grow from where the play area reaches.

enclose() walls the play area in, so it is the operation that makes a board “closed”.

normalize() does not produce a closed board. A play area that reaches the board edge is confined by the edge, not by a wall, and no wall is invented for it.

§Edits invalidate emergent types and properties

Editing, resizing, mirroring and rotation invalidates everything keyed to position:

  • BoxID, GoalID, and PusherID are reassigned by scanning the edited board in row-major order
  • SokobanPlus is keyed to those IDs and is silently invalidated.
  • SavedGame moves are directional and no longer describe the transformed board. In particular, Trioban direction changes depend on each triangle’s orientation and cannot be repaired by substituting direction characters.
  • Zobrist hashes of boards (game engine feature) are position-keyed and do not survive editing.

Invalidation is natural consequences of how all of above types are defined: they need a stable board configuration to make any sense. Important thing to have in mind when editing the board: finish the design before assigning ie. Sokoban+ to it.

§Serde

With the serde feature, GameBoard is a two field map of the printed board and the Tessellation needed to parse it back:

{ "tessellation": "Sokoban", "board": "#####\n#@$.#\n#####" }

Deserialization goes through parsed(), so a document can never produce a board that violates the width-normalization invariant. Unknown fields are rejected. The board text is accepted in any form parsed() accepts, run-length encoding included, but is always written out uncompressed with visible floors.

use sokoban_elements::*;

#[rustfmt::skip]
let board_text = [
    "#####",
    "#@$.#",
    "#####",
]
.join("\n");

let board = GameBoard::parsed(&board_text, Tessellation::Sokoban)?;

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

Implementations§

Source§

impl GameBoard

Source

pub fn new(width: usize, height: usize, tessellation: Tessellation) -> Self

Constructs blank board of given size, all of it’s tiles being TileKind::Floor.

A board with no columns is the empty board: if either width or height is zero, both width() and height() of constructed board are zero.

§Examples
use sokoban_elements::*;

let board = GameBoard::new(0, 5, Tessellation::Sokoban);

assert_eq!(board.width(), 0);
assert_eq!(board.height(), 0);
Source

pub fn tessellation(&self) -> Tessellation

Source

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

Parses board string in context of given Tessellation.

§Examples

Constructing GameBoard from text:

use sokoban_elements::*;

let board_text = [
    "    #####",
    "    #   #",
    "    #$  #",
    "  ###  $##",
    "  #  $ $ #",
    "### # ## #   ######",
    "#   # ## #####  ..#",
    "# $  $          ..#",
    "##### ### #@##  ..#",
    "    #     #########",
    "    #######",
]
.join("\n");

let board = GameBoard::parsed(&board_text, Tessellation::Sokoban)?;
§Grammar

Game board strings use following characters:

tilechar
Wall‘#’
Pusher‘p’, ‘@’, ‘m’
Pusher on goal‘P’, ‘+’, ‘M’
Box‘b’, ‘$’
Goal‘.’ , ‘o’
Floor’ ’, ‘-’, ‘_’

Game board strings mostly follow SokobanYASC specification:

::::::::::::::::::::::::::: Board ::::::::::::::::::::::::::
::                                                        ::
:: Remarks:                                               ::
::                                                        ::
::   The first and the last non-empty square in each row  ::
::   must be a wall or a box on a goal. An empty interior ::
::   row is written with at least one "-" or "_".         ::
::                                                        ::
::   Boards may be run-length encoded (RLE), e.g.,        ::
::   "###----p.#" may be encoded as "3#4-p.#", and        ::
::   "#-#-#-##-#-#-#" may be encoded as "2(3(#-)#)".      ::
::   A row cannot be split over multiple lines.           ::
::                                                        ::
::   Rows may be combined on a single line by using "|"   ::
::   as a row separator, e.g., "--3#|3#-#|#pb.#|5#".      ::
::   A "|" at the end of a line is optional and may be    ::
::   omitted.                                             ::
Source

pub fn parsed_from_tiles( data: &[TileKind], width: usize, height: usize, tessellation: Tessellation, ) -> Result<Self, BoardSyntaxError>

Constructs board from row major slice of TileKind.

A board with no columns is the empty board: if either width or height is zero, data must be empty and constructed board is the empty board.

§Errors

InputSizeNotMatchedByDeclaredSize when length of data is not exactly width * height. Sizes too large to be represented are reported the same way.

Source

pub fn printed(&self, visible_floor: bool) -> String

Source

pub fn width(&self) -> usize

Number of board columns.

Returns 0 for empty board. Board width and height() are either both zero or both greater than zero.

Source

pub fn height(&self) -> usize

Number of board rows.

Returns 0 for empty board. Board height and width() are either both zero or both greater than zero.

Source

pub fn size(&self) -> Size2D

Source

pub fn pushers_count(&self) -> usize

Source

pub fn boxes_count(&self) -> usize

Source

pub fn goals_count(&self) -> usize

Source

pub fn is_blank(&self) -> bool

Source

pub fn pushers(&self) -> Result<Vec<Pusher>, BoardSyntaxError>

All Pusher pieces on this board, in row major order.

PusherID of each piece is assigned by scanning board top to bottom and each row left to right, starting from 0. See PusherID docs.

Tile with pusher standing on goal (TileKind::PusherOnGoal) yields both a Pusher here and a Goal in goals().

§Errors

TooManyPushers when board has more pushers than PusherID can represent.

§Examples
use sokoban_elements::*;

#[rustfmt::skip]
let board_text = [
    "#####",
    "#@$.#",
    "#####",
]
.join("\n");

let board = GameBoard::parsed(&board_text, Tessellation::Sokoban)?;

let pushers = board.pushers()?;

assert_eq!(pushers.len(), board.pushers_count());
assert_eq!(pushers[0].id, PusherID::from(0_u8));
assert_eq!((pushers[0].x, pushers[0].y), (1, 1));
Source

pub fn boxes( &self, sokoban_plus: Option<&SokobanPlus>, ) -> Result<Vec<Boxbox>, BoardSyntaxError>

All Boxbox pieces on this board, in row major order.

BoxID of each piece is assigned by scanning board top to bottom and each row left to right, starting from 0. See BoxID docs.

Tile with box standing on goal (TileKind::BoxOnGoal) yields both a Boxbox here and a Goal in goals().

Sokoban+ ID of each piece is looked up in sokoban_plus by assigned BoxID. Without sokoban_plus, no box gets Sokoban+ ID. See SokobanPlus.

§Errors

TooManyBoxes when board has more boxes than BoxID can represent.

§Examples
use sokoban_elements::*;

#[rustfmt::skip]
let board_text = [
    "#####",
    "#@$*#",
    "#####",
]
.join("\n");

let board = GameBoard::parsed(&board_text, Tessellation::Sokoban)?;

let boxes = board.boxes(None)?;

assert_eq!(boxes.len(), board.boxes_count());
assert_eq!(boxes[0].id, BoxID::from(0_u8));
assert_eq!((boxes[0].x, boxes[0].y), (2, 1));
assert_eq!(boxes[0].plus_id, None);

// box standing on goal is both a box and a goal
assert_eq!((boxes[1].x, boxes[1].y), (3, 1));
assert_eq!(board.goals(None)?.len(), 1);

Reading Sokoban+ IDs:

use sokoban_elements::*;

#[rustfmt::skip]
let board_text = [
    "#####",
    "#@$*#",
    "#####",
]
.join("\n");

let board = GameBoard::parsed(&board_text, Tessellation::Sokoban)?;
let sokoban_plus = SokobanPlus::parsed("0 42", "42 0")?;

let boxes = board.boxes(Some(&sokoban_plus))?;

assert_eq!(boxes[0].plus_id, None);
assert_eq!(boxes[1].plus_id, Some(PlusID::new(42)?));
Source

pub fn goals( &self, sokoban_plus: Option<&SokobanPlus>, ) -> Result<Vec<Goal>, BoardSyntaxError>

All Goal pieces on this board, in row major order.

GoalID of each piece is assigned by scanning board top to bottom and each row left to right, starting from 0. See GoalID docs.

Tiles with box or pusher standing on goal (TileKind::BoxOnGoal, TileKind::PusherOnGoal) yield a Goal here in addition to the piece standing on it.

Sokoban+ ID of each piece is looked up in sokoban_plus by assigned GoalID. Without sokoban_plus, no goal gets Sokoban+ ID. See SokobanPlus.

§Errors

TooManyGoals when board has more goals than GoalID can represent.

§Examples
use sokoban_elements::*;

#[rustfmt::skip]
let board_text = [
    "#####",
    "#+$.#",
    "#####",
]
.join("\n");

let board = GameBoard::parsed(&board_text, Tessellation::Sokoban)?;

let goals = board.goals(None)?;

assert_eq!(goals.len(), board.goals_count());

// pusher standing on goal is both a pusher and a goal
assert_eq!(goals[0].id, GoalID::from(0_u8));
assert_eq!((goals[0].x, goals[0].y), (1, 1));
assert_eq!((goals[1].x, goals[1].y), (3, 1));
Source

pub fn iter(&self) -> GameBoardIter<'_> ⓘ

Row major iterator over ((x, y), tile) pairs of this board.

§Examples
use sokoban_elements::*;

let board = GameBoard::new(3, 2, Tessellation::Sokoban);

let positions: Vec<(usize, usize)> = board.iter().map(|(position, _)| position).collect();

assert_eq!(positions, vec![(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1)]);
Source

pub fn mirror(&mut self, axis: MirrorAxis)

Mirrors the board across axis as a rigid motion of its tessellation.

MirrorAxis::Horizontal exchanges the top and bottom edges; MirrorAxis::Vertical exchanges the left and right edges. Re-anchoring may add one row or column to keep tile geometry intact.

§Examples
use sokoban_elements::*;

let mut board = GameBoard::parsed("@$.", Tessellation::Sokoban)?;
board.mirror(MirrorAxis::Vertical);
assert_eq!(board.printed(false), ".$@");
Source

pub fn rotate(&mut self, rotation: Rotation)

Rotates the board by one natural step of its tessellation.

The step is 90 degrees for Sokoban and Octoban, and 60 degrees for Hexoban and Trioban; see Tessellation::rotation_step_degrees(). Rotation keeps the complete rotated bounding box and does not trim floor-only edges.

§Examples
use sokoban_elements::*;

#[rustfmt::skip]
let board_text = [
    "@",
    "$",
    ".",
]
.join("\n");
let mut board = GameBoard::parsed(&board_text, Tessellation::Sokoban)?;
board.rotate(Rotation::Clockwise);
assert_eq!(board.printed(false), ".$@");
Source

pub fn grow(&mut self, width: NonZeroU16, height: NonZeroU16)

Grows the board to at least width columns and height rows, filling with TileKind::Floor.

Each axis is grown independently to the larger of its current and its wanted size, so a board is never shrunk: an axis already at or past its wanted size is left alone, and a call asking for no growth on either axis is a no-op.

Growth is added to the Bottom and Right edges only. Existing tiles therefore keep their (x, y), and no perpendicular padding line is ever added, whatever the Tessellation.

This is also the way back from the empty board, which the one-edge-at-a-time methods cannot grow.

§Examples
use std::num::NonZeroU16;

use sokoban_elements::*;

let mut board = GameBoard::new(3, 2, Tessellation::Sokoban);

board.grow(NonZeroU16::try_from(5)?, NonZeroU16::try_from(1)?);

// width grew, height was already past the wanted one and stayed
assert_eq!(board.width(), 5);
assert_eq!(board.height(), 2);

// editing can empty a board, and growing is the way back
board.remove_columns(VerticalEdge::Left, 42);
assert_eq!(board.size(), Size2D::new(0, 0));

board.grow(NonZeroU16::try_from(4)?, NonZeroU16::try_from(3)?);
assert_eq!(board.size(), Size2D::new(4, 3));
Source

pub fn add_rows(&mut self, edge: HorizontalEdge, count: usize) -> usize

Adds count rows of TileKind::Floor to given board edge.

Returns count when the board is non-empty. A Top addition may also add one left padding column to preserve tile geometry.

Does nothing to the empty board: a board with no columns can only grow rows of zero length, and a board with no columns is the empty board. Grow the empty board with grow() instead.

§Examples
use sokoban_elements::*;

let mut board = GameBoard::new(3, 2, Tessellation::Sokoban);

board.add_rows(HorizontalEdge::Top, 2);

assert_eq!(board.width(), 3);
assert_eq!(board.height(), 4);
Source

pub fn remove_rows(&mut self, edge: HorizontalEdge, count: usize) -> usize

Removes up to count rows from given board edge and returns the number removed.

Removal saturates at the board height. A Top removal that retains rows may also add one left padding column to preserve tile geometry.

§Examples
use sokoban_elements::*;

let mut board = GameBoard::new(3, 2, Tessellation::Sokoban);

board.remove_rows(HorizontalEdge::Bottom, 1);
assert_eq!(board.height(), 1);

// removing more rows than there are empties the board
board.remove_rows(HorizontalEdge::Bottom, 42);
assert_eq!(board.width(), 0);
assert_eq!(board.height(), 0);
Source

pub fn add_columns(&mut self, edge: VerticalEdge, count: usize) -> usize

Adds count columns of TileKind::Floor to given board edge.

Returns count when the board is non-empty. An odd Left addition on Trioban or Octoban also adds one top padding row to preserve tile geometry.

Does nothing to the empty board, which has no rows to add columns to. Grow the empty board with grow() instead.

§Examples
use sokoban_elements::*;

let mut board = GameBoard::new(3, 2, Tessellation::Sokoban);

board.add_columns(VerticalEdge::Right, 4);

assert_eq!(board.width(), 7);
assert_eq!(board.height(), 2);
Source

pub fn remove_columns(&mut self, edge: VerticalEdge, count: usize) -> usize

Removes up to count columns from given board edge and returns the number removed.

Removal saturates at the board width. An odd Left removal that retains columns on Trioban or Octoban also adds one top padding row to preserve tile geometry. Removing all columns also removes the rows, because a board with no columns is the empty board.

§Examples
use sokoban_elements::*;

let mut board = GameBoard::new(3, 2, Tessellation::Sokoban);

board.remove_columns(VerticalEdge::Left, 1);
assert_eq!(board.width(), 2);

board.remove_columns(VerticalEdge::Left, 2);
assert_eq!(board.width(), 0);
assert_eq!(board.height(), 0);
Source

pub fn trim(&mut self)

Removes “outer” floor-only rows and columns in one geometry-preserving crop.

A tile is retained when it is not empty floor - walls, boxes, goals and pushers all count, so a goal-only or pusher-only edge line survives trimming.

The result is the smallest rigidly anchored rectangle. It may retain one padding row or column when the tight rectangle would change tile geometry.

Trimming leaves the empty board exactly when there is nothing to retain, which is what is_blank() reports.

Source

pub fn enclose(&mut self) -> Result<(), BoardSyntaxError>

Surrounds the play area with walls, growing the board where there is no room for them.

StaticBoardAnalysis::flood_fill() stops at walls and at the board edge, so the play area is always confined by the two together. A board is “closed” exactly when the edge is not doing any of that work, which is what StaticBoardAnalysis::is_playable_area_confined() reports and what this method establishes. There is no need to reconstruct anything inward: every on-board position next to the play area and not in it is already a wall, because that is why the flood stopped there.

One row or column is added to each edge the play area reaches, and then every empty floor tile on the resulting outer edge becomes a Wall. Edges are grown Bottom, Right, Top, Left, because only Top and Left shift content and may add a perpendicular padding line. Those padding lines are floor too, so walling the outer edge afterwards seals them without tracking where they landed.

Nothing is overwritten but floor, so no piece, goal or wall is ever lost. Two consequences:

  • Dead floor on an edge that was not grown becomes wall as well. It lies outside the play area, so the puzzle is unchanged, and it only happens on a board that was leaking somewhere - an already closed board is left exactly as it is, which makes this method idempotent.
  • The result is not tightened. Follow with trim() or normalize() when a tight board is wanted.

A board with no Pusher has no play area to enclose and is left untouched, as is the empty board.

§Examples
use sokoban_elements::*;

// The pusher can walk off the top of this board, so the top is not a wall's job yet.
#[rustfmt::skip]
let board_text = [
    "#-@-#",
    "#####",
]
.join("\n");

let mut board = GameBoard::parsed(&board_text, Tessellation::Sokoban)?;
board.enclose()?;

#[rustfmt::skip]
let enclosed = [
    "#####",
    "#-@-#",
    "#####",
]
.join("\n");

assert_eq!(board.printed(true), enclosed);

// Enclosing an already closed board changes nothing.
board.enclose()?;
assert_eq!(board.printed(true), enclosed);
Source

pub fn normalize(&mut self) -> Result<(), BoardSyntaxError>

All non-playable tiles are replaced by floors and missing walls around play area are reconstructed.

The reconstructed board is cropped once using the same smallest rigid anchor as trim(). The crop is the bounding box of everything this method keeps: the play area plus the reconstructed walls confining it.

This does not make a board closed. A play area that reaches the board edge is confined there by the edge itself rather than by a wall, and those edge tiles are kept as they are - no wall is invented around them. enclose() is the operation that makes room and walls them, and the two compose: board.enclose()?; board.normalize()?;.

A board with no Pusher has no play area to normalize around, and is left untouched.

becomes

§Todo

Following is the stuff that SokobanYASC does, see what we can implement too

  • fill jagged (i.e., “stair-case” like) wall edges
  • remove redundant walls and floors,
  • remove unused boxes and goals
  • change immovable boxes on non goal squares to walls
  • change immovable boxes on goal squares to walls
  • fill unnecessary illegal box squares

Trait Implementations§

Source§

impl Clone for GameBoard

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 GameBoard

Source§

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

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

impl<'de> Deserialize<'de> for GameBoard

Source§

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

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

impl Eq for GameBoard

Source§

impl Index<(usize, usize)> for GameBoard

Source§

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

index is (x, y)

§Panics

Panics if index is outside of board.

Source§

type Output = TileKind

The returned type after indexing.
Source§

impl Index<Point2D> for GameBoard

Source§

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

index is Point2D of the tile.

§Panics

Panics if index is outside of board.

Source§

type Output = TileKind

The returned type after indexing.
Source§

impl Index<usize> for GameBoard

Source§

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

index is linear board position: y * width + x

§Panics

Panics if index is outside of board.

Source§

type Output = TileKind

The returned type after indexing.
Source§

impl IndexMut<(usize, usize)> for GameBoard

Source§

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

index is (x, y)

§Panics

Panics if index is outside of board.

Source§

impl IndexMut<Point2D> for GameBoard

Source§

fn index_mut(&mut self, index: Point2D) -> &mut Self::Output

index is Point2D of the tile.

§Panics

Panics if index is outside of board.

Source§

impl IndexMut<usize> for GameBoard

Source§

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

index is linear board position: y * width + x

§Panics

Panics if index is outside of board.

Source§

impl<'a> IntoIterator for &'a GameBoard

Source§

type IntoIter = GameBoardIter<'a>

Which kind of iterator are we turning this into?
Source§

type Item = ((usize, usize), TileKind)

The type of the elements being iterated over.
Source§

fn into_iter(self) -> Self::IntoIter

Creates an iterator from a value. Read more
Source§

impl PartialEq for GameBoard

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 GameBoard

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 GameBoard

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<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
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.