package alt_advancement import ( "fmt" "sync" ) // MasterList is a specialized alternate advancement master list optimized for: // - Fast ID-based lookups (O(1)) // - Fast group-based lookups (O(1)) // - Fast class-based lookups (O(1)) // - Fast level-based lookups (O(1)) // - Efficient filtering and prerequisite validation type MasterList struct { // Core storage altAdvancements map[int32]*AltAdvancement // NodeID -> AltAdvancement mutex sync.RWMutex // Group indices for O(1) lookups byGroup map[int8][]*AltAdvancement // Group -> AAs byClass map[int8][]*AltAdvancement // ClassReq -> AAs byLevel map[int8][]*AltAdvancement // MinLevel -> AAs // Cached metadata groups []int8 // Unique groups (cached) classes []int8 // Unique classes (cached) metaStale bool // Whether metadata cache needs refresh } // NewMasterList creates a new specialized alternate advancement master list func NewMasterList() *MasterList { return &MasterList{ altAdvancements: make(map[int32]*AltAdvancement), byGroup: make(map[int8][]*AltAdvancement), byClass: make(map[int8][]*AltAdvancement), byLevel: make(map[int8][]*AltAdvancement), metaStale: true, } } // refreshMetaCache updates the groups and classes cache func (m *MasterList) refreshMetaCache() { if !m.metaStale { return } groupSet := make(map[int8]struct{}) classSet := make(map[int8]struct{}) // Collect unique groups and classes for _, aa := range m.altAdvancements { groupSet[aa.Group] = struct{}{} if aa.ClassReq > 0 { classSet[aa.ClassReq] = struct{}{} } } // Clear existing caches and rebuild m.groups = m.groups[:0] for group := range groupSet { m.groups = append(m.groups, group) } m.classes = m.classes[:0] for class := range classSet { m.classes = append(m.classes, class) } m.metaStale = false } // AddAltAdvancement adds an alternate advancement with full indexing func (m *MasterList) AddAltAdvancement(aa *AltAdvancement) bool { if aa == nil { return false } m.mutex.Lock() defer m.mutex.Unlock() // Check if exists if _, exists := m.altAdvancements[aa.NodeID]; exists { return false } // Add to core storage m.altAdvancements[aa.NodeID] = aa // Update group index m.byGroup[aa.Group] = append(m.byGroup[aa.Group], aa) // Update class index m.byClass[aa.ClassReq] = append(m.byClass[aa.ClassReq], aa) // Update level index m.byLevel[aa.MinLevel] = append(m.byLevel[aa.MinLevel], aa) // Invalidate metadata cache m.metaStale = true return true } // GetAltAdvancement retrieves by node ID (O(1)) func (m *MasterList) GetAltAdvancement(nodeID int32) *AltAdvancement { m.mutex.RLock() defer m.mutex.RUnlock() return m.altAdvancements[nodeID] } // GetAltAdvancementClone retrieves a cloned copy of an alternate advancement by node ID func (m *MasterList) GetAltAdvancementClone(nodeID int32) *AltAdvancement { m.mutex.RLock() defer m.mutex.RUnlock() aa := m.altAdvancements[nodeID] if aa == nil { return nil } return aa.Clone() } // GetAllAltAdvancements returns a copy of all alternate advancements map func (m *MasterList) GetAllAltAdvancements() map[int32]*AltAdvancement { m.mutex.RLock() defer m.mutex.RUnlock() // Return a copy to prevent external modification result := make(map[int32]*AltAdvancement, len(m.altAdvancements)) for id, aa := range m.altAdvancements { result[id] = aa } return result } // GetAltAdvancementsByGroup returns all alternate advancements in a group (O(1)) func (m *MasterList) GetAltAdvancementsByGroup(group int8) []*AltAdvancement { m.mutex.RLock() defer m.mutex.RUnlock() return m.byGroup[group] } // GetAltAdvancementsByClass returns all alternate advancements for a class (O(1)) func (m *MasterList) GetAltAdvancementsByClass(classID int8) []*AltAdvancement { m.mutex.RLock() defer m.mutex.RUnlock() // Return class-specific AAs plus universal AAs (ClassReq == 0) var result []*AltAdvancement // Add class-specific AAs if classAAs := m.byClass[classID]; classAAs != nil { result = append(result, classAAs...) } // Add universal AAs (ClassReq == 0) if universalAAs := m.byClass[0]; universalAAs != nil { result = append(result, universalAAs...) } return result } // GetAltAdvancementsByLevel returns all alternate advancements available at a specific level func (m *MasterList) GetAltAdvancementsByLevel(level int8) []*AltAdvancement { m.mutex.RLock() defer m.mutex.RUnlock() var result []*AltAdvancement // Collect all AAs with MinLevel <= level for minLevel, aas := range m.byLevel { if minLevel <= level { result = append(result, aas...) } } return result } // GetAltAdvancementsByGroupAndClass returns AAs matching both group and class func (m *MasterList) GetAltAdvancementsByGroupAndClass(group, classID int8) []*AltAdvancement { m.mutex.RLock() defer m.mutex.RUnlock() groupAAs := m.byGroup[group] if groupAAs == nil { return nil } var result []*AltAdvancement for _, aa := range groupAAs { if aa.ClassReq == 0 || aa.ClassReq == classID { result = append(result, aa) } } return result } // GetGroups returns all unique groups using cached results func (m *MasterList) GetGroups() []int8 { m.mutex.Lock() // Need write lock to potentially update cache defer m.mutex.Unlock() m.refreshMetaCache() // Return a copy to prevent external modification result := make([]int8, len(m.groups)) copy(result, m.groups) return result } // GetClasses returns all unique classes using cached results func (m *MasterList) GetClasses() []int8 { m.mutex.Lock() // Need write lock to potentially update cache defer m.mutex.Unlock() m.refreshMetaCache() // Return a copy to prevent external modification result := make([]int8, len(m.classes)) copy(result, m.classes) return result } // RemoveAltAdvancement removes an alternate advancement and updates all indices func (m *MasterList) RemoveAltAdvancement(nodeID int32) bool { m.mutex.Lock() defer m.mutex.Unlock() aa, exists := m.altAdvancements[nodeID] if !exists { return false } // Remove from core storage delete(m.altAdvancements, nodeID) // Remove from group index groupAAs := m.byGroup[aa.Group] for i, a := range groupAAs { if a.NodeID == nodeID { m.byGroup[aa.Group] = append(groupAAs[:i], groupAAs[i+1:]...) break } } // Remove from class index classAAs := m.byClass[aa.ClassReq] for i, a := range classAAs { if a.NodeID == nodeID { m.byClass[aa.ClassReq] = append(classAAs[:i], classAAs[i+1:]...) break } } // Remove from level index levelAAs := m.byLevel[aa.MinLevel] for i, a := range levelAAs { if a.NodeID == nodeID { m.byLevel[aa.MinLevel] = append(levelAAs[:i], levelAAs[i+1:]...) break } } // Invalidate metadata cache m.metaStale = true return true } // UpdateAltAdvancement updates an existing alternate advancement func (m *MasterList) UpdateAltAdvancement(aa *AltAdvancement) error { if aa == nil { return fmt.Errorf("alternate advancement cannot be nil") } m.mutex.Lock() defer m.mutex.Unlock() // Check if exists old, exists := m.altAdvancements[aa.NodeID] if !exists { return fmt.Errorf("alternate advancement %d not found", aa.NodeID) } // Remove old AA from indices (but not core storage yet) groupAAs := m.byGroup[old.Group] for i, a := range groupAAs { if a.NodeID == aa.NodeID { m.byGroup[old.Group] = append(groupAAs[:i], groupAAs[i+1:]...) break } } classAAs := m.byClass[old.ClassReq] for i, a := range classAAs { if a.NodeID == aa.NodeID { m.byClass[old.ClassReq] = append(classAAs[:i], classAAs[i+1:]...) break } } levelAAs := m.byLevel[old.MinLevel] for i, a := range levelAAs { if a.NodeID == aa.NodeID { m.byLevel[old.MinLevel] = append(levelAAs[:i], levelAAs[i+1:]...) break } } // Update core storage m.altAdvancements[aa.NodeID] = aa // Add new AA to indices m.byGroup[aa.Group] = append(m.byGroup[aa.Group], aa) m.byClass[aa.ClassReq] = append(m.byClass[aa.ClassReq], aa) m.byLevel[aa.MinLevel] = append(m.byLevel[aa.MinLevel], aa) // Invalidate metadata cache m.metaStale = true return nil } // Size returns the total number of alternate advancements func (m *MasterList) Size() int { m.mutex.RLock() defer m.mutex.RUnlock() return len(m.altAdvancements) } // Clear removes all alternate advancements from the master list func (m *MasterList) Clear() { m.mutex.Lock() defer m.mutex.Unlock() // Clear all maps m.altAdvancements = make(map[int32]*AltAdvancement) m.byGroup = make(map[int8][]*AltAdvancement) m.byClass = make(map[int8][]*AltAdvancement) m.byLevel = make(map[int8][]*AltAdvancement) // Clear cached metadata m.groups = m.groups[:0] m.classes = m.classes[:0] m.metaStale = true } // ForEach executes a function for each alternate advancement func (m *MasterList) ForEach(fn func(int32, *AltAdvancement)) { m.mutex.RLock() defer m.mutex.RUnlock() for id, aa := range m.altAdvancements { fn(id, aa) } } // ValidateAll validates all alternate advancements in the master list func (m *MasterList) ValidateAll() []error { m.mutex.RLock() defer m.mutex.RUnlock() var errors []error for nodeID, aa := range m.altAdvancements { if !aa.IsValid() { errors = append(errors, fmt.Errorf("invalid AA data: node_id=%d", nodeID)) } // Validate prerequisites if aa.RankPrereqID > 0 { prereq := m.altAdvancements[aa.RankPrereqID] if prereq == nil { errors = append(errors, fmt.Errorf("AA %d has invalid prerequisite node ID %d", nodeID, aa.RankPrereqID)) } } // Validate positioning if aa.Col < MIN_AA_COL || aa.Col > MAX_AA_COL { errors = append(errors, fmt.Errorf("AA %d has invalid column %d", nodeID, aa.Col)) } if aa.Row < MIN_AA_ROW || aa.Row > MAX_AA_ROW { errors = append(errors, fmt.Errorf("AA %d has invalid row %d", nodeID, aa.Row)) } // Validate costs and ranks if aa.RankCost < MIN_RANK_COST || aa.RankCost > MAX_RANK_COST { errors = append(errors, fmt.Errorf("AA %d has invalid rank cost %d", nodeID, aa.RankCost)) } if aa.MaxRank < MIN_MAX_RANK || aa.MaxRank > MAX_MAX_RANK { errors = append(errors, fmt.Errorf("AA %d has invalid max rank %d", nodeID, aa.MaxRank)) } } return errors }