jingcai-php/app/Service/JingCaiTrait.php

731 lines
24 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Service;
use App\Enums\LottState;
use App\Enums\OptimizeType;
use App\Enums\OrderType;
use App\Enums\PayState;
use App\Enums\PlayType;
use App\Model\Config;
use App\Model\Order;
use App\Model\Zq\JczqOdds;
use App\Utils\Helps;
use App\Utils\ThrowException;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
trait JingCaiTrait
{
/**
* 2元最高中50W
* @param $prize
* @param $chuanNum 1,2,3,4 ...
* @return int|mixed
*/
public static function getTiCaiOneBetsPrize($prize, $chuanNum) {
$twoWan = 200000;
$fiveWan = 500000;
if ($chuanNum == 2 || $chuanNum == 3) {
if ($prize >= $twoWan) {
$prize = $twoWan;
}
} else if ($chuanNum == 4) {
if ($prize >= $fiveWan) {
$prize = $fiveWan;
}
}
return $prize;
}
public function checkCloseTimeOver($closeTime) {
ThrowException::isTrue(date('Y-m-d H:i:s') >= $closeTime, '投注已截止');
}
/**
* @param $fieldData "{"win":1.1,"draw":1.2,"loss":1.3}"
* @return array|array[]
*/
public function maxPrizeSearchMaxOdd($fieldData)
{
$result = [];
$max = -1;
foreach ($fieldData as $field => $odd) {
if ($odd > $max) {
$max = $odd;
$result = [$field => $odd];
}
}
return $result;
}
public function maxPrizeSumPlayOdd($oddData)
{
$winOdds = 0;
foreach ($oddData as $playData) {
foreach ($playData as $field => $odd) {
$winOdds+=$odd;
}
}
return $winOdds;
}
public function betsInfoInOtherBetsInfo($bigBetsInfo, $smallBetsInfo)
{
if (!$bigBetsInfo || !$smallBetsInfo) {
return false;
}
$arr = array_intersect($bigBetsInfo['keys'], $smallBetsInfo['keys']);
return count($arr) == count($smallBetsInfo['keys']);
}
public function betsInfoToPiaoTags($betsInfo)
{
$piaoTags = [];
foreach ($betsInfo['info'] as $item) {
$piaoTags[] = $item['id'] . '-' . $item['play'];
}
return $piaoTags;
}
public function betsInfoInPiaoTags($betsInfo, $piaoTagArray)
{
$betsInfoTag = $this->betsInfoToPiaoTags($betsInfo);
$arr = array_intersect($betsInfoTag, $piaoTagArray);
return count($arr) == count($betsInfoTag);
}
public function betsInfoInDanGuanPiaoPlay($betsInfo, $danGuanPlay)
{
if (count($betsInfo['info']) != 1) {
return false;
}
foreach ($betsInfo['info'] as $item) {
return $item['play'] == $danGuanPlay;
}
return false;
}
public function getMaxPassModeKey($passModeKeys) {
foreach ($passModeKeys as $passKey) {
$chuanArr = JclqService::getPassMode($passKey);
foreach ($chuanArr as $chang => $ge) {
$chuanChange = $chang .'.1';
$keysArray[$chuanChange] = $chuanChange;
}
}
sort($keysArray);
return array_pop($keysArray);
}
public function getZuheListFromPiaos($piaos)
{
$zuheList = [];
foreach ($piaos as $piao) {
foreach ($piao['bets'] as $bet) {
$betUnique = $bet['unique'];
if (@$zuheList[$betUnique]) {
@$zuheList[$betUnique]['repeat_num'] += 1;
} else {
$bet['repeat_num'] = 1;
$zuheList[$betUnique] = $bet;
}
}
}
return array_values($zuheList);
}
public function createPiaoV3($oddsData, $passModeKeys, $betsNum, $isJingCai = true)
{
sort($passModeKeys);
$piaoPrepare = [];
$piaoDanGuanPrepare = [];
$hasDanGuan = in_array('1.1', $passModeKeys);
foreach ($oddsData as $id => $oddsItem) {
$piaoItem = [];
foreach ($oddsItem as $play => $playItem) {
$key = $id . '-'. $play;
$piaoItem[$key] = $key;
if ($hasDanGuan) {
$piaoDanGuanPrepare[$play] = $play;
}
}
$piaoPrepare[] = $piaoItem;
}
$piaoTags = Helps::getCombinationData($piaoPrepare, count($oddsData));
$piaoArray = [];
$piaoDanguanArray = [];
$piaoDanGuanTags = array_values($piaoDanGuanPrepare);
foreach ($passModeKeys as $passModeKey) {
$keyBetsData = $this->generateBetsInfo($oddsData, [$passModeKey], $betsNum);
foreach ($keyBetsData as $betsItem) {
if ($isJingCai && $passModeKey == '1.1') {
foreach ($piaoDanGuanTags as $pk => $danGuanPlay) {
$in = $this->betsInfoInDanGuanPiaoPlay($betsItem, $danGuanPlay);
if ($in) {
$piaoDanguanArray[$pk]['bets_list'][] = $betsItem;
$piaoDanguanArray[$pk]['pass_mode'][$passModeKey] = $passModeKey;
}
}
} else {
foreach ($piaoTags as $pk => $piaoTagArray) {
$in = $this->betsInfoInPiaoTags($betsItem, $piaoTagArray);
if ($in) {
$piaoArray[$pk]['bets_list'][] = $betsItem;
$piaoArray[$pk]['pass_mode'][$passModeKey] = $passModeKey;
}
}
}
}
}
// 计算每张票的金额
$lastKey = $this->getMaxPassModeKey($passModeKeys);
$piaoResult = [];
foreach ($piaoArray as $items) {
$maxPrizeBetsInfo = null;
$betsInfoMaxPrize = 0;
foreach ($items['bets_list'] as $betsInfo) {
$betsModeKey = count($betsInfo['ids']) . '.1';
if ($betsModeKey == $lastKey) {
$betKItemPrize = $this->getBetsInfoPrize($betsInfo);
if ($betKItemPrize > $betsInfoMaxPrize) {
$betsInfoMaxPrize = $betKItemPrize;
$maxPrizeBetsInfo = $betsInfo;
}
}
}
$zhuNum = 0;
$piaoPrize = 0;
$betsArray = [];
foreach ($items['bets_list'] as $betsInfo) {
if ($this->betsInfoInOtherBetsInfo($maxPrizeBetsInfo, $betsInfo)) {
$betKItemPrize = $this->getBetsInfoPrize($betsInfo);
$piaoPrize += $betKItemPrize;
}
$betsArray[] = $betsInfo;
$zhuNum += 1;
}
$piaoResult[] = [
'is_single' => false,
'max_prize_bets_info' => $maxPrizeBetsInfo,
'pass_mode' => $items['pass_mode'],
'bets_num' => $betsNum,
'bets' => $betsArray,
'zhang' => ceil($betsNum/50),
'prize' => $piaoPrize,
'zhu_num' => $zhuNum,
'zhu_money' => Helps::floatFormat($zhuNum * Config::lotteryUnitPrice()),
];
}
if ($isJingCai && $hasDanGuan) {
foreach ($piaoDanguanArray as $items) {
$zhuNum = 0;
$piaoPrize = 0;
$betsArray = [];
foreach ($items['bets_list'] as $betsInfo) {
$betKItemPrize = $this->getBetsInfoPrize($betsInfo);
// $betsInfoKey = $this->getBetsInfoPlayType($betsInfo['keys'][0]);
//
// if ($betKItemPrize > @$danGuanMaxPrize[$betsInfoKey]) {
// $danGuanMaxPrize[$betsInfoKey] = $betKItemPrize;
// }
$piaoPrize += $betKItemPrize;
$betsArray[] = $betsInfo;
$zhuNum += 1;
}
$piaoResult[] = [
'is_single' => true,
'max_prize_bets_info' => null,
'pass_mode' => $items['pass_mode'],
'bets_num' => $betsNum,
'bets' => $betsArray,
'zhang' => ceil($betsNum/50),
'prize' => $piaoPrize,
'zhu_num' => $zhuNum,
'zhu_money' => Helps::floatFormat($zhuNum * Config::lotteryUnitPrice()),
];
}
}
return $piaoResult;
}
private function generateChaiPiaoV3Item($item) {
$zhang = $item['zhang'] ;
$betsNum = $item['bets_num'];
$zhuNum = 0;
$piaoPrize = 0;
$betsArray = [];
foreach ($item['bets'] as $k => &$betsInfo) {
$betsInfo['bets_num'] = $item['bets_num'];
$betKItemPrize = $this->getBetsInfoPrize($betsInfo);
$piaoPrize += $betKItemPrize;
$betsArray[] = $betsInfo;
$zhuNum += 1;
}
if ($item['max_prize_bets_info']) {
$item['max_prize_bets_info']['bets_num'] = $item['bets_num'];
}
return [
'is_single' => $item['is_single'],
'max_prize_bets_info' => $item['max_prize_bets_info'],
'pass_mode' => $item['pass_mode'],
'bets_num' => $betsNum,
'bets' => $betsArray,
'zhang' => $zhang,
'prize' => $piaoPrize,
'zhu_num' => $zhuNum,
'zhu_money' => Helps::floatFormat($zhuNum * Config::lotteryUnitPrice()),
];
}
public function chaiPiaoGenV3($piaosV3) {
$endResult = [];
foreach ($piaosV3 as $item) {
if ($item['bets_num'] <= 50) {
$endResult[] = $item;
continue;
}
$zhang = intval($item['bets_num']/50);
$endItem1 = $item;
$endItem1['zhang'] = $zhang;
$endItem1['bets_num'] = $zhang * 50;
$endResult[] = $this->generateChaiPiaoV3Item($endItem1);
if ($item['bets_num']%50 == 0) {
continue;
}
$endItem2 = $item;
$endItem2['zhang'] = 1;
$endItem2['bets_num'] = $item['bets_num'] - $endItem1['bets_num'];
$endResult[] = $this->generateChaiPiaoV3Item($endItem2);
}
return $endResult;
}
public function getBetsInfoPlayType($key) {
$arr = explode('-', $key);
array_pop($arr);
return implode('-', $arr);
}
public function createPiaosV2($data, $passModeKeys, $betsNum, $optimizeZuheList = null)
{
if (!$optimizeZuheList) {
return $this->createPiaoV3($data, $passModeKeys, $betsNum);
}
$piaos = [];
foreach ($optimizeZuheList as $zuhe) {
$chuan = count($zuhe['ids']);
$tempZhang = intval(floor($zuhe['bets_num']/50));
if ($tempZhang > 0) {
$piaos[] = [
'is_single' => $chuan == 1,
'max_prize_bets_info' => $zuhe,
'pass_mode' => [$chuan . '.' . 1],
'bets_num' => $tempZhang * 50,
'bets' => [$zuhe],
'zhang' =>$tempZhang,
'prize' => $zuhe['all_odds'] * $tempZhang * 50 * Config::lotteryUnitPrice(),
'zhu_num' => 1,
'zhu_money' => Helps::floatFormat(1 * Config::lotteryUnitPrice()),
];
$yuBetsNum = $zuhe['bets_num']%50;
$piaos[] = [
'is_single' => $chuan == 1,
'max_prize_bets_info' => $zuhe,
'pass_mode' => [$chuan . '.' . 1],
'bets_num' => $yuBetsNum,
'bets' => [$zuhe],
'zhang' =>1,
'prize' => $zuhe['all_odds'] * $yuBetsNum * Config::lotteryUnitPrice(),
'zhu_num' => 1,
'zhu_money' => Helps::floatFormat(1 * Config::lotteryUnitPrice()),
];
} else {
$tempZhang = 1;
$betsNum = $zuhe['bets_num'];
$piaos[] = [
'is_single' => $chuan == 1,
'max_prize_bets_info' => $zuhe,
'pass_mode' => [$chuan . '.' . 1],
'bets_num' => $betsNum,
'bets' => [$zuhe],
'zhang' =>$tempZhang,
'prize' => $zuhe['all_odds'] * $betsNum * Config::lotteryUnitPrice(),
'zhu_num' => 1,
'zhu_money' => Helps::floatFormat(1 * Config::lotteryUnitPrice()),
];
}
}
return $piaos;
}
public function getBetsOddFromCombinationData($key, $dataOdd) {
foreach ($dataOdd as $combinationData) {
foreach ($combinationData as $comKey => $odd) {
if ($key == $comKey) {
return $odd;
}
}
}
Log::error('getBetsOddFromCombinationData error', [
'key' => $key,
'dataOdd' => $dataOdd
]);
ThrowException::run('数据错误,请重试!!');
}
/**
* 奖金优化
* @param $betsInfoArray
* @param $type 默认平均优化
* @return mixed
*/
public function optimizePrize($betsInfoArray, $money, $optimizeType)
{
ThrowException::isTrue(!OptimizeType::hasValue($optimizeType),'无效的奖金优化方式');
if ($optimizeType == OptimizeType::AVG) {
$result = $this->optimizeAvgPrize($betsInfoArray, $money);
}
if ($optimizeType == OptimizeType::HOT) {
$result = $this->optimizeHotPrize($betsInfoArray, $money);
}
if ($optimizeType == OptimizeType::COLD) {
$result = $this->optimizeColdPrize($betsInfoArray, $money);
}
usort($result, function($a, $b) {
return $a['all_odds'] - $b['all_odds'] > 0 ? 1 : -1;
});
return $result;
}
/**
* 不废话直接上公式。公式一返奖率等于每个赔率的倒数之和的倒数。即R% = (sum(oddn)^(-1))^(-1)公式二胜率等于返还率除以赔率。无论你投注几个选项都能得出其胜率对的的数组。根据你的投注选项按100%进行加权处理,就得到了实际的投注比例。用本金乘以各自的投注比例,就得到了奖金优化的结果。
作者:知乎用户
链接https://www.zhihu.com/question/407882370/answer/2210122355
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
* @param $betsInfoArray
* @param $money
* @return array
*/
public function optimizeAvgPrize($betsInfoArray, $money)
{
$optimizeResult = [];
foreach ($betsInfoArray as $item) {
$item['bets_num'] = 1;
$optimizeResult[$item['unique']] = $item;
}
$allocatedMoney = count($betsInfoArray) * Config::lotteryUnitPrice();
$nextMinResultId = function($array) {
$nextResult = [];
foreach ($array as $item) {
$nextResult[$item['unique']] = $item['bets_num'] * $item['all_odds'] + $item['all_odds'];
}
$minResultId = array_search(min($nextResult), $nextResult);
return $minResultId;
};
while ($allocatedMoney < $money) {
// 找到下次增加的
$allocateUnique = $nextMinResultId($optimizeResult);
$optimizeResult[$allocateUnique]['bets_num'] += 1;
// dd($optimizeResult);
$allocatedMoney += Config::lotteryUnitPrice();
}
return array_values($optimizeResult);
}
public function optimizeColdPrize($betsInfoArray, $money)
{
$minUnique = '';
$maxOdd = null;
$allBeiNum = 0;
foreach ($betsInfoArray as &$item) {
$beiNum = ceil($money / ($item['all_odds'] * Config::lotteryUnitPrice()));
$allBeiNum += $beiNum;
$item['bets_num'] = $beiNum;
if ($maxOdd === null) {
$maxOdd = $item['all_odds'];
$minUnique = $item['unique'];
}
if ($item['all_odds'] > $maxOdd) {
$maxOdd = $item['all_odds'];
$minUnique = $item['unique'];
}
}
$useAvg = false;
foreach ($betsInfoArray as &$item) {
if ($item['unique'] == $minUnique) {
$item['bets_num'] += ($money - ($allBeiNum * Config::lotteryUnitPrice())) / 2;
if ($item['bets_num'] <= 0) {
$useAvg = true;
}
break;
}
}
if ($useAvg) {
return $this->optimizeAvgPrize($betsInfoArray, $money);
}
return $betsInfoArray;
}
public function optimizeHotPrize($betsInfoArray, $money)
{
$minUnique = '';
$minOdd = null;
$allBeiNum = 0;
foreach ($betsInfoArray as &$item) {
$beiNum = ceil($money / ($item['all_odds'] * Config::lotteryUnitPrice()));
$allBeiNum += $beiNum;
$item['bets_num'] = $beiNum;
if ($minOdd === null) {
$minOdd = $item['all_odds'];
$minUnique = $item['unique'];
}
if ($item['all_odds'] < $minOdd) {
$minOdd = $item['all_odds'];
$minUnique = $item['unique'];
}
}
$useAvg = false;
foreach ($betsInfoArray as &$item) {
if ($item['unique'] == $minUnique) {
$item['bets_num'] += ($money - ($allBeiNum * Config::lotteryUnitPrice())) / 2;
if ($item['bets_num'] <= 0) {
$useAvg = true;
}
break;
}
}
if ($useAvg) {
return $this->optimizeAvgPrize($betsInfoArray, $money);
}
return $betsInfoArray;
}
public function avg($array=[])
{
$array = [
[
'odds' => 1,'bets_num' => 100
]
];
$fanJiangDaoshu = 0;
$allBeiShu = 0;
foreach ($array as $item) {
$onePrize = $item['odds'] * 2;
$fanJiangDaoshu += 1/$onePrize;
$allBeiShu += $item['bet_num'];
}
$fanJiangLv = 1/$fanJiangDaoshu;
$avgResult = [];
foreach ($array as &$item) {
$item['avg'] = intval(round($allBeiShu * $fanJiangLv/($item['odds']*2)));
}
return $array;
}
public function betsInfoToOdds($betsInfo)
{
$odds = [];
foreach ($betsInfo['info'] as $oddsId => $item) {
$odds[$oddsId] = [
$item['play'] => [
$item['result'] => $item['odd']
]
];
}
return $odds;
}
public function getBetsInfoPrize($betInfo) {
$combinationPrizeOdd = 1;
foreach ($betInfo['info'] as $infoItem) {
$combinationPrizeOdd *= $infoItem['odd'];
}
$oneBetsPrize = $combinationPrizeOdd * Config::lotteryUnitPrice();
$oneBetsPrize = self::getTiCaiOneBetsPrize($oneBetsPrize, $betInfo['chuan_num']);
return $betInfo['bets_num'] * $oneBetsPrize;
}
public function fadanValid($buyMoney, $minPrize) {
ThrowException::isTrue($buyMoney < 100, '发单最低金额100');
return bc_lt($minPrize, $buyMoney * 1.6);
}
public static function getPassMode($key)
{
$passMode = self::passModes();
$res = Arr::get($passMode, $key, []);
// 兼容8以上的串关
if (!$res) {
$tmpArr = explode('.', $key);
$tmpChang = Arr::get($tmpArr, 0);
if ($tmpChang > 8) {
$res = [ $tmpChang => 1];
}
}
if (!$res) {
Log::error('getPassMode error', [
'key' => $key,
]);
ThrowException::run('投注失败,请重试!!');
}
return $res;
}
// 串关规则
public static function passModes()
{
$data = [
1 => [
1 => [1 => 1]
],
2 => [
1 => [2 => 1,],
3 => [1 => 2, 2 => 1,]
],
3 => [
1 => [3 => 1,],
3 => [2 => 3,],
4 => [2 => 3, 3 => 1,],
6 => [1 => 3, 2 => 3,],
7 => [1 => 3, 2 => 3, 3 => 1,],
],
4 => [
1 => [4 => 1],
4 => [3 => 4,],
5 => [3 => 4, 4 => 1,],
6 => [2 => 6,],
10 => [1 => 4, 2 => 6,],
11 => [2 => 6, 3 => 4, 4 => 1,],
14 => [1 => 4, 2 => 6, 3 => 4,],
15 => [1 => 4, 2 => 6, 3 => 4, 4 => 1,],
],
5 => [
1 => [5 => 1],
5 => [4 => 5],
6 => [4 => 5, 5 => 1],
10 => [2 => 10],
15 => [1 => 5, 2 => 10],
16 => [3 => 10, 4 => 5, 5 => 1],
20 => [2 => 10, 3 => 10],
25 => [1 => 5, 2 => 10, 3 => 10],
26 => [2 => 10, 3 => 10, 4 => 5, 5 => 1],
30 => [1 => 5, 2 => 10, 3 => 10, 4 => 5],
31 => [1 => 5, 2 => 10, 3 => 10, 4 => 5, 5 => 1],
],
6 => [
1 => [6 => 1],
6 => [5 => 6],
7 => [5 => 6, 6 => 1],
15 => [2 => 15],
20 => [3 => 20],
21 => [1 => 6, 2 => 15],
22 => [4 => 15, 5 => 6, 6 => 1],
35 => [2 => 15, 3 => 20],
41 => [1 => 6, 2 => 15, 3 => 20],
42 => [3 => 20, 4 => 15, 5 => 6, 6 => 1],
50 => [2 => 15, 3 => 20, 4 => 15],
56 => [1 => 6, 2 => 15, 3 => 20, 4 => 15],
57 => [2 => 15, 3 => 20, 4 => 15, 5 => 6, 6 => 1],
62 => [1 => 6, 2 => 15, 3 => 20, 4 => 15, 5 => 6],
63 => [1 => 6, 2 => 15, 3 => 20, 4 => 15, 5 => 6, 6 => 1],
],
7 => [
1 => [7 => 1],
7 => [6 => 1],
8 => [6 => 1, 7 => 1],
21 => [5 => 21],
35 => [4 => 35],
120 => [2 => 21, 3 => 35, 4 => 35, 5 => 21, 6 => 1, 7 => 1],
127 => [1 => 7, 2 => 21, 3 => 35, 4 => 35, 5 => 21, 6 => 1, 7 => 1],
],
8 => [
1 => [8 => 1],
8 => [7 => 1],
9 => [7 => 1, 8 => 1],
28 => [6 => 28],
56 => [5 => 56],
70 => [4 => 70],
247 => [2 => 28, 3 => 56, 4 => 70, 5 => 56, 6 => 28, 7 => 1, 8 => 1],
255 => [1 => 8, 2 => 28, 3 => 56, 4 => 70, 5 => 56, 6 => 28, 7 => 1, 8 => 1],
]
];
return $data;
}
public function canCreateFanDanOrder($customerId) {
ThrowException::isTrue(!Helps::genDanEnable(), '系统维护中...');
$fadanNum = Order::usable()
->where('type', OrderType::FADAN)
->where('customer_id', $customerId)
->where('pay_state', PayState::SUCCESS)
->where('odds_late_close_time', '>', date('Y-m-d H:i:s'))
->count();
ThrowException::isTrue($fadanNum >= 3, '发单数量超限,请稍后再试');
}
// 是否可以继续创建合买
public function canCreateUnionOrder($customerId) {
// $existFadan = Order::where('type', OrderType::UNION)
// ->where('customer_id', $customerId)
// ->where('customer_id', $customerId)
// ->where('pay_state', PayState::SUCCESS)
// ->whereIn('lottery_state', [LottState::WAIT, LottState::DRAFT, LottState::PENDING])
// ->count();
//
// ThrowException::isTrue($existFadan > 0, '已有未开奖的合买,不可再次创建合买');
}
}