28

我想建立一个可以模拟足球(足球)比赛的模拟引擎。如果你能帮助我,那就太好了。对我来说重要的是决定发生哪些动作。每个动作的事件监听器可以在以后轻松实现。该功能应该只模拟游戏结果和对发生动作的评论。不需要 2D/3D 图形。我们谈论的是Hattrick 之类的游戏。


我建议你首先有一系列的行动记录。

$minutes = 数组(1, 3, 4, 7, 11, 13, ..., 90, 92);

对于这些分钟中的每一分钟,您都可以模拟一次攻击。

攻击队由之前的骰子决定: $attacking = mt_rand(1, 2);

所以对我来说最重要的部分是攻击功能。

请编辑我的方法或将其用作示例。你能帮我改进一下吗?该函数应该是复杂的,以便结果尽可能真实。但是您需要在高可预测性和过于随机的结果之间找到一些东西。我只想改进这个功能。

我的做法:

<?php
function Chance_Percent($chance, $universe = 100) {
    $chance = abs(intval($chance));
    $universe = abs(intval($universe));
    if (mt_rand(1, $universe) <= $chance) {
        return true;
    }
    return false;
}
function simulate_attack($teamname_att, $teamname_def, $strength_att, $strength_def) {
    global $minute, $goals, $_POST, $matchReport, $fouls, $yellowCards, $redCards, $offsides, $schuesse, $taktiken;
    // input values: attacker's name, defender's name, attacker's strength array, defender's strength array
    // players' strength values vary from 0.1 to 9.9
    // ADJUSTMENT START
    switch ($taktiken[$teamname_att][0]) {
        case 1: $strength_att['defenders'] *= 1.1; $strength_att['forwards'] *= 0.9; break;
        case 3: $strength_att['defenders'] *= 0.9; $strength_att['forwards'] *= 1.1; break;
    }
    switch ($taktiken[$teamname_def][0]) {
        case 1: $strength_def['defenders'] *= 1.1; $strength_def['forwards'] *= 0.9; break;
        case 3: $strength_def['defenders'] *= 0.9; $strength_def['forwards'] *= 1.1; break;
    }
    // ADJUSTMENT END
    $matchReport .= '<p>'.$minute.'\': '.comment($teamname_att, 'attack');
    $offense_strength = $strength_att['forwards']/$strength_def['defenders'];
    $defense_strength = $strength_def['defenders']/$strength_att['forwards'];
    if (Chance_Percent(50*$offense_strength*($taktiken[$teamname_att][2]/2)*($taktiken[$teamname_att][3]/2))) {
        // attacking team passes 1st third of opponent's field side
        $matchReport .= ' '.comment($teamname_def, 'attack_advance');
        if (Chance_Percent(25*($taktiken[$teamname_def][4]/2))) {
            // the defending team fouls the attacking team
            $fouls[$teamname_def]++;
            $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul');
            if (Chance_Percent(43)) {
                // yellow card for the defending team
                // chance is correct for my purpose
                $yellowCards[$teamname_def]++;
                $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_yellow');
            }
            elseif (Chance_Percent(3)) {
                // red card for the defending team
                // chance is correct for my purpose (only 1.43% because it's an alternative way)
                $redCards[$teamname_def]++;
                $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_red');
            }
            // indirect free kick
            // only 58.23% because it's an alternative way
            $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick');
            if (Chance_Percent(25)) {
                // shot at the goal
                $schuesse[$teamname_att]++;
                $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick_shot');
                if (Chance_Percent(25)) {
                    // attacking team scores (6.25% chance)
                    $goals[$teamname_att]++;
                    $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick_shot_score');
                }
                else {
                    // defending goalkeeper saves
                    // only 18.75% because it's an alternative way
                    $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick_shot_save');
                }
            }
            else {
                // defending team cleares the ball
                // only 75% because it's an alternative way
                $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick_clear');
            }
        }
        elseif (Chance_Percent(17)) {
            // attacking team is caught offside
            // only 4.25% because it's an alternative way
            $offsides[$teamname_att]++;
            $matchReport .= ' '.comment($teamname_def, 'attack_advance_offside');
        }
        else {
            if (Chance_Percent(25*($taktiken[$teamname_def][5]/2))) {
                // the defending team fouls the attacking team
                $fouls[$teamname_def]++;
                $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul');
                if (Chance_Percent(43)) {
                    // yellow card for the defending team
                    // chance is correct for my purpose
                    $yellowCards[$teamname_def]++;
                    $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_yellow');
                }
                elseif (Chance_Percent(3)) {
                    // red card for the defending team
                    // chance is correct for my purpose (only 1.43% because it's an alternative way)
                    $redCards[$teamname_def]++;
                    $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_red');
                }
                if (Chance_Percent(19)) {
                    // penalty for the attacking team
                    $schuesse[$teamname_att]++;
                    $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_penalty');
                    if (Chance_Percent(77)) {
                        // attacking team scores (77% chance according to Wikipedia)
                        $goals[$teamname_att]++;
                        $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_penalty_score');
                    }
                    elseif (Chance_Percent(50)) {
                        // shot misses the goal
                        // only 11.5% because it's an alternative way
                        $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_penalty_miss');
                    }
                    else {
                        // defending goalkeeper saves
                        // only 11.5% because it's an alternative way
                        $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_penalty_save');
                    }
                }
                elseif (Chance_Percent(28)) {
                    // direct free kick
                    // only 22.68% because it's an alternative way
                    $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_dFreeKick');
                    if (Chance_Percent(33)) {
                        // shot at the goal
                        $schuesse[$teamname_att]++;
                        $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_dFreeKick_shot');
                        if (Chance_Percent(33)) {
                            // attacking team scores (10.89% chance)
                            $goals[$teamname_att]++;
                            $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_dFreeKick_shot_score');
                        }
                        else {
                            // defending goalkeeper saves
                            $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_dFreeKick_shot_save');
                        }
                    }
                    else {
                        // defending team cleares the ball
                        // only 77% because it's an alternative way
                        $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_dFreeKick_clear');
                    }
                }
                else {
                    // indirect free kick
                    // only 58.23% because it's an alternative way
                    $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick');
                    if (Chance_Percent(25)) {
                        // shot at the goal
                        $schuesse[$teamname_att]++;
                        $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick_shot');
                        if (Chance_Percent(25)) {
                            // attacking team scores (6.25% chance)
                            $goals[$teamname_att]++;
                            $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick_shot_score');
                        }
                        else {
                            // defending goalkeeper saves
                            // only 18.75% because it's an alternative way
                            $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick_shot_save');
                        }
                    }
                    else {
                        // defending team cleares the ball
                        // only 75% because it's an alternative way
                        $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick_clear');
                    }
                }
            }
            else {
                // attack passes the 2nd third of the opponent's field side - good chance
                $matchReport .= ' '.comment($teamname_def, 'attack_advance_advance');
                if (Chance_Percent(62*($taktiken[$teamname_att][6]/2)*($taktiken[$teamname_att][7]/2)/($taktiken[$teamname_att][8]/2)*($taktiken[$teamname_att][9]/2)/($taktiken[$teamname_def][10]/2))) {
                    // shot at the goal
                    $schuesse[$teamname_att]++;
                    $matchReport .= ' '.comment($teamname_def, 'attack_advance_advance_shot');
                    if (Chance_Percent(30*$strength_def['goalkeeper']/7/($taktiken[$teamname_att][11]/2))) {
                        // the attacking team scores
                        // only 8.78% because it's an alternative way
                        // if goalkeeper has strenth 7 then chance is 8.78% otherwise lower/higher
                        $goals[$teamname_att]++;
                        $matchReport .= ' '.comment($teamname_def, 'attack_advance_advance_shot_score');
                    }
                    else {
                        if (Chance_Percent(50)) {
                            // the defending defenders block the shot
                            $matchReport .= ' '.comment($teamname_def, 'attack_advance_advance_shot_block');
                        }
                        else {
                            // the defending goalkeeper saves
                            $matchReport .= ' '.comment($teamname_def, 'attack_advance_advance_shot_save');
                        }
                    }
                }
            }
        }
    }
    // attacking team doesn't pass 1st third of opponent's field side
    elseif (Chance_Percent(15*$defense_strength*($taktiken[$teamname_att][12]/2)*($taktiken[$teamname_att][13]/2))) {
        // quick counter attack - playing on the break
        // only 7.5% because it's an alternative way
        // if defense has strength 7 then chance is 7.5% otherwise lower/higher
        $strength_att['defenders'] = $strength_att['defenders']*0.8; // weaken the current attacking team's defense
        $matchReport .= ' '.comment($teamname_def, 'attack_quickCounterAttack');
        $matchReport .= ' ['.$goals[$_POST['team1']].':'.$goals[$_POST['team2']].']</p>'; // close comment line
        return simulate_attack($teamname_def, $teamname_att, $strength_def, $strength_att); // new attack - this one is finished
    }
    else {
        // ball goes into touch - out of the field
        $matchReport .= ' '.comment($teamname_def, 'attack_throwIn');
        if (Chance_Percent(33)) {
            // if a new chance is created
            if (Chance_Percent(50)) {
                // throw-in for the attacking team
                $matchReport .= ' '.comment($teamname_def, 'attack_throwIn_att');
                $matchReport .= ' ['.$goals[$_POST['team1']].':'.$goals[$_POST['team2']].']</p>'; // close comment line
                return simulate_attack($teamname_att, $teamname_def, $strength_att, $strength_def); // new attack - this one is finished
            }
            else {
                // throw-in for the defending team
                $matchReport .= ' '.comment($teamname_def, 'attack_throwIn_def');
                $matchReport .= ' ['.$goals[$_POST['team1']].':'.$goals[$_POST['team2']].']</p>'; // close comment line
                return simulate_attack($teamname_def, $teamname_att, $strength_def, $strength_att); // new attack - this one is finished
            }
        }
    }
    $matchReport .= ' ['.$goals[$_POST['team1']].':'.$goals[$_POST['team2']].']</p>'; // close comment line
    return TRUE; // finish the attack
}
?>

