enum

package
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: 7 Imported by: 0

Documentation

Overview

Package enum allows you to represent enum values.

In a number of cases you have a property that can only have a handful of possible values. You want to verify that the value is always one of those legal values, and make sure that you can compare it to a known constant so you can make sure you don't have a typo at compile time instead of run time. It's also nice to have them have an order in many cases, and to be serialized with the string value so it's easier to read.

Enums are useful for this case. An EnumSet contains multiple enums, and you can create an EnumValue which can be used as a property on a PropertyReader object.

The idiomatic way to create an enum is the following.

In components.go:

	const (
		colorRed = iota
		colorBlue
		colorGreen
    )

    const (
        cardSpade = iota
        cardHeart
        cardDiamond
        cardClub
    )

    var enums = enum.NewSet()

    var colorEnum = enums.MustAdd("color", map[int]string{
        colorRed: "Red",
        colorBlue: "Blue",
        colorGreen: "Green",
    })

    var cardEnum = enums.MustAdd("card", map[int]string{
        cardSpade: "Spade",
        cardHeart: "Heart",
        cardDiamond: "Diamond",
        cardClub: "Club",
    })

And then in your main.go:

func (g *GameDelegate) EmptyGameState() boardgame.ConfigurableSubState {

    //You could also just return a zero-valued struct if you used struct
    //tags for the enum. See the Constructors section of boardgame package
    //doc for more.
    return &gameState{
        MyIntProp: 0,
        MyColorEnumProp: colorEnum.NewVal(),
    }
}

func (g *GameDelegate) ConfigureEnums() *enum.Set {
    return enums
}

This is a fair bit of boilerplate to inlude in your components.go. You can use the `boardgame-util codegen` tool to generate the repetitive boilerplate for you.

Instead of the above code for components.go, you'd instead only include the following:

//boardgame:codegen
const (
    colorRed = iota
    colorBlue
    colorGreen
)

//boardgame:codegen
const (
    cardSpade = iota
    cardHeart
    cardDiamond
    cardClub
)

Then, the rest of the example code shown above in components.go would be automatically generated, including the ConfigureEnums definition on the structs in your package that appear to be your game delegate, if you don't have your own definition of that method. The longest common prefix for each name in the constant block would be used as the name of the enum. codegen has more options for controlling the precise way the enums are created; see codegen's package doc for more information.

Ranged Enums

You can also create RangeEnums. These are just normal enums, but with a multi-index enumeration translated to and from string values in a known way.

When you create RangeEnums, you don't use codegen, because the values are created for you with only minimal configuration.

set.AddRange returns a RangeEnum directly. If you have a normal Enum, you can use RangeEnum to get access to the RangeEnum, if valid, or nil otherwise. Generally you should store RangeEnumVal directly in your structs if it's a range value, so you don't have to up-convert.

Tree Enums

You can also create TreeEnums. These are just normal enums, but that additionally encode a tree structure on top of the values. A tree enum always has value 0 as the root node, with string value "", and that all other nodes have at the top of their ancestor chain.

A common use for Tree Enums is for the Phase enum in your game state, allowing there to be sets of sub-phases that the game is in.

Tree Enum values can be either branches (has 1 or more children) or leaves (have no children). Typically a branch node means "all of the values below me," while a leaf node means "precisely this value". In some contexts it only makes sense for the value to be set to a leaf node. For example, if the PhaseEnum in your game is a TreeEnum, then the state will refuse to be saved if the value is not a leaf value. BranchDefaulValue is the best way to get the default leaf value within a sub-tree.

Creating Tree Enums with boardgame-util codegen

`boardgame-util codegen` is able to make TreeEnums for you automatically.

