func (g *GameDelegate) ComputedPlayerProperties(player boardgame.ImmutableSubState) boardgame.PropertyCollection {
	return nil
}
ComputedPlayerProperties returns nil.
package base
import (
	"math"
	"sort"
	"strings"
	"github.com/jkomoros/boardgame/behaviors"
	"github.com/jkomoros/boardgame/moves/interfaces"
	"github.com/jkomoros/boardgame"
	"github.com/jkomoros/boardgame/enum"
	"github.com/jkomoros/boardgame/errors"
	"github.com/sirupsen/logrus"
)
type GameDelegate struct {
	manager *boardgame.GameManager
	cachedGroupMembershipProperties []string
}
func (g *GameDelegate) Diagram(state boardgame.ImmutableState) string {
	return "This should be overriden to render a reasonable state here"
}
func (g *GameDelegate) DisplayName() string {
	return strings.Title(g.Manager().Delegate().Name())
}
func (g *GameDelegate) Description() string {
	return ""
}
func (g *GameDelegate) Manager() *boardgame.GameManager {
	return g.manager
}
func (g *GameDelegate) SetManager(manager *boardgame.GameManager) {
	g.manager = manager
}
func (g *GameDelegate) DynamicComponentValuesConstructor(deck *boardgame.Deck) boardgame.ConfigurableSubState {
	return nil
}
func (g *GameDelegate) ProposeFixUpMove(state boardgame.ImmutableState) boardgame.Move {
	isDebug := g.Manager().Logger().Level >= logrus.DebugLevel
	var logEntry *logrus.Entry
	if isDebug {
		logEntry = g.Manager().Logger().WithFields(logrus.Fields{
			"game":    state.Game().ID(),
			"version": state.Version(),
		})
		logEntry.Debug("***** ProposeFixUpMove called *****")
	}
	for _, move := range state.Game().Moves() {
		var entry *logrus.Entry
		if isDebug {
			entry = logEntry.WithField("movetype", move.Info().Name())
		}
		if !IsFixUp(move) {
			continue
		}
		err := move.Legal(state, boardgame.AdminPlayerIndex)
		if err == nil {
			if isDebug {
				entry.Debug(move.Info().Name() + " : MATCH")
			}
			return move
		}
		if isDebug {
			entry.Debug(move.Info().Name() + " : " + err.Error())
		}
	}
	if isDebug {
		logEntry.Debug("NO MATCH")
	}
	return nil
}
func (g *GameDelegate) CurrentPlayerIndex(state boardgame.ImmutableState) boardgame.PlayerIndex {
	index, err := state.ImmutableGameState().Reader().PlayerIndexProp("CurrentPlayer")
	if err != nil {
		return boardgame.ObserverPlayerIndex
	}
	return index.EnsureValid(state)
}
func (g *GameDelegate) CurrentPhase(state boardgame.ImmutableState) int {
	phaseEnum, err := state.ImmutableGameState().Reader().ImmutableEnumProp("Phase")
	if err != nil {
		return -1
	}
	return phaseEnum.Value()
}
func (g *GameDelegate) PhaseEnum() enum.Enum {
	result := g.Manager().Chest().Enums().Enum("phase")
	if result != nil {
		return result
	}
	return g.Manager().Chest().Enums().Enum("Phase")
}
const defaultGroupsName = "group"
func (g *GameDelegate) GroupEnum() enum.Enum {
	return g.Manager().Chest().Enums().Enum(defaultGroupsName)
}
func (g *GameDelegate) DistributeComponentToStarterStack(state boardgame.ImmutableState, c boardgame.Component) (boardgame.ImmutableStack, error) {
	return nil, errors.New("DistributeComponentToStarterStack was called, but the component was not stored in a stack")
}
func (g *GameDelegate) GroupMembership(playerState boardgame.ImmutableSubState) map[int]bool {
	if playerState != nil && g.cachedGroupMembershipProperties == nil {
		groupEnum := g.Manager().Delegate().GroupEnum()
		if groupEnum == nil {
			return nil
		}
		props := make([]string, 0)
		for propName, propType := range playerState.Reader().Props() {
			if propType != boardgame.TypeEnum {
				continue
			}
			enumVal, err := playerState.Reader().ImmutableEnumProp(propName)
			if err != nil {
				continue
			}
			if enumVal.Enum().SubsetOf(groupEnum) {
				props = append(props, propName)
			}
		}
		g.cachedGroupMembershipProperties = props
	}
	if len(g.cachedGroupMembershipProperties) == 0 {
		return nil
	}
	result := make(map[int]bool, len(g.cachedGroupMembershipProperties))
	for _, propName := range g.cachedGroupMembershipProperties {
		enumVal, err := playerState.Reader().ImmutableEnumProp(propName)
		if err != nil {
			continue
		}
		result[enumVal.Value()] = true
	}
	return result
}
const computedGroupNameDelimiter = "-"
const computedGroupNameFunctionSame = "same"
const computedGroupNameFunctionsDifferent = "different"
var legalComputedGroupNameFunctions = map[string]bool{
	computedGroupNameFunctionSame:       true,
	computedGroupNameFunctionsDifferent: true,
}
func doComputedGroupMembership(fun string, e enum.Enum, playerMembership, viewingAsPlayerMembership map[int]bool) bool {
	for _, key := range e.Values() {
		p := playerMembership[key]
		v := viewingAsPlayerMembership[key]
		switch fun {
		case computedGroupNameFunctionSame:
			if p != v {
				return false
			}
		case computedGroupNameFunctionsDifferent:
			if p != v {
				return true
			}
		}
	}
	switch fun {
	case computedGroupNameFunctionSame:
		return true
	case computedGroupNameFunctionsDifferent:
		return false
	}
	return false
}
func (g *GameDelegate) ComputedPlayerGroupMembership(groupName string, playerMembership, viewingAsPlayerMembership map[int]bool) (bool, error) {
	parts := strings.Split(groupName, computedGroupNameDelimiter)
	if len(parts) == 2 {
		fun := strings.ToLower(parts[0])
		if _, ok := legalComputedGroupNameFunctions[fun]; !ok {
			return false, errors.New(parts[0] + " was used as a computed group name function but it's not a known one")
		}
		e := g.Manager().Chest().Enums().Enum(parts[1])
		if e == nil {
			return false, errors.New(parts[1] + " was used as a computed group name enum but it's not a legal enum")
		}
		groupEnum := g.Manager().Delegate().GroupEnum()
		if groupEnum == nil {
			return false, errors.New("A computed group name used an enum, but there is no Group enum")
		}
		if !e.SubsetOf(groupEnum) {
			return false, errors.New(parts[1] + " enum is not a subset of GroupEnum")
		}
		return doComputedGroupMembership(fun, e, playerMembership, viewingAsPlayerMembership), nil
	}
	return false, errors.New("Unsupported group name: " + groupName)
}
func (g *GameDelegate) SanitizationPolicy(prop boardgame.StatePropertyRef, groupMembership map[string]bool) boardgame.Policy {
	manager := g.Manager()
	inflater := manager.Internals().StructInflater(prop)
	if inflater == nil {
		return boardgame.PolicyInvalid
	}
	policyMap := inflater.PropertySanitizationPolicy(prop.PropName)
	var applicablePolicies []int
	for groupName, isMember := range groupMembership {
		if !isMember {
			continue
		}
		if policy, ok := policyMap[groupName]; ok {
			applicablePolicies = append(applicablePolicies, int(policy))
		}
	}
	if len(applicablePolicies) == 0 {
		return boardgame.PolicyVisible
	}
	sort.Ints(applicablePolicies)
	return boardgame.Policy(applicablePolicies[0])
}
func (g *GameDelegate) ComputedGlobalProperties(state boardgame.ImmutableState) boardgame.PropertyCollection {
	return nil
}