169 lines
3.2 KiB
Go
169 lines
3.2 KiB
Go
package game
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"errors"
|
|
"sync"
|
|
"time"
|
|
|
|
"chessstars/internal/engine"
|
|
)
|
|
|
|
var ErrNotFound = errors.New("game: not found")
|
|
var ErrAlreadyJoined = errors.New("game: already has an opponent")
|
|
var ErrSelfJoin = errors.New("game: can't join your own game")
|
|
|
|
type Entry struct {
|
|
mu sync.Mutex
|
|
Engine *engine.Engine
|
|
ID string
|
|
WhiteID int64
|
|
WhiteName string
|
|
BlackID int64
|
|
BlackName string
|
|
VsBot bool
|
|
BotDepth int
|
|
Scored bool
|
|
CreatedAt time.Time
|
|
|
|
Variant engine.Variant
|
|
StartFEN string
|
|
RuleFlags engine.RuleFlags
|
|
}
|
|
|
|
func (e *Entry) Lock() { e.mu.Lock() }
|
|
func (e *Entry) Unlock() { e.mu.Unlock() }
|
|
|
|
func (e *Entry) Open() bool {
|
|
return !e.VsBot && e.BlackID == 0
|
|
}
|
|
|
|
type Manager struct {
|
|
mu sync.Mutex
|
|
games map[string]*Entry
|
|
}
|
|
|
|
func NewManager() *Manager {
|
|
return &Manager{games: make(map[string]*Entry)}
|
|
}
|
|
|
|
func newID() string {
|
|
b := make([]byte, 16)
|
|
_, _ = rand.Read(b)
|
|
return hex.EncodeToString(b)
|
|
}
|
|
|
|
func (m *Manager) Create(variant engine.Variant, fen string, rules engine.RuleFlags, whiteID int64, whiteName string, blackID int64, blackName string, vsBot bool, botDepth int) (*Entry, error) {
|
|
id := newID()
|
|
eng, err := engine.New(variant, fen, rules, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
entry := &Entry{
|
|
Engine: eng, ID: id,
|
|
WhiteID: whiteID, WhiteName: whiteName,
|
|
BlackID: blackID, BlackName: blackName,
|
|
VsBot: vsBot, BotDepth: botDepth,
|
|
CreatedAt: time.Now(),
|
|
Variant: variant, StartFEN: fen, RuleFlags: rules,
|
|
}
|
|
m.mu.Lock()
|
|
m.games[id] = entry
|
|
m.mu.Unlock()
|
|
return entry, nil
|
|
}
|
|
|
|
func (m *Manager) Get(id string) (*Entry, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
e, ok := m.games[id]
|
|
if !ok {
|
|
return nil, ErrNotFound
|
|
}
|
|
return e, nil
|
|
}
|
|
|
|
func (m *Manager) Join(id string, userID int64, userName string) (*Entry, error) {
|
|
m.mu.Lock()
|
|
e, ok := m.games[id]
|
|
m.mu.Unlock()
|
|
if !ok {
|
|
return nil, ErrNotFound
|
|
}
|
|
|
|
e.Lock()
|
|
defer e.Unlock()
|
|
if !e.Open() {
|
|
return nil, ErrAlreadyJoined
|
|
}
|
|
if e.WhiteID == userID {
|
|
return nil, ErrSelfJoin
|
|
}
|
|
e.BlackID = userID
|
|
e.BlackName = userName
|
|
return e, nil
|
|
}
|
|
|
|
func (m *Manager) ListOpen() []*Entry {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
var out []*Entry
|
|
for _, e := range m.games {
|
|
e.Lock()
|
|
open := e.Open()
|
|
e.Unlock()
|
|
if open {
|
|
out = append(out, e)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (m *Manager) ListForUser(userID int64) []*Entry {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
var out []*Entry
|
|
for _, e := range m.games {
|
|
e.Lock()
|
|
mine := e.WhiteID == userID || e.BlackID == userID
|
|
e.Unlock()
|
|
if mine {
|
|
out = append(out, e)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (m *Manager) ListAll() []*Entry {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
out := make([]*Entry, 0, len(m.games))
|
|
for _, e := range m.games {
|
|
out = append(out, e)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (m *Manager) Cleanup(finishedTTL, openTTL time.Duration) int {
|
|
now := time.Now()
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
removed := 0
|
|
for id, e := range m.games {
|
|
e.Lock()
|
|
age := now.Sub(e.CreatedAt)
|
|
status := e.Engine.Status()
|
|
shouldRemove := (status != engine.StatusOngoing && age > finishedTTL) ||
|
|
(e.Open() && age > openTTL)
|
|
e.Unlock()
|
|
|
|
if shouldRemove {
|
|
delete(m.games, id)
|
|
removed++
|
|
}
|
|
}
|
|
return removed
|
|
}
|