优化:完善百家乐AI决策,增加历史记录上下文、底部仓位预留与智能观望广播功能

This commit is contained in:
2026-04-02 10:21:20 +08:00
parent 3a460b9ac6
commit 69e41fbbd9
2 changed files with 180 additions and 58 deletions
+102 -25
View File
@@ -76,17 +76,7 @@ class AiBaccaratBetJob implements ShouldQueue
return; // 资金不足以支撑最小下注
}
// 下注金额:可用余额的 2% ~ 5%,并在 min_bet 和 max_bet 之间
$percent = rand(2, 5) / 100.0;
$amount = (int) round($availableGold * $percent);
$amount = max($minBet, min($amount, $maxBet));
// 如果依然大于实际 jjb (保险兜底),则放弃
if ($amount > $user->jjb) {
return;
}
// 4. 获取近期路单(最多取 20 局)
// 4. 获取近期路单和 AI 历史下注
$recentResults = BaccaratRound::query()
->where('status', 'settled')
->orderByDesc('id')
@@ -94,14 +84,56 @@ class AiBaccaratBetJob implements ShouldQueue
->pluck('result')
->toArray();
// 5. 优先调用 AI 接口预测下注方向
$predictionService = app(BaccaratPredictionService::class);
$aiPrediction = $predictionService->predict($recentResults);
$decisionSource = 'ai';
$betType = $aiPrediction;
$historicalBets = BaccaratBet::query()
->join('baccarat_rounds', 'baccarat_bets.round_id', '=', 'baccarat_rounds.id')
->where('baccarat_bets.user_id', $user->id)
->where('baccarat_rounds.status', 'settled')
->orderByDesc('baccarat_rounds.id')
->limit(10)
->select('baccarat_bets.*')
->get()
->map(function ($bet) {
return [
'round_id' => $bet->round_id,
'bet_type' => $bet->bet_type,
'amount' => $bet->amount,
'profit' => $bet->profit ?? 0,
];
})
->toArray();
// AI 不可用时回退本地路单决策(保底逻辑)
if ($betType === null) {
// 5. 调用 AI 接口出具统筹策略
$predictionService = app(BaccaratPredictionService::class);
$context = [
'recent_results' => $recentResults,
'available_gold' => $availableGold,
'historical_bets' => $historicalBets,
];
$aiPrediction = $predictionService->predict($context);
$decisionSource = 'ai';
$betType = 'pass';
$amount = 0;
$reason = '';
if ($aiPrediction) {
$betType = $aiPrediction['action'];
$percent = $aiPrediction['percentage'];
$reason = $aiPrediction['reason'];
// 限定单局最高下注不超过可用金额的 5% 以防止 AI "乱梭哈" 破产
$percent = min(5, max(0, $percent));
if ($betType !== 'pass') {
$amount = (int) round($availableGold * ($percent / 100.0));
$amount = max($minBet, min($amount, $maxBet));
if ($amount > $user->jjb) {
$amount = $user->jjb;
}
}
} else {
// AI 不可用时回退本地路单决策(保底逻辑)
$decisionSource = 'local';
$bigCount = count(array_filter($recentResults, fn (string $r) => $r === 'big'));
$smallCount = count(array_filter($recentResults, fn (string $r) => $r === 'small'));
@@ -110,27 +142,39 @@ class AiBaccaratBetJob implements ShouldQueue
if ($strategy <= 10) {
$betType = 'triple'; // 10% 概率博豹子
} elseif ($bigCount > $smallCount) {
// 大偏热:70% 概率顺势买大,30% 逆势买小
$betType = rand(1, 100) <= 70 ? 'big' : 'small';
} elseif ($smallCount > $bigCount) {
$betType = rand(1, 100) <= 70 ? 'small' : 'big';
} else {
$betType = rand(0, 1) ? 'big' : 'small';
$betType = rand(0, 10) === 0 ? 'pass' : (rand(0, 1) ? 'big' : 'small');
}
if ($betType !== 'pass') {
$percent = rand(2, 5) / 100.0;
$amount = (int) round($availableGold * $percent);
$amount = max($minBet, min($amount, $maxBet));
}
}
// 记录 AI 小班长本次决策日志
$labelMap = ['big' => '大', 'small' => '小', 'triple' => '豹子'];
$labelMap = ['big' => '大', 'small' => '小', 'triple' => '豹子', 'pass' => '观望'];
Log::channel('daily')->info('AI小班长百家乐决策', [
'round_id' => $round->id,
'decision_source' => $decisionSource === 'ai' ? 'AI接口预测' : '本地路单兜底',
'ai_prediction' => $aiPrediction ? $labelMap[$aiPrediction] : 'null(不可用)',
'final_bet' => $labelMap[$betType] ?? $betType,
'decision_source' => $decisionSource === 'ai' ? 'AI接口策略' : '本地路单兜底',
'action' => $labelMap[$betType] ?? $betType,
'bet_amount' => $amount,
'reason' => $reason,
'roadmap_20' => implode('→', array_map(fn (string $r) => $labelMap[$r] ?? $r, array_reverse($recentResults))),
]);
// 5. 执行下注 (同 BaccaratController::bet 逻辑)
if ($betType === 'pass') {
// 观望时仅广播,不执行真正的金币扣除下注逻辑
$this->broadcastPassMessage($user, $round->room_id ?? 1, $reason);
return;
}
// 6. 执行下注 (同 BaccaratController::bet 逻辑)
DB::transaction(function () use ($user, $round, $betType, $amount, $currency) {
// 幂等:同一局只能下一注
$existing = BaccaratBet::query()
@@ -178,6 +222,39 @@ class AiBaccaratBetJob implements ShouldQueue
$this->broadcastBetMessage($user, $round->room_id ?? 1, $betType, $amount, $decisionSource);
}
/**
* 广播 AI小班长的观望文案到聊天室
*
* @param User $user AI小班长用户
* @param int $roomId 聊天室 ID
* @param string $reason 观望理由
*/
private function broadcastPassMessage(User $user, int $roomId, string $reason): void
{
if (empty($reason)) {
$reason = '风大雨大,保本最大,这把我决定观望一下!';
}
$chatState = app(ChatStateService::class);
$content = "🌟 🎲 【百家乐】 <b>{$user->username}</b> 本局选择挂机观望:✨ <br/><span style='color:#666;'>[🤖 策略分析] {$reason}</span>";
$msg = [
'id' => $chatState->nextMessageId($roomId),
'room_id' => $roomId,
'from_user' => '系统传音',
'to_user' => '大家',
'content' => $content,
'is_secret' => false,
'font_color' => '#d97706',
'action' => '',
'sent_at' => now()->toDateTimeString(),
];
$chatState->pushMessage($roomId, $msg);
broadcast(new MessageSent($roomId, $msg));
SaveMessageJob::dispatch($msg);
}
/**
* 广播 AI小班长本次下注情况到聊天室
*