应该对随机性产生影响的战术设置:

  • 调整(1=防守,2=中立,3=进攻):数值越高,防守越弱,进攻越强
  • speed of play (1=slow, 2=medium, 3=fast):数值越高,机会越大,但获得快速反击的风险越高
  • 传球距离(1=短,2=中,3=长):值越高,获得的机会越少,但越位机会越多
  • 创造变化(1=安全,2=中等,3=有风险):价值越高,您的机会就越大,但获得快速反击的风险越高
  • 防守压力(1=低,2=中,3=高):数值越高,反击越快
  • 侵略性(1=低,2=中,3=高):值越高,你会被犯规阻止的攻击越多

战术设置的整合:

所有战术设置都有一个值,可以是“1”、“2”或“3”。“2”总是中性/中等。所以我将这些值除以 2。我得到的比率是 0.5 或 1 或 1.5。我想我可以很容易地乘以这个机会,以整合战术影响力。但是出现了一个问题:如果我将一个机会乘以 2 个或更多战术值,它可能会高于 100%(例如 60 x 1.5 x 1.5)。所以我不能这样整合战术。我还可以做些什么?


非常感谢你!

更新(2014 年):几年后,我现在在 GitHub 上以开源形式发布了游戏的完整代码库。如果有人感兴趣,您将在此文件中找到此模拟的具体实现。

