Files
chatroom/app/Models/HorseRace.php

160 lines
4.5 KiB
PHP

<?php
/**
* 文件功能:赛马竞猜局次模型
*
* 代表一场赛马比赛,包含参赛马匹信息、场次状态、
* 注池统计以及赛果信息。提供当前场次查询和赔率计算方法。
*
* @author ChatRoom Laravel
*
* @version 1.0.0
*/
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class HorseRace extends Model
{
protected $fillable = [
'status',
'bet_opens_at',
'bet_closes_at',
'race_starts_at',
'race_ends_at',
'horses',
'winner_horse_id',
'total_bets',
'total_pool',
'settled_at',
];
/**
* 属性类型转换。
*/
protected function casts(): array
{
return [
'bet_opens_at' => 'datetime',
'bet_closes_at' => 'datetime',
'race_starts_at' => 'datetime',
'race_ends_at' => 'datetime',
'settled_at' => 'datetime',
'horses' => 'array',
'winner_horse_id' => 'integer',
'total_bets' => 'integer',
'total_pool' => 'integer',
];
}
/**
* 本场所有下注记录。
*/
public function bets(): HasMany
{
return $this->hasMany(HorseBet::class, 'race_id');
}
/**
* 判断当前是否在押注时间窗口内。
*/
public function isBettingOpen(): bool
{
return $this->status === 'betting'
&& now()->between($this->bet_opens_at, $this->bet_closes_at);
}
/**
* 查询当前正在进行的场次(状态为 betting 且押注未截止)。
*/
public static function currentRace(): ?static
{
return static::query()
->whereIn('status', ['betting', 'running'])
->latest()
->first();
}
/**
* 生成参赛马匹列表(根据马匹数量随机选名)。
*
* @param int $count 马匹数量
* @return array<int, array{id: int, name: string, emoji: string}>
*/
public static function generateHorses(int $count): array
{
// 可用马匹名池(原版竞技风格)
$namePool = [
['name' => '赤兔', 'emoji' => '🐎'],
['name' => '乌骓', 'emoji' => '🐴'],
['name' => '的卢', 'emoji' => '🎠'],
['name' => '绝影', 'emoji' => '🦄'],
['name' => '紫骍', 'emoji' => '🐎'],
['name' => '爪黄', 'emoji' => '🐴'],
['name' => '汗血', 'emoji' => '🎠'],
['name' => '飞电', 'emoji' => '⚡'],
];
// 随机打乱并取前 N 个
shuffle($namePool);
$selected = array_slice($namePool, 0, $count);
$horses = [];
foreach ($selected as $i => $horse) {
$horses[] = [
'id' => $i + 1,
'name' => $horse['name'],
'emoji' => $horse['emoji'],
];
}
return $horses;
}
/**
* 计算本场可派奖总池。
*
* 可派奖池 = 系统初始化资金池 + 玩家总下注 - 抽水,
* 且至少不低于中奖马匹总下注,避免出现“押中反亏”的体验。
*
* @param array<int, int|float> $horseBetAmounts
*/
public static function calcDistributablePool(array $horseBetAmounts, int $housePercent, int $seedPool = 0, int $winnerPool = 0): float
{
$totalPool = array_sum($horseBetAmounts);
$netPlayerPool = $totalPool * (1 - $housePercent / 100);
return max($netPlayerPool + max(0, $seedPool), $winnerPool);
}
/**
* 根据注池计算各马匹实时赔率(含系统初始化资金池)。
*
* @param array<int, int|float> $horseBetAmounts 各马匹的注额数组 [horse_id => amount]
* @return array<int, float> horse_id => 赔率(含本金)
*/
public static function calcOdds(array $horseBetAmounts, int $housePercent, int $seedPool = 0): array
{
$totalPool = array_sum($horseBetAmounts);
if ($totalPool <= 0) {
return array_map(fn () => 1.0, $horseBetAmounts);
}
$distributablePool = static::calcDistributablePool($horseBetAmounts, $housePercent, $seedPool);
$odds = [];
foreach ($horseBetAmounts as $horseId => $amount) {
if ($amount <= 0) {
$odds[$horseId] = round($distributablePool, 2);
} else {
$odds[$horseId] = round($distributablePool / $amount, 2);
}
}
return $odds;
}
}