boardgame

package module
v0.0.0-...-6b15219 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 18, 2023 License: Apache-2.0 Imports: 20 Imported by: 0

README

boardgame is a work-in-progress package that aspires to make it easy to define multi-player boardgames that can be easily hosted in a high-quality web app with minimal configuration.

You formally define the rules and logic of your boardgame in Go by defining a state object and a series of moves that can be made. You also define a simple Polymer-based web component that renders a given game state client-side.

boardgame is under active development as a hobby project and different components of it vary in their completeness and polish.

Getting started

A comprehensive getting started guide, including a walkthrough of all of the important concepts in a real-world example, is in the tutorial.

Demo

Demo showing automatic card and token animations

Design Goals

  • Don't Repeat Yourself Write your normative logic that defines your game in Go a single time.
  • Minimize Code Writing your game should feel like just transcribing the rules into a formal model, not like a challenging coding exercise.
  • Batteries Included Common operations, like "deal cards from the draw stack to each player's hand until each player has 3 cards" should require barely any logic to accomplish.
  • Minimize Boilerplate Structs with powerful defaults to anonymously embed in your structs, and easy-to-use code generation tools where required
  • Clean Layering If you don't like the default behavior, override just a small portion to make it do what you want
  • Flexible Powerful enough to model any real-world board or card game
  • Make Cheaters' Lives Hard Don't rely on security by obscurity to protect secret game state: sanitize properties before transmitting to the client
  • Identify Errors ASAP If you've configured something incorrectly, ideally it's found either by the type checker or at the moment the program boots up, not later when someone's playing a game. The library is designed to minimize interface{}, allowing you to rely on the type system as much as possible, and many common configuration errors will be detected and reported when you call NewGameManager.
  • Fast Minimal reliance on features like reflection at runtime
  • Minimize Javascript Most client-side views are 10's of lines of templates and databinding, sometimes without any javascript at all
  • Rich animations and UI Common operations like cards moving between stacks should make use of flexible, fast animations computed automatically
  • Robust tooling A swiss-army-knife utility called boardgame-util makes it easy to generate boilerplate code, generate starter projects, run development servers, make sure the database is structured well, and more.

Status