4

4 回答 4

35

建立一个基于“权重”的模拟(是的,我刚刚发明了这个术语)。每个变量(无论其类型如何)都有一个“权重”。例如,球员有重量。一个好的球员有额外的重量。受伤的球员体重较轻,甚至根本不需要等待(或者可能是负重?)。

您将所有重量加在一起(两支球队的,因为这是一场足球比赛)。该权重类似于获胜机会百分比。例如;

A队的权重= 56B队的权重= 120

重量已经表明,一支球队比另一支球队要好得多(不管重量是如何确定的......也许他们的球很圆,谁在乎)。

根据重量,您可以计算中奖机会;A队获胜机率=32%B队获胜机率=68%

现在你可以编写一个算法来模拟一场比赛,受胜率的影响。我曾经写过一个这样的算法来绘制广告。就我而言,广告的点击次数就是权重。权重越大,广告被我的算法选中的机会就越大。

我通过取一个很大的数字(比如 1000)来编写算法,然后根据权重百分比为每个广告分配一个数字范围。在这种情况下,团队 A 获得 1000 的 32% 范围即 0 - 320,团队 B 获得 68% 的范围,即 321 - 1000。然后我的算法会(随机)抽取一个介于 0 和 1000 之间的数字。范围最大(因此获胜机会最大)的广告(或您的团队)最有可能被算法选中,尽管结果可能不同.

这种算法对于平衡结果(如果用户可以创建自己的团队、购买更好的球员等)来说非常棒(尽管并不完美)。您还可以通过该算法在游戏中绘制任何事件,只需为事件添加权重即可。

