package search import ( "math/rand/v2" "time" "git.vakhrushev.me/av/remembos/internal/memos" ) // Memory is the result of the search algorithm. type Memory struct { Memo *memos.Memo Tier int YearsAgo int ShowCount int Date time.Time } // candidate is an intermediate struct during scoring. type candidate struct { memo *memos.Memo yearsAgo int showCount int } // weightedSelect picks one candidate using weighted random selection. // preferOlder gives higher weight to older memos. maxYearsBack normalizes recency. func weightedSelect(candidates []candidate, preferOlder bool, maxYearsBack int) *candidate { if len(candidates) == 0 { return nil } if len(candidates) == 1 { return &candidates[0] } scores := make([]float64, len(candidates)) var total float64 for i, c := range candidates { var recencyFactor float64 if preferOlder && maxYearsBack > 0 && c.yearsAgo > 0 { recencyFactor = float64(c.yearsAgo) / float64(maxYearsBack) } else { recencyFactor = 1.0 } showCountPenalty := 1.0 / (1.0 + float64(c.showCount)) score := recencyFactor * showCountPenalty if score < 0.01 { score = 0.01 // minimum weight } scores[i] = score total += score } r := rand.Float64() * total //nolint:gosec // non-cryptographic use var cumulative float64 for i, s := range scores { cumulative += s if r <= cumulative { return &candidates[i] } } return &candidates[len(candidates)-1] }