用PHP构建疯狂三月锦标赛对阵表

作者 Jim Hall

Jim Hall 将他的疯狂三月脚本提升到了一个新的水平。

每年三月,我的办公室都会密切关注 NCAA 大学男子篮球锦标赛,也称为“疯狂三月”。您可以打印出对阵表,并预测每轮比赛的获胜球队。我的几位同事对此相当认真,他们总是打印出他们的对阵表并将其钉在他们的工作隔间里供所有人观看。通常,获胜者会为其他参与者买披萨午餐。

我也想参与其中,但不幸的是,我缺少有效参与的一件事:我不太关注篮球。

但是,我不想错过疯狂三月,所以几年前,我创建了一个 Bash 脚本来帮助我填写我的疯狂三月对阵表。这效果很好。我运行了脚本,然后使用结果填写了我的疯狂三月对阵表。

请参阅我之前的两篇文章,了解有关该脚本的更多信息: Bash Shell 脚本:构建更好的疯狂三月对阵表 (2017) 和 Bash Shell 脚本:构建您的疯狂三月对阵表 (2016)。

今年,我决定将事情提升到一个新的水平。与其运行 Bash 脚本,为什么不创建一个网页来自动填写每轮的获胜者呢?这样我就真的不需要填写疯狂三月对阵表了;我可以直接打印出我网页上的结果。

猜测每场比赛的获胜者

在 NCAA 大学男子篮球锦标赛中,来自四个赛区的 64 支球队在一系列单场淘汰赛中竞争。“单场淘汰赛”意味着每轮比赛结束后,获胜球队晋级下一轮;失败的球队将被淘汰。NCAA 在第一轮比赛中对来自每个赛区的顶尖球队进行种子排名,并为每支球队分配排名值。通常,排名较高的球队(1-8)比排名较低的球队(9-16)表现更好。

您可以使用这个“排名”值来猜测谁可能赢得或输掉一场比赛。排名第 1 的球队应该比排名第 16 的球队表现更好,但排名第 8 的球队应该与排名第 9 的球队表现大致相同。我发现模拟比赛结果的最佳方法是创建一个虚拟“骰子”,我“掷”骰子来确定获胜者。骰子面根据每支球队的获胜机会而定,如下面的简单算法所示,用于生成自定义骰子

  • A 队获得 a=16-rankA+1 面
  • B 队获得 b=16-rankB+1 面

在这种假设下,排名第 1 的球队 (A) 对阵排名第 16 的球队 (B) 将生成一个虚拟骰子,其中 a=16-1+1=16 个“A 队”面和 b=16-16+1=1 个“B 队”面,结果是 17 面的骰子。同样,更势均力敌的比赛,例如排名第 8 的球队对阵排名第 9 的球队,将创建一个骰子,其中 a=16-8+1=9 个“A 队”面和 b=16-9+1=8 个“B 队”面,结果是另一个 17 面的骰子。

然而,并非总是 17 面的骰子。排名第 1 的球队对阵排名第 9 的球队将生成一个骰子,其中 a=16-1+1=16 个“A 队”面和 b=16-9+1=8 个“B 队”面,或者是一个 24 面的骰子。

编写一个 PHP 函数来猜测单场比赛的获胜者非常简单


function guesswinner ($rankA, $rankB)
{
  $chanceA = 16 - $rankA + 1;
  $chanceB = 16 - $rankB + 1;

  $result = rand (1, $chanceA + $chanceB);

  if ($result <= $chanceA) {
    return $rankA;
  }
  else {
    return $rankB;
  }
}

guesswinner() 函数接受两个参数,即 A 队和 B 队的排名值,并返回获胜者。该函数根据两支球队的排名计算相对获胜机会,然后使用 rand() 函数猜测 1 到获胜机会总和之间的随机数。请注意,您可以使用两种方式调用 rand():如果您在不带参数的情况下调用 rand(),PHP 函数将返回 0 到某个最大值之间的随机值。如果您提供两个值作为参数,rand() 将给出这两个值之间的随机数。例如,rand(1, n) 返回 1 到 n(包括 n)之间的随机数。