您可以根据该团队中的其他权重因素(连续进行了多少场比赛,他们的医务人员有多好(或称重))为每个团队增加重量(例如队友的受伤) , ETC)。如果你做对了重量,你可以获得一个非常平衡(并且易于扩展)的模拟算法,它既可以预测(就像现实生活中的一些比赛一样),也可以完全令人惊讶(再次,就像现实生活中的比赛一样)。

更新: 战术影响 你添加了战术影响,加上“你会怎么做?”的问题,所以我会详细说明。你目前正在做的(据我所知)是你取一个百分比(发生某事的机会)并将其乘以一个比率,这样它就会更多/更少发生。

然而,因为你可以有多个比率,你最终有超过 100% 的机会。

首先,对于一支球队的每一个战术优势,(可能)对另一支球队都有反击优势。例如,如果 A 队在进球方面具有权重,B 队在制止进球方面具有反制权。这个总和就是宇宙(100%)。现在,两种战术优势的重量构成了该宇宙的一部分,或总重量(正如我在上面解释的)。

假设A 队有 80%的把握在某分钟内进球,B 队有 20%的把握阻止它(基于重量系统)。但是,因为B队刚刚获得了一名非常优秀的门将,所以B队的战术影响很大。这种影响应该改变事件的机会,而不是宇宙本身!换句话说,您最终的总机会不应该超过 100%(尽管在某些情况下,这不一定是坏事)

因此,您应该根据战术影响为 B 队增加权重,然后根据新的权重重新计算机会。

分配权重

现在,就像您评论的那样,分配权重并不容易。如果你必须“衡量”球员的素质,那当然不会。称重不仅仅是说一个球员是“坏”或“好”,你必须对他们进行实际评分(就像在高中时一样!)。最高等级越大,加权系统越准确。

现在,为战术影响分配权重要容易一些。假设您有以下影响;

  • 停止目标
  • 进球数
  • 防御
  • 攻击

现在,创建一个总重量池(比如 1000,我喜欢这个数字)。这些是您可以分配的“战术点”。这四个影响构成了匹配,因此您可以为每个影响分配 250 分。这个数字(250)是每个影响的范围。

这些分数的分配,每支球队,取决于球队的权重因素(比如,他们有一个好的门将吗?)

例如,守门员与对手守门员(也许还有介于守门员和对手之间的人,但让我们保持简单)权衡。假设A 队的守门员重量为 80%B 队的守门员为 20%。这可以评估他们的好坏程度,这与他们获得的战术点数直接相关。因此,A 队获得 250 个停球得分中的 80%,B 队获得其中的 20%

其余的点可以平均分配。在我的示例中,我只选择了两个守门员,因为无论目标是否停止。实际上,可能有更多的权重因素(供您计算)。

将它们全部划分后,您可以使用战术点来确定比赛。每分钟您都可以重新计算获胜的机会。每分钟,您还可以重新计算战术影响(例如另一名球员进入球场,或一名球员受伤)。

是的,你会得到很多变量。但是你得到的越多,比赛就越好。变量(或重量/配重)越多,感觉就越像现实生活。

于 2009-09-15T13:45:58.510 回答
8

嗯,这会很复杂,但如果你想真实地模拟一场足球比赛,你需要投入更多的变量。不是你的所有球队都会进攻,你会有防守者,这些防守者会减轻对方球队进攻的力量。

我会推荐一个更像这样的流程:

1) A 队有球在他们的场地一侧。它会尝试得分。从 1-100 (0-99) 生成一个表格,并用以下因素填充它:A 队球员的技能、对方球员的防守能力、与球门的距离、疲劳程度(比赛持续时间)。这个表看起来像这样(想象 +1 使它更简单,所以 1-100 而不是 0-99):

  1. 1-50:球员成功推进球
  2. 51-60:球员未能推进球但保持控球权
  3. 61-73 : 球员未能推进球并失去控球权
  4. 74-82 : 球员在前场将球传给另一名进攻球员
  5. 83-95 : 球员失去对球的控球权,而敌人试图得分
  6. 95-100 : 严重失败 : 球员失去控球权并立即让对方进球

