Laravel扑克牌洗牌代码偶尔返回不足52张牌,请求排查问题
Laravel扑克牌洗牌代码偶发牌组数量不足52张的问题排查
我在Laravel环境下编写了扑克牌洗牌代码,包含生成新牌组、确定牌组切割长度、生成随机洗牌次数的函数,以及从数据库读取牌组并执行洗牌的逻辑。代码可按预期完成洗牌操作,但存在偶发问题:返回的牌组数量并非始终为52张,有时会少于52张,求指出问题原因。
相关辅助函数代码
public function newDeck() { $suits = ['S', 'H', 'C', 'D']; $values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; foreach($suits as $suit) { foreach($values as $value) { $deck[$suit . $value] = $value; } } return $deck; } // 返回牌组将要切割的长度 public function cutLength($len) { for ($i = 0; $i < 1; ++$i) { $cutLength[] = rand(1,$len-1); } return $cutLength; } // 返回总洗牌次数 public function shuffleCount($count) { for ($i = 0; $i < 1; ++$i) { $shuffleCount[] = rand(1,$count); } return $shuffleCount; }
洗牌逻辑代码
$left = json_decode(Game::where('gamer_id', $request->id)->latest()->first()->game_deck, TRUE); // 单次洗牌的轮次 $m = $game->shuffleCount(5); for($j = 0; $j < $m[0]; $j++) { // 完整牌组的数量 $leftLength = count($left); // 牌组切割的位置 $fullDeckCutLength = $game->cutLength($leftLength); // right是牌组的下半部分 $right = array_slice($left, $fullDeckCutLength[0], $leftLength - $fullDeckCutLength[0]); // left是牌组的上半部分 $left = array_slice($left, 0, $fullDeckCutLength[0]); // 清空完整牌组切割长度数组 $fullDeckCutLength = []; // 检查右侧牌组的数量 $rightLength = count($right); // 如果右侧牌组数量少于左侧的20%,则直接合并到左侧牌组顶部 if($rightLength < 0.2 * $leftLength) { $left = array_merge($right, $left); $right = []; continue; } else { // 单次洗牌 $n = $game->shuffleCount(10); for($i = 0; $i < $n[0]; $i++) { // 统计右侧牌组数量 $rightLength = count($right); // 确定从右侧牌组顶部移到左侧的牌数 $rightDeckCutLength = $game->cutLength($rightLength); // 从右侧牌组顶部移除对应数量的牌 $rtl = array_slice($right, 0, $rightDeckCutLength[0]); // 将这些牌放到左侧牌组顶部 $left = array_merge($rtl, $left); // 清空移牌数组 $rtl = []; // 右侧牌组剩余的牌 $right = array_slice($right, $rightDeckCutLength[0], $rightLength - $rightDeckCutLength[0]); // 清空右侧牌组切割长度数组 $rightDeckCutLength = []; // 检查右侧牌组的数量 $rightLength = count($right); // 如果右侧牌组数量少于左侧的20%,则合并到左侧并结束循环 if($rightLength < 0.2 * $leftLength) { $left = array_merge($right, $left); $right = []; $n = []; break; } elseif($i == $n[0]) { $left = array_merge($right, $left); $right = []; $n = []; } } } } dd($left);
问题原因分析
循环边界判断错误导致牌丢失
内部洗牌循环的条件是for($i = 0; $i < $n[0]; $i++),这意味着$i的取值范围是0到$n[0]-1,永远不会等于$n[0]。因此代码中的elseif($i == $n[0])分支永远不会触发。当循环正常执行完所有次数后,$right中剩余的牌没有被合并到$left中,直接被丢弃,导致最终牌组数量少于52张。辅助函数冗余(不影响牌数,但可优化)
cutLength和shuffleCount函数中的for循环完全多余,只执行一次的循环可以直接替换为返回包含随机数的数组,比如:public function cutLength($len) { return [rand(1, $len-1)]; }
修复方案
移除无效的elseif($i == $n[0])分支,改为在内部循环结束后,统一检查$right是否有剩余牌,若有则合并到$left:
// 替换原内部循环的elseif分支,改为循环结束后处理剩余牌 for($i = 0; $i < $n[0]; $i++) { // 原有逻辑... if($rightLength < 0.2 * $leftLength) { $left = array_merge($right, $left); $right = []; $n = []; break; } } // 循环结束后,若right还有剩余牌,合并到left if (!empty($right)) { $left = array_merge($right, $left); $right = []; }
内容的提问来源于stack exchange,提问作者Bilal Yousaf
相关产品推荐
相关产品推荐

