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