Files
2026-02-12 18:00:55 +03:00

68 lines
1.4 KiB
Go

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]
}