You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);

问题原因分析

  1. 循环边界判断错误导致牌丢失
    内部洗牌循环的条件是for($i = 0; $i < $n[0]; $i++),这意味着$i的取值范围是0到$n[0]-1,永远不会等于$n[0]。因此代码中的elseif($i == $n[0])分支永远不会触发。当循环正常执行完所有次数后,$right中剩余的牌没有被合并到$left中,直接被丢弃,导致最终牌组数量少于52张。

  2. 辅助函数冗余(不影响牌数,但可优化)
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 13:55:17