The library is currently relativley full-featured. Here are a few of the known gaps (and the issues that track their status):

  • Support of Multiple Browsers (Issue #394) Currently the web-app only fully works in Chrome, with support for animations in Safari, and with limited testing in other browsers
  • More Contorl over Animations (Issue #396) Currently moves that are applied one after another don't pause to allow animations to play
  • Examples with a board None of the example games in the main repo use a board, which means that tools aimed at board-based-games aren't fleshed out
  • Smooth Upgrading (Issue #184) If you change the shape of the state objects in your game, there's currently no way to handle older versions of the game stored in your database.

Many more small things are still rough or not implemented. Please file issues or comment on existing issues in the tracker for things you're missing!

Documentation

Overview

Package boardgame is a framework that makes it possible to build boardgames with minimial fuss.

This package contains the core boardgame logic engine. Other packages extend and build of of this base. package boardgame/base provides base implementations of the various types of objects your game logic must provide. boardgame/moves provides a collection of powerful move objects to base your own logic off of. boardgame/server is a package that, given a game definition, creates a powerful Progressive Web App, complete with automatically-generated animations.

The boardgame/boardgame-util command is a powerful swiss army knife of functionality to help create game packages and run servers based on that automatically.

The documentation in this package is primarily detail about how the various concepts wire together. For a high-level overview of how everything works and tour of the main concepts, see TUTORIAL.md.

The primary entry point for use of this package is defining your own GameDelegate. The methods and documentation from there will point to other parts of this package.

Index

Constants

View Source
const SanitizationDefaultGroup = "all"

SanitizationDefaultGroup is the implied sanitization group name if no group name is included. See StructInflater.PropertySanitizationPolicy for more on sanitization policy groups.

View Source
const SanitizationDefaultPlayerGroup = "other"

SanitizationDefaultPlayerGroup is the implied sanitization group name if no group name is included for player states. See StructInflater.PropertySanitizationPolicy for more on sanitization policy groups.

Variables

View Source
var ErrPropertyImmutable = errors.New("that property is an immutable type in the underlying object")

ErrPropertyImmutable should be returned by PropertyReadSetters' Mutable{Enum,Stack,Timer}Prop when the underlying property is actually an immutable variant of that type of object, or for when Configure*Prop (the immutable variant) is used on mutable properties.

View Source
var ErrTooManyFixUps = errors.New("we recursed deeply in fixup, which implies that ProposeFixUp has a move that is always legal")

ErrTooManyFixUps is returned from game.ProposeMove if too many fix up moves are applied, which implies that there is a FixUp move configured to always be legal, and is evidence of a serious error in your game logic.

Functions

func DefaultMarshalJSON

func DefaultMarshalJSON(obj interface{}) ([]byte, error)

DefaultMarshalJSON is a simple wrapper around json.MarshalIndent, with the right defaults set. If your structs need to implement MarshaLJSON to output JSON, use this to encode it.

Types

type Agent

type Agent interface {
	//Name is the unique, static name for this type of agent. Many games will
	//have only one type of agent, but the reason for this field is that some
	//games will have different agents with radically different play styles.
	//The name is how agents will be looked up within a manager.
	Name() string

	//DisplayName is a name for the agent that is human-friendly and need not
	//be unique. "Artificial Intelligence", "Computer", "Robby the Robot" are
	//all reasonable examples.
	DisplayName() string

	//SetUpForGame is called when a Game is being set up and Agents are
	//configured for some of the players. This is the chance of the Agent to
	//initialize its state. Whatever state is returned will be stored in the
	//storage layer and passed back to the Agent later.
	SetUpForGame(game *Game, player PlayerIndex) (agentState []byte)

	//ProposeMove is where the meat of Agents happen. It is called once after
	//every MoveChain is made on game (that is, after every player Move and
	//its attendant chain of FixUp moves have all been applied). It is passed
	//the index of the player it is playing at, the game, and the last-stored
	//state for this agent. The game may be interrogated for CurrentState,
	//PlayerMoves, etc, but should NOT have ProposeMove called directly. This
	//method should return a non-nil move ≈ if it wants to propose one.
	//newState is the new state for this agent; if it is nil, a new state will
	//not be saved and the next time ProposeMove is called the previously used
	//state will be provided again.
	ProposeMove(game *Game, player PlayerIndex, agentState []byte) (move Move, newState []byte)
}

Agent represents an Artificial Intelligence agent that plays as a specific player in a specific game. Agents are created and affiliated with GameManagers, and then when new games are set up they may be asked to play on behalf of a player. After moves are made on a game they are asked if they have a move to propose. In many cases the state of the game is sufficient, but in some cases Agents may need to store additional information; this is handled by the agent marshaling and unmarshaling byte sequences themselves.

type Board

type Board interface {
	ImmutableBoard
	Spaces() []Stack
	SpaceAt(index int) Stack
	// contains filtered or unexported methods
}

Board represents an array of growable Stacks. They're useful for representing spaces on a board, which may allow unlimited components to reside in them, or have a maxium number of occupants. If each board's space only allows a single item, it's often equivalent--and simpler--to just use a single Stack of a FixedSize. Get one from deck.NewBoard(). See also ImmutableBiard, which is the same, but without mutator methods.

type Component

type Component interface {

	//Values returns the ComponentValues struct that you associated with this
	//component via deck.AddComponent during GameDelegate.ConfigureDecks().
	Values() ComponentValues

	//Deck returns the deck that this component is part of.
	Deck() *Deck

	//DeckIndex returns the index into the deck that this component is. This
	//is always fixed and never changes in any game.
	DeckIndex() int

	//Equivalent checks whether two components are equivalent--that is,
	//whether they represent the same index in the same deck.
	Equivalent(other Component) bool

	//Instance returns a mutable ComponentInstance representing the specific
	//instantiation of this component in the given state of the given game.
	//Will never return nil, even if the component isn't valid in this state
	//----although later things like ContainingStack may error later in that
	//case. ComponentInstance is where the methods for moving the instance to
	//other stacks lie.
	Instance(st State) ComponentInstance

	//ImmutableInstance returns an ImmutableComponentInstance representing the
	//specific instantiation of this component in the given state of the given
	//game. Will never return nil, even if the component isn't valid in this
	//state--although later things like ContainingStack may error later in
	//that case.
	ImmutableInstance(st ImmutableState) ImmutableComponentInstance

	//Generic returns true if this Component is the generic component for this
	//deck. You might get this component if you ask for a component from a
	//sanitized stack. This method is a convenience method equivalent to
	//checking for equivalency to Deck().GenericComponent().
	Generic() bool
	// contains filtered or unexported methods
}

A Component represents a movable resource in the game. Cards, dice, meeples, resource tokens, etc are all components. You don't define these yourself; it's an interface because the core engine uses different underlying structs in different cases. Values is a struct that stores the specific values for the component, as defined in your ConfigureDecks method on GameDelegate. Components are the same across all games of this type. Component is a generic notion of that type of obejct; see also ComponentInstance and ImmutableComponentInstance for a notion of a SPECIFIC instantiation of a given type of component within a specific game. Component references should not be compared directly for equality, as sometimes different underlying objects will represent the same notional component (in order to satisfy both the Component and ComponentInstance interfaces simultaneously). Instead, use Equivalent() to test that two Components refer to the same conceptual Component.

type ComponentChest

type ComponentChest struct {
	// contains filtered or unexported fields
}

ComponentChest is a list of all decks, enums, and constants, for this game type. Each game type has one ComponentChest, which is an immutable set of all components in this game type. Every game of a given game type uses the same ComponentChest. NewGameManager creates a new ComponentChest, filling it with values returned from your GameDelegate's ConfigureDecks(), ConfigureConstants(), and ConfigureEnums().

func (*ComponentChest) Constant

func (c *ComponentChest) Constant(name string) interface{}

Constant returns the constant of that name, that was configured via your GameDelegate's ConfigureConstants().

func (*ComponentChest) ConstantNames

func (c *ComponentChest) ConstantNames() []string

ConstantNames returns all of the names of constants in this chest that would return something from Constant().

func (*ComponentChest) Deck

func (c *ComponentChest) Deck(name string) *Deck

Deck returns the deck in the chest with the given name. Those Decks() are associated with this ComponentChest based on what your GameDelegate returns from ConfigureDecks().

func (*ComponentChest) DeckNames

func (c *ComponentChest) DeckNames() []string

DeckNames returns all of the valid deck names--the names that would return a valid deck if passed to Deck().

func (*ComponentChest) Enums

func (c *ComponentChest) Enums() *enum.Set

Enums returns the enum.Set in use in this chest--the set that was returned from your GameDelegate's ConfigureEnums().

func (*ComponentChest) Manager

func (c *ComponentChest) Manager() *GameManager

Manager returns the GameManager that this ComponentChest is associated with.

func (*ComponentChest) MarshalJSON

func (c *ComponentChest) MarshalJSON() ([]byte, error)

MarshalJSON returns all of the decks, connstants, and enums in this chest.

type ComponentInstance

type ComponentInstance interface {

	//ComponentInstance can be used anywhere that ImmutableComponentInstance
	//can be.
	ImmutableComponentInstance

	//DynamicValues returns the Dynamic Values for this component in the state
	//this instance is associated with, if it exists. A convenience so you
	//don't have to go find them within  state.DynamicComponentValues
	//yourself.
	DynamicValues() SubState

	//ContainingStack will return the stack and slot index for the associated
	//component, if that location is not sanitized, and the componentinstance
	//is legal for the state it's in. If no error is returned,
	//stack.ComponentAt(slotIndex) == c will evaluate to true.
	ContainingStack() (stack Stack, slotIndex int, err error)

	//MoveTo moves the specified component in its current stack to the
	//specified slot in the destination stack. The destination stack must be
	//different than the one the component's currently in--if you're moving
	//components within a stack, use SwapComponent. In destination, slotIndex
	//must point to a valid "slot" to put a component, such that after
	//insertion, using that index on the destination will return that
	//component. In default Stacks, slots are any index from 0 up to and
	//including stack.Len(), because the stack will grow to insert the
	//component between existing components if necessary. For SizedStacks,
	//slotIndex must point to a currently empty slot.
	//MoveTo{First,Last,Next}Slot methods are useful if you want to move to
	//those locations. If you want the precise location of the inserted
	//component to not be visible, see SecretMoveTo.
	MoveTo(other Stack, slotIndex int) error

	//SecretMoveTo is equivalent to MoveTo, but after the move the Ids of all
	//components in destination will be scrambled. SecretMoveTo is useful when
	//the destination stack will be sanitized with something like PolicyOrder,
	//but the precise location of this insertion should not be observable. For
	//example, if you're cutting a given card to an unknown location deep in
	//the middle of a large stack.
	SecretMoveTo(other Stack, slotIndex int) error

	//MoveToFirstSlot moves the component to the first valid slot in the other
	//stack. For default Stacks, this is always 0. For SizedStacks, this is
	//the first empty slot from the left. A convenience wrapper around
	//stack.FirstSlot.
	MoveToFirstSlot(other Stack) error

	//MoveToLastSlot moves the component to the last valid slot in the other
	//stack. For default Stacks, this is always Len(). For SizedStacks, this
	//is the first empty slot from the right. A convenience wrappar around
	//stack.LastSlot().
	MoveToLastSlot(other Stack) error

	//MoveToNextSlot moves the component to the next valid slot in the other
	//stack where the component could be added without splicing. For default
	//stacks this is equivalent to MoveToLastSlot. For fixed size stacks this
	//is equivalent to MoveToFirstSlot. A convenience wrapper arond
	//stack.NextSlot().
	MoveToNextSlot(other Stack) error

	//SlideToFirstSlot takes the given component and moves it to the start of
	//the same stack, moving everything else up. It is equivalent to removing
	//the component, moving it to a temporary stack, and then moving it back
	//to the original stack with MoveToFirstSlot--but of course without
	//needing the extra scratch stack.
	SlideToFirstSlot() error

	//SlideToLastSlot takes the given component and moves it to the end of the
	//same stack, moving everything else down. It is equivalent to removing
	//the component, moving it to a temporary stack, and then moving it back
	//to the original stack with MoveToLastSlot--but of course without needing
	//the extra scratch stack.
	SlideToLastSlot() error
}

ComponentInstance is a mutable instantiation of a specific type of component in a particular state of a particular game. You generally get these from a Stack that contains them. The instance contains many methods to move the component to other stacks or locations. See also ImmutableComponentInstance, which is similar but lacks mutator methods.

type ComponentValues

type ComponentValues interface {
	//Reader is the way that the engine will enumerate and extract properties
	//on the ComponentValues.
	Reader
	//ContainingComponent is the component that this ComponentValues is
	//embedded in. It should return the component that was passed to
	//SetContainingComponent.
	ContainingComponent() Component
	//SetContainingComponent is called to let the component values know what
	//its containing component is.
	SetContainingComponent(c Component)
}

ComponentValues is the interface that the Values property of a Component must implement. You define your own ComponentValues to describe the immutable properties of the components in your game type. You associate a given ComponentValues struct with a given Component in deck.AddComponent. base.ComponentValues is designed to be anonymously embedded in your component to implement the latter part of the interface. 'boardgame- util codegen' can be used to implement Reader.

type ConfigurableSubState

type ConfigurableSubState interface {
	//Every SubState should be able to have its containing State set and read
	//back, so each sub-state knows how to reach up and over into other parts of
	//the over-arching state. You can implement this interface by emedding
	//base.SubState in your struct. This is how the values returned in
	//StateGetter methods are installed on your struct.
	ContainingStateConnector

	//This is how the values set via the StateSetter methods are retrieved from
	//your struct.
	StateGetter

	//ReadSetConfigurer defines the method to retrieve the
	//PropertyReadSetConfigurer for this object type. Typically this getter--
	//and the underlying PropertyReadSetConfigurer it returns--are generated
	//via `boardgame-util codegen`.
	ReadSetConfigurer
}

ConfigurableSubState is the interface for many types of structs that store properties and configuration specific to your game type. The values returned from your GameDelegate's GameStateConstructor, PlayerStateConstructor, and DynamicComponentValues constructor must all implement this interface.

A ConfigurableSubState is a struct that has a collection of properties all of a given small set of legal types, enumerated in PropertyType. These are the core objects to maintain state in your game type. The types of properties on these objects are strictly defined to ensure the shapes of the objects are simple and knowable.

The engine in general doesn't know the shape of your underlying structs, so it uses the ProeprtyReadSetConfigurer interface to interact with your objects. See the documetnation for PropertyReadSetConfigurer for more.

Many legal property types, like string and int, are simple and can be Read and Set as you'd expect. But some, called interface types, are more complex because they denote objects that carry configuration information in their instantiation. Stacks, Timers, and Enums are examples of these. These interface types can be Read and have their sub-properties Set. But they also must be able to be Configured, which is to say instantied and set onto the underlying struct.

ConfigurableSubState is the most powerful interface for interacting with these types of objects, because it has methods to Read, Set, and Configure all properties. In certain cases, however, for example with an ImmutableState, it might not be appropriate to allow Setting or Configuring propeties. For this reason, the interfaces are split into a series of layers, building up from only Reader methods up to adding Set proeprties, and then terminating by layering on Configure methods.

Typically your game's sub-states satisfy this interface by embedding base.SubState, and then using `boardgame-util codegen` to generate the underlying code for the PropertyReadSetConfigurer for your object type.

type ConfigurationValidator

type ConfigurationValidator interface {
	//ValidConfiguration will be checked when the NewGameManager is being set
	//up, and if it returns an error the manager will fail to be created.
	ValidConfiguration(exampleState State) error
}

ConfigurationValidator is an interface that certain types must implement. These will be called typically during NewGameManager set up, and are an opportunity for the structs to report configuration errors that can only be discovered at runtime. If an error is reported then NewGameManager will fail, which means that the misconfiguration can be detected early almost guaranteeing it will be detected by the game package author. For example, many moves in the moves package must be embedded in structs that contain particular methods in the embedding struct, and that can only be validated at runtime. Typically you don't need to implement this yourself; the types of structs that have it will have a stub implementation in the base package, and the primary beneficiaries of this are more complex embeddable library structs like those found in the moves package.

type ContainingStateConnector

type ContainingStateConnector interface {
	//ConnectContainingState is called when the SubState is almost done being
	//initialized and just needs to be told who its containing State is and what
	//piece of the containing State this SubState is (i.e if it's a Game,
	//Player, or DynamicComponentValues, and what its Player or DeckIndex is∑).
	//The values passed here should be returned from State(), ImmutableState(),
	//and StatePropertyRef(). Although even ImmutableStates will see the full,
	//mutable State via this method, they should not do anything mutable with
	//it. The StatePropertyRef passed will have PropName as "" since it refers
	//to the entire Reader, not a specific property on it.
	ConnectContainingState(state State, ref StatePropertyRef)

	//FinishStateSetUp is called once the SubState is fully initialized and
	//ConnectContainingStack has been called. The game engine doesn't require
	//anything to happen here, but this is where behaviors are typically
	//connected.
	FinishStateSetUp()
}

ContainingStateConnector is an interface that is used in SubState and related interfaces. It is the way that the engine will tell the SubState what values to return from StateGetter and other realted interfaces. Typically you use base.SubState to implement this automatically.

type Deck

type Deck struct {
	// contains filtered or unexported fields
}

A Deck represents an immutable collection of a certain type of component for a given game type. A Deck, like a Component, is immutable for a given game type and shared by every game of that type. Every Component lives in one deck in a position that is always the same; the Component's identity is defined by the deck it is in and the index in that deck.

Stack is a related concept; they represent the collections in your GameState, PlayerStates, and DynamicComponentValues where ComponentInstances live in a given state.

Each State of every game in this game type must have each component from each deck live in precisely one position in one stack at all times. This is referred to as the "component invariant", and captures the notion that the component instance is a physical entity that can only be in one place at one time and must exist somewhere. This invariant is established via careeful design of the library. When a given game is created, GameDelegate.DistributeComponentToStarterStack ensures that every component instance for the game has a home befofre the game starts, and after that the move methods on ComponentInstance ensure that a component can't ever be copied.

You generally only create decks inside of your GameDelegate's ConfigureDecks() method, which is called when the GameManager is being set up.

func NewDeck

func NewDeck() *Deck

NewDeck returns a new deck, ready to have components added to it. Typically you call this within your GameDelegate's ConfigureDecks() method.

func (*Deck) AddComponent

func (d *Deck) AddComponent(v ComponentValues)

AddComponent adds a new component with the given values to the next spot in the deck. This is where you affiliate the specific immutable properties specific to this component and your game with the component. v may be nil. This method is only legal to be called before the Deck has been installed into a ComponentChest, which is to say generally only within your GameDelegate's ConfigureDecks() method. Although technically there is no problem with using a different underlying struct for different components in the same deck, in practice it is strongly discouraged because often you will blindly cast returned component values for a given deck into the underlying struct.

func (*Deck) Chest

func (d *Deck) Chest() *ComponentChest

Chest points back to the chest we're part of.

func (*Deck) ComponentAt

func (d *Deck) ComponentAt(index int) Component

ComponentAt returns the component at a given index. It handles empty indexes and shadow indexes correctly.

func (*Deck) Components

func (d *Deck) Components() []Component

Components returns a list of Components in order in this deck, equivalent to calling ComponentAt() from 0 to deck.Len()

func (*Deck) GenericComponent

func (d *Deck) GenericComponent() Component

GenericComponent returns the component that is considereed fully generic for this deck. This is the component that every component will be if a Stack affilated with this deck is sanitized with PolicyLen, for example. If you want to figure out if a Stack was sanitized according to that policy, you can compare the component to this. To override the ComponentValues in this GenericComponent, call SetGenericValues.

func (*Deck) Len

func (d *Deck) Len() int

Len returns the number of components in this deck.

func (*Deck) MarshalJSON

func (d *Deck) MarshalJSON() ([]byte, error)

MarshalJSON marshasl in a format appropriate for use on the client.

func (*Deck) Name

func (d *Deck) Name() string

Name returns the name of this deck; the string by which it could be retrived from the ComponentChest it resides in. This name is implied by the string key the deck was associated with in the return value from GameDelegate.ConfigureDecks().

func (*Deck) NewBoard

func (d *Deck) NewBoard(length int, maxSize int) Board

NewBoard returns a new board associated with the given deck. length is the number of spaces to create. maxSize is the maximum size for each growable Stack in the board. 0 means "no limitation". If you pass maxSize of 1, consider simply using a sized Stack for that property instead, as those are semantically equivalent, and a sized Stack is simpler. Typically you'd use this in your GameDelegate's GameStateConstructor() and other similar methods; although in practice it is much more common to use struct-tag based inflation, making direct use of this constructor unnecessary. See StructInflater for more.

func (*Deck) NewSizedStack

func (d *Deck) NewSizedStack(size int) SizedStack

NewSizedStack returns a new SizedStack (a stack whose FixedSize() will return true) associated with this deck. Typically you'd use this in your GameDelegate's GameStateConstructor() and other similar methods; although in practice it is much more common to use struct-tag based inflation, making direct use of this constructor unnecessary. See StructInflater for more.

func (*Deck) NewStack

func (d *Deck) NewStack(maxSize int) Stack

NewStack returns a new default (growable Stack) with the given size based on this deck. The returned stack will allow up to maxSize items to be inserted. If you don't want to set a maxSize on the stack (you often don't) pass 0 for maxSize to allow it to grow without limit. Typically you'd use this in your GameDelegate's GameStateConstructor() and other similar methods; although in practice it is much more common to use struct-tag based inflation, making direct use of this constructor unnecessary. See StructInflater for more.

func (*Deck) SetGenericValues

func (d *Deck) SetGenericValues(v ComponentValues)

SetGenericValues sets the ComponentValues to return for every generic component that is returned via GenericComponent(). May only be set before added to a chest, that is within your GameDelegate's ConfigureDecks method. Should be the same underlying struct type as the ComponentValues used for other components of this type.

type DelayedError

type DelayedError chan error

DelayedError is a chan on which an error (or nil) will be sent at a later time. Primarily returned from game.ProposeMove(), so the method can return immediately even before the move is processed, which might take a long time if there are many moves ahead in the queue.

type Game

type Game struct {
	// contains filtered or unexported fields
}

A Game represents a specific game between a collection of Players; an instantiation of a game of the given type. Create a new one with GameManager.NewGame().

func (*Game) Agents

func (g *Game) Agents() []string

Agents returns the agent configuration for the game.

func (*Game) Created

func (g *Game) Created() time.Time

Created returns the time stamp when this game was first created.

func (*Game) CurrentState

func (g *Game) CurrentState() ImmutableState

CurrentState returns the state object for the current state. Equivalent, semantically, to game.State(game.Version())

func (*Game) Finished

func (g *Game) Finished() bool

Finished is whether the came has been completed. If it is over, the Winners will be set. A game is finished when GameDelegate.CheckGameFinished() returns true. Once a game is Finished it may never be un-finished, and no more moves may ever be applied to it.

func (*Game) ID

func (g *Game) ID() string

ID returns the unique id string that corresponds to this particular game. The ID is used in URLs and to retrieve this particular game from storage.

func (*Game) JSONForPlayer

func (g *Game) JSONForPlayer(player PlayerIndex, state ImmutableState) (interface{}, error)

JSONForPlayer returns an object appropriate for being json'd via json.Marshal. The object is the equivalent to what MarshalJSON would output, only as an object, and with state sanitized for the current player. State should be a state for this game (e.g. an old version). If state is nil, the game's CurrentState will be used. This is effectively equivalent to state.SanitizeForPlayer().

func (*Game) Manager

func (g *Game) Manager() *GameManager

Manager is a reference to the GameManager that controls this game.

func (*Game) MarshalJSON

func (g *Game) MarshalJSON() ([]byte, error)

MarshalJSON returns a marshaled version of the output of JSONForPlayer for AdminPlayerIndex.

func (*Game) Modifiable

func (g *Game) Modifiable() bool

Modifiable returns true if this instantiation of the game can be modified. Games that are created via GameManager.NewGame() or retrieved from GameManager.Game() can be modified directly via ProposeMove, and the game object will be updated as those changes are made. Games that return Modifiable() false can still have ProposeMove called on them; they will simply forward the move to a game for this Id that is modifiable.

func (*Game) Modified

func (g *Game) Modified() time.Time

Modified returns the timstamp when the last move was applied to this game.

func (*Game) Move

func (g *Game) Move(version int) (Move, error)

Move returns the Move that was applied to get the Game to the given version; an inflated version of the MoveStorageRecord. Not to be confused with Moves(), which returns examples of moves that haven't yet been applied, but have their defaults set based on the current state.

func (*Game) MoveByName

func (g *Game) MoveByName(name string) Move

MoveByName returns a move of the given name set to reasonable defaults for the game at its current state. Moves() is similar to this, but returns all moves.

func (*Game) MoveRecords

func (g *Game) MoveRecords(upToVersion int) []*MoveStorageRecord

MoveRecords returns all of the move storage records up to upToVersion, in ascending order. If upToVersion is 0 or less, game.Version() will be used for upToVersion. It is cached so repeated calls should be fast. This is a wrapper around game.Manager().Storage().Moves(), cached for performance.

func (*Game) Moves

func (g *Game) Moves() []Move

Moves returns an array of all Moves with their defaults set for this current state. This method is useful for getting a list of all moves that could possibly be applied to the game at its current state. base.GameDelegate.ProposeFixUpMove uses this. Not to be confused with Move(), which returns an inflated version of a move that has already been succdefully applied to this game in the past.

func (*Game) Name

func (g *Game) Name() string

Name returns the name of this game type. Convenience method for game.Manager().Delegate().Name().

func (*Game) NumAgentPlayers

func (g *Game) NumAgentPlayers() int

NumAgentPlayers returns the number of players who have agents configured on them.

func (*Game) NumPlayers

func (g *Game) NumPlayers() int

NumPlayers returns the number of players for this game, based on how many PlayerStates are in CurrentState. Note that if your game logic is complex, this is likely NOT what you want, instead you might want GameDelegate.NumSeatedActivePlayers. See the package doc of boardgame/behaviors for more.

func (*Game) ProposeMove

func (g *Game) ProposeMove(move Move, proposer PlayerIndex) DelayedError

ProposeMove is the way to propose a move to the game. DelayedError will return an error in the future if the move was unable to be applied, or nil if the move was applied successfully. Proposer is the PlayerIndex of the player who is notionally proposing the move. If you don't know which player is moving it, AdminPlayerIndex is a reasonable default that will generally allow any move to be made. After the move is applied, your GameDelegate's ProposeFixUpMove will be called; if any move is returned it will be applied, repeating the cycle until no moves are returned from ProposeFixUpMove. DelayedError will only resolve once any applicable FixUp moves have been applied already. This is legal to call on a non-modifiable game--the change will be dispatched to a modifiable version of the game with this ID, and afterwards this Game object's state will be updated in place with the new values after the change (by automatically calling Refresh()).

func (*Game) Refresh

func (g *Game) Refresh()

Refresh goes and sets this game object to reflect the current state of the underlying game in Storage. Basically, when you call manager.Game() you get a snapshot of the game in storage at that moment. If you believe that the underlying game in storage has been modified, calling Refresh() will re-load the snapshot, effectively. You only have to do this if you suspect that a modifiable version of this game somewhere in another application binary that's currently running may have changed since this game object was created. You don't need to call this after calling ProposeMove, even on non- modifiable games; it will have been called for you already. If you only have one instance of your application binary running at a time, you never need to call this.

func (*Game) State

func (g *Game) State(version int) ImmutableState

State returns the state of the game at the given version. Because states can only be modffied in moves, the state returned is immutable.

func (*Game) StorageRecord

func (g *Game) StorageRecord() *GameStorageRecord

StorageRecord returns a GameStorageRecord representing the aspects of this game that should be serialized to storage.

func (*Game) Variant

func (g *Game) Variant() Variant

Variant returns a copy of the Variant passed to NewGame to create this game originally.

func (*Game) Version

func (g *Game) Version() int

Version returns the version number of the highest State that is stored for this game. This number will increase by one every time a move is applied.

func (*Game) Winners

func (g *Game) Winners() []PlayerIndex

Winners is the player indexes who were winners. Typically, this will be one player, but it could be multiple in the case of tie, or 0 in the case of a draw. Will return nil if Finished() is not yet true.

type GameDelegate

type GameDelegate interface {

	//Name is a string that defines the type of game this is. This must return
	//the package name that contains the game (e.g.
	//"github.com/jkomoros/mygame" should return "mygame"), since the package
	//name and the delegate.Name() are both used at different times in the
	//system, since one can be determined statically and the other only at
	//run-time. NewGameManager will fail if that is not true.The name should
	//be unique and compact since it will sometimes be used in a URL path.
	//Good examples are "tictactoe", "blackjack". Once configured, names
	//should never change over the lifetime of the gametype, since it will be
	//persisted in storage.
	Name() string

	//DisplayName is a string that defines the type of game this is in a way
	//appropriate for humans. The name should be unique but human readable. It
	//is purely for human consumption, and may change over time with no
	//adverse effects. Good examples are "Tic Tac Toe", "Blackjack".
	//Subclasses should override this.
	DisplayName() string

	//Description is a string that describes the game type in a descriptive
	//sentence, for use in showing to end users. A reasonable value for
	//"tictactoe" is "A classic game where players compete to get three in a
	//row"
	Description() string

	//ConfigureMoves will be called during creation of a GameManager in
	//NewGameManager. This is the time to install moves onto the manager by
	//returning a list of moves to install. This is the single most important
	//configuration point for your game logic, as the collection of moves for
	//the game--and the logic of when they apply--is the bedrock of your game
	//logic. Typically you use moves.Combine and friends to organize your list
	//of moves to install. If the moves you add are illegal for any reason,
	//NewGameManager will fail with an error. By the time this is called.
	//delegate.SetManager will already have been called, so you'll have access
	//to the manager via Manager().
	ConfigureMoves() []MoveConfig

	//ConfigureAgents will be called when creating a new GameManager. Emit the
	//agents you want to install.
	ConfigureAgents() []Agent

	//ConfigureDecks will be called when the GameManager is being booted up.
	//Each entry in the return value will be added to the ComponentChest that
	//is being created for this game type. This method is where you create
	//individual decks via NewDeck and associate the right underlying
	//ComponentValues with each component via AddComponent.
	ConfigureDecks() map[string]*Deck

	//ConfigureEnums is called during set up of a new GameManager. Return the
	//set of enums you want to be associated with this GameManagaer's Chest.
	//`boardgame-util codegen` will often generate this automatically for you.
	ConfigureEnums() *enum.Set

	//ConfigureConstants is called during set-up of a new GameManager. Return
	//the map of constants you want to create, which will be configured onto
	//the newly created chest. If any of the constants cannot be added to the
	//ComponentChest, errors, the GameManager will fail to be set up.
	//Constants are primarily useful in two cases: first, when you want to
	//have access to a constant value client-side, and second, when you want
	//to be able to use a constant value in a struct tag provided as an
	//instruction for a StructInflater.
	ConfigureConstants() PropertyCollection

	//GameStateConstructor and PlayerStateConstructor are called to get an
	//instantiation of the concrete game/player structs that your package
	//defines. This is used both to create the initial state, but also to
	//inflate states from the database. These methods should always return the
	//underlying same type of struct when called. This means that if different
	//players have very different roles in a game, there might be many
	//properties that are not in use for any given player. The simple
	//properties (ints, bools, strings) should all be their zero-value.
	//Importantly, all Stacks, Timers, and Enums should be non- nil, because
	//an initialized struct contains information about things like MaxSize,
	//Size, and a reference to the deck they are affiliated with. GameManger
	//will automatically create and use StructInflaters for these types of
	//objects, allowing you to use tag-based configuration to automatically
	//inflate these properties. See the documentation for StructInflater for
	//more.
	GameStateConstructor() ConfigurableSubState
	//PlayerStateConstructor is similar to GameStateConstructor, but playerIndex
	//is provided as a convenience if it's useful (your constructor need not do
	//anything wiht it, typically `return new(playerState)` is sufficient).
	PlayerStateConstructor(player PlayerIndex) ConfigurableSubState

	//DynamicComponentValuesConstructor returns an empty
	//DynamicComponentValues for the given deck. DynamicComponentValues are
	//useful for representing when a given component has mutable properties
	//associated with it--for example, if a given card could have a stack of
	//tokens on top, the stack of tokens would be a property on a
	//DynamicComponentValues associated with that card component. If nil is
	//returned, then the components in that deck don't have any dynamic
	//component state. This method must always return the same underlying type
	//of struct for the same deck. Like GameStateConstructor and
	//PlayerStateConstructor, the engine will automatically create
	//StructInflaters for these objects, allowing you to use tag-based
	//inflation of properties. See StructInflate for more. If the returned
	//object also implements the ComponentValues interface, then
	//SetContainingComponent will be called on the DynamicComponent whenever
	//one is created, with a reference back to the component it's associated
	//with.
	DynamicComponentValuesConstructor(deck *Deck) ConfigurableSubState

	//DistributeComponentToStarterStack is called during set up of a given
	//Game to establish the Deck/Stack invariant that every component in the
	//chest is placed in precisely one Stack. Game will call this on each
	//component in the Chest in order. This is where the logic goes to make
	//sure each Component goes into its correct starter stack. You must return
	//a non-nil Stack for each call, after which the given Component will be
	//inserted into NextSlotIndex of that stack. If that is not the ordering
	//you desire, you can fix it up in FinishSetUp by using SwapComponents. If
	//any errors are returned, any nil Stacks are returned, or any returned
	//stacks don't have space for another component, NewGame will fail and
	//return an error. State and Component are only provided for reference; do
	//not modify them.
	DistributeComponentToStarterStack(state ImmutableState, c Component) (ImmutableStack, error)

	//BeginSetup is called on a newly created Game before components are
	//distributed via DistributeComponentToStarterStack. If you need to modify
	//your state before components are distributed, do it here. It is also
	//where the variant configuration for your gametype will be passed (it
	//will already have been checked for legality and had all configure
	//defaults set), although you can also retrieve that at any time via
	//game.Variant(). This is a good place to configure state that will be
	//necessary for you to make the right decisions in
	//DistributeComponentToStarterStack, or to transcribe config information
	//you were passed into properties on your gameState as appropriate. If
	//error is non-nil, Game setup will be aborted, with the reasoning
	//including the error message provided.
	BeginSetUp(state State, variant Variant) error

	//FinishSetUp is called during NewGame, *after* components have been
	//distributed to their StarterStack. This is the last chance to modify the
	//state before the game's initial state is considered final. For example,
	//if you have a card game this is where you'd make sure the starter draw
	//stacks are shuffled. If your game has multiple rounds, or if you don't
	//want the game to start with it already set-up (e.g. you want to show
	//animations of starter cards being dealt) then it's probably best to do
	//most of the logic in a SetUp phase. See the README for more. If error is
	//non-nil, Game setup will be aborted, with the reasoning including the
	//error message provided.
	FinishSetUp(state State) error

	//CheckGameFinished should return true if the game is finished, and who
	//the winners are. Called after every move is applied.
	CheckGameFinished(state ImmutableState) (finished bool, winners []PlayerIndex)

	//ProposeFixUpMove is called after a move has been applied. It may return
	//a FixUp move, which will be applied before any other moves are applied.
	//If it returns nil, we may take the next move off of the queue. FixUp
	//moves are useful for things like shuffling a discard deck back into a
	//draw deck, or other moves that are necessary to get the GameState back
	//into reasonable shape. base.GameDelegate's defintion is almost always
	//suficient.
	ProposeFixUpMove(state ImmutableState) Move

	//DefaultNumPlayers returns the number of users that new games of this
	//type default to. For example, for tictactoe, it will be 2. If 0 is
	//provided to manager.NewGame(), we wil use this value instead.
	DefaultNumPlayers() int

	//Min/MaxNumPlayers should return the min and max number of players,
	//respectively. The engine doesn't use this directly, instead looking at
	//LegalNumPlayers. Typically your LegalNumPlayers will check the given
	//number of players is between these two extremes.
	MinNumPlayers() int
	MaxNumPlayers() int

	//LegalNumPlayers will be consulted when a new game is created. It should
	//return true if the given number of players is legal, and false
	//otherwise. If this returns false, the NewGame will fail with an error.
	//Game creation will automatically reject a numPlayers that does not
	//result in at least one player existing. Generally this is simply
	//checking to make sure the number of players is between Min and Max
	//(inclusive), although some games could only allow, for example, even
	//numbers of players.
	LegalNumPlayers(numPlayers int) bool

	//PlayerMayBeActive should return whether the given PlayerIndex may be
	//"active". In general, this should just return true all of the time,
	//because any player index that is between 0 and NumPlayers is valid. But
	//there are certain times when a given player actually isn't valid. For
	//example, a notional player "seat" might not be filled by a real player
	//currently. This is primarily used for things like PlayerIndex.Next() and
	//Valid(). This is a place for things like behaviors.InactivePlayer to hook
	//different behavior into the core engine.
	PlayerMayBeActive(player ImmutableSubState) bool

	//Variants returns a VariantConfig, which describes the different
	//categories of configuration values and the legal values they may take on
	//when a new game is created. In general if you want to inspect legal
	//variants in your own game logic you shouldn't call this, but instead
	//call gameManager.Variants() which will ensure your VariantConfig is
	//initalized and memoize the return result.
	Variants() VariantConfig

	//CurrentPlayerIndex returns the index of the "current" player for the
	//given game state--a notion that is game specific (and sometimes
	//inapplicable). If CurrentPlayer doesn't make sense (perhaps the game
	//never has a notion of current player, or the type of round that we're in
	//has no current player), this should return ObserverPlayerIndex. The
	//result of this method is used to power state.CurrentPlayer.
	CurrentPlayerIndex(state ImmutableState) PlayerIndex

	//CurrentPhase returns the phase that the game state is currently in.
	//Phase is a formalized convention used in moves.Default to make it easier
	//to write fix-up moves that only apply in certain phases, like SetUp. The
	//return result is primarily used in moves.Default to check whether it is
	//one of the phases in a give Move's LegalPhases. See moves.Default for
	//more information. The only use of this method in the main library is
	//when generating a MoveStorageRecord.
	CurrentPhase(state ImmutableState) int

	//PhaseEnum returns the enum for game phases (the return values of
	//CurrentPhase are expected to be valid enums within that enum). If this
	//returns a non-nil enums.TreeEnum, then the state will not be able to be
	//saved if CurrentPhase() returns a value that is not a leaf-node. The
	//core package doesn't rely on this method directly.
	PhaseEnum() enum.Enum

	//GroupEnum should return the enum to use for group membership in
	//SanitizationPolicy. This enum's string values will be legal keys to
	//be passed to delegate.SanitizationPolicy in addition to the built-in
	//values.
	GroupEnum() enum.Enum

	//GroupMembership should return the groups this playerState is part of
	//(where the ints are valid values from GroupEnum()). This information will
	//be passed into SanitizationPolicy after being transformed to have string
	//keys, and extended with 'all', and any built ins like self or other, and
	//ComputedPlayerGroupMembership . A nil return value is legal.
	GroupMembership(playerState ImmutableSubState) map[int]bool

	//ComputedPlayerGroupMembership is an opportunity for your game's
	//sanitization logic to handle more complex group membership that is tied to
	//how the player state in question compares to the playerState related to
	//the player the state is being sanitized for. For example,
	//base.GameDelegate does lots of special behavior e.g. 'same-ENUMNAME' via
	//overriding this method. playerMembership and viewingAsPlayerMembership
	//will be the return values of delegate.GroupMembership for the different
	//player states. Note that viewingAsPlayerMembership might be a zero-entry
	//map if the viewingAsPlayer is ObserverPlayerIndex. Your method should
	//return an error if the groupName is not one it knows how to process. This
	//is only applied on players, and not other types of subStates currently.
	ComputedPlayerGroupMembership(groupName string, playerMembership, viewingAsPlayerMembership map[int]bool) (bool, error)

	//SanitizationPolicy is consulted when sanitizing states. It is called for
	//each prop in the state, including the set of groups that this player is a
	//mamber of. In practice the default behavior of base.GameDelegate, which
	//uses struct tags to figure out the policy, is sufficient and you do not
	//need to override this. For more on how sanitization works, see the
	//documenation for Policy. The statePropetyRef passed will never be
	//StateGroupComponentValues, and will always have the Index properties set
	//to 0, but remember that the returned Policy will be applied to all
	//Indexes. The string keys will be the string values of GroupEnum()'s keys,
	//as well as always including 'all' and potentially also 'self' and 'other',
	//and any other keys that were provided that didn't error for
	//delegate.ComputedPlayerGroupMembership(). See the documentation of
	//StructInflater.PropertySanitizationPolicy for more about the special
	//values of 'all', 'other', and 'self'.
	SanitizationPolicy(prop StatePropertyRef, groupMembership map[string]bool) Policy

	//If you have computed properties that you want to be included in your
	//JSON (for example, for use clientside), export them here by creating a
	//dictionary with their values.
	ComputedGlobalProperties(state ImmutableState) PropertyCollection
	ComputedPlayerProperties(player ImmutableSubState) PropertyCollection

	//Diagram should return a basic debug rendering of state in multi-line
	//ascii art. Useful for debugging. State.Diagram() will reach out to this
	//method.
	Diagram(s ImmutableState) string

	//SetManager configures which manager this delegate is in use with. A
	//given delegate can only be used by a single manager at a time.
	SetManager(manager *GameManager)

	//Manager returns the Manager that was set on this delegate.
	Manager() *GameManager
}

GameDelegate is the key entrypoint for the game logic specific to the game you are defining. Think of it is as the brain that is inserted into the robot shell of GameManager to imbue it with life. Typically your package that defines your game will have one public entrypoint, which is to return a GameDelegate for that packge. All logic specific to your game is configured via the return values of various methods in your GameDelegate. Your GameDelegate defines configuration for the type of game in general (via Configure* methods), as well as lifecycle methods for specific games (e.g. DistributeComponentToStarterStack). base.GameDelegate is a useful base struct to embed in your own GameDelegate, providing reasonable default behavior for nearly every method in GameDelegate.

type GameManager

type GameManager struct {
	// contains filtered or unexported fields
}

GameManager defines the logic and machinery for a given game type. It is the core game object that represents a game type in the engine. GameManager is a struct provided by the core engine; think of your GameDelegate as the game- type specific brain that will be plugged into the generic GameManager to imbue it with life. GameManagers manage fetching specific games from storage, proposing moves, and other lifecycle methods. All games of a certain type on a certain computer use the same GameManager.

func NewGameManager

func NewGameManager(delegate GameDelegate, storage StorageManager) (*GameManager, error)

NewGameManager creates a new game manager with the given GameDelegate. It will validate that the various sub-states are reasonable, and will call ConfigureMoves and ConfigureAgents and then check that all tiems are configured reaasonably. It does a large amount of verification and wiring up of your game type to get it ready for use, and will error if any part of the configuration appears suspect.

func (*GameManager) AgentByName

func (g *GameManager) AgentByName(name string) Agent

AgentByName will return the agent with the given name, or nil if one doesn't exist. See also Agents()

func (*GameManager) Agents

func (g *GameManager) Agents() []Agent

Agents returns a slice of all agents configured on this Manager via GameDelegate.ConfigureAgents.

func (*GameManager) Chest

func (g *GameManager) Chest() *ComponentChest

Chest is the ComponentChest in use for this game.

func (*GameManager) Delegate

func (g *GameManager) Delegate() GameDelegate

Delegate returns the GameDelegate configured for these games, that was associated with this GameManager in NewGameManager.

func (*GameManager) ExampleMoveByName

func (g *GameManager) ExampleMoveByName(name string) Move

ExampleMoveByName returns an example move with that name, but without initializing it with a state. See also ExampleMoves().

func (*GameManager) ExampleMoves

func (g *GameManager) ExampleMoves() []Move

ExampleMoves returns a list of example moves, which are moves not initalized based on a state. The list of moves is based on what your GameDelegate.ConfigureMoves() returned.

func (*GameManager) ExampleState

func (g *GameManager) ExampleState() ImmutableState

ExampleState will return a fully-constructed state for this game, with a single player and no specific game object associated. This is a convenient way to inspect the final shape of your State objects using your various Constructor() methods and after tag-based inflation. Primarily useful for meta- programming approaches, used often in the moves package.

func (*GameManager) Game

func (g *GameManager) Game(id string) *Game

Game fetches a new non-modifiable copy of the given game from storage. If you want a modifiable version, see ModifiableGame. You'd use this method instead of ModifiableGame in situations where you're on a read-only servant binary.

func (*GameManager) Internals

func (g *GameManager) Internals() *ManagerInternals

Internals returns a ManagerInternals for this manager. All of the methods on a ManagerInternals are designed to be used only in very specific conditions; users of this package should almost never do anything with these

func (*GameManager) Logger

func (g *GameManager) Logger() *logrus.Logger

Logger returns the logrus.Logger that is in use for this game. This is a reasonable place to emit info or debug information specific to your game. This is initialized to a default logger when NewGameManager is called, and calls to SetLogger will fail if the logger is nil, so this will always return a non-nil logger. Change the logger in use for this GameManager via SetLogger().

func (*GameManager) ModifiableGame

func (g *GameManager) ModifiableGame(id string) *Game

ModifiableGame returns a modifiable Game with the given ID. Either it returns one it already knows about that is resident in memory, or it creates a modifiable version from storage (if one is stored in storage). If a game cannot be created from those ways, it will return nil. The primary way to avoid race conditions with the same underlying game being stored to the store is that only one modifiable copy of a Game should exist at a time. It is up to the specific user of boardgame to ensure that is the case. As long as manager.Game is used, a single manager in a given application binary will not allow multiple modifiable versions of a single game to be "checked out". However, if there could be multiple managers loaded up at the same time for the same store, it's possible to have a race condition. For example, it makes sense to have only a single server that takes in proposed moves from a queue and then applies them to a modifiable version of the given game.

func (*GameManager) NewDefaultGame

func (g *GameManager) NewDefaultGame() (*Game, error)

NewDefaultGame returns a NewGame with everything set to default. Simple sugar for NewGame(0, nil, nil).

func (*GameManager) NewGame

func (g *GameManager) NewGame(numPlayers int, variantValues map[string]string, agentNames []string) (*Game, error)

NewGame returns a new specific game instation that is set up with these options, persisted to the datastore, starter state created, first round of fix up moves applied, and in general ready for the first move to be proposed. The variant will be passed to delegate.Variant().NewVariant(). If the game you want to access has already been created, use GameManager.Game() or ModifiableGame().

func (*GameManager) SetLogger

func (g *GameManager) SetLogger(logger *logrus.Logger)

SetLogger configures the manager to use the given logger (which can be accessed via GameManager.Logger()) Will fail if logger is nil.

func (*GameManager) Storage

func (g *GameManager) Storage() StorageManager

Storage is the StorageManager games that was associated with this GameManager in NewGameManager.

func (*GameManager) Variants

func (g *GameManager) Variants() VariantConfig

Variants returns the VariantConfig for this game type. A simple wrapper around gameDelegate.Variants() that calls Initialize() on that result and also memoizes it for performance.

type GameStorageRecord

type GameStorageRecord struct {
	//Name is the type of the game, from its manager. Used for sanity
	//checking.
	Name string
	ID   string
	//SecretSalt for this game for things like component Ids. Should never be
	//transmitted to an insecure or untrusted environment; the only way to
	//access it outside this package is via this field, because it must be
	//able to be persisted to and read from storage.
	SecretSalt string `json:",omitempty"`
	Version    int
	Winners    []PlayerIndex
	Finished   bool
	Created    time.Time
	//Modified is updated every time a new move is applied.
	Modified time.Time
	//NumPlayers is the reported number of players when it was created.
	//Primarily for convenience to storage layer so they know how many players
	//are in the game.
	NumPlayers int
	Agents     []string
	Variant    Variant
}

GameStorageRecord is a simple struct with public fields representing the important aspects of a game that should be serialized to storage. The fields are broken out specifically so that the storage layer can understand these properties in queries. Typically you don't use this struct directly, instead getting an inflated version via something like GameManager.ModifiableGame() and then using the associated methods on the struct to get at the undelying values.

type ImmutableBoard

type ImmutableBoard interface {
	ImmutableSpaces() []ImmutableStack
	ImmutableSpaceAt(index int) ImmutableStack
	Len() int
	// contains filtered or unexported methods
}

ImmutableBoard is a version of a Board without any of the mutator methods. See Board for more.

type ImmutableComponentInstance

type ImmutableComponentInstance interface {
	//ImmutableComponentInstances have all of the information of a base Component, as
	//often that's the information you most need.
	Component

	//ID returns a semi-stable ID for this component within this game and the
	//current state this component instance is associated with. Within this
	//game, it will only change when the shuffleCount for this component
	//changes (that is, when the component is in a stack that is Shuffle()'d
	//or when the ComponentInstance is in a stach that has a component moved
	//to it via SecretMoveTo). Across games the Id for the "same" component
	//will be different, in a way that cannot be guessed without access to
	//game.SecretSalt. Typically your game logic can ignore IDs and not use
	//them; they're provided to enable certain scenarios and animations in the
	//core engine and other packages. See the documentation for Policy for
	//more on IDs and why they exist.
	ID() string

	//ImmutableDynamicValues returns the Dynamic Values for this component in the state
	//this instance is associated with. A convenience so you don't have to go
	//find them within the state.DynamicComponentValues yourself.
	ImmutableDynamicValues() ImmutableSubState

	//ContainingImmutableStack will return the stack and slot index for the
	//associated component, if that location is not sanitized, and the
	//componentinstance is legal for the state it's in. If no error is
	//returned, stack.ComponentAt(slotIndex) == c will evaluate to true.
	ContainingImmutableStack() (stack ImmutableStack, slotIndex int, err error)

	//ImmutableState returns the State object that this ComponentInstance is affiliated
	//with.
	ImmutableState() ImmutableState
	// contains filtered or unexported methods
}

ImmutableComponentInstance is a specific instantiation of a component as it exists in the particular State it is associated with. They are like a ComponentInstance, but without mutating methods. ImmutableComponentInstances also implement all of the Component information, as a convenience you often need both bits of inforamation. The downside of this is that two Component values can't be compared directly for equality because they may be different underlying objects and wrappers. If you want to see if two Components that might be from different states refer to the same underlying conceptual Component, use Equivalent(). However, ImmutableComponentInstances compared with another ImmutableComponentInstance for the same component in the same state will be equal. See also ComponentInstance, which extends this interface with mutators as well.

type ImmutableSizedStack

type ImmutableSizedStack interface {
	//An ImmutableSizedStack can be used everywhere a normal ImmutableStack
	//can. Note the behavior of an ImmutableSizedStack's base ImmutableStack
	//methods will often be different than a default stack.
	ImmutableStack

	//FirstComponentIndex returns the index of the first non-nil component
	//from the left.
	FirstComponentIndex() int
	//LastComponentIndex returns the index of the first non-nil component from
	//the right.
	LastComponentIndex() int
}

ImmutableSizedStack is a specific type of Stack that has a specific number of slots, any of which may be nil. Although very few methods are added, the basic behavior of the Stack methods is quite different for these kinds of stacks. See also SizedStack, which adds mutator methods to this definition. See the documentation for Stack for more about the hierarchy of Stack objects.

type ImmutableStack

type ImmutableStack interface {

	//Deck returns the Deck associated with this stack.
	Deck() *Deck

	//Len returns the number of slots in the Stack. For a normal Stack this is
	//the number of items in the stack. For SizedStacks, this is the number of
	//slots--even if some are unfilled.
	Len() int

	//NumComponents returns the number of components that are in this stack.
	//For default Stacks this is the same as Len(); for SizedStacks, this is
	//the number of non-nil slots.
	NumComponents() int

	//SlotsRemaining returns how many slots there are left in this stack to
	//add items. For default stacks this will be the number of slots until
	//maxSize is reached (or MaxInt64 if there is no maxSize). For SizedStacks
	//this will be the number of empty slots.
	SlotsRemaining() int

	//MaxSize returns the Maxium Size, if set, for default stacks. For sized
	//stacks it will return the number of total current slots (filled and
	//unfilled), which is equivalent to Len().
	MaxSize() int

	//ImmutableComponentAt retrieves the component at the given index in the
	//stack. See also Stack.ComponentAt.
	ImmutableComponentAt(index int) ImmutableComponentInstance

	//ImmutableComponents returns all of the components. Equivalent to calling
	//ImmutableComponentAt from 0 to Len(). See also Stack.Components().
	ImmutableComponents() []ImmutableComponentInstance

	//ImmutableFirst returns a reference to the first non-nil component from
	//the left, or nil if empty. For default stacks, this is simply a
	//convenience wrapper around stack.ImmutableComponentAt(0). SizedStacks
	//however will use the result of FirstComponentIndex().
	ImmutableFirst() ImmutableComponentInstance

	//ImmutableLast returns a reference to the first non-nil component from
	//the right, or nil if empty. For default stacks, this is simply a
	//convenience wrapper around stack.ComponentAt(stack.Len() - 1).
	//SizedStacks however will use the result of LastComponentIndex().
	ImmutableLast() ImmutableComponentInstance

	//IDs returns a slice of strings representing the IDs of each component at
	//each index. Under normal circumstances this will be the results of
	//calling c.ID() on each component in order. This information will be
	//elided or modified if the state has been sanitized. See documentation
	//for Policy for more on when and how these might be changed.
	IDs() []string

	//LastSeen represents an unordered list of the last version number at
	//which the given ID was seen in this stack. A component is "seen" at
	//three moments: 1) when it is moved to this stack, 2) immediately before
	//its ID is scrambled, and 3) immediately after its ID is scrambled.
	//LastSeen thus represents the last time that we knew for sure it was in
	//this stack --although it may have been in this stack after that, and may
	//no longer be in this stack. This information may be elided if the stack
	//has been sanitized. See the documentation for Policy for more about when
	//this might be elided.
	IDsLastSeen() map[string]int

	//ShuffleCount is the number of times during this game that Shuffle (or
	//PublicShuffle) have been called on this stack. Not visible in some
	//sanitization policies, see Policy for more.
	ShuffleCount() int

	//ImmutableSizedStack will return a version of this stack that implements
	//the ImmutableSizedStack interface, if that's possible, or nil otherwise.
	ImmutableSizedStack() ImmutableSizedStack

	//MergedStack will return a version of this stack that implements the
	//MergedStack interface, if that's possible, or nil otherwise.
	MergedStack() MergedStack

	//ImmutableBoard will return the Board that this Stack is part of, or nil
	//if it is not part of a board.
	ImmutableBoard() ImmutableBoard

	//If Board returns a non-nil Board, this will return the index within the
	//Board that this stack is.
	BoardIndex() int
	// contains filtered or unexported methods
}

ImmutableStack is a flavor of Stack, but minus any methods that can be used to mutate anything. See the documentation for Stack for more about the hierarchy of Stack-based types. ImmutableStack is the lowest-common- denominator that all Stack types implement.

type ImmutableState

type ImmutableState interface {

	//ImmutableGameState is a reference to to the underlying object returned
	//from your GameDelegate.GameStateConstructor(), and can be safely cast
	//back to that underlying struct so you can access its methods directly in
	//a type- checked way. The difference is that the object formally exposed
	//lacks the mutator methods, although when you cast back you'll get access
	//to the full struct--be careful not to mutate things as they will not be
	//persisted. See State.GameState for more.
	ImmutableGameState() ImmutableSubState
	//Each PlayerState is a reference to to the underlying object returned
	//from your GameDelegate.PlayerStateConstructor(), and can be safely cast
	//back to that underlying struct so you can access its methods directly in
	//a type- checked way. The difference is that the object formally exposed
	//lacks the mutator methods, although when you cast back you'll get access
	//to the full struct--be careful not to mutate things as they will not be
	//persisted. See State.PlayerStates for more.
	ImmutablePlayerStates() []ImmutableSubState
	//Each SubState is a reference to to the underlying object returned from
	//your GameDelegate.DynamicComponentValuesConstructor() for the deck with
	//that name, and can be safely cast back to that underlying struct so you
	//can access its methods directly in a type- checked way. The difference
	//is that the object formally exposed lacks the mutator methods, although
	//when you cast back you'll get access to the full struct--be careful not
	//to mutate things as they will not be persisted. DynamicComponentValues
	//returns a map of deck name to array of component values, one per
	//component in that deck. See State.DynamicComponentValues for more.
	ImmutableDynamicComponentValues() map[string][]ImmutableSubState

	//ImmutableCurrentPlayer returns the ImmutablePlayerState corresponding to
	//the result of delegate.CurrentPlayerIndex(), or nil if the index isn't
	//valid. This object is the same underlying struct that you returned from
	//GameDelegate.PlayerStateConstructor and can be cast back safely to
	//access the underlying methods. See State.CurrentPlayer for more.
	ImmutableCurrentPlayer() ImmutableSubState
	//CurrentPlayerIndex is a simple convenience wrapper around
	//delegate.CurrentPlayerIndex(state) for this state.
	CurrentPlayerIndex() PlayerIndex

	//Version returns the version number the state is (or will be once
	//committed).
	Version() int

	//Copy returns a deep copy of the State, including copied version of the
	//Game and Player States. Note that copying uses the
	//ProperyReadSetConfigurer interface, so any properties not enumerated
	//there or otherwise defined in the constructors on your GameDelegate will
	//not be copied.
	Copy(sanitized bool) (ImmutableState, error)

	//Diagram returns a basic, ascii rendering of the state for debug rendering.
	//It thunks out to Delegate.Diagram.
	Diagram() string

	//Santizied will return false if this is a full-fidelity State object, or
	//true if it has been sanitized, which means that some properties might be
	//hidden or otherwise altered. This should return true if the object was
	//created with Copy(true)
	Sanitized() bool

	//SanitizedForPlayer produces a copy state object that has been sanitized
	//for the player at the given index. The state object returned will have
	//Sanitized() return true. Will call GameDelegate.SanitizationPolicy to
	//construct the effective policy to apply. See the documentation for
	//Policy for more on sanitization.
	SanitizedForPlayer(player PlayerIndex) (ImmutableState, error)

	//Game is the Game that this state is part of. Calling
	//Game.State(state.Version()) should return a state equivalent to this State
	//(modulo sanitization, if applied). This almost always returns non-nil,
	//except if you generated the state via GameManager.ExampleState.
	Game() *Game

	//Manager returns the GameManager associated with this state, and never
	//returns nil. Typically you can fetch this via Game().Manager(), but in
	//some cases, like when the State is generated from
	//GameManager.ExampleState(), Game() will return nil.
	Manager() *GameManager

	//StorageRecord returns a StateStorageRecord representing the state.
	StorageRecord() StateStorageRecord
	// contains filtered or unexported methods
}

ImmutableState is a version of State, but minus any mutator methods. Because states may not be modified except by moves, in almost every case where a state is passed to game logic you define (whether on your GameDelegate methods, or Legal() on your move structs), an ImmutableState will be passed instead. If an ImmutableState is passed to your method, it's a strong signal that you shouldn't modify the state. Note that idiomatic use (e.g. concreteStates) will cast an ImmutableState to a State immediately in order to retrieve the concrete structs underneath, but if you do that you have to be careful not to inadvertently modify the state because the changes won't be persisted. See the documentation for State for more about states in general.

type ImmutableStateGetter

type ImmutableStateGetter interface {
	//ImmutableState() returns the state that was set via SetState(), but as an
	//ImmutableState so it has a subset of functionality directly visible.
	ImmutableState() ImmutableState

	//StatePropertyRef should return the value that was set via
	//SetStatePropetyRef. This is a good way for the substate to understand what
	//index it has, for example the player index.
	StatePropertyRef() StatePropertyRef
}

ImmutableStateGetter is included in ImmutableSubState, SubState, and ConfigureableSubState as the way to keep track of which ImmutableState a given SubState is part of. See also StateSetter, which adds getters for mutable States. Typically you use base.SubState to implement this automatically.

type ImmutableSubState

type ImmutableSubState interface {
	ContainingStateConnector
	ImmutableStateGetter
	Reader
}

ImmutableSubState is the interface that all non-modifiable sub-state objects (PlayerStates. GameStates, and DynamicComponentValues) implement. It is like SubState, but minus any mutator methods. See ConfigurableSubState for more on the SubState type hierarchy.

type ImmutableTimer

type ImmutableTimer interface {
	//Active returns true if the given timer has been Start()'d and has not
	//yet fired or been canceled.
	Active() bool
	//TimerLeft reutrns the amount of time until the timer fires, if it is
	//active.
	TimeLeft() time.Duration
	// contains filtered or unexported methods
}

ImmutableTimer is a Timer that does not have any mutator methods. See Timer for more.

type ManagerInternals

type ManagerInternals struct {
	// contains filtered or unexported fields
}

ManagerInternals is a special struct that has debug-only methods hanging off of it. Some methods need to be exposed outside of the package due to how the sub-packages are organized. All of the methods off of this object are designed to be used only by other sub-packages, and should be used at your own risk.

func (*ManagerInternals) AddCommittedCallback

func (m *ManagerInternals) AddCommittedCallback(st State, callback func()) error

AddCommittedCallback adds a function that will be called once the state is successfully saved. Typically you'd do something with this in your Move's Apply() method if you wanted to note in some external system whether the move had actually been successfully committed or not. Will be called back immediately after the state is successfully saved to the database and before any other fixup moves are called. This is only designed to be called from within a move's Apply function.

func (*ManagerInternals) ForceFixUp

func (m *ManagerInternals) ForceFixUp(game *Game) DelayedError

ForceFixUp forces the engine to check if a FixUp move applies, even if no player move is waiting to apply. Typically moves are only legal based on the state, so if a move hasn't been applied they can't be legal. But in some cases, like for server seating players, there's some outside state that might have changed that could cause a move to be legal even though the game state didn't change.

func (*ManagerInternals) ForceNextTimer

func (m *ManagerInternals) ForceNextTimer() bool

ForceNextTimer forces the next timer to fire even if it's not supposed to fire yet. Will return true if there was a timer that was fired, false otherwise.

func (*ManagerInternals) InflateMoveStorageRecord

func (m *ManagerInternals) InflateMoveStorageRecord(rec *MoveStorageRecord, game *Game) (Move, error)

InflateMoveStorageRecord takes a move storage record and turns it into a move associated with that game, if possible. Returns nil if not possible. You rarely need this; it's exposed primarily for the use of boardgame /boardgame-util/lib/golden.

func (*ManagerInternals) OrphanExampleMove

func (m *ManagerInternals) OrphanExampleMove(config MoveConfig) (Move, error)

OrphanExampleMove returns a move from the config that will be similar to a real move, in terms of struct-based auto-inflation, etc. This is exposed primarily for moves.AutoConfigrer, and generally shouldn't be used by others.

func (*ManagerInternals) RecreateGame

func (m *ManagerInternals) RecreateGame(rec *GameStorageRecord) (*Game, error)

RecreateGame creates a new game that has the same properties as the provided GameStorageRecord. It is very rarely what you want; see NewGame(), Game(), and ModifiableGame(). RecreateGame is most useful in debugging or testing scenarios where you want a game to have the same ID and SecretSalt as a previously created game, so the moves can be applied deterministically with the same input. rec generally should be a GameStorageRecord representing a game that was created in a different storage pool; if a game with that ID already exists in this storage pool RecreateGame will error.

func (*ManagerInternals) StructInflater

func (m *ManagerInternals) StructInflater(propRef StatePropertyRef) *StructInflater

StructInflater returns the autp-created StructInflater for the given type of property in your state, allowing you to retrieve the inflater in use to inspect for e.g. SanitizationPolicy configuration. Typically you don't use this directly--it's primarily provided for base.GameDelegate to use.

type MergedStack

type MergedStack interface {
	//A MergedStack can be used anywhere an ImmutableStack can be.
	ImmutableStack

	//Valid will return a non-nil error if the stack isn't valid currently
	//for example if the two stacks being merged are different sizes for an
	//overlapped stack. Valid is checked just before state is saved. If any
	//stack returns any non-nil for this then the state will not be saved.
	Valid() error

	//ImmutableStacks returns the stacks that this MergedStack is based on.1
	ImmutableStacks() []ImmutableStack

	//Overlapped will return true if the MergedStack is an overlapped stack
	//(in contrast to a concatenated stack).
	Overlapped() bool
}

MergedStack is a special variant of ImmutableStack that is actually formed from multiple underlying stacks combined. MergedStacks can never be mutated directly; instead, mutate the underlying stacks. See the documentation for Stack for more about the hierarchy of Stack types.

func NewConcatenatedStack

func NewConcatenatedStack(stack ...ImmutableStack) MergedStack

NewConcatenatedStack returns a new merged stack where all of the components in the first stack will show up, then all of the components in the second stack, and on down the list of stacks. In practice this is useful as a computed property when you have a logical stack made up of components that are santiized followed by components that are not sanitized, like in a blackjack hand. All stacks must be from the same deck, or Valid() will error.

func NewOverlappedStack

func NewOverlappedStack(stack ...ImmutableStack) MergedStack

NewOverlappedStack returns a new merged stack where any gaps in the first stack will be filled with whatever is in the same position in the second stack, and so on down the line. In practice this is useful as a computed property when you have a logical stack made up of components where some are sanitized and some are not, like the grid of cards in Memory. All stacks must be from the same deck, and return non-nil objects from ImmutableSizedStack() for all, otherwise Valid() will error.

type Move

type Move interface {

	//Legal returns nil if this proposed move is legal at the given state, or
	//an error if the move is not legal. The error message may be shown
	//directly to the end- user so be sure to make it user friendly. proposer
	//is set to the notional player that is proposing the move. proposer might
	//be a valid player index, or AdminPlayerIndex (for example, if it is a
	//FixUpMove it will typically be AdminPlayerIndex). AdminPlayerIndex is
	//always allowed to make any move. It will never be ObserverPlayerIndex,
	//because by definition Observers may not make moves. If you want to check
	//that the person proposing is able to apply the move for the given
	//player, and that it is their turn, you would do something like test
	//m.TargetPlayerIndex.Equivalent(proposer),
	//m.TargetPlayerIndex.Equivalent(game.CurrentPlayer). Legal is one of the
	//most key parts of logic for your game type. It is important for fix up
	//moves in particular to have carefully-designed Legal() methods, as the
	//ProposeFixUpMove on base.GameDelegate (which you almost always use)
	//walks through each move and returns the first one that is legal at this
	//game state--so if one of your moves is erroneously legal more often than
	//it should be it could be mistakenly applied, perhaps in an infinite
	//loop!
	Legal(state ImmutableState, proposer PlayerIndex) error

	//Apply applies the move to the state by modifying hte right properties.
	//It is handed a copy of the state to modify. If error is non-nil it will
	//not be applied to the game. It should not be called directly; use
	//Game.ProposeMove. Legal() will have been called before and returned nil.
	//Apply is the only place (outside of some of the Game initalization logic
	//on GameDelegate) where you are allowed to modify the state direclty and
	//are passed a State, not an ImmutableState.
	Apply(state State) error

	//Sets the move to have reasonable defaults for the given state.For
	//example, if the Move has a TargetPlayerIndex property, a reasonable
	//default is state.CurrentPlayer(). DefaultsForState is used to set
	//reasonable defaults for fix up moves. Typically you can skip this.
	DefaultsForState(state ImmutableState)

	//HelpText is a human-readable sentence describing what the move does in
	//general. HelpText should be the same for all moves of the same type, and
	//should not vary with the Move's specific properties. For example, the
	//HelpText for "Place Token" might be "Places the current user's token in
	//the specified slot on the board." Primarily useful just to show to a
	//user in an interface.
	HelpText() string

	//Info returns the MoveInfo object that was affiliated with this object by
	//SetInfo. It includes information about when the move was applied, the
	//name of the move, and other information.
	Info() *MoveInfo

	//SetInfo will be called after the constructor is called to set the
	//information, including what type the move is.
	SetInfo(m *MoveInfo)

	//TopLevelStruct should return the value that was set via
	//SetTopLevelStruct. It returns the Move that is at the top of the
	//embedding chain (because structs that are embedded anonymously can only
	//access themselves and not their embedders). This is useful because in a
	//number of cases embedded moves (for example moves in the moves package)
	//need to consult a method on their embedder to see if any of their
	//behavior should be overridden.
	TopLevelStruct() Move

	//SetTopLevelStruct is called right after the move is constructed, with
	//the top-level struct. This should be returned from TopLevelStruct.
	SetTopLevelStruct(m Move)

	//Moves alos have a ValidConfiguration, because moves, especially
	//sub-classes of the moves package, require set-up that can only be verified
	//at run time (for example, verifying that the embedder implements a certain
	//inteface)
	ConfigurationValidator

	//Moves, like ConfigurableSubStates, must only have all of their
	//important, persistable properties available to be inspected and modified
	//via a PropertyReadSetConfigurer. The game engine will use that interface
	//to create new moves, inflate old moves from storage, and copy moves.
	//Typically you generate this automatically for your moves with `boargame-
	//util codegen`.
	ReadSetConfigurer
}

Move is the struct that are how all modifications are made to States after initialization. Packages define structs that implement different Moves for all types of valid modifications. Moves are objects your own packages will returen. Use base.Move or moves.Default for a convenient composable base Move that will allow you to skip most of the boilerplate overhead. Your Move is similar to a SubState in that all of the persistable properties must be one of the enumerated types in PropertyType, excluding a few types. Your Moves are installed based on what your GameDelegate returns from ConfigureMoves(). See MoveConfig for more about things that must be true about structs you return. The two primary methods for your game logic are Legal() and Apply().

type MoveConfig

type MoveConfig interface {
	//Name is the name for this type of move. No other Move structs
	//in use in this game should have the same name, but it should be human-
	//friendly. For example, "Place Token" is a reasonable name, as long as no
	//other types of Move-structs will return that name in this game. Required.
	Name() string

	//Constructor should return a zero-valued Move of the given type. Normally
	//very simple: just a new(MyMoveType). Required. The moves you create may
	//not have an fields of Stack, Board, or Timer type, but may have enum.Val
	//type. Those fields must be non-nil; like delegate.GameStateConstructor
	//and others, a StructInflater will be created for each move type, which
	//allows you to provide inflation configuration via struct tags. See
	//StructInflater for more. Like ConfigurableSubState, all of the
	//properties to persist must be accessible via their ReadSetConfigurer, as
	//that is how the core engine serializes them, re-inflates them from
	//storage, and copies them.
	Constructor() func() Move

	//CustomConfiguration is an optional PropertyCollection. Some move types--
	//especially in the `moves` package--stash configuration options here that
	//will change how all moves of this type behave. Individual moves would
	//reach through via Info().CustomConfiguration() to retrieve the
	//values stored there. Different move types will store different types of
	//information there--to avoid a collision the convention is to use a
	//string name that starts with your fully qualified package name, then a
	//dot, then the propertyname, like so:
	//"github.com/jkomoros/boardgame/moves.MoveName". Those strings are often
	//encoded as package-private constants, and a
	//interfaces.CustomConfigurationOption functor factory is provided to set
	//those from outside the package. Generally you don't use this directly,
	//but moves.AutoConfigurer will help you set these for what specific
	//moves in that package expect.
	CustomConfiguration() PropertyCollection
}

MoveConfig is a collection of information used to create a Move. Your delegate's ConfigureMoves() will emit a slice of them to define which moves are valid for your game. Typically you'll use moves.Combine, moves.Add, moves.AddWithPhase, combined with moves.AutoConfigurer.Configure() to generate these. This is an interface and not a concrete struct because other packages, like moves, add more behavior to the ones they return. If you want just a vanilla one without using the moves package, use NewMoveConfig.

func NewMoveConfig

func NewMoveConfig(name string, constructor func() Move, customConfiguration PropertyCollection) MoveConfig

NewMoveConfig returns a simple MoveConfig that will return the provided parameters from its getters. Typically you don't use this, but rather use the output of moves.AutoConfigurer.Config().

type MoveInfo

type MoveInfo struct {
	// contains filtered or unexported fields
}

MoveInfo is an object that contains meta-information about a move. It is fetched via move.Info().

func (*MoveInfo) CustomConfiguration

func (m *MoveInfo) CustomConfiguration() PropertyCollection

CustomConfiguration returns the configuration object associated with this move when it was installed from its MoveConfig.CustomConfiguration().

func (*MoveInfo) Initiator

func (m *MoveInfo) Initiator() int

Initiator returns the move version that initiated this causal chain: the player Move that was applied that led to this chain of fix up moves as proposed by GameDelegate.ProposeFixUpMove. The Initiator of a PlayerMove is its own version, so this value will be less than or equal to its own version. The value of Initator is unspecified until after the move has been successfully committed.

func (*MoveInfo) Name

func (m *MoveInfo) Name() string

Name returns the name of the move type that this move is, based on the value passed in the affiliated MoveConfig from your GameDelegate.ConfigureMoves(). Calling manager.ExampleMove() with that string value will return a similar struct.

func (*MoveInfo) Timestamp

func (m *MoveInfo) Timestamp() time.Time

Timestamp returns the time that the given move was made.

func (*MoveInfo) Version

func (m *MoveInfo) Version() int

Version returns the version of this move--or the version that it will be when successfully committed.

type MoveStorageRecord

type MoveStorageRecord struct {
	Name      string
	Version   int
	Initiator int
	//The Phase as returned by Delegate.CurrentPhase() for the state the move
	//was in before it was applied. This is captured in this field because
	//moves in the moves package need to quickly inspect this value without
	//fully inflating the move structs.
	Phase int
	//The player index of the proposer of the move.
	Proposer  PlayerIndex
	Timestamp time.Time
	//The actual JSON serialized blob representing the properties of the move.
	Blob json.RawMessage
}

MoveStorageRecord is a record representing the Move that was made to get the game to its most recent version. It pops out various fields that StorageManagers could conceivably want to understand. Typically you don't use this directly, but instead fetch information for moves from game.Moves() and game.Move().

func StorageRecordForMove

func StorageRecordForMove(move Move, currentPhase int, proposer PlayerIndex) *MoveStorageRecord

StorageRecordForMove returns a MoveStorageRecord. Can't hang off of Move itself since Moves are provided by users of the library.

type PlayerIndex

type PlayerIndex int

PlayerIndex is an int that represents the index of a given player in a game. Normal values are [0, game.NumPlayers). Special values are AdminPlayerIndex and ObserverPlayerIndex. The logic of incrementing or decrementing the indexes and comparing them follows considerable non-trivial logic, so you should NEVER treat them like integers unless you're very sure of what you're doing. Instead use the methods on them, like Next(), Prev(), and Equivalent(). Typically instead of reading these directly, you use the result of p.EnsureValid().

const AdminPlayerIndex PlayerIndex = -2

AdminPlayerIndex is a special PlayerIndex that denotes the omniscient admin who can see all state and make moves whenever they want. This PlayerIndex is used for example to apply moves that your GameDelegate.ProposeFixUpMove returns, as well as when Timer's fire. It is also used when the server is in debug mode, allowing the given player to operate as the admin.

const ObserverPlayerIndex PlayerIndex = -1

ObserverPlayerIndex is a special PlayerIndex that denotes that the player in question is not one of the normal players, but someone generically watching. All hidden state should be hidden to them, and GroupSelf will never trigger for them.

func (PlayerIndex) EnsureValid

func (p PlayerIndex) EnsureValid(state ImmutableState) PlayerIndex

EnsureValid returns either the current value, if it is Valid(), or the next valid index. Typically instead of using a PlayerIndex directly you should use the result of this, which verifies that even if the current player has become invalid since the PlayerIndex was last touched, you still get a valid player index.

func (PlayerIndex) Equivalent

func (p PlayerIndex) Equivalent(other PlayerIndex) bool

Equivalent checks whether the two playerIndexes are equivalent. For most indexes it checks if both are the same. ObserverPlayerIndex returns false when compared to any other PlayerIndex. AdminPlayerIndex returns true when compared to any other index (other than ObserverPlayerIndex). This method is useful for verifying that a given TargerPlayerIndex is equivalent to the proposer PlayerIndex in a move's Legal method. moves.CurrentPlayer handles that logic for you.

func (PlayerIndex) Next

func (p PlayerIndex) Next(state ImmutableState) PlayerIndex

Next returns the next PlayerIndex, wrapping around back to 0 if it overflows, skipping any players where GameDelegate returns false for PlayerMayBeActive (if all players return false for PlayerMayBeActive it will return the current value). PlayerIndexes of AdminPlayerIndex and Observer PlayerIndex will not be affected.

func (PlayerIndex) Previous

func (p PlayerIndex) Previous(state ImmutableState) PlayerIndex

Previous returns the previous PlayerIndex, wrapping around back to len(players -1) if it goes below 0, skipping any players where GameDelegate returns false for PlayerMayBeActive (if all players return false, it will leave at the same value). PlayerIndexes of AdminPlayerIndex and Observer PlayerIndex will not be affected.

func (PlayerIndex) String

func (p PlayerIndex) String() string

String returns the int value of the PlayerIndex.

func (PlayerIndex) Valid

func (p PlayerIndex) Valid(state ImmutableState) bool

Valid returns true if the PlayerIndex's value is legal in the context of the current State--that is, it is either AdminPlayerIndex, ObserverPlayerIndex, or between 0 (inclusive) and game.NumPlayers(). It additionaly checks GameDelegate PlayerIndexMayBeActive returns true, for non-special indexes. See also WithinBounds(), which doesn't check whether the player may be active.

func (PlayerIndex) WithinBounds

func (p PlayerIndex) WithinBounds(state ImmutableState) bool

WithinBounds returns true if the index is a legal index. That is, ObserverPlayerIndex, AdminPlayerIndex, or between 0 and numPlayers - 1. It does not check whether GameDelegate.PlayerMayBeActive is true. See also Valid().

type Policy

type Policy int

Policy is the type that reprsents a sanitization policy.

A sanitization policy reflects how to tranform a given State property when presenting to someone outside of the target group. They are returned from your GameDelegate's SanitizationPolicy method, and the results are used to configure how properties are modified or elided in state.SanitizedForPlayer.

For most types of properties, there are two effective policies: PolicyVisible, in which the property is left untouched, and PolicyHidden, in which case the value is sanitized to its zero value.

However Stacks are much more complicated and have more policies. Even when the specific components in a stack aren't visible, it can still be important to know that a given ComponentInstance in one state is the same as another ComponentInstance in another state, which allows for example animations of the same logical card from one stack to another, even though the value of the card is not visible.

In order to do this, every component has a semi-stable Id. This Id is calculated based on a hash of the component, deck, deckIndex, gameId, and also a secret salt for the game. This way, the same component in different games will have different Ids, and if you have never observed the value of the component in a given game, it is impossible to guess it. However, it is possible to keep track of the component as it moves between different stacks within a game.

Every stack has an ordered list of Ids representing the Id for each component. Components can also be queried for their Id.

Stacks also have an unordered set of IdsLastSeen, which tracks the last time the Id was affirmitively seen in a stack. The basic time this happens is when a component is first inserted into a stack. (See below for additional times when this hapepns)

Different Sanitization Policies will do different things to Ids and IdsLastSeen, according to the following table:

| Policy         | Values Behavior                                                  | Ids()                       | IdsLastSeen() | ShuffleCount() |Notes                                                                                                  |
|----------------|------------------------------------------------------------------|-----------------------------|---------------|----------------|-------------------------------------------------------------------------------------------------------|
| PolicyVisible  | All values visible                                               | Present                     | Present       | Present        | Visible is effectively no transformation                                                              |
| PolicyOrder    | All values replaced by generic component                         | Present                     | Present       | Present        | PolicyOrder is similar to PolicyLen, but the order of components is observable                        |
| PolicyLen      | All values replaced by generic component                         | Sorted Lexicographically    | Present       | Present        | PolicyLen makes it so it's only possible to see the length of a stack, not its order.                 |
| PolicyNonEmpty | Values will be either 0 components or a single generic component | Absent                      | Present       | Absent         | PolicyNonEmpty makes it so it's only possible to tell if a stack had 0 items in it or more than zero. |
| PolicyHidden   | Values are completely empty                                      | Absent                      | Absent        | Absent         | PolicyHidden is the most restrictive; stacks look entirely empty.                                     |

However, in some cases it is not possible to keep track of the precise order of components, even with perfect observation. The canonical example is when a stack is shuffled. Another example would be when a card is inserted at an unknown location in a deck.

For this reason, a component's Id is only semi-stable. When one of these secret moves has occurred, the Ids is randomized. However, in order to be able to keep track of where the component is, the component is "seen" in IdsLastSeen immediately before having its Id scrambled, and immediately after. This procedure is referred to as "scrambling" the Ids.

stack.Shuffle() automatically scrambles the ids of all items in the stack. SecretMoveComponent, which is similar to the normal MoveComponent, moves the component to the target stack and then scrambles the Ids of ALL components in that stack as described above. This is because if only the new item's id changed, it would be trivial to observe that the new Id is equivalent to the old Id.

Note that DynamicComponentValues behave slightly differently than values in other SubStates; all properties in them are effectively PolicyHidden unless the component they are attached to is PolicyVisible (either directly, or transatively)--in which case their configured policy is used.

const (
	//PolicyVisible means non sanitized. For non-group properties (e.g. strings, ints, bools), any
	//policy other than PolicyVisible is effectively PolicyHidden.
	PolicyVisible Policy = iota

	//PolicyOrder means that for groups (e.g. stacks, int slices), return a
	//group that has the same length, and whose Ids() represents the identity of
	//the items. In practice, stacks will be set so that their NumComponents()
	//is the same, but every component that exists returns the GenericComponent.
	//This policy is similar to Len, but allows observers to keep track of the
	//identity of cards as they are reordered in the stack.
	PolicyOrder

	//PolicyLen means that for groups (e.g. stacks, int slices), return a group
	//that has the same length. For all else, it's effectively PolicyHidden. In
	//practice, stacks will be set so that their NumComponents() is the same,
	//but every component that exists returns the GenericComponent.
	PolicyLen

	//PolicyNonEmpty means that for groups, PolicyNonEmpty will allow it to be
	//observed that the stack's NumComponents is either Empty (0 components) or
	//non-empty (1 components). So for default Stacks, it will either have no
	//components or 1 component. And for SizedStack, either all of the slots
	//will be empty, or the first slot will be non-empty. In all cases, the
	//Component present, if there is one, will be the deck's GenericComponent.
	PolicyNonEmpty

	//PolicyHidden returns effectively the zero value for the type. For
	//stacks, the deck it is, and the Size (for SizedStack) is set, but
	//nothing else is.
	PolicyHidden

	//PolicyInvalid is not a valid Policy. It can be provided to signal an
	//illegal policy, which will cause the sanitization policy pipeline to
	//error.
	PolicyInvalid
)

type PropertyCollection

type PropertyCollection map[string]interface{}

PropertyCollection is just an alias for map[string]interface{}. It is used as the return value for a number of things, including GameDelegate.ConfigureConstants, and MoveConfig.CustomConfigration.

func (PropertyCollection) Copy

Copy returns a shallow copy of PropertyCollection

type PropertyReadSetConfigurer

type PropertyReadSetConfigurer interface {

	//PropertyReadSetConfigurer adds configuration methods to
	//PropertyReadSetter.
	PropertyReadSetter

	//Configure*Prop allows you to set the named property to the given
	//container value. Use this if PropMutable(name) returns true.
	ConfigureEnumProp(name string, value enum.Val) error
	ConfigureStackProp(name string, value Stack) error
	ConfigureBoardProp(name string, value Board) error
	ConfigureTimerProp(name string, value Timer) error

	//ConfigureImmutable*Prop allows you to set the container for container values for
	//whom MutableProp(name) returns false.
	ConfigureImmutableEnumProp(name string, value enum.ImmutableVal) error
	ConfigureImmutableStackProp(name string, value ImmutableStack) error
	ConfigureImmutableBoardProp(name string, value ImmutableBoard) error
	ConfigureImmutableTimerProp(name string, value ImmutableTimer) error

	//ConfigureProp is like SetProp, except that it does not fail if the type
	//is one of the Interface types. If you know the underlying type it's always better
	//to use the typed accessors.
	ConfigureProp(name string, value interface{}) error
}

PropertyReadSetConfigurer is a core interface that the engine uses to interact with user-provided structs of unknown shape, to read, set, and configure their properties. The PropertyReadSetConfigurer interface is used to interact with an underlying struct.

Only certain types of properties are supported, as enumerated by PropertyType. In certain contexts (for example, Move), even some of those types are not allowed.

The engine uses this interface extremely often, to create new blank values of your structs, inflate them based on serialized information in storage, and even to copy them. As far as the game engine is concerned, if a given field on your struct cannot be accessed via this interface, it doesn't exist. The engine uses this interface instead of reflection to a) make it easier to enforce that only certain shapes of objects are allowed, making them easier to reason about, and b) for performance so reflection can be skipped as these objects often have to be manipulated in tight loops.

Some properties (like int, string, bool) are straightforward and can be read and set as expected. However, there's also a class of properties called Interface properties, including Timer, Stack, and Enum. These are special because they must be instantiated, and some of their instantiation includes important information about their underlying type. For example, a Stack() must be associated with a given deck, and may never host components who are not a member of that deck. For that reason, there's a different between Setting (which is just mutating a property, for example my moving a component within the stack), and Configuring a property, which is setting up its fundamental instantiation.

PropertyReadSetConfigurer is the maximally powerful interface that allows reading, setting, and configuring properties. PropertyReadSetter and PropertyReader have subsets of the functionality.

Typically, a PropertyReadSetConfigurer for your struct will be fetched from a method called PropertyReader(), PropertyReadSetter(), or PropertyReadSetConfigurer on your object. See ReadSetConfigurer for more.

Creating the code for your object to implement this interface is extremely tedious and error prone. `boardgame-util codegen` is a powerful utility that will automatically generate the code for you based on a magic comment in the documentation for your struct. In this way we get the best of reflection (flexibility) and the best of hard-coded (performance).

type PropertyReadSetter

type PropertyReadSetter interface {
	//All PropertyReadSetters have read interfaces
	PropertyReader

	//SetTYPEProp sets the given property name to the given type.
	SetIntProp(name string, value int) error
	SetBoolProp(name string, value bool) error
	SetStringProp(name string, value string) error
	SetPlayerIndexProp(name string, value PlayerIndex) error
	SetIntSliceProp(name string, value []int) error
	SetBoolSliceProp(name string, value []bool) error
	SetStringSliceProp(name string, value []string) error
	SetPlayerIndexSliceProp(name string, value []PlayerIndex) error

	//PropMutable will return whether the given property is backed by an
	//underlying mutable object or not. For non-interface types this should
	//always be true, because Set*Prop always exists. For interface types,
	//this will be true if the underlying property is stored as the non-
	//Immutable variant, false otherwise.
	PropMutable(name string) bool

	//For interface types the setter also wants to give access to the mutable
	//underlying value so it can be mutated in place. ReadSetters should
	//return ErrPropertyImmutable if the underlying interface property is the
	//immutable variant (that is, PropMutable returns false for that prop
	//name).
	EnumProp(name string) (enum.Val, error)
	StackProp(name string) (Stack, error)
	BoardProp(name string) (Board, error)
	TimerProp(name string) (Timer, error)

	//SetProp sets the property with the given name. If the value does not
	//match the underlying slot type, it should return an error. If the type
	//is one of the interface types, it should fail because those need to be
	//Configured, not Set. If you know the underlying type it's always better
	//to use the typed accessors.
	SetProp(name string, value interface{}) error
}

PropertyReadSetter is a version of PropertyReadSetConfigurer that has no Configuration methods. See PropertyReadSetConfigurer for more about this interface hierarchy.

type PropertyReader

type PropertyReader interface {
	//Props returns a list of all property names that are defined for this
	//object.
	Props() map[string]PropertyType
	//IntProp fetches the int property with that name, returning an error if
	//that property doese not exist.
	IntProp(name string) (int, error)
	BoolProp(name string) (bool, error)
	StringProp(name string) (string, error)
	IntSliceProp(name string) ([]int, error)
	BoolSliceProp(name string) ([]bool, error)
	StringSliceProp(name string) ([]string, error)
	PlayerIndexSliceProp(name string) ([]PlayerIndex, error)
	PlayerIndexProp(name string) (PlayerIndex, error)

	//The interface types will only return read-only versions of their objects
	//in a Reader context, even if the underlying objects are mutable
	//versions.
	ImmutableEnumProp(name string) (enum.ImmutableVal, error)
	ImmutableStackProp(name string) (ImmutableStack, error)
	ImmutableBoardProp(naem string) (ImmutableBoard, error)
	ImmutableTimerProp(name string) (ImmutableTimer, error)
	//Prop fetches the given property generically. If you already know the
	//type, it's better to use the typed methods.
	Prop(name string) (interface{}, error)
}

PropertyReader is a version of PropertyReadSetConfigurer that has no mutating methods. See PropertyReadSetConfigurer for more about this interface hierarchy.

type PropertyType

type PropertyType int

PropertyType is an enumeration of the types that are legal to have on an underyling object that can return a Reader. This ensures that State objects are not overly complex and can be reasoned about cleanly. See PropertyReadSetConfigurer and ConfigurableSubState for more.

const (
	//TypeIllegal is used to signal error states.
	TypeIllegal PropertyType = iota
	//TypeInt is a basic int
	TypeInt
	//TypeBool is a basic bool
	TypeBool
	//TypeString is a basic stirng
	TypeString
	//TypePlayerIndex is a basic PlayerIndex
	TypePlayerIndex
	//TypeEnum represents an enum.Val or enum.ImmutableVal.
	TypeEnum
	//TypeIntSlice represents a slice of ints
	TypeIntSlice
	//TypeBoolSlice represents a slice of bools
	TypeBoolSlice
	//TypeStringSlice represents a slice of strings
	TypeStringSlice
	//TypePlayerIndexSlice represents a slice of PlayerIndexes
	TypePlayerIndexSlice
	//TypeStack can in practice be any kind of object in the Stack hierarchy,
	//including SizedStack, Stack, MergedStack's, etc.
	TypeStack
	//TypeBoard is a Board object, which is basically a collection of Stacks.
	TypeBoard
	//TypeTimer is a Timer.
	TypeTimer
)

func (PropertyType) BaseType

func (t PropertyType) BaseType() PropertyType

BaseType returns the non-slice version for slice types. e.g. TypeInt for TypeIntSlice, and TypeEnum for TypeEnum. Most useful for codegen package.

func (PropertyType) IsInterface

func (t PropertyType) IsInterface() bool

IsInterface outputs true if the underlying type is an "interface" type, that is Enum, Stack, Board, or Timer. Most useful for the codegen package.

func (PropertyType) IsSlice

func (t PropertyType) IsSlice() bool

IsSlice returns true if the type represents a slice (e.g. TypeBoolSlice). Most useful for the codegen package.

func (PropertyType) String

func (t PropertyType) String() string

String outputs things like "TypeInt" for TypeInt.

type ReadSetConfigurer

type ReadSetConfigurer interface {
	ReadSetter
	ReadSetConfigurer() PropertyReadSetConfigurer
}

ReadSetConfigurer is the interface to fetch a PropertyReadSetConfigurer from an object. See ConfigurableSubState and PropertyReadSetConfigurer for more.

type ReadSetter

type ReadSetter interface {
	Reader
	ReadSetter() PropertyReadSetter
}

ReadSetter is the interface to fetch a PropertyReadSetter from an object. See ConfigurableSubState and PropertyReadSetConfigurer for more.

type Reader

type Reader interface {
	Reader() PropertyReader
}

Reader is the interface to fetch a PropertyReader from an object. See ConfigurableSubState and PropertyReadSetConfigurer for more.

type SizedStack

type SizedStack interface {
	//SizedStack can be used anywhere a Stack can be.
	Stack

	//FirstComponentIndex returns the index of the first non-nil component
	//from the left.
	FirstComponentIndex() int
	//LastComponentIndex returns the index of the first non-nil component from
	//the right.
	LastComponentIndex() int

	//FirstSlot returns the index of the first empty slot from the left.
	FirstSlot() int

	//NextSlot returns the index of the next valid slot in the slot, which is
	//equivalent to FirstSlot() for sized stacks.
	NextSlot() int

	//LastSlot returns the index of the first empty component slot from the
	//right.
	LastSlot() int
}

SizedStack is Stack, but with SizedStack related methods. See the documentation for Stack for more about how SizedStacks are different than Stacks. Note that although a SizedStack has only a few more methods than a normal Stack, its Stack methods will also have different methods than a "normal" stack.

type Stack

type Stack interface {
	//A Stack can be used anywhere an ImmutableStack can be.
	ImmutableStack

	//ComponentAt fetches the ComponentInstance that exists at the given
	//index. For default stacks, this will never be nil as long as the index
	//is between 0 and Stack.Len(). For SizedStacks, however, this might be
	//nil if the given slot is empty. See also ImmutableComponentAt, which has
	//the same behavior but returns an ImmutableComponentInstance.
	ComponentAt(componentIndex int) ComponentInstance

	//Components returns all of the ComponentInstances. Equivalent to calling
	//ComponentAt from 0 to Len().  The index of each ComponentInstance will
	//correspond to the index you could fetch that item at via
	//Stack.ComponentAt. SizedStacks will return a slice that may have nils if
	//that slot is empty. See also Stack.Components().
	Components() []ComponentInstance

	//First returns a reference to the first non-nil component from the left,
	//or nil if empty. For default stacks, ths is simply a convenience for
	//ComponentAt(0). For SizedStacks, this returns the component at
	//SizedStack.FirstComponentIndex. See also ImmutableFirst.
	First() ComponentInstance

	//Last() returns a reference to the first non-nil component from the
	//right, or nil if empty. For defaults stacks, this is simply a
	//convenience for ComponentAt(stack.Len() - 1). For SizedStacks this
	//returns the component at LastComponentIndex(). See also ImmutableLast.
	Last() ComponentInstance

	//MoveAllTo is a convenience method that moves all of the components in
	//this stack to the other stack, by repeatedly calling
	//stack.First().MoveToNextSlot(other). All other Move* methods can be
	//found on ComponentInstance. This will fail if the SlotsRemaining in
	//other Stack are less than the NumComponents of this stack. In pracitce
	//this is rarely moved, because it chunks all of the component moves up
	//into one notional Move, meaning that animations will show all of the
	//components moving at once. Instead, often moves.MoveAllComponents is
	//used to chunk up the move into a series of distinct Moves.
	MoveAllTo(other Stack) error

	//Shuffle shuffles the order of the stack, so that it has the same items,
	//but in a different order. In a SizedStack, the empty slots will move
	//around as part of a shuffle. Shuffling will scramble all of the ids in
	//the stack, such that the Ids of all items in the stack change. See the
	//documenation for Policy for more on Id scrambling. Shuffle uses
	//state.Rand() as a source of randomness, allowing it to be deterministic
	//if other things also use state.Rand().
	Shuffle() error

	//PublicShuffle is the same as Shuffle, but the Ids are not scrambled
	//after the shuffle. PublicShuffle makes sense in cases where only a small
	//number of cards are shuffled and a preternaturally savvy observer should
	//be able to keep track of them. The normal Shuffle() is almost always
	//what you want. PublicShuffle uses state.Rand() as a source of
	//randomness, allowing it to be deterministic if other things also use
	//state.Rand().
	PublicShuffle() error

	//SwapComponents swaps the position of two components within this stack
	//without changing the size of the stack (in SizedStacks, it is legal to
	//swap empty slots). i,j must be between [0, stack.Len()). This is like a
	//ComponentInstance.MoveTo, except within the same stack.
	SwapComponents(i, j int) error

	//SortComponents sorts the stack's components in the order implied by less
	//by repeatedly calling SwapComponents. Errors if any SwapComponents
	//errors. If error is non-nil, the stack may be left in an arbitrary order.
	SortComponents(less func(i, j ImmutableComponentInstance) bool) error

	//Resizable returns true if calls to any of the methods that change the
	//Size of the stack are legal to call in general. Currently only stacks
	//within a Board return Resizable false. If this returns false, any of
	//those size mutating methods will fail with an error.
	Resizable() bool

	//ContractSize changes the size of the stack. For default stacks it
	//contracts the MaxSize, if non-zero. For SizedStack it will reduce the
	//size by removing the given number of slots, starting from the right.
	//This method will fail if there are more components in the stack
	//currently than would fit in newSize.
	ContractSize(newSize int) error

	//ExpandSize changes the size of the stack. For default stacks it
	//increases MaxSize (unless it is zero). For SizedStack it does it by
	//adding the given number of newSlots to the end.
	ExpandSize(newSlots int) error

	//SetSize is a convenience method that will call ContractSize or
	//ExpandSize depending on the current Len() and the target len. Calling
	//SetSize on a stack that is already that size is a no-op. For default
	//stacks, this is the only sway to switch from a zero MaxSize (no limit)
	//to a non-zero MaxSize().
	SetSize(newSize int) error

	//SizeToFit is a simple convenience wrapper around ContractSize. It
	//automatically sizes the stack down so that there are no empty slots.
	SizeToFit() error

	//Board will return a mutable reference to the Board we're part of,
	//if we're part of a board.
	Board() Board

	//SizedStack will return a version of this stack that implements
	//the MutableSizedStack interface, if that's possible, or nil otherwise.
	SizedStack() SizedStack
	// contains filtered or unexported methods
}

Stack is one of the fundamental types in the engine. Stacks model things like a stack of cards, a collection of resource tokens, etc. Each component in each deck must reside in precisely one stack in each state, the so called "component invariant". This captures the notion that each ComponentInstance is a physical object that resides in one spot at any given time. See also the documentation for Deck.

Stacks fundamentally keep track of a list of ComponentInstances in specific slots within the stack, and can return those components via ComponentAt(). ComponentInstances can be moved into a Stack via one of their Move* methods; those methods will fail if the ComponentInstance cannot be moved to that slot for any reason. The only exception is Stack.MoveAllTo(), which operates on Stacks, not ComponentInstances.

Stacks are known as an interface type whose initial value contains important information about their type (e.g. which Deck they're affiliated with), meaning that your GameDelegate.GameStateConstructor() and others are supposed to initalize them to a non-nil value before returning the struct they're in. You generally retrieve them from Deck.NewStack() or Deck.NewSizedStack(). In practice you often use tags on your struct to instruct the StructInflaters on how to initialize them for you. See StructInflater for more.

There are a hierarchy of different types for Stacks, with diferent behavior for these methods.

  • ImmutableStack
  • Stack
  • SizedStack
  • ImmutableSizedStack
  • MergedStack

The default Stack is known as a "growable" stack. The number of slots it has is precisely the number of ComponentInstaces it contains, and when a new ComponentInstance is moved in, a new slot is simply spliced into the right place, growing the length of the stack. (That is, unless the stack is already at MaxSize(), in which case the move will fail.) Growable stacks can never have an empty slot, meaning that ComponentAt() (as long as the index is between 0 and stack.Len()) will always return a non-nil ComponentInstance. Stacks can have an optional MaxSize, which sets the maximum limit for the size of the stack. That can be set via Stack.SetSize() and related methods.

Stack has a sub-class with slightly different behavior, called the SizedStack. The SizedStack implements the same methods that a normal Stack does, but with a few extra methods and with different behavior for some of the core methods. A SizedStack has a fixed, defined number of slots. Those slots may be empty. Moving a ComponentInstance in to an occupied slot will fail, unlike in a growable stack where a new slot will be created automatically. SizedStacks add methods for fetching the first index from the left where a ComponentInstance resides, and the first index from the right where one resides. SizedStacks can have their number of slots modified via SetSize() and friends. Unlike a normal growable Stack, SizedStacks must always have a size set.

The core engine primarily reasons about Stack, not the sub-types. PropertyReadSetConfigurers, for example, will return the Stack for a given stack object, not the SizedStack. The way you access the extra methods is via the SizedStack() getter on the default Stack() interface. If the underlying Stack is a SizedStack, that method will return a non-nil object that implements the SizedStack interface. In general you rarely need to do this, as the default Stack methods are almost always sufficient, and this check for non-nil is mainly a way to determine if the Stack you have will operate like a growable or sized stack. See SizedStack for more.

Stack contains mutator methods, but in some cases, like in an ImmutableSubState, the stack shouldn't be modified. That's why the non- mutating methods are encapsulated in the ImmutableStack interface, which Stack embeds. There's also an ImmutableSizedStack interface defined, so you can test if an ImmutableStack will operate like a growable or sized Stack.

There is one extra type of Stack: the MergedStack. These specisl stacks are actually combinations of underlying normal stacks, with their output merged together. Because these merged stacks are just wrappers around other stacks, they don't have any mutators, so they extend the ImmutableStack interface, and don't implement the Stack interface. ImmutableStack is thus the lowest common denominator: the interface that all Stack objects implement. See MergedStack for more.

type State

type State interface {
	//State contains all of the methods of a read-only state.
	ImmutableState
	//GameState is a reference to to the underlying object returned from your
	//GameDelegate.GameStateConstructor(), and can be safely cast back to that
	//underlying struct so you can access its methods directly in a type-
	//checked way.
	GameState() SubState
	//Each PlayerState is a reference to to the underlying object returned
	//from your GameDelegate.PlayerStateConstructor(), and can be safely cast
	//back to that underlying struct so you can access its methods directly in
	//a type- checked way.
	PlayerStates() []SubState
	//Each SubState is a reference to to the underlying object returned from
	//your GameDelegate.DynamicComponentValuesConstructor() for the deck with
	//that name, and can be safely cast back to that underlying struct so you
	//can access its methods directly in a type- checked way.
	DynamicComponentValues() map[string][]SubState

	//CurrentPlayer returns the PlayerState corresponding to the result of
	//delegate.CurrentPlayerIndex(), or nil if the index isn't valid. This
	//object is the same underlying struct that you returned from
	//GameDelegate.PlayerStateConstructor and can be cast back safely to
	//access the underlying methods.
	CurrentPlayer() SubState

	//Rand returns a source of randomness. All game logic should use this rand
	//source. It is deterministically seeded when it is created for this state
	//based on the game's ID, the game's secret salt, and the version number
	//of the state. Repeated calls to Rand() on the same state will return the
	//same random generator. If games use this source for all of their
	//randomness it allows the game to be played back detrministically, which
	//is useful in some testing scenarios. Rand is only available on State,
	//not ImmutableState, because all methods that aren't mutators in your
	//game logic should be deterministic.
	Rand() *rand.Rand
	// contains filtered or unexported methods
}

State represents the entire semantic state of a game at a given version. For your specific game, GameState and PlayerStates will actually be concrete structs to your particular game. State is a container of gameStates, playerStates, and dynamicComponentValues for your game. Games often define a top-level concreteStates() *myGameState, []*myPlayerState so at the top of methods that accept a State they can quickly get concrete, type-checked types with only a single conversion leap of faith at the top. States contain mutable refrences to their contained SubStates, whereas ImmutableState does not. Most of the methods you define that accept states from the core game engine will be an ImmutableState, because the only time States should be modified is when the game is initally being set up before the first move, and during a move's Apply() method.

type StateGetter

type StateGetter interface {
	ImmutableStateGetter
	//State should return the state that was set via SetState.
	State() State
}

StateGetter is included in SubState and ConfigureableSubState as the way to keep track of which State a given SubState is part of. See also ImmutableStateGetter, which adds getters for ImmutableStates. Typically you use base.SubState to implement this automatically.

type StateGroupType

type StateGroupType int

StateGroupType is the top-level grouping object used in a StatePropertyRef.

const (
	//StateGroupGame refers to the GameState part of State
	StateGroupGame StateGroupType = iota
	//StateGroupPlayer refers to the PlayerState part of State
	StateGroupPlayer
	//StateGroupComponentValues referes to the non-dynamic values of the given
	//component.
	StateGroupComponentValues
	//StateGroupDynamicComponentValues refers to the DynamicComponentValues part
	//of State.
	StateGroupDynamicComponentValues
)

type StatePropertyRef

type StatePropertyRef struct {
	//Group is which of Game, Player, or DynamicComponentValues this is a
	//reference to.
	Group StateGroupType
	//PropName is the specific property on the given SubStateObject specified
	//by the rest of the StatePropertyRef.
	PropName string
	//DeckName is only used when Group is StateGroupComponentValues or
	//StateGroupDynamicComponentValues
	DeckName string

	//PlayerIndex is the index of the player, if Group is StateGroupPlayer and
	//the intent of the StatePropertyRef is to select a specific player's state.
	//0 is always legal. Note that AdminPlayerIndex and ObserverPlayerIndex are
	//never valid.
	PlayerIndex PlayerIndex
	//DeckIndex is used only when the Group is StateGroupComponentValues or
	//StateGroupDynamicComponentValues and the intent of the StatePropertyRef is
	//to select a specific ComponentValues or DynamicComponentValues. 0 is
	//always legal.
	DeckIndex int
}

A StatePropertyRef is a reference to a particular property or item in a Property in a State, in a structured way. Currently used primarily as an input to your GameDelegate's SanitizationPolicy method. Another idiomatic use is when you need to fetch a value from one of your SubStates via a string property name, and want to easily test whether that property name is valid. In that case, the idiom is to generate a global variable containing the StatePropertyRef, and call its Validate in GameDelegate.BeginSetUp. The zero-value is suitably generic.

func (StatePropertyRef) Reader

Reader fetches the PropertyReader that is selected by this StatePropertyRef, returning an error if it doesn't exist.

func (StatePropertyRef) Validate

func (r StatePropertyRef) Validate(exampleState ImmutableState) error

Validate checks to ensure that the StatePropertyRef is configured in a legal way, for example that PlayerIndex is only set to a non-default value when Group is StateGroupPlayer. exampleState is optional--if it is provided, then additional checks are done, for example ensuring that the actual named property exists, and if Index properties are non-default, that they denote a valid index.

func (StatePropertyRef) WithDeckIndex

func (r StatePropertyRef) WithDeckIndex(index int) StatePropertyRef

WithDeckIndex is a convenience method to return a copy of StatePropertyRef, just with DeckIndex set to index.

func (StatePropertyRef) WithPlayerIndex

func (r StatePropertyRef) WithPlayerIndex(index PlayerIndex) StatePropertyRef

WithPlayerIndex is a convenience method to return a copy of StatePropertyRef, just with PlayerIndex set to index.

type StateStorageRecord

type StateStorageRecord json.RawMessage

StateStorageRecord is a record representing a state that can be written to storage and later returned. It is an encoded json blob, and can be written directly to storage with no modification. Typically you don't use this representation directly, instead fetching a game from the GameManager and then using State() for a fully-inflated state.

type StorageManager

type StorageManager interface {
	//State returns the StateStorageRecord for the game at the given version,
	//or nil.
	State(gameID string, version int) (StateStorageRecord, error)

	//Move returns the MoveStorageRecord for the game at the given version, or
	//nil.
	Move(gameID string, version int) (*MoveStorageRecord, error)

	//Moves is like Move but returns all moves from fromVersion (exclusive) to
	//toVersion (inclusive). If fromVersion == toVersion, should return
	//toVersion. In many storage subsystems this is cheaper than repeated
	//calls to Move, which is why it's broken out separately.
	Moves(gameID string, fromVersion, toVersion int) ([]*MoveStorageRecord, error)

	//Game fetches the GameStorageRecord with the given ID from the store, if
	//it exists.
	Game(id string) (*GameStorageRecord, error)

	//AgentState retrieves the most recent state for the given agent
	AgentState(gameID string, player PlayerIndex) ([]byte, error)

	//SaveGameAndCurrentState stores the game and the current state (at
	//game.Version()) into the store at the same time in a transaction. Move
	//is normally provided but will be be nil if game.Version() is 0, denoting
	//the initial state for a game.
	SaveGameAndCurrentState(game *GameStorageRecord, state StateStorageRecord, move *MoveStorageRecord) error

	//SaveAgentState saves the agent state for the given player
	SaveAgentState(gameID string, player PlayerIndex, state []byte) error

	//PlayerMoveApplied is called after a PlayerMove and all of its resulting
	//FixUp moves have been applied. Most StorageManagers don't need to do
	//anything here; it's primarily useful as a callback to signal that a run
	//of moves has been applied, e.g. in the server.
	PlayerMoveApplied(game *GameStorageRecord) error

	//FetchInjectedDataForGame is an override point for other layers to inject
	//triggers for bits of game logic to call into. dataType should be the name
	//of the package that publishes the data type, to avoid collissions (for
	//example, 'github.com/jkomoros/boardgame/server/api.PlayerToSeat'). Things,
	//like server, will override this method to add new data types. Base storage
	//managers need only return nil in all cases.
	FetchInjectedDataForGame(gameID string, dataType string) interface{}
}

StorageManager is the interface that storage layers implement. The core engine expects one of these to be passed in via NewGameManager as the place to store and retrieve game information. A number of different implementations are available in boardgame/storage that can all be used. Typically you don't use this interface directly--it's defined just to formalize the interface between the core engine and the underlying storage layer.

type StructInflater

type StructInflater struct {
	// contains filtered or unexported fields
}

StructInflater is an object that inspects structs for tags with instructions using reflection. Later, it can use what it learned to auto-inflate nil interface properties (e.g. Timers, Stacks, and Enums) on those structs using the instructions encoded in the tag. After the creation of a StructInflater, reflection is no longer necessary, so operations are fast. In addition, StructInflaters can inspect a struct for whether it's valid (has no nil properties, has no illegal property types in this context), as well as reporting what the configuration of sanitization policies was based on the struct tags. Get a new one from NewStructInflater. For more about the precise configuration of struct tags that StructInlater understands, see the documenation for the methods on StructInflater that make use of it, including Inflate() and PropertySanitizationPolicy().

func NewStructInflater

func NewStructInflater(exampleObj Reader, illegalTypes map[PropertyType]bool, chest *ComponentChest) (*StructInflater, error)

NewStructInflater returns a new StructInflater configured for use based on the given object. NewStructInflater does all of the reflection necessary to do auto-inflation later, meaning that although this is a bit slow, later calls on the StructInflater don't need to use reflection again. Chest must be non-nil, so that we can validate that the tag-based configuration denotes valid properties. If illegalTypes is non-nil, then this constructor, and calls to this StructInflater's Valid() method, will error if the struct has any of those fields defined.

NewStructInflater checks for any number of illegal or nonsensical conditions, including checking Valid() on the return value, as well as verifying that if the exampleObj also has a ReadSetter that things like MergedStacksa are not accesible from mutable reader accessors, retuning an error and a nil StructInflater if anything is invalid.

You typically do not use this directly; the base library will automatically create ones for you for its own use to infalte your gameStates, playerStates, dynamicComponentValueStates, and Moves, and which you can get access to via manager.Internals().StructInflater().

func (*StructInflater) Inflate

Inflate uses tag-based configuration it detected when this StructInflater was created in order to fill in instantiated values for nil Interface properties (e.g. Stacks, Timers, and Enums). It skips any properties that didn't have configuration provided via struct tags, or that were already non-nil. It assumes that the object is the same underlying shape as the one that was passed when this StructInflater was created.

The struct tag based configuration is interpreted as follows:

For Timers, no struct-based configuration is necessary, any property of type Timer is simply replaced with a timer object (since timers don't take any configuration to set-up).

For default (growable) Stacks, the struct tag is `stack`. The contents of the tag is the name of the deck this stack is affiliated with. If the given deck name does not exist in the ComponentChest in use, the StructInflater would have failed to be created. You can also optionally provide a max-size by appending ",SIZE" into your struct tag.

For sized Stacks, you provide the same types of configuration as for stacks, but with the struct tag of "sizedstack" instead. Note that whereas a max-size of a growable stack is the default, a size of 0 for a sizedstack is effectively useless, so generally for sized stacks you provide a size.

For boards, you provide a stack tag, and also a "board" tag which denotes how many slots the board should have.

For merged stacks, you provide a tag of either "concatenate" or "overlap" and then a comma-separated list of the names of stacks on this object to combine in that way. If any of those property names are not defined on this object, the StructInflater would have failed to have been crated.

For enums, you provide the name of the enum this val is associated with by providing the struct-tag "enum". If that named enum does not exist in the ComponentChest in use, the StructInflater would have failed to be created.

For every integer-based property described above, you can replace the int in the struct value with the name of a constant value that is defined on this ComponentChest. We'll fetch that constant and use that for the int (erroring if it's not an int).

func (*StructInflater) PropertySanitizationPolicy

func (s *StructInflater) PropertySanitizationPolicy(propName string) map[string]Policy

PropertySanitizationPolicy returns the policy (map[string]Policy) based on the struct tags from the example struct given originally to NewStructInflater. It does not use reflection, relying on reflection at the time of creation of the StructInflater. In particular, it interprets policy tags in the following way:

It looks for struct tag configuration with the `sanitize` keyword.

Keywords are interpreted by splitting at "," into a series of configurations. For each configuration, a group name ("all", "other", "self", or one of the other valid values described below) is followed by a ":" and then a policy, one of "visible", "order", "len", "nonempty", and "hidden".

If the group name is omitted for a config item, it is assumed to be "all" (for non-playerState structs), or "other" for playerState structs. We decide if a struct is a playerState if it can be cast to a boardgame.PlayerState. The constants for "all" and "other" are available as SanitizationDefaultGroup and SanitizationDefaultPlayerGroup.

Any string key that is a member of enum returned from delegate.GroupEnum() may be used, not just 'all', 'other', or 'self'. In addition, any group name that is handled by your delegate's ComputedPlayerGroupMembership() may be used.

Group name 'all' will always be passed to delegate.SanitizationPolicy. Group name 'self' will be passed for PlayerStates where that player is also the player the state is being sanitized for. Group name 'other' is the opposite behavior of 'self'. This behavior means that for example you would typically have a private Hand stack in your playerState have policy "other:len", so that players can only view their own hands, and no one else can.

This means all of the following are valid:

type myPlayerState struct {
    base.SubState
    playerIndex boardgame.PlayerIndex
    VisibleHand boardgame.Stack //Equivalent to `sanitize:"all:visible"`
    HiddenHand boardgame.Stack `sanitize:"len"` // Equivalent to `sanitize:"other:len"`, since this is a player state.
    OtherStack boardgame.Stack `sanitize:"nonempty,self:len"` //Eqiuvalent to `sanitize:"other:nonempty,self:len"`
}

Missing policy configuration is interpreted for that property as though it said `sanitize:"all:visible"`

func (*StructInflater) Valid

func (s *StructInflater) Valid(obj Reader) error

Valid will return an error if the object has any properties defined of a type that is part of the illegalTypes passed to the StructInflater constructor, or if any Interface property (e.g. Stack, Timer, Enum) is currently nil. Valid can help ensure that a given object has been fully inflated.

type SubState

type SubState interface {
	ContainingStateConnector
	StateGetter
	ReadSetter
}

SubState is the interface that all sub-state objects (PlayerStates, GameStates, and DynamicComponentValues) implement. it is like ConfigurableSubState, but minus any configure methods. This means they can't be used to configure the substates at creation time but can be used to mutate them, for example in move.Apply(). See ConfigurableSubState for more on the SubState type hierarchy.

type Timer

type Timer interface {
	ImmutableTimer
	//Start begins a timer that will automatically call game.ProposeMove(Move,
	//AdminPlayerIndex) after the given duration has elapsed. Generally called
	//from within a move.Apply() method.
	Start(time.Duration, Move)
	//Cancel cancels a previously Start()'d timer, so that it will no longer
	//fire. If the timer was not active, it's a no-op. The return value is
	//whether the timer was active before being canceled. Generally called
	//during a move.Apply() method.
	Cancel() bool
	// contains filtered or unexported methods
}

Timer is a type of property that can be used in states that represents a countdown. Timers must exist in a given SubState in your state, and must always be non-nil, even if they aren't actively being used.

func NewTimer

func NewTimer() Timer

NewTimer returns a new blank timer, ready for use. Typically this would be used inside of GameDelegate.GameStateConstructor and friends. In practice however this is not necessary because the auto-crated StructInflaters for your structs will install a non-nil Timer even if not struct tags are provided, because no configuration is necessary. See StructInflater for more.

type Variant

type Variant map[string]string

Variant represents a specific configuration of options for a specific game. It is just a map of keys to values that are passed to your game so it can configure different alternate rulesets, for example using a Short variant that uses fewer cards and should play faster, or using a different deck of cards than normal. The variant configuration will be considered legal if it passes GameDelegate.Variants().LegalVariant(), and will be passed to GameDelegate.BeginSetup so that you can set up your game in whatever way makes sense for a given Variant. Your GameDelegate defines what valid keys and values are, and how they should be displayed to end-users, with its return value for GameDelegate.Variants(). See VariantConfig for more on that object type.

type VariantConfig

type VariantConfig map[string]*VariantKey

VariantConfig defines the legal keys, and their legal values, that a Variant may have in this game--that is, the set of alternate rule-sets that can be provided for this game type. You return one of these from your GameDelegate's Variants() method. Your VariantConfig also defines the display name and description for each key and each value that will be displayed to the end- user.

At a high level, a VariantConfig is just a collection of config keys and the values they may take on.

Those are complemented via DisplayNames and Descriptions to show to end-users to help them understand what each one does.

Finally, for a given key, it's possible to provide a Default value that will be used if no value is provided.

Technically you should provide Name and DisplayName for each key and value; however VariantConfig.Initialize() coes through and sets those to reasonable defaults, so in practice you can omit a lot of duplication and boilerplate. For example, Initialize() sets each VariantKey to have the name that it was given in the map it is a part of, and DisplayNames are set by splitting the name field and title casing it.

The value you return from your GameDelegate.Variatns() doesn't have to call Initialize(); the GameManager will automatically do that so as long as you fetch your variant config from GameManager.Variants(), the config will be initialized.

See also VariantKey and VariantDisplayInfo.

Here's an example showing a lot of the defaulting in action:

func (g *gameDelegate) Variants() boardgame.VariantConfig {

	//As long as you access this via gameManager.Variants() instead of
	//directly from the delegate, Initialize will have already been called
	//for us, so we can just return the object directly.

	return boardgame.VariantConfig{
		"color": {

			//You can skip setting the VariantDiplayInfo.Name,
			//.DisplayName here because initialize (which we call at the
			//end of this method) will automatically use the name of the
			//entry in the map, and then the displayname will be set to a
			//reasonable title-casing.

			Values: map[string]*boardgame.VariantDisplayInfo{
				"red": {
					//Name can be omitted because Initialize() will
					//automatically set it bassed on this value's name in
					//the map.

					//Because DisplayName has been set expclitily it will
					//not be overriden in Initialize.
					DisplayName: "Very Red",
					Description: "The color red",
				},
				//You can leave the value empty, which will automatically
				//create a new value during Initalize with the Name coming
				//from the map, and DisplayName set automatically.
				"blue": nil,
			},

			//By setting this, any new Variant created from our NewVariant
			//will always have the "color" key to either the value
			//provided, or "blue".
			Default: "blue",
		},
		"hand-size": {
			VariantDisplayInfo: boardgame.VariantDisplayInfo{
				//DisplayName will be "Hand Size" automatically
				Description: "How big of a hand players get on initial deal",
			},
			Default: "normal",
			Values: map[string]*boardgame.VariantDisplayInfo{
				"small": {
					Description: "A small hand",
				},
				"normal": {
					Description: "A normal-sized hand",
				},
				"large": {
					Description: "A large hand",
				},
			},
		},
	}

}

func (VariantConfig) Initialize

func (v VariantConfig) Initialize()

Initialize calls initalize on each Key in config, setting reasonable defaults if they weren't provided. Typically your GameDelegate.Variants() doesn't have to call this, as the GameManager will. See the documentation for the VariantConfig struct for more.

func (VariantConfig) LegalVariant

func (v VariantConfig) LegalVariant(variant Variant) error

LegalVariant returns whether the given variant has keys and values that are enumerated and legal in this config. In paticular, ensures that every key in variant is defined in this config, and the value for each key is one of the legal values according to the config. Nil configs are OK. The engine calls this autoamtically in NewGame to verify the passed variant is legal for this game type.

func (VariantConfig) NewVariant

func (v VariantConfig) NewVariant(variantValues map[string]string) (Variant, error)

NewVariant returns a new variant with the given values set. Any extra keys that are not in VariantConfig will lead to an error, as well as any values that are illegal for their key. Any missing key/value pairs will be set to their default, if the key has a default. Typically you don't call this directly, but it is called for you implicitly within NewGame.

func (VariantConfig) Valid

func (v VariantConfig) Valid() error

Valid returns an error if there is any misconfiguration in this VariantConfig in general. In particular, it verifies that the implied name for each key matches its explicit Name property, and the same for values. It also verifies that if there's a default it denotes a valid value that was explicitly listed. Effectively this checks if Initialize() has been called or not. NewGameManager will check this during creation of a new game type.

type VariantDisplayInfo

type VariantDisplayInfo struct {
	//The string to actually display to the user
	DisplayName string
	//An optional description to be shown to the user to describe what the
	//setting does.
	Description string
	//The string that is actually used in the game engine. Name can often be
	//skipped because it is often set implicitly during initialization of the
	//containing object.
	Name string
}

VariantDisplayInfo is information about a given value and how to display it to end- users, with a DisplayName and Description. It is used as part of VariantKey both to describe the Key itself as well as to give information about the values within the key for each value. See VariantConfig for more.

func (*VariantDisplayInfo) Initialize

func (d *VariantDisplayInfo) Initialize(nameInParent string)

Initialize sets the name to the given name. It also sets the display name automatically if one wasn't provided by replacing "_" and "-" with spaces and title casing name. It's called automatically by VariantKey.Initalize.

type VariantKey

type VariantKey struct {
	//VariantKey has a DisplayInfo embedded in it the defines the display name
	//and description for this configuration key.
	VariantDisplayInfo
	//The name of the value, in Values, that is default if none provided. Must
	//exist in the Values map or Valid() will error.
	Default string
	//The specific values this key may take, along with their display
	//information. For example, "blue", "red".
	Values map[string]*VariantDisplayInfo
}

VariantKey represents a specific key in your VariantConfig that has a particular meaning for this game type. For example, "color". See VariantConfig for more.

func (*VariantKey) Initialize

func (v *VariantKey) Initialize(nameInParent string)

Initialize is given the name of this key within its parent's map. The provided name will override whatever Name was already set and also sets the display name. Called by VariantConfig.Initialize, and also calls all Values' Initialize. See VariantConfig for more.

Directories

Path Synopsis
Package base contains a number of base classes for common objects in boardgame.
Package base contains a number of base classes for common objects in boardgame.
Package behaviors defines a handful of convenient behaviors that can be anonymously embedded into your SubState (e.g.
Package behaviors defines a handful of convenient behaviors that can be anonymously embedded into your SubState (e.g.
boardgame-util is a comprehensive CLI tool to help administer projects built with boardgame.
boardgame-util is a comprehensive CLI tool to help administer projects built with boardgame.
lib/build
Package build is comprised of two sub-packages, api and static, that are two halves of building a functioning webapp given a config.json.
Package build is comprised of two sub-packages, api and static, that are two halves of building a functioning webapp given a config.json.
lib/build/api
Package api is a package that can create and cleanup api server binaries.
Package api is a package that can create and cleanup api server binaries.
lib/build/static
Package static is a library that helps automate creating the static directory of files for the webapp to function.
Package static is a library that helps automate creating the static directory of files for the webapp to function.
lib/codegen
Package codegen is a simple program, designed to be run from go:generate, that helps generate the annoying boilerplate to implement boardgame.PropertyReader and boardgame.PropertyReadSetter, as well as generating the boilerplate for enums.
Package codegen is a simple program, designed to be run from go:generate, that helps generate the annoying boilerplate to implement boardgame.PropertyReader and boardgame.PropertyReadSetter, as well as generating the boilerplate for enums.
lib/codegen/examplepkg
Package examplepkg is just an example package for testing.
Package examplepkg is just an example package for testing.
lib/config
Package config is a simple library that manages config set-up for boardgame-util and friends, reading from config.json and config.SECRET.json files.
Package config is a simple library that manages config set-up for boardgame-util and friends, reading from config.json and config.SECRET.json files.
lib/gamepkg
Package gamepkg is a package that helps locate, validate, and modify game package imports.
Package gamepkg is a package that helps locate, validate, and modify game package imports.
lib/golden
Package golden is a package designed to make it possible to compare a game to a golden run for testing purposes.
Package golden is a package designed to make it possible to compare a game to a golden run for testing purposes.
lib/path
Package path includes a few simple convenience methods for dealing with paths
Package path includes a few simple convenience methods for dealing with paths
lib/stub
Package stub is a library that helps generate stub code for new games
Package stub is a library that helps generate stub code for new games
components
dice
Package dice is a simple package that defines die components with variable numbers of sides.
Package dice is a simple package that defines die components with variable numbers of sides.
playingcards
Package playingcards is a convenience package that helps define and work with a set of american playing cards.
Package playingcards is a convenience package that helps define and work with a set of american playing cards.
Package enum allows you to represent enum values.
Package enum allows you to represent enum values.
graph
Package graph is a simple package that provides facilities for creating simple graphs where each Node is a particular value in an enum.
Package graph is a simple package that provides facilities for creating simple graphs where each Node is a particular value in an enum.
Package errors is a package that implements FriendlyError.
Package errors is a package that implements FriendlyError.
examples
blackjack
Package blackjack implements a simple blackjack game.
Package blackjack implements a simple blackjack game.
checkers
Package checkers is a simple example of the classic checkers game.
Package checkers is a simple example of the classic checkers game.
debuganimations
Package debuganimations is a very simple debug "game" designed to allow us to exercise component animations very directly and purely, in order to build and debug that system.
Package debuganimations is a very simple debug "game" designed to allow us to exercise component animations very directly and purely, in order to build and debug that system.
memory
Package memory is a simple example game based on memory--where players take turn flipping over two cards, and keeping them if they match.
Package memory is a simple example game based on memory--where players take turn flipping over two cards, and keeping them if they match.
pig
Package pig is a very simple game involving dice rolls.
Package pig is a very simple game involving dice rolls.
tictactoe
Package tictactoe is an exceedingly simple game based on boardgame.
Package tictactoe is an exceedingly simple game based on boardgame.
internal
patchtree
Package patchtree is a simple library that knows how to interpret a folder structure of jd diffs and apply them on top of a base.
Package patchtree is a simple library that knows how to interpret a folder structure of jd diffs and apply them on top of a base.
patchtree/cmd/patchtree-helper
patchtree-helper is a simple command that wraps patchtree.ExpandTree and ContractTree.
patchtree-helper is a simple command that wraps patchtree.ExpandTree and ContractTree.
Package moves is a convenience package that implements composable Moves to make it easy to implement common logic.
Package moves is a convenience package that implements composable Moves to make it easy to implement common logic.
interfaces
Package interfaces is a collection of interfaces that your objects can implement to configure how the moves package's base moves operate.
Package interfaces is a collection of interfaces that your objects can implement to configure how the moves package's base moves operate.
server
api
api/extendedgame
Package extendedgame is the definition of a StorageRecord for ExtendedGame.
Package extendedgame is the definition of a StorageRecord for ExtendedGame.
api/listing
Package listing is a simple package of constants for listing of games, primarily in a separate package to avoid circular dependencies.
Package listing is a simple package of constants for listing of games, primarily in a separate package to avoid circular dependencies.
storage
bolt
Package bolt provides a bolt-backed database that implements both boardgame.StorageManager and boardgame/server.StorageManager.
Package bolt provides a bolt-backed database that implements both boardgame.StorageManager and boardgame/server.StorageManager.
filesystem
Package filesystem is a storage layer that stores information about games as JSON files within a given folder, (or somewhere nested in a folder within base folder) one per game.
Package filesystem is a storage layer that stores information about games as JSON files within a given folder, (or somewhere nested in a folder within base folder) one per game.
filesystem/record
Package record is a package to open, read, and save game records stored in filesystem's format.
Package record is a package to open, read, and save game records stored in filesystem's format.
internal/helpers
Package helpers has generic implementations of finicky methods, like Moves(), ListGames() that are appropriate for storage managers who don't get a performance boost from well-crafted queries to use.
Package helpers has generic implementations of finicky methods, like Moves(), ListGames() that are appropriate for storage managers who don't get a performance boost from well-crafted queries to use.
internal/test
Package test is a package that is used to run a boardgame/server.StorageManager implementation through its paces and verify it does everything correctly.
Package test is a package that is used to run a boardgame/server.StorageManager implementation through its paces and verify it does everything correctly.
memory
Package memory is a storage manager that just keeps the games and storage in memory, which means that when the program exits the storage evaporates.
Package memory is a storage manager that just keeps the games and storage in memory, which means that when the program exits the storage evaporates.
mysql
Package mysql provides a mysql-backed database that implements both boardgame.StorageManager and boardgame/server.StorageManager.
Package mysql provides a mysql-backed database that implements both boardgame.StorageManager and boardgame/server.StorageManager.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL