hexz

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Dec 22, 2024 License: MIT Imports: 41 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var EnableInitialDrawAssumption = true

Functions

func ExportSVG

func ExportSVG(file string, boards []*Board, captions []string) error

func ExportSVGWithStats

func ExportSVGWithStats(file string, boards []*Board, moves []*GameEngineMove, stats []*pb.SuggestMoveStats, scoreKind pb.SuggestMoveStats_ScoreKind, captions []string) error

ExportSVG writes a HTML document to file that contains SVG renderings of the given boards. stats contains optional evaluation statistics (typically from MCTS), captions contains optional captions of the boards.

func MCTSStatsToProto

func MCTSStatsToProto(stats *MCTSStats) *pb.SuggestMoveStats

func NewCPUPlayerServiceClient

func NewCPUPlayerServiceClient(addr string) (pb.CPUPlayerServiceClient, error)

func ScaleRGB

func ScaleRGB(col1 string, col2 string, scale float64) (string, error)

func ValidateEmailAddress

func ValidateEmailAddress(address string) error

Types

type Board

type Board struct {
	Turn         int
	Move         int
	LastRevealed int       // Move at which fields were last revealed
	FlatFields   []Field   // The 1-d array backing the "2d" Fields.
	Fields       [][]Field // The board's fields. Subslices of FlatFields.
	Score        []int     // Depending on the number of players, 1 or 2 elements.
	Resources    []ResourceInfo
	State        GameState
}

func NewBoard

func NewBoard() *Board

Creates a new, empty board with nil score and nil resources.

func (*Board) Copy

func (b *Board) Copy() *Board

func (*Board) FromProto

func (b *Board) FromProto(bp *pb.Board) error

func (*Board) Proto

func (b *Board) Proto() *pb.Board

func (*Board) ViewFor

func (b *Board) ViewFor(playerNum int) *BoardView