2) 如果是 1,请再次掷骰,但现在有一组不同的选项可以站在对方一边。在 2 的偶数中,再次在桌子上滚动相同的卷。如果是三人,则为对方球队进行相同的掷骰,但使用不同的桌子,因为他们更接近球门。如果是 4,再次进行掷骰,但将球员的统计数据更改为 A 队的球员 2,并假设数字更接近目标。在 5 的情况下,根据随机玩家的技能滚动一张桌子,其中对方球队成功或失败。如果是六,立即给对方球队一个进球(在足球比赛中,这种情况发生的几率不到 5%,更像是 0.01%,但你也可以在另一张桌子上滚动包括受伤或看起来很愚蠢十秒钟的严重故障)。

3) 根据结果重复该过程。

我可以给你代码示例,但我认为你有基本的想法。

更新:我认为其他人已经回答了如何使用您的方法计算权重。我的方法实际上会有所不同,我将在这里解释不同之处......

1) 你的计算基本上假设每隔这么多分钟就有一次进球尝试,此时有一组复杂的计算试图模拟球员得分或不得分的机会。我的计算会假设球一直在比赛中,并根据一系列统计数据从一组可能的结果移动到下一个可能的结果,而不是被认为是对球门的一系列来回射门。这在概念上会有所不同,因为它是跟随球而不是球员,并且球将被分配给由上述因素加权的一定数量的可能性。

2)您加权事物的方法涉及多个权重,正如其他人所描述的那样,这会增加您在一定数量上的百分比变化。在我的场景中,您将机会分解为一个表格,以便绝对最多只有 100 个可能的结果,然后您使用 mt_rand 在这组结果中命中一个数字,这通常称为命中表或彩票。

为了做到这一点,您将获得基本机会,然后对其进行加权,从而为您提供成长空间。例如,假设一个球得分的基本机会是 10/100。如果某人是 1,则变为 5。2,保持 10。3,变为 15。所以现在,滚动 0-14 被灵活地分配给这些值,而命中表中的其他值则移动以适应。这会带来概率问题,因为您也应该假设您也应该扩大牌桌,但这会被动地降低您从 10/100 到 15/105 的命中率(考虑到所有其他可能的结果)。

如果你想继续你已经开始的道路,我认为我在这里所说的大部分内容都行不通,除非你只是将这些值放入一个表中,并像你已经做的那样在每次攻击中计算它们。

如果您想坚持使用当前的嵌套随机语句,我会说使用其他人提供的解决方案之一。祝你好运!

于 2009-09-15T14:38:57.970 回答
2

我建议您将所有概率转换为百分比:

function Chance($chance, $universe = 100)
{
    $chance = abs(intval($chance));
    $universe = abs(intval($universe));

    if (mt_rand(1, $universe) <= $chance)
    {
        return true;
    }

    return false;
}

Chance(25); // 25%
Chance(5, 1000); // 0.5%

此外,我只会用其他人的条件实例化一些概率,例如:

if ($attack === true)
{
    if (Chance(15 * $aggressivity) === true)
    {
        if (Chance(10 * $aggressivity) === true)
        {
            // red card
        }

        else
        {
            // yellow card
        }
    }
}

编辑:我刚回家,我真的需要睡觉,但在快速浏览了您的编辑后,我有了一个您可能会感兴趣的想法。如果不使用 1、2 或 3 的调整值,而不使用球队的战术位置,会怎样?例如,一支 4-4-2 阵型的球队对阵 5-3-2 阵型的球队比 3-3-4 阵型球队进球的机会更少。假设位置总是三连冠(XYZ),那么比较哪支球队在防守、传球和得分方面表现最好是很容易的。

一个简单的公式可能是这样的:

A: 4-4-2 (Defending Team)
B: 3-2-5 (Attacking Team)

B 进球的机会是 (4 / 5) ^ -1 = 0.2 = 20%,反之(A 队进球)将是 (3 / 2) ^ -1 = 0.5 = 50%。PS:这个现在好像没什么意义了,不过我早上会试着再看一遍。

还有一件事:在一张红牌之后,为什么犯错的球队保持不变?它应该变得更弱(少一个玩家)IMO。

于 2009-09-15T14:50:17.113 回答
0

这才是真正的把戏不是吗?也许开始考虑球员和球队有哪些变量会影响行动的成功和概率。

程序流程可能如下所示: 1. 根据变量计算发生了哪个事件,并让其中也有一些随机性 2. 计算事件的成功率。

恐怕你必须自己做这个和编程;)

于 2009-09-15T13:08:43.983 回答