使用这种简单的方法,排名第 1 的球队比排名第 16 的球队有更大的获胜机会,但排名第 8 的球队与排名第 9 的球队的赔率大致相同。这与构建虚拟“骰子”并通过随机数“掷”骰子相同。

自动化对阵表

现在我有一个函数可以猜测单场比赛的结果,我可以创建一个函数来“打”整个对阵表。由于疯狂三月只有几轮,并且每轮都有可预测的种子轮,因此打对阵表的函数只是跟踪每场比赛的获胜者,并将这些获胜者传递到下一轮


function playbracket ($name)
{
  print <<<END
<div id="$name">
  <h2>$name</h2>
END;

  /* seed round */

  print <<<END
  <div class="round0">
    <p>1</p><p>16</p>
    <p>8</p><p>9</p>
    <p>5</p><p>12</p>
    <p>4</p><p>13</p>
    <p>6</p><p>11</p>
    <p>3</p><p>14</p>
    <p>7</p><p>10</p>
    <p>2</p><p>15</p>
  </div>
END;

  /* round 1 results */

  $round1A = guesswinner (1, 16);
  $round1B = guesswinner (8,  9);
  $round1C = guesswinner (5, 12);
  $round1D = guesswinner (4, 13);
  $round1E = guesswinner (6, 11);
  $round1F = guesswinner (3, 14);
  $round1G = guesswinner (7, 10);
  $round1H = guesswinner (2, 15);

  print <<<END
  <div class="round1">
    <p>$round1A</p>
    <p>$round1B</p>
    <p>$round1C</p>
    <p>$round1D</p>
    <p>$round1E</p>
    <p>$round1F</p>
    <p>$round1G</p>
    <p>$round1H</p>
  </div>
END;

  /* round 2 results */

  $round2A = guesswinner ($round1A, $round1B);
  $round2B = guesswinner ($round1C, $round1D);
  $round2C = guesswinner ($round1E, $round1F);
  $round2D = guesswinner ($round1G, $round1H);

  print <<<END
  <div class="round2">
    <p>$round2A</p>
    <p>$round2B</p>
    <p>$round2C</p>
    <p>$round2D</p>
  </div>
END;
  /* round 3 results */

  $round3A = guesswinner ($round2A, $round2B);
  $round3B = guesswinner ($round2C, $round2D);

  print <<<END
  <div class="round3">
    <p>$round3A</p>
    <p>$round3B</p>
  </div>
END;

  /* round 4 results */

  $round4A = guesswinner ($round3A, $round3B);

print <<<END
  <div class="round4">
    <p>$round4A</p>
  </div>
</div>
END;
}

最后,如果您正在跟进,您只需要为四个赛区中的每一个调用 playbracket() 函数。您将剩下“最终四强”,其中包含每个赛区的获胜者,但我将把这些比赛的最终决定留给您自己解决


<html>
<head>
  <title>My Brackets</title>
  <style>
...
  </style>
</head>
<body>
<?php
function guesswinner ($rankA, $rankB)
{
...
}

function playbracket ($name)
{
...
}
?>

<h1>My Brackets</h1>

<div id="me">
<?php playbracket ("midwest"); ?>
<?php playbracket ("east"); ?>
</div>

<div id="ws">
<?php playbracket ("west"); ?>
<?php playbracket ("south"); ?>
</div>
</body>
</html>

对阵表样式

上面的 PHP 代码给出了单场淘汰赛对阵表的一系列结果,但生成的 HTML 代码需要一些样式才能看起来像对阵表。为此,我使用了层叠样式表中的一个巧妙函数:Flexbox。

要解释 Flexbox,首先要了解 HTML 是一组嵌套元素,<div> 和 <p>。让我们来了解一下它们。最外层的元素是两个 div。第一个 div 包含来自中西部赛区和东部赛区的结果:div#me。

第二个 div 包含来自西部赛区和南部赛区的结果:div#ws。

