优化:完善百家乐AI决策,增加历史记录上下文、底部仓位预留与智能观望广播功能
This commit is contained in:
+102
-25
@@ -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小班长本次下注情况到聊天室
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user