Each player has a different view of the board. In particular, player A should not see the hidden moves of player B. To not give cheaters a chance, we should never send the hidden moves out to other players at all (i.e., we shouldn't just rely on our UI which would not show them; cheaters can easily intercept the http response.)

type BoardView

type BoardView struct {
	Turn      int            `json:"turn"`
	Move      int            `json:"move"`
	Fields    [][]Field      `json:"fields"` // The board's fields.
	Score     []int          `json:"score"`  // Depending on the number of players, 1 or 2 elements.
	Resources []ResourceInfo `json:"resources"`
	State     GameState      `json:"state"`
}

A player's or spectator's view of the board. See type Board for the internal representation that holds the complete information.

type CPUPlayer

type CPUPlayer interface {
	SuggestMove(ctx context.Context, ge *GameEngineFlagz) (*GameEngineMove, *pb.SuggestMoveStats, error)
}

type CellType

type CellType int

type Field

type Field struct {
	Type    CellType `json:"type"`
	Owner   int      `json:"owner,omitempty"` // Player number owning this field. 0 for unowned fields.
	Hidden  bool     `json:"hidden,omitempty"`
	Value   int      `json:"v"`                 // Some games assign different values to cells.
	Blocked uint8    `json:"blocked,omitempty"` // Indicates which players this field is blocked for.
	// Internal fields, not exported in JSON
	Lifetime int    `json:"-"` // Moves left until this cell gets cleared. -1 means infinity.
	NextVal  [2]int `json:"-"` // If this cell would be occupied, what value would it have? (For Flagz)
}

type GameEngine

type GameEngine interface {
	Reset()
	NumPlayers() int
	ValidCellTypes() []CellType
	MakeMove(move GameEngineMove) bool
	MakeMoveError(move GameEngineMove) error
	Board() *Board
	IsDone() bool
	Winner() (playerNum int) // Results are only meaningful if IsDone() is true. 0 for draw.
	GameType() api.GameType
	// Encodes the current state of the game engine.
	Proto() *pb.GameEngineState
	// Sets this game engine into the state defined by the given encoded state.
	FromProto(s *pb.GameEngineState) error
}

func DecodeGameEngine

func DecodeGameEngine(s *pb.GameEngineState) (GameEngine, error)

func NewGameEngine

func NewGameEngine(gameType api.GameType) GameEngine

Dispatches on the gameType to create a corresponding GameEngine. The returned GameEngine is initialized and ready to play.

type GameEngineClassic

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

func NewGameEngineClassic

func NewGameEngineClassic() *GameEngineClassic

func (*GameEngineClassic) Board

func (g *GameEngineClassic) Board() *Board

func (*GameEngineClassic) FromProto

func (g *GameEngineClassic) FromProto(s *pb.GameEngineState) error

func (*GameEngineClassic) GameType

func (g *GameEngineClassic) GameType() api.GameType

func (*GameEngineClassic) Init

func (g *GameEngineClassic) Init()

func (*GameEngineClassic) InitialResources

func (g *GameEngineClassic) InitialResources() ResourceInfo

func (*GameEngineClassic) IsDone

func (g *GameEngineClassic) IsDone() bool

func (*GameEngineClassic) MakeMove

func (g *GameEngineClassic) MakeMove(m GameEngineMove) bool

func (*GameEngineClassic) MakeMoveError

func (g *GameEngineClassic) MakeMoveError(m GameEngineMove) error

func (*GameEngineClassic) MoveHistory

func (g *GameEngineClassic) MoveHistory() []GameEngineMove

func (*GameEngineClassic) NumPlayers

func (g *GameEngineClassic) NumPlayers() int

func (*GameEngineClassic) Proto

func (g *GameEngineClassic) Proto() *pb.GameEngineState

func (*GameEngineClassic) Reset

func (g *GameEngineClassic) Reset()

func (*GameEngineClassic) ValidCellTypes

func (g *GameEngineClassic) ValidCellTypes() []CellType

func (*GameEngineClassic) Winner

func (g *GameEngineClassic) Winner() (playerNum int)

type GameEngineFlagz

type GameEngineFlagz struct {
	B *Board
	// Used to efficiently process moves and determine game state for flagz.
	FreeCells   int    // Number of unoccupied cells
	NormalMoves [2]int // Number of normal cell moves the players can make

}

func NewGameEngineFlagz

func NewGameEngineFlagz() *GameEngineFlagz

func (*GameEngineFlagz) Board

func (g *GameEngineFlagz) Board() *Board

func (*GameEngineFlagz) Clone

func (g *GameEngineFlagz) Clone() *GameEngineFlagz

func (*GameEngineFlagz) FromProto

func (g *GameEngineFlagz) FromProto(s *pb.GameEngineState) error

Decodes the given encoded state of a game engine and sets this game engine to the given state. The random source of the existing game engine is kept, since the serialized state does not contain one.

func (*GameEngineFlagz) GameType

func (g *GameEngineFlagz) GameType() api.GameType

func (*GameEngineFlagz) InitializeResources

func (g *GameEngineFlagz) InitializeResources()

func (*GameEngineFlagz) IsDone

func (g *GameEngineFlagz) IsDone() bool

func (*GameEngineFlagz) MakeMove

func (g *GameEngineFlagz) MakeMove(m GameEngineMove) bool

func (*GameEngineFlagz) MakeMoveError

func (g *GameEngineFlagz) MakeMoveError(m GameEngineMove) error

func (*GameEngineFlagz) NumPlayers

func (g *GameEngineFlagz) NumPlayers() int

func (*GameEngineFlagz) PopulateInitialCells

func (g *GameEngineFlagz) PopulateInitialCells()

func (*GameEngineFlagz) Proto

func (g *GameEngineFlagz) Proto() *pb.GameEngineState

Serializes the state of this game engine.

func (*GameEngineFlagz) RandomMove

func (g *GameEngineFlagz) RandomMove() (GameEngineMove, error)

Suggests a move for the player whose turn it is. Uses a random strategy. Probably not very smart.

func (*GameEngineFlagz) Reset

func (g *GameEngineFlagz) Reset()

func (*GameEngineFlagz) ValidCellTypes

func (g *GameEngineFlagz) ValidCellTypes() []CellType

func (*GameEngineFlagz) ValidMoves

func (g *GameEngineFlagz) ValidMoves() []*GameEngineMove

func (*GameEngineFlagz) Winner

func (g *GameEngineFlagz) Winner() (playerNum int)

type GameEngineFreeform

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

func NewGameEngineFreeform

func NewGameEngineFreeform() *GameEngineFreeform

func (*GameEngineFreeform) Board

func (g *GameEngineFreeform) Board() *Board

func (*GameEngineFreeform) FromProto

func (g *GameEngineFreeform) FromProto(s *pb.GameEngineState) error

func (*GameEngineFreeform) GameType

func (g *GameEngineFreeform) GameType() api.GameType

func (*GameEngineFreeform) Init

func (g *GameEngineFreeform) Init()

func (*GameEngineFreeform) InitialResources

func (g *GameEngineFreeform) InitialResources() ResourceInfo

func (*GameEngineFreeform) IsDone

func (g *GameEngineFreeform) IsDone() bool

func (*GameEngineFreeform) MakeMove

func (g *GameEngineFreeform) MakeMove(m GameEngineMove) bool

func (*GameEngineFreeform) MakeMoveError

func (g *GameEngineFreeform) MakeMoveError(m GameEngineMove) error

func (*GameEngineFreeform) MoveHistory

func (g *GameEngineFreeform) MoveHistory() []GameEngineMove

func (*GameEngineFreeform) NumPlayers

func (g *GameEngineFreeform) NumPlayers() int

func (*GameEngineFreeform) Proto

func (*GameEngineFreeform) Reset

func (g *GameEngineFreeform) Reset()

func (*GameEngineFreeform) ValidCellTypes

func (g *GameEngineFreeform) ValidCellTypes() []CellType

func (*GameEngineFreeform) Winner

func (g *GameEngineFreeform) Winner() (playerNum int)

type GameEngineMove

type GameEngineMove struct {
	PlayerNum int
	Move      int
	Row       int
	Col       int
	CellType  CellType
}

func (*GameEngineMove) FromProto

func (m *GameEngineMove) FromProto(pm *pb.GameEngineMove)

func (*GameEngineMove) Proto

func (m *GameEngineMove) Proto() *pb.GameEngineMove

func (*GameEngineMove) String

func (m *GameEngineMove) String() string

type GameHistoryResponse

type GameHistoryResponse struct {
	GameId      string                      `json:"gameId"`
	PlayerNames []string                    `json:"playerNames"`
	GameType    api.GameType                `json:"gameType,omitempty"`
	Entries     []*GameHistoryResponseEntry `json:"entries"`
}

JSON for game history.

type GameHistoryResponseEntry

type GameHistoryResponseEntry struct {
	Timestamp time.Time    `json:"timestamp"` // RFC3339 formatted.
	EntryType string       `json:"entryType"` // One of {"move", "undo", "redo", "reset"}.
	Move      *MoveRequest `json:"move"`      // Only populated if the EntryType is "move"
	Board     *BoardView   `json:"board"`
	// For single-player flagz: scores that the CPU assigns to each move.
	MoveScores *MoveScores `json:"moveScores,omitempty"`
}

type GameInfo

type GameInfo struct {
	Id       string       `json:"id"`
	Host     string       `json:"host"`
	Started  time.Time    `json:"started"`
	GameType api.GameType `json:"gameType"`
}

Used in responses to list open and active games (/hexz/opengames, /hexz/activegames).

type GameRepr

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

GameRepr is the "handle" to a game that HTTP handlers should use. It contains a proto representation of the game, as well as a (lazily initialized) GameEngine representation.

func NewGameRepr

func NewGameRepr(state *pb.GameState) *GameRepr

func (*GameRepr) AddPlayer

func (g *GameRepr) AddPlayer(p *pb.Player)

func (*GameRepr) AllPlayersJoined

func (g *GameRepr) AllPlayersJoined() bool

func (*GameRepr) Engine

func (g *GameRepr) Engine() GameEngine

func (*GameRepr) GameID

func (g *GameRepr) GameID() string

func (*GameRepr) LastMove

func (g *GameRepr) LastMove() *pb.GameEngineMove

Returns the most recent move made in the game, or nil if no move has been made yet.

func (*GameRepr) MakeMove

func (g *GameRepr) MakeMove(move GameEngineMove) error

func (*GameRepr) PlayerNames

func (g *GameRepr) PlayerNames() []string

func (*GameRepr) PlayerNum

func (g *GameRepr) PlayerNum(playerId string) int

func (*GameRepr) PubsubID

func (g *GameRepr) PubsubID() string

func (*GameRepr) Redo

func (g *GameRepr) Redo() error

func (*GameRepr) Reset

func (g *GameRepr) Reset()

func (*GameRepr) State

func (g *GameRepr) State() *pb.GameState

func (*GameRepr) Undo

func (g *GameRepr) Undo() error

type GameRequest

type GameRequest[R any] struct {
	// contains filtered or unexported fields
}

GameRequest is a generic struct that holds commonly used data needed to process a request for a game. It is not used in API requests or responses.

type GameSettingsRequest

type GameSettingsRequest struct {
	GameId             string `json:"gameId"`
	CPUThinkTimeMillis int64  `json:"cpuThinkTimeMillis"`
}

type GameState

type GameState string
const (
	Initial  GameState = "initial"
	Running  GameState = "running"
	Finished GameState = "finished"
)

type GameStateResponse

type GameStateResponse struct {
	GameId           string `json:"gameId"`
	EncodedGameState []byte `json:"encodedGameState"`
}

Used in /hexz/status responses.

type HexzTestClient

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

type LocalCPUPlayer

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

func NewLocalCPUPlayer

func NewLocalCPUPlayer(playerId api.PlayerId, maxThinkTime time.Duration, maxIterations int) *LocalCPUPlayer

func (*LocalCPUPlayer) SuggestMove

SuggestMove calculates a suggested move (using MCTS). The GameEngineFlagz ge will not be modified.

type LoginNamesResponse

type LoginNamesResponse struct {
	Names []string `json:"names"`
}

type MCTS

type MCTS struct {
	UctFactor float64
	// If true, SuggestMove returns the most frequently vistited child node,
	// not the one with the highest win rate.
	ReturnMostFrequentlyVisited bool
	// Sample boards that led to a win/loss.
	WinningBoard *Board
	LosingBoard  *Board
	// For explicit memory handling.
	Mem  []mcNode
	Next int
}

func NewMCTS

func NewMCTS() *MCTS

func NewMCTSWithMem

func NewMCTSWithMem(cap int) *MCTS

func (*MCTS) Reset

func (mcts *MCTS) Reset()

func (*MCTS) SuggestMove

func (mcts *MCTS) SuggestMove(gameEngine *GameEngineFlagz, maxDuration time.Duration, maxIterations int) (GameEngineMove, *MCTSStats)

type MCTSMoveStats

type MCTSMoveStats struct {
	Row        int
	Col        int
	CellType   CellType
	U          float64
	Q          float64
	Iterations int
}

type MCTSStats

type MCTSStats struct {
	Iterations  int
	MaxDepth    int
	TreeSize    int
	LeafNodes   []int         // Per depth level, 0=root
	BranchNodes []int         // Per depth level, 0=root
	VisitCounts []map[int]int // Per depth level, maps visit count to number of nodes with that count.
	Elapsed     time.Duration
	Moves       []MCTSMoveStats
	BestMoveQ   float64
}

func (*MCTSStats) MaxQ

func (s *MCTSStats) MaxQ() float64

func (*MCTSStats) MinQ

func (s *MCTSStats) MinQ() float64

func (*MCTSStats) MoveScores

func (s *MCTSStats) MoveScores() *MoveScores

func (*MCTSStats) String

func (s *MCTSStats) String() string

type MoveRequest

type MoveRequest struct {
	Move int      `json:"move"` // Used to discard move requests that do not match the game's current state.
	Row  int      `json:"row"`
	Col  int      `json:"col"`
	Type CellType `json:"type"`
}

JSON for incoming requests from UI clients.

type MoveScores

type MoveScores struct {
	NormalCell [][]float64 `json:"normalCell"` // Scores for placing a normal cell on a field.
	Flag       [][]float64 `json:"flag"`       // Scores for placing a flag on a field.
}

type MoveSuggesterServer

type MoveSuggesterServer struct {
	// Needs to be embedded to have MoveSuggesterServer implement pb.CPUPlayerServiceServer.
	pb.UnimplementedCPUPlayerServiceServer
	// contains filtered or unexported fields
}

func NewMoveSuggesterServer

func NewMoveSuggesterServer(config *MoveSuggesterServerConfig) *MoveSuggesterServer

func (*MoveSuggesterServer) Serve

func (s *MoveSuggesterServer) Serve() error

func (*MoveSuggesterServer) SuggestMove

type MoveSuggesterServerConfig

type MoveSuggesterServerConfig struct {
	Addr         string // e.g. "localhost:50051".
	CpuThinkTime time.Duration
	CpuMaxFlags  int
}

type RemoteCPUPlayer

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

func NewRemoteCPUPlayer

func NewRemoteCPUPlayer(client pb.CPUPlayerServiceClient, playerId api.PlayerId, maxThinkTime time.Duration, maxIterations int) *RemoteCPUPlayer

func (*RemoteCPUPlayer) MaxIterations

func (cpu *RemoteCPUPlayer) MaxIterations() int

func (*RemoteCPUPlayer) ModelKey

func (cpu *RemoteCPUPlayer) ModelKey(ctx context.Context) (*pb.ModelKey, error)

func (*RemoteCPUPlayer) SuggestMove

type Renderer

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

func NewRenderer

func NewRenderer(templateDir string) (*Renderer, error)

NewRenderer creates a new Renderer that reads templates from the given templates folder. That folder is expected to contain the *.html template files (no subdirs).

func (*Renderer) Render

func (r *Renderer) Render(w io.Writer, filename string, data map[string]any) error

func (*Renderer) SetAutoReload

func (r *Renderer) SetAutoReload(enabled bool)

type ResetRequest

type ResetRequest struct {
	Message string `json:"message"`
}

type ResourceInfo

type ResourceInfo struct {
	NumPieces [cellTypeLen]int `json:"numPieces"`
}

Information about the resources each player has left.

type ServerConfig

type ServerConfig struct {
	ServerHost string
	ServerPort int
	// Path prefix that all URLs for this server have.
	// Usually "/hexz/", but when running behind a reverse proxy it might differ.
	URLPathPrefix      string
	DocumentRoot       string                // Path to static resource files.
	GameHistoryRoot    string                // Path to game history files.
	RemoteCPUPlayerURL string                // Base URL of the remote CPU player server. If emtpy, a local CPU player is used.
	CPUPlayerMode      pb.CPUPlayerMode_Enum // Type of CPU player to use.
	RedisAddr          string                // Address of the Redis server. If empty, local storage is used.
	PostgresURL        string                // URL of the PostgreSQL server. If empty, no persistent storage is used.
	FromAddress        string                // Address to use for transactional emails
	InactivityTimeout  time.Duration         // Time after which a game is ended due to inactivity.
	PlayerRemoveDelay  time.Duration         // Time to wait before removing an unregistered player from the game.
	LoginTTL           time.Duration
	CpuThinkTime       time.Duration
	CpuMaxFlags        int
	AuthTokenSha256    string // Used in http Basic authentication for /statusz. Must be a SHA256 checksum.
	TlsCertChain       string
	TlsPrivKey         string
	DebugMode          bool
	// The VCS (typically: git) revision that the binary was built at.
	// Can be used in .js / .wasm URLs as a query parameter to avoid
	// the usual browser caching problems.
	VCSRevision string
}

type ServerEvent

type ServerEvent struct {
	Timestamp time.Time  `json:"timestamp"` // RFC3339 formatted.
	Board     *BoardView `json:"board"`
	// Role of the client receiving the event. 0: spectator, 1, 2: players.
	Role          int      `json:"role"`
	PlayerNames   []string `json:"playerNames"`
	Announcements []string `json:"announcements"`
	// Number of the player that wins. 0 if no winner yet or draw.
	Winner int `json:"winner,omitempty"`
	// Only populated in the first event.
	GameInfo *ServerEventGameInfo `json:"gameInfo,omitempty"`
	// Only populated if the game was reset and a new one starts under a
	// new game ID. Clients are expected to maintain their SSE connection
	// and start playing the new game.
	// The new game's board and other data are part of the same message.
	NewGameID string `json:"newGameId"`
}

type ServerEventGameInfo

type ServerEventGameInfo struct {
	// Indicates which cell types exist in this type of game.
	ValidCellTypes []CellType `json:"validCellTypes"`
	// The type of game we're playing.
	GameType api.GameType `json:"gameType"`
	// True if this is a game of one player against a CPU player, which
	// should be run on the client side.
	ClientSideCPUPlayer bool `json:"clientSideCPUPlayer"`
}

Sent in an initial message to clients.

type StatelessServer

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

StatelessServer is the handle to the "stateless" server implementation. Instances should be created using a builder. See NewStatelessServerBuilder.

func (*StatelessServer) Serve

func (s *StatelessServer) Serve()

type StatelessServerBuilder

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

func NewStatelessServerBuilder

func NewStatelessServerBuilder(config *ServerConfig, playerStore hexzmem.PlayerStore, gameStore hexzmem.GameStore, renderer *Renderer) *StatelessServerBuilder

func (*StatelessServerBuilder) Build

func (*StatelessServerBuilder) WithCPUPlayerServiceClient

func (b *StatelessServerBuilder) WithCPUPlayerServiceClient(client pb.CPUPlayerServiceClient) *StatelessServerBuilder

func (*StatelessServerBuilder) WithDatabaseStore

func (b *StatelessServerBuilder) WithDatabaseStore(dbStore hexzsql.DatabaseStore) *StatelessServerBuilder

func (*StatelessServerBuilder) WithFlashStore

func (b *StatelessServerBuilder) WithFlashStore(flashStore hexzmem.FlashStore) *StatelessServerBuilder

func (*StatelessServerBuilder) WithTokenStore

func (b *StatelessServerBuilder) WithTokenStore(tokenStore hexzmem.TokenStore) *StatelessServerBuilder

func (*StatelessServerBuilder) WithUserService

func (b *StatelessServerBuilder) WithUserService(userService users.Service) *StatelessServerBuilder

type StatuszCounter

type StatuszCounter struct {
	Name  string `json:"name"`
	Value int64  `json:"value"`
}

type StatuszDistrib

type StatuszDistrib struct {
	Name    string                 `json:"name"`
	Buckets []StatuszDistribBucket `json:"buckets"`
}

type StatuszDistribBucket

type StatuszDistribBucket struct {
	Lower float64 `json:"lower"`
	Upper float64 `json:"upper"` // exclusive
	Count int64   `json:"count"`
}

type StatuszResponse

type StatuszResponse struct {
	Started            time.Time         `json:"started"`
	UptimeSeconds      int               `json:"uptimeSeconds"`
	Uptime             string            `json:"uptime"` // 1h30m3.5s
	NumOngoingGames    int               `json:"numOngoingGames"`
	NumLoggedInPlayers *int              `json:"numLoggedInPlayers,omitempty"` // pointer to make this one optional (remote store does not support count).
	Counters           []StatuszCounter  `json:"counters"`
	Distributions      []*StatuszDistrib `json:"distributions"`
}

type UndoRedoRequest

type UndoRedoRequest struct {
	GameId      string `json:"gameId"`
	Action      string `json:"action"`      // One of {"undo", "redo"}
	CurrentMove int    `json:"currentMove"` // The current move number (not the one to be undone)
}

Jump to

Keyboard shortcuts

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