这些 div 的内容基本相同:一个赛区显示在左侧,另一个赛区显示在右侧。例如,它看起来像这样

每个赛区 div 都包含一个标题和一系列 div,用于疯狂三月中的每一轮,它看起来像这样

每场比赛结果的段落未显示,否则示例将非常长!

输入 Flexbox 的强大样式布局方法。对于每个 Flexbox 父元素,您可以定义各种属性来定义 Flexbox 内的排列方式,例如方向

  • row:从左到右
  • row-reverse:从右到左
  • column:从上到下
  • column-reverse:从下到上

我的 CSS 代码将“左”列“中西部”和“西部”设置为从左到右(“row”)显示内容,并将“右”列“东部”和“南部”设置为从右到左(“row-reverse”)显示其内容。这就是我对 Flexbox 样式设置的本质。您可以在流行的 CSS Tricks 网站上查看有关 Flexbox 的更多属性。现在开始代码


body {
  background-color: #fff;
  color: #000;
  font-family: sans-serif;
  margin: 0;
}
h1 {
  background-color: #333;
  color: #fff;
  font-size: 1em;
  text-align: center;
}

div#me,
div#ws {
  display: flex;
  flex-direction: row;
}

div#midwest,
div#east,
div#west,
div#south {
  display: flex;
  width: 50%;
}

div#midwest,
div#west {
  flex-direction: row;
}
div#east,
div#south {
  flex-direction: row-reverse;
}

h2 {
  background-color: #369;
  color: #fff;
  font-size: 1em;
  text-align: center;
  text-transform: uppercase;
  width: 25%;
}
div.round0,
div.round1,
div.round2,
div.round3,
div.round4 {
  display: flex;
  flex-direction: column;
  width: 15%;
}
div.round0 p {
  background-color: rgba(0,0,255,.1);
}
div.round1 p {
  background-color: rgba(0,0,255,.2);
}
div.round2 p {
  background-color: rgba(0,0,255,.3);
}
div.round3 p {
  background-color: rgba(0,0,255,.4);
}
div.round4 p {
  background-color: rgba(0,0,255,.5);
}

div > p {
  border: 1px solid #333;
  flex-grow: 1;
}

我的对阵表

每次您运行 PHP 代码时,例如在网页中,您都将生成一个新的 NCAA 疯狂三月篮球对阵表。它是完全随机的,因此对阵表的每次迭代都会有所不同。图 1 和图 2 显示了示例运行。

图 1. 示例运行:中西部和东部

图 2. 示例运行:西部和南部

在此示例运行中,我的脚本选择了中西部赛区的 5 号球队、东部赛区的 1 号球队、西部赛区的 5 号球队和南部赛区的 2 号球队。对阵表大多是可预测的;排名较高的球队往往战胜排名较低的球队。但也有一些意外,例如中西部赛区的 11 号球队在与排名较高的球队(6 号对 11 号,以及 11 号对 3 号)对抗后晋级了几轮,然后才被排名更高的球队击败。这个对阵表是对 NCAA 疯狂三月对阵表的相当真实的模拟,这足以让我的对阵表与我那些关注大学篮球的朋友们的对阵表相提并论。

使用脚本构建您的 NCAA 疯狂三月篮球对阵表的重点不是剥夺游戏的乐趣。相反,由于我对篮球不太熟悉,以编程方式构建我的对阵表使我可以参与办公室篮球比赛。它很有趣,而且不需要对体育统计数据有太多了解。我的脚本给了我一个关注比赛的理由,但即使我的对阵表表现不佳,也不会有情感上的投入。

Jim Hall 是一位开源软件倡导者和开发者,最著名的是 FreeDOS 的创始人。Jim 还非常积极地参与 GNOME 等开源软件项目的可用性测试。在工作中,Jim 是 Hallmentum 的首席执行官,Hallmentum 是一家 IT 执行咨询公司,帮助 CIO 和 IT 领导者进行战略规划和组织发展。

加载 Disqus 评论