232 lines
6.1 KiB
Go
232 lines
6.1 KiB
Go
package player
|
||
|
||
import (
|
||
"math"
|
||
grand "math/rand"
|
||
)
|
||
|
||
// RandomMatrixNoSingleColorBright 是在 RandomMatrixNoSingleColorBias 基础上的变体,
|
||
// 增加 brightnessScale 参数用于控制允许的 offset(亮度提升)的比例。
|
||
// 参数说明:
|
||
// - seed: 随机种子
|
||
// - alphaRow: 最后 5 字节(alpha 行)固定值
|
||
// - brightChance: 原有决定 ModeB 的概率(常量行概率)
|
||
// - redBias: 保持原有 R 偏好(0..1)
|
||
// - brightnessScale: 0..1,0 表示不额外提升亮度(行为近似原函数),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 写入 out(ModeA one-hot,ModeB 暂置 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.6,brightnessScale=0.5
|
||
func RandomMatrixNoSingleColorBrightDefault(seed int64, alphaRow [5]float32, brightChance float64) [20]float32 {
|
||
return RandomMatrixNoSingleColorBright(seed, alphaRow, brightChance, 0.6, 0.5)
|
||
}
|