The signal to create a TreeEnum is that you have an item in your enum whose string value evaluates to "". (Theoretically the int value of that "" node should also be 0, but the actual constant value for constants in Enums is currently ignored due to #631).

//boardgame:codegen
const (
  //Because the next item's string value is "" (there is no text beyond the shared prefix), this will be a tree enum
  phase = iota
  phaseRed
  phaseBlue
)

Creates a TreeEnum shaped like:

""
  Red
  Blue

Branch nodes are created based on the implicit ordering and structure of the final string values, splitting at "> " as the delimeter:

//boardgame:codegen
const (
  phase = iota
  phaseRed
  //display:"Red > Circle"
  phaseRedCircle
  phaseBlue
  //display:"Blue > Circle"
  phaseBlueCircle
)

Creates:

""
  Red
    Circle
  Blue
    Circle

Of course, writing the display name is annoying, so if the name of the const has has an underscore `010` then it will be interpreted as " > " when creating the string value:

//boardgame:codegen
const (
  phase = iota
  phaseRed
  //Next line's string value is "Red > Circle"
  phaseRed010Circle
  phaseBlue
  phaseBlue010Circle
)

Creates:

""
  Red
    Circle
  Blue
    Circle

If there are node names that are implied but not explicitly created in your code, codegen will define a reasonably-named global constant automatically.

//boardgame:codegen
const (
    phase = iota
    //phaseRed is not explicitly created, but it is implied by
    //phaseRed010Circle; PhaseRed will be created
    phaseRed10Circle
)

Creates:

""
    Red
        Circle

Supplying underscores in constant names is ugly and error-prone. codegen will automatically create tree breaks at word boundaries, combining multiple words in a row into one node if it makes sense. It will continue to create implied nodes if necessary. This almost always does what you want and means that you can skip including "010" in your constant names. There's one exception: if you have a node with a single child (e.g. "Blue" with a single child of "Green"), codegen by default will combine those into one multi-word node: "Blue Green". If you don't want that to happen, just be explicit about the node break, either with a display value that includes the delimiter there, or by using the underscore.

//boardgame:codegen
const (
    phase = iota
    phaseBlueGreen
    //phaseBlueGreenOne is implied; a constant named phaseBlueGreenOne
    //will be created
    phaseBlueGreenOneA
    phaseBlueGreenOneB
    //The next item will result in a single child named "Two A"
    phaseBlueGreenTwoA
    //The next item will result in a child of Three followed by a child of
    //A since there's an explicit tree break.
    phaseBlueGreenThree010A
)

Creates:

""
    Blue Green
        One
            A
            B
        Two A
        Three
            A

Index

Constants

View Source
const IllegalValue = math.MaxInt64

IllegalValue is the senitnel value that will be returned for illegal values.

View Source
const TreeNodeDelimiter = " > "

TreeNodeDelimiter is the string used to join the individual node names into one (e.g. "Normal > Deal Cards > Deal To First Player")

Variables

This section is empty.

Functions

This section is empty.

Types

type Enum

type Enum interface {
	//Values returns all values that are in this enum--all values for which
	//enum.Valid(val) would return true.
	Values() []int
	//DefaultValue returns the default value for this enum (the lowest valid
	//value in it). TreeEnums will instead return BranchDefaultValue for 0, to
	//ensure that the DefaultValue is a leaf.z
	DefaultValue() int
	//RandomValue returns a random value that is Valid() for this enum. r is
	//the source of randomness to use; almost always a good idea to pass
	//state.Rand() so the calculations can be deterministic. If r is nil a
	//fallback will be used.
	RandomValue(r *rand.Rand) int
	//MaxValue is the highest valid integer value in the enum. Note that values
	//need not be sequential, so this number might be very different than
	//len(Values()) - 1.
	MaxValue() int
	//Valid returns whether the given value is a valid member of this enum.
	Valid(val int) bool
	//String returns the string value associated with the given value.
	String(val int) string
	//Name returns the name of this enum; if set is the set this enum is part of,
	//set.Enum(enum.Name()) == enum will be true.
	Name() string
	//ValueFromString returns the enum value that corresponds to the given string,
	//or IllegalValue if no value has that string.
	ValueFromString(in string) int
	//NewVal returns an enum.Val that is permanently set to the provided
	//val. If that value is not valid for this enum, it will error.
	NewImmutableVal(val int) (ImmutableVal, error)
	NewVal() Val
	//NewMutableVal returns a new EnumValue associated with this enum, set to the
	//Enum's DefaultValue to start.
	//NewDefaultVal is a convenience shortcut for creating a new const that is
	//set to the default value, which is moderately common enough that it makes
	//sense to do it without the possibility of errors.
	NewDefaultVal() ImmutableVal

	//MustNewVal is like NewVal, but if it would have errored it panics
	//instead. It's convenient for initial set up where the whole app should fail
	//to startup if it can't be configured anyway, and dealing with errors would
	//be a lot of boilerplate.
	MustNewImmutableVal(val int) ImmutableVal
	MustNewVal(val int) Val

	//SubsetOf returns true if every int key in this enum is also present in
	//other, and the string values for each int key are the same.
	SubsetOf(other Enum) bool

	//RangeEnum will return a version of this Enum that satisifies the
	//RangeEnum interface, if possible, nil otherwise.
	RangeEnum() RangeEnum

	//TreeEnum will return a version of this Enum that satisfies the TreeEnum
	//interface, if possible, nil otherwise.
	TreeEnum() TreeEnum
}

Enum is a named set of values within a set. Get a new one with enumSet.Add(). It's an interface to better support anonymous-embedding scenarios.

type ImmutableRangeVal

type ImmutableRangeVal interface {
	ImmutableVal

	//RangeValue will return an array of indexes that this value represents.
	RangeValue() []int
}

ImmutableRangeVal is a Val that comes from a RangeEnum.

type ImmutableTreeVal

type ImmutableTreeVal interface {
	ImmutableVal
	TreeValGetters
}

ImmutableTreeVal is a value from a TreeEnum.

type ImmutableVal

type ImmutableVal interface {
	Enum() Enum
	Value() int
	String() string
	ImmutableCopy() ImmutableVal
	Copy() Val
	Equals(other ImmutableVal) bool

	//ImmutableRangeVal will return a version of this Val that implements RangeVal(),
	//if that's possible, nil otherwise.≈
	ImmutableRangeVal() ImmutableRangeVal

	//ImmutableTreeVAl will return a version of this Val that implements
	//TreeVal, if that's possible, nil otherwise.
	ImmutableTreeVal() ImmutableTreeVal
}

ImmutableVal is an instantiation of an Enum that cannot be changed. You retrieve it from enum.NewImmutableVal(val).

type RangeEnum

type RangeEnum interface {
	Enum

	NewImmutableRangeVal(indexes ...int) (ImmutableRangeVal, error)
	NewRangeVal() RangeVal

	MustNewImmutableRangeVal(indexes ...int) ImmutableRangeVal
	MustNewRangeVal(indexes ...int) RangeVal

	//RangeDimensions will return the number of dimensions used to create this
	//enum with AddRange.
	RangeDimensions() []int

	//ValueToRange will return the multi-dimensional value associated with the
	//given value. A simple convenience wrapper around
	//enum.MutableNewVal(val).RangeValues(), except it won't panic if that
	//value isn't legal.
	ValueToRange(val int) []int

	//RangeToValue takes multi-dimensional indexes and returns the int value
	//associated with those indexes. Will return IllegalValue if it wasn't
	//legal.
	RangeToValue(indexes ...int) int
}

RangeEnum is a special type of Enum that also allows indexing via numbers.

type RangeVal

type RangeVal interface {
	Val

	//RangeValue will return an array of indexes that this value represents.
	RangeValue() []int

	//SetRangeValue can be used to set Range values via the indexes directly.
	SetRangeValue(indexes ...int) error
}

RangeVal is a MutableVal that comes from a RangeEnum.

type Set

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

Set is a set of enums where each Enum's values are unique. Normally you will create one in your package, add enums to it during initalization, and then use it for all managers you create.

func CombineSets

func CombineSets(sets ...*Set) (*Set, error)

CombineSets returns a new EnumSet that contains all of the EnumSets combined into one. The individual enums will literally be the same as the enums from the provided sets, so enum equality will work.

func MustCombineSets

func MustCombineSets(sets ...*Set) *Set

MustCombineSets wraps CombineEnumSets, but instead of erroring will panic. Useful for package-level declarations outside of init().

func NewSet

func NewSet() *Set

NewSet returns a new Set. Generally you'll call this once in a package and create the set during initalization.

func (*Set) Add

func (e *Set) Add(enumName string, values map[int]string) (Enum, error)

Add ads an enum with the given name and values to the enum manager. Will error if that name has already been added or if the config you provide has more than one string with the same value.

func (*Set) AddRange

func (e *Set) AddRange(enumName string, dimensionSize ...int) (RangeEnum, error)

AddRange creates a new Enum that automatically enumerates all indexes in the multi-dimensional space provided. Each dimensionSize must be greater than 0 or AddRange will error. At its core a RangeEnum is just an enum with a known mapping of multiple dimensions into string values in a known, stable way, and with additional convenience methods to automatically convert between that mapping.

//Returns an enum like:
//0 -> '0'
//1 -> '1'
AddRange("single", 2)

// Returns an enum like:
// 0 -> '0_0'
// 1 -> '0_1'
// 2 -> '0_2'
// 3 -> '1_0'
// 4 -> '1_1'
// 5 -> '1_2'
AddRange("double", 2,3)

func (*Set) AddTree

func (s *Set) AddTree(enumName string, values map[int]string, parents map[int]int) (TreeEnum, error)

AddTree adds a new tree enum. Unlike a normal enum, you also must pass amap of value to its parent. Every value in values must also be present in parents. In a TreeEnum, the root is always value 0 and always has string value "". You may omit the root value if you choose because it is implied. The string value map should be just the name of the node itself. The effective name of the node will be joined all of its ancestors. Unlike normal enums, multiple values can have the same name, as long as the fully qualified name (including all of the ancestors' node names) are unique. Typically you rely on `boardgame-util codegen` to create these on your behalf, because the initial set-up is finicky with the two maps.

func (*Set) Combine

func (e *Set) Combine(name string, enums ...Enum) (Enum, error)

Combine creates a new enum that is the combination of the enumerated enums. The enums need not be in this set. Will error if the combined enum is invalid (e.g. overlapping index or strings)

func (*Set) Enum

func (e *Set) Enum(name string) Enum

Enum returns the Enum with the given name. In general you keep a reference to the enum yourself, but this is useful for programatically enumerating the enums.

func (*Set) EnumNames

func (e *Set) EnumNames() []string

EnumNames returns a list of all names in the Enum.

func (*Set) Finish

func (e *Set) Finish()

Finish finalizes an EnumSet so that no more enums may be added. After this is called it is safe to use this in a multi-threaded environment. Repeated calls do nothing. ComponenChest automatically calls Finish() on the set you pass it.

func (*Set) MarshalJSON

func (e *Set) MarshalJSON() ([]byte, error)

MarshalJSON marshals the enum set in a form appropriate for being transmitted to the client.

func (*Set) MustAdd

func (e *Set) MustAdd(enumName string, values map[int]string) Enum

MustAdd is like Add, but instead of an error it will panic if the enum cannot be added. This is useful for defining your enums at the package level outside of an init().

func (*Set) MustAddRange

func (e *Set) MustAddRange(enumName string, dimensionSize ...int) RangeEnum

MustAddRange is like AddRange, but instead of an error it will panic if the enum cannot be added. This is useful for defining your enums at the package level outside of an init().

func (*Set) MustAddTree

func (s *Set) MustAddTree(enumName string, values map[int]string, parents map[int]int) TreeEnum

MustAddTree is like AddTree, but instead of an error it will panic if the enum cannot be added. This is useful for defining your enums at the package level outside of an init().

func (*Set) MustCombine

func (e *Set) MustCombine(name string, enums ...Enum) Enum

MustCombine is like Combine, but if it would have errored it will panic. Suitable for usage at package-level initalization, where any panics will be found during initialization.

type TreeEnum

type TreeEnum interface {
	Enum

	//IsLeaf returns true if the given value in the enum represents a leaf (as
	//opposed to branch)
	IsLeaf(val int) bool

	//Parent returns the value of the node who is the direct parent. The root
	//will return itself.
	Parent(val int) int

	//Ancestors returns the path from the root down to the given value,
	//INCLUDING the value itself.
	Ancestors(val int) []int

	//Children returns all of the val beneath this branch that are direct
	//descendents, either including or excluding non-leaf nodes.
	Children(node int, includeBranches bool) []int

	//Descendants returns all enumvals beneath this point, recursively.
	Descendants(node int, includeBranches bool) []int

	//BranchDefaultValue is like DefaultVal, but only for nodes underneath
	//this node. It returns the left-most leaf node under this node. If node
	//is a leaf, it returns itself. Otherwise, it returns the
	//BranchDefaultValue of its first child.
	BranchDefaultValue(node int) int

	NewImmutableTreeVal(val int) (ImmutableTreeVal, error)
	NewTreeVal() TreeVal

	MustNewImmutableTreeVal(val int) ImmutableTreeVal
	MustNewTreeVal(val int) TreeVal
}

TreeEnum is a special type of Enum where the list of values also have a tree structure that can be interrogated. TreeEnums always have 0 map to "" as the root value.

type TreeVal

type TreeVal interface {
	Val
	TreeValGetters
}

TreeVal is a value from a tree enum.

type TreeValGetters

type TreeValGetters interface {
	//IsLeaf is a convenience for val.Enum().TreeEnum().IsLeaf(val.Value())
	IsLeaf() bool

	//Parent is a convenience for val.Enum().TreeEnum().Parent(val).
	Parent() int

	//Ancestors is a convenience for val.Enum().TreeEnum().Ancestors(val).
	Ancestors() []int

	//Children is a convenience for val.Enum().TreeEnum().Children(val).
	Children(includeBranches bool) []int

	//Descendants is a convenience for val.Enum().TreeEnum().Descendents(val).
	Descendants(includeBranches bool) []int

	//BranchDefaultValue is a convenience for
	//val.Enum().TreeEnum().BranchDefaulValue(val).
	BranchDefaultValue() int

	//NodeString returns the name of this specific node, whereas String
	//returns the fully qualified name. So whereas String() might return
	//"Normal - Default - Save Item", NodeString() will return "Default".
	NodeString() string
}

TreeValGetters is the collection of methods that TreeVals have beyodn normal Vals. It is factored out into a separate interface to clarify how ImmutableTreeVal and TreeVal differ from their non-treeval types.

type Val

type Val interface {
	ImmutableVal
	//SetValue changes the value. Returns true if successful. Will fail if the
	//value is locked or the val you want to set is not a valid number for the
	//enum this value is associated with.
	SetValue(val int) error
	//SetStringValue sets the value to the value associated with that string.
	SetStringValue(str string) error

	//RangeVal returns a version of this Val that implements RangeVal, if
	//that's posisble, nil otherwise.
	RangeVal() RangeVal

	//TreeVal returns a version of this Val that implements TreeVal, if that's
	//possible, nil otherwise.
	TreeVal() TreeVal
}

Val is an instantiation of a value that must be set to a value in the given enum. You retrieve one from enum.NewMutableVal().

Directories

Path Synopsis
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.

Jump to

Keyboard shortcuts

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