From 98462ca34de7096e2b346f90c2e524889ec0bbc3 Mon Sep 17 00:00:00 2001 From: hide_d Date: Fri, 4 Feb 2022 03:48:28 +0900 Subject: [PATCH] =?UTF-8?q?feat(WIP):=20=EB=B3=B4=EC=83=81=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20Betting=20=EB=AA=A8=EB=93=88=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/API/Betting/Bet.php | 33 +---- hwe/sammo/Betting.php | 235 ++++++++++++++++++++++++++++++++++ hwe/sammo/DTO/BettingInfo.php | 1 + 3 files changed, 240 insertions(+), 29 deletions(-) create mode 100644 hwe/sammo/Betting.php diff --git a/hwe/sammo/API/Betting/Bet.php b/hwe/sammo/API/Betting/Bet.php index 46bc53e7..36e343f1 100644 --- a/hwe/sammo/API/Betting/Bet.php +++ b/hwe/sammo/API/Betting/Bet.php @@ -4,13 +4,11 @@ namespace sammo\API\Betting; use sammo\Session; use DateTimeInterface; +use sammo\Betting; use sammo\DB; use sammo\DTO\BettingItem; use sammo\Validator; -use sammo\Json; -use sammo\DTO\BettingInfo; use sammo\GameConst; -use sammo\General; use sammo\KVStorage; use sammo\Util; @@ -54,18 +52,9 @@ class Bet extends \sammo\BaseAPI $amount = $this->args['amount']; $gameStor = KVStorage::getStorage($db, 'game_env'); - $bettingStor = KVStorage::getStorage($db, 'betting'); - $rawBettingInfo = $bettingStor->getValue("id_{$bettingID}"); - if($rawBettingInfo === null){ - return '해당 베팅이 없습니다'; - } - try{ - $bettingInfo = new BettingInfo($rawBettingInfo); - } - catch(\Error $e){ - return $e->getMessage(); - } + $bettingHelper = new Betting($bettingID); + $bettingInfo = $bettingHelper->getInfo(); if($bettingInfo->finished){ return '이미 종료된 베팅입니다'; @@ -87,21 +76,7 @@ class Bet extends \sammo\BaseAPI return '필요한 선택 수를 채우지 못했습니다.'; } - sort($bettingType, SORT_NUMERIC); - $bettingType = array_unique($bettingType, SORT_NUMERIC);//NOTE: key로 바로 사용하므로 중요함 - if(count($bettingType) != $bettingInfo->selectCnt){ - return '중복된 값이 있습니다.'; - } - - if($bettingType[0] < 0){ - return '올바르지 않은 값이 있습니다.(0 미만)'; - } - - if(Util::array_last($bettingType) >= count($bettingInfo->candidates)){ - return '올바르지 않은 값이 있습니다.(초과)'; - } - - $bettingTypeKey = Json::encode($bettingType); + $bettingTypeKey = $bettingHelper->convertBettingKey($bettingType); $inheritStor = KVStorage::getStorage($db, "inheritance_{$session->userID}"); diff --git a/hwe/sammo/Betting.php b/hwe/sammo/Betting.php new file mode 100644 index 00000000..7a345bf8 --- /dev/null +++ b/hwe/sammo/Betting.php @@ -0,0 +1,235 @@ +getValue("id_{$bettingID}"); + if ($rawBettingInfo === null) { + throw new \RuntimeException("해당 베팅이 없습니다: {$bettingID}"); + } + $this->info = new BettingInfo($rawBettingInfo); + } + + private function _convertBettingKey(array $bettingType): string + { + return Json::encode($bettingType); + } + + public function convertBettingKey(array $bettingType): string + { + $selectCnt = $this->info->selectCnt; + sort($bettingType, SORT_NUMERIC); + $bettingType = array_unique($bettingType, SORT_NUMERIC); //NOTE: key로 바로 사용하므로 중요함 + if (count($bettingType) != $selectCnt) { + throw new \InvalidArgumentException('중복된 값이 있습니다.'); + } + + if ($bettingType[0] < 0) { + throw new \InvalidArgumentException('올바르지 않은 값이 있습니다.(0 미만)'); + } + + if (Util::array_last($bettingType) >= count($this->info->candidates)) { + throw new \InvalidArgumentException('올바르지 않은 값이 있습니다.(초과)'); + } + + return $this->_convertBettingKey($bettingType); + } + + public function getInfo(): BettingInfo + { + return $this->info; + } + + /** @param int[] $result */ + private function _calcRewardExclusive(array $bettingType): array + { + $db = DB::db(); + $totalAmount = $db->queryFirstField('SELECT sum(amount) FROM ng_betting WHERE betting_id = %i'); + + if ($totalAmount == 0) { + return []; + } + + $winnerList = $db->queryAllLists( + 'SELECT general_id, user_id, amount WHERE betting_id = %i AND betting_type = %s', + $this->bettingID, + $this->_convertBettingKey($bettingType) + ); + + $subAmount = 0; + foreach ($winnerList as [,, $amount]) { + $subAmount += $amount; + } + + if ($subAmount == 0) { + return []; + } + + $multiplier = $totalAmount / $subAmount; + $selectCnt = $this->info->selectCnt; + + $result = []; + foreach ($winnerList as [$generalID, $userID, $amount]) { + $result[$generalID] = [ + 'generalID' => $generalID, + 'userID' => $userID, + 'amount' => $amount * $multiplier, + 'matchPoint' => $selectCnt, + ]; + } + return $result; + } + + + + /** @param int[] $winnerType */ + public function calcReward(array $winnerType): array + { + $selectCnt = $this->info->selectCnt; + if ($selectCnt == 1) { + return $this->_calcRewardExclusive($winnerType); + } + + if ($this->info->isExlusive) { + return $this->_calcRewardExclusive($winnerType); + } + //아래는 2개 이상, 복합 보상 옵션 + + $winnerTypeMap = []; + foreach ($winnerType as $typeVal) { + $winnerTypeMap[$typeVal] = $typeVal; + } + + $calcMatchPoint = function ($bettingType) use ($winnerTypeMap): int { + $result = 0; + foreach ($bettingType as $typeVal) { + if (key_exists($typeVal, $winnerTypeMap)) { + $result += 1; + } + } + return $result; + }; + + $totalAmount = 0; + $subAmount = []; + $subWinners = []; + + foreach (Util::range($selectCnt + 1) as $matchPoint) { + $subAmount[$matchPoint] = 0; + $subWinners[$matchPoint] = []; + } + + $db = DB::db(); + foreach ($db->queryAllLists( + 'SELECT general_id, user_id, amount, betting_type WHERE betting_id = %i', + $this->bettingID + ) as [$generalID, $userID, $amount, $bettingTypeKey]) { + $bettingType = Json::decode($bettingTypeKey); + $matchPoint = $calcMatchPoint($bettingType); + $totalAmount += $amount; + if ($generalID == 0) { + continue; + } + $subAmount[$matchPoint] += $amount; + $subWinners[$matchPoint][] = [ + 'generalID' => $generalID, + 'userID' => $userID, + 'amount' => $amount, + 'matchPoint' => $matchPoint, + ]; + } + + $remainRewardAmount = $totalAmount; + $rewardAmount = []; + foreach (Util::range($selectCnt, 0, -1) as $matchPoint) { + if (count($subWinners[$matchPoint]) == 0) { + continue; + } + if ($subAmount[$matchPoint] == 0) { + continue; + } + + $givenRewardAmount = $remainRewardAmount / 2; + $rewardAmount[$matchPoint] = $givenRewardAmount; + $remainRewardAmount -= $givenRewardAmount; // /2가 아니라 다른 값이 될 경우를 대비.. + } + + foreach (Util::range(1, $selectCnt) as $matchPoint) { + if (!key_exists($matchPoint, $rewardAmount)) { + continue; + } + $rewardAmount[$matchPoint] += $remainRewardAmount; + break; + } + + $result = []; + + foreach (Util::range($selectCnt + 1, 0, -1) as $matchPoint) { + if (!key_exists($matchPoint, $rewardAmount)) { + continue; + } + $subReward = $rewardAmount[$matchPoint]; + if ($subReward == 0) { + continue; + } + $multiplier = $subReward / $subAmount[$matchPoint]; + foreach ($subWinners[$matchPoint] as $subWinner) { + $subWinner['amount'] *= $multiplier; + $result[$subWinner['generalID']] = $subWinner; + } + } + + return $result; + } + + public function giveReward(array $winnerType) + { + $rewardList = $this->calcReward($winnerType); + + $db = DB::db(); + + if ($this->info->reqInheritancePoint) { + foreach ($rewardList as $rewardItem) { + if ($rewardItem['userID'] === null) { + continue; + } + $userID = $rewardItem['userID']; + $inheritStor = KVStorage::getStorage($db, "inheritance_{$userID}"); + $previousPoint = ($inheritStor->getValue('previous') ?? [0, 0])[0]; + $previousPointText = number_format($previousPoint); + + $userLogger = new UserLogger($userID); + $amount = $rewardItem['amount']; + $amountText = number_format($amount); + $nextPoint = $previousPoint + $amount; + $nextPointText = number_format($nextPoint); + $userLogger->push("{$this->info->name} 베팅 보상으로 {$amountText} 포인트 획득.", "inheritPoint"); + $userLogger->push("포인트 {$previousPointText} => {$nextPointText}", "inheritPoint"); + $userLogger->flush(); + } + } else { + foreach (General::createGeneralObjListFromDB(Util::squeezeFromArray($rewardList, 'generalID'), ['gold', 'npc'], 1) as $gambler) { + $reward = Util::round($rewardList[$gambler->getID()]['amount']); + $gambler->increaseVar('gold', $reward); + if (($gambler->getNPCType() == 0) || ($gambler->getNPCType() == 1 && $gambler->getRankVar('betgold', 0) > 0)) { + $gambler->increaseRankVar('betwingold', $reward); + $gambler->increaseRankVar('betwin', 1); + } + $rewardText = number_format($reward); + $gambler->getLogger()->pushGeneralActionLog("{$this->info->name}의 베팅 보상으로 {$rewardText}의 금 획득!", ActionLogger::EVENT_PLAIN); + $gambler->applyDB($db); + } + } + } +} diff --git a/hwe/sammo/DTO/BettingInfo.php b/hwe/sammo/DTO/BettingInfo.php index 13fae86e..0ca1dd0d 100644 --- a/hwe/sammo/DTO/BettingInfo.php +++ b/hwe/sammo/DTO/BettingInfo.php @@ -20,6 +20,7 @@ class BettingInfo extends DataTransferObject public string $name; public bool $finished; public int $selectCnt; + public ?bool $isExlusive; public bool $reqInheritancePoint; public int $openYearMonth; public int $closeYearMonth;