Files
bl/logic/service/player/color.go

232 lines
6.1 KiB
Go
Raw Normal View History

package player
import (
"math"
grand "math/rand"
)
// RandomMatrixNoSingleColorBright 是在 RandomMatrixNoSingleColorBias 基础上的变体,
// 增加 brightnessScale 参数用于控制允许的 offset亮度提升的比例。
// 参数说明:
// - seed: 随机种子
// - alphaRow: 最后 5 字节alpha 行)固定值
// - brightChance: 原有决定 ModeB 的概率(常量行概率)
// - redBias: 保持原有 R 偏好0..1
// - brightnessScale: 0..10 表示不额外提升亮度行为近似原函数1 表示允许最大可用 offset受每行 sum 限制)
// 返回20 字节矩阵(直接 uint8 原样使用)
func RandomMatrixNoSingleColorBright(seed int64, alphaRow [5]float32, brightChance, redBias, brightnessScale float64) [20]float32 {
var out [20]float32
// clamp params
if brightChance < 0 {
brightChance = 0
}
if brightChance > 1 {
brightChance = 1
}
if redBias < 0 {
redBias = 0
}
if redBias > 1 {
redBias = 1
}
if brightnessScale < 0 {
brightnessScale = 0
}
if brightnessScale > 1 {
brightnessScale = 1
}
const minOffsetBase = 24 // 最小偏移基线(你可以调大以更亮)
const maxOffsetBase = 180 // 最大偏移基线(经验上不宜太接近 255
grand.Seed(seed)
// 1) 先按原逻辑决定 ModeA / ModeB 与 picks保留 redBias 偏好)
modeB := make([]bool, 3)
modeBCount := 0
for r := 0; r < 3; r++ {
if grand.Float64() < brightChance {
modeB[r] = true
modeBCount++
} else {
modeB[r] = false
}
}
if modeBCount == 3 {
which := int(grand.Intn(3))
modeB[which] = false
modeBCount--
}
// assign ModeA picks with red bias (similar于之前实现)
available := []int{0, 1, 2, 3}
picks := make(map[int]int)
used := make(map[int]bool)
modeARows := []int{}
for r := 0; r < 3; r++ {
if !modeB[r] {
modeARows = append(modeARows, r)
}
}
weightedPick := func(avoidUsed bool) int {
cands := []int{}
ws := []float64{}
for _, c := range available {
if avoidUsed && used[c] {
continue
}
w := 1.0
if c == 0 {
w += redBias * 3.0
} // 红色加权
cands = append(cands, c)
ws = append(ws, w)
}
if len(cands) == 0 {
for _, c := range available {
w := 1.0
if c == 0 {
w += redBias * 3.0
}
cands = append(cands, c)
ws = append(ws, w)
}
}
sum := 0.0
for _, w := range ws {
sum += w
}
rv := grand.Float64() * sum
acc := 0.0
for i, w := range ws {
acc += w
if rv <= acc {
return cands[i]
}
}
return cands[len(cands)-1]
}
for _, row := range modeARows {
p := weightedPick(true)
picks[row] = p
used[p] = true
}
// 2) 先把 multipliers 写入 outModeA one-hotModeB 暂置 multipliers=0
for r := 0; r < 3; r++ {
base := r * 5
if !modeB[r] {
p := picks[r]
for c := 0; c < 4; c++ {
if c == p {
out[base+c] = 1
} else {
out[base+c] = 0
}
}
out[base+4] = 0 // 暂时保持 0后面可能根据 sum 注入 offset
} else {
for c := 0; c < 4; c++ {
out[base+c] = 0
}
out[base+4] = 0 // 将在下一步设置合法 offset
}
}
// 3) 基于每行 multipliers 的 sum 计算允许的 offset 上限,并在允许范围内按 brightnessScale 随机选择 offset
// allowedMaxOffset = min(maxOffsetBase, floor(255*(1 - sum))) 然后乘以 brightnessScale线性缩放
hadOffsetRow := false
for r := 0; r < 3; r++ {
base := r * 5
// 计算 multipliers sum目前为 0/1
sum := 0
for c := 0; c < 4; c++ {
sum += int(out[base+c])
}
// 允许的最大 offset绝对上限由 sum 决定)
allowedBySum := int(math.Floor(255.0 * (1.0 - float64(sum))))
if allowedBySum < 0 {
allowedBySum = 0
}
// 想要的最大基线由 maxOffsetBase 决定,再乘以 brightnessScale
desiredMax := int(float64(maxOffsetBase) * brightnessScale)
// 最终可用最大 offset至少要大于等于 minOffsetBase 才能选择)
allowedMax := allowedBySum
if desiredMax < allowedMax {
allowedMax = desiredMax
}
if allowedMax < minOffsetBase {
// 不足最小基线,设为 0不可用 offset
out[base+4] = 0
} else {
// 在 [minOffsetBase, allowedMax] 随机取一个 offset
off := minOffsetBase + int(grand.Intn(allowedMax-minOffsetBase+1))
out[base+4] = float32(off)
hadOffsetRow = true
}
}
// 4) 保证至少有一行有 offset避免整个图都只靠拷贝导致暗
if !hadOffsetRow {
// 尝试找到 sum < 1 的行 preferentially Rout (row 0) then others
rowChosen := -1
for r := 0; r < 3; r++ {
base := r * 5
sum := 0
for c := 0; c < 4; c++ {
sum += int(out[base+c])
}
if sum < 1 {
rowChosen = r
break
}
}
if rowChosen == -1 {
// 所有行 sum==1很可能全部 one-hot我们需要强制把其中一行的 multiplier 置为 0 并赋 offset
rowChosen = int(grand.Intn(3))
base := rowChosen * 5
for c := 0; c < 4; c++ {
out[base+c] = 0
}
}
// 计算 allowedBySum 并设置 offset 为 min(allowedBySum, desired),至少设为 minOffsetBase
base := rowChosen * 5
sum := 0
for c := 0; c < 4; c++ {
sum += int(out[base+c])
}
allowedBySum := int(math.Floor(255.0 * (1.0 - float64(sum))))
if allowedBySum < minOffsetBase {
// 若仍不足则设为 min(minOffsetBase, allowedBySum) 或强制给一个较小的值
if allowedBySum > 0 {
out[base+4] = float32(allowedBySum)
} else {
out[base+4] = float32(minOffsetBase) // 最后手段(风险低,因为我们可能已把 sum=0
}
} else {
desiredMax := int(float64(maxOffsetBase) * brightnessScale)
if desiredMax > allowedBySum {
desiredMax = allowedBySum
}
if desiredMax < minOffsetBase {
desiredMax = minOffsetBase
}
off := minOffsetBase + int(grand.Intn(desiredMax-minOffsetBase+1))
out[base+4] = float32(off)
}
}
// 5) 最后 5 个字节alpha 行)由 caller 指定并固定
for i := 0; i < 5; i++ {
out[15+i] = alphaRow[i]
}
return out
}
// 便利 wrapper默认 redBias=0.6brightnessScale=0.5
func RandomMatrixNoSingleColorBrightDefault(seed int64, alphaRow [5]float32, brightChance float64) [20]float32 {
return RandomMatrixNoSingleColorBright(seed, alphaRow, brightChance, 0.6, 0.5)
}