package casino import ( "fmt" "math/rand" "sort" "sync" ) type Manager struct { mu sync.Mutex tables map[string]*Table machines map[string]*Machine rng *rand.Rand } func NewManager(seed int64) *Manager { return &Manager{ tables: make(map[string]*Table), machines: make(map[string]*Machine), rng: rand.New(rand.NewSource(seed)), } } func (m *Manager) EnsureMachine(roomID int, cfg MachineConfig) *Machine { cfg = cfg.Normalize() key := tableKey(roomID, cfg.ID) m.mu.Lock() defer m.mu.Unlock() if machine := m.machines[key]; machine != nil { return machine } machine := newMachine(roomID, cfg, m.rng) m.machines[key] = machine return machine } func tableKey(roomID int, tableID string) string { return fmt.Sprintf("%d:%s", roomID, tableID) } func (m *Manager) EnsureTable(roomID int, cfg TableConfig) *Table { cfg = cfg.Normalize() key := tableKey(roomID, cfg.ID) m.mu.Lock() defer m.mu.Unlock() if table := m.tables[key]; table != nil { return table } table := newTable(roomID, cfg, m.rng) m.tables[key] = table return table } func (m *Manager) Table(roomID int, tableID string) *Table { m.mu.Lock() defer m.mu.Unlock() return m.tables[tableKey(roomID, tableID)] } func (m *Manager) Tick() []Event { m.mu.Lock() tables := make([]*Table, 0, len(m.tables)) for _, table := range m.tables { tables = append(tables, table) } m.mu.Unlock() sort.Slice(tables, func(i, j int) bool { if tables[i].RoomID != tables[j].RoomID { return tables[i].RoomID < tables[j].RoomID } return tables[i].Config.ID < tables[j].Config.ID }) var events []Event for _, table := range tables { events = append(events, table.tick()...) } m.mu.Lock() machines := make([]*Machine, 0, len(m.machines)) for _, machine := range m.machines { machines = append(machines, machine) } m.mu.Unlock() sort.Slice(machines, func(i, j int) bool { if machines[i].RoomID != machines[j].RoomID { return machines[i].RoomID < machines[j].RoomID } return machines[i].Config.ID < machines[j].Config.ID }) for _, machine := range machines { events = append(events, machine.tick()...) } return events } func (m *Manager) JoinMachine(roomID int, cfg MachineConfig, name string) []Event { return m.EnsureMachine(roomID, cfg).join(name) } func (m *Manager) SetMachineBet(roomID int, machineID, name string, amount int) []Event { m.mu.Lock() machine := m.machines[tableKey(roomID, machineID)] m.mu.Unlock() if machine == nil { return nil } return machine.setBet(name, amount) } func (m *Manager) StartMachine(roomID int, machineID, name string, wallet Wallet) []Event { m.mu.Lock() machine := m.machines[tableKey(roomID, machineID)] m.mu.Unlock() if machine == nil { return nil } return machine.start(name, wallet) } func (m *Manager) EnableMachineAutoBet(roomID int, machineID, name string, wallet Wallet) []Event { m.mu.Lock() machine := m.machines[tableKey(roomID, machineID)] m.mu.Unlock() if machine == nil { return nil } return machine.enableAutoBet(name, wallet) } func (m *Manager) StopMachineAutoBet(roomID int, machineID, name string) ([]Event, bool) { m.mu.Lock() machine := m.machines[tableKey(roomID, machineID)] m.mu.Unlock() if machine == nil { return nil, false } return machine.stopAutoBet(name) } func (m *Manager) LeaveMachine(roomID int, machineID, name string) []Event { m.mu.Lock() machine := m.machines[tableKey(roomID, machineID)] m.mu.Unlock() if machine == nil { return nil } return machine.leave(name) } func (m *Manager) Join(roomID int, cfg TableConfig, name string) []Event { return m.EnsureTable(roomID, cfg).join(name) } func (m *Manager) Leave(roomID int, tableID, name string) []Event { if table := m.Table(roomID, tableID); table != nil { return table.leave(name) } return nil } func (m *Manager) Bet(roomID int, tableID, name string, amount int, wallet Wallet) []Event { if table := m.Table(roomID, tableID); table != nil { return table.bet(name, amount, wallet) } return []Event{{RoomID: roomID, TableID: tableID, Type: EventResult, Player: name, PrivateTo: name, Message: "That game is not available here."}} } func (m *Manager) MarkDisconnected(name string) []Event { m.mu.Lock() tables := make([]*Table, 0, len(m.tables)) for _, table := range m.tables { tables = append(tables, table) } m.mu.Unlock() var events []Event for _, table := range tables { events = append(events, table.markDisconnected(name)...) } m.mu.Lock() machines := make([]*Machine, 0, len(m.machines)) for _, machine := range m.machines { machines = append(machines, machine) } m.mu.Unlock() for _, machine := range machines { events = append(events, machine.markDisconnected(name)...) } return events } func (m *Manager) MarkConnected(name string) { m.mu.Lock() defer m.mu.Unlock() for _, table := range m.tables { table.markConnected(name) } for _, machine := range m.machines { machine.markConnected(name) } } func (m *Manager) Participation(name string) (*Table, bool) { m.mu.Lock() defer m.mu.Unlock() for _, table := range m.tables { if table.hasParticipant(name) { return table, true } } for _, machine := range m.machines { if machine.hasParticipant(name) { return nil, true } } return nil, false } func (m *Manager) MachineFor(name string) (*Machine, bool) { m.mu.Lock() defer m.mu.Unlock() for _, machine := range m.machines { if machine.hasParticipant(name) { return machine, true } } return nil, false } func (m *Manager) MachineBusy(name string) bool { m.mu.Lock() defer m.mu.Unlock() for _, machine := range m.machines { if machine.isBusy(name) { return true } } return false }