PHP 编写的 25个游戏脚本


Posted in PHP onMay 11, 2009

无论是一个人玩简单的使用纸和笔的游戏,还是同一群人玩复杂的桌面角色扮演游戏,或者任意类型的联机游戏,本系列都提供了适合您的内容。“用 PHP 可以编写的 30 个游戏脚本” 系列中的每篇文章都将分别用不到 300 词的文字介绍 10 个脚本(3d10 表示 “掷三个 10 面的骰子”),这些介绍性文字甚至对于开发新手来说都十分简单,而且对于经验丰富的游戏玩家来说也十分有用。本系列的目的在于为您提供可以修改的内容来满足自身的需求,以便您可以在下一次游戏交流会上通过展示您的笔记本来给朋友和玩家们留下深刻印象。

开始之前

作为一名游戏专家/设计者和开发人员,我经常发现自己在运行、规划和玩游戏时,很少编写有用的实用程序和脚本。有时我需要快速想出创意。其他时候,我只需要编出一大堆非玩家角色(Non-Player Character,NPC)的名称。偶尔,我还需要处理数字、处理一些异常或者将一些文字游戏集成到游戏中。只需事先完成一点脚本工作,就可以更好地管理这些任务。

本文将探究在各种游戏中可以使用的 10 个基本脚本。代码压缩包包含所讨论的每个脚本的完整源代码,并且可以在 chaoticneutral 查看脚本实际运行情况。

我们将快速地介绍这些脚本。有关如何查找主机或设置服务器的内容将不做介绍。有很多 Web 托管公司提供 PHP,并且如果需要安装自己的 PHP,XAMPP 安装程序使用起来也十分简单。我们将不会花费大量时间谈论 PHP 最佳实践或游戏设计技术。本文介绍的脚本易于理解、使用简单并可以快速掌握。

简单的掷骰器

许多游戏和游戏系统都需要骰子。让我们先从简单的部分入手:掷一个六面骰子。实际上,滚动一个六面骰子就是从 1 到 6 之间选择一个随机数字。在 PHP 中,这十分简单:echo rand(1,6);。

在许多情况下,这基本上很简单。但是在处理机率游戏时,我们需要一些更好的实现。PHP 提供了更好的随机数字生成器:mt_rand()。在不深入研究两者差别的情况下,可以认为 mt_rand 是一个更快、更好的随机数字生成器:echo mt_rand(1,6);。如果把该随机数字生成器放入函数中,则效果会更好。

清单 1. 使用 mt_rand() 随机数字生成器函数

function roll () { 

return mt_rand(1,6); 

} 

echo roll();

然后可以把需要滚动的骰子类型作为参数传递给函数。

清单 2. 将骰子类型作为参数传递

function roll ($sides) { 

return mt_rand(1,$sides); 

} 

echo roll(6); // roll a six-sided die 

echo roll(10); // roll a ten-sided die 

echo roll(20); // roll a twenty-sided die

从这里开始,我们可以继续根据需要一次滚动多个骰子,返回结果数组;也可以一次性滚动多个不同类型的骰子。但是大多数任务都可以使用这个简单的脚本。

随机名称生成器

如果正在运行游戏、编写故事或者一次性创建大批字符,有时会疲于应付不断出现的新名字。让我们看一看可用于解决此问题的一个简单随机名称生成器。首先,让我们创建两个简单数组 — 一个用于名字,一个用于姓氏。

清单 3. 名字和姓氏的两个简单数组

$male = array( 

"William", 

"Henry", 

"Filbert", 

"John", 

"Pat", 

); 

$last = array( 

"Smith", 

"Jones", 

"Winkler", 

"Cooper", 

"Cline", 

);

然后就可以从每个数组中选择一个随机元素:echo $male[array_rand($male)] . ' ' . $last[array_rand($last)];。要一次性提取多个名称,只需混合数组并根据需要提取。

清单 4. 混合名称数组

shuffle($male); 

shuffle($last); 

for ($i = 0; $i <= 3; $i++) { 

echo $male[$i] . ' ' . $last[$i]; 

}

基于此基本概念,我们可以创建保存名字和姓氏的文本文件。如果在文本文件的每一行中存放一个名字,则可以轻松地用换行符分隔文件内容以构建源代码数组。

清单 5. 创建名称的文本文件

$male = explode('\n', file_get_contents('names.female.txt'));

$last = explode('\n', file_get_contents('names.last.txt'));

构建或查找一些好的名字文件(代码归档 中附带了一些文件),此后我们绝不再需要为名字烦恼。

场景生成器

利用构建名字生成器使用的相同基本原理,我们可以构建场景生成器。此生成器不但在角色扮演游戏中十分有用,而且在需要用到伪随机环境集合(可用于角色扮演、即兴创作、写作等情况)的情况下也十分有用。我最喜欢的游戏之一,Paranoia 在其 GM Pack 中包括了 “任务混合器(mission blender)”。任务混合器可用于在快速滚动骰子时整合完整任务。让我们整合自己的场景生成器。

考虑以下场景:您醒来后发现自己迷失于丛林中。您知道自己必须赶去纽约,但是不知道原因。您可以听到附近的狗叫声及清晰的敌方搜寻者的声音。您浑身发冷、不住颤抖,而且没有武器。该场景中的每一句话都介绍场景的特定方面:

“您醒来后发现自己迷失于丛林中” — 这句话将建立设置。

“您知道自己必须赶去纽约” — 这句话将描述目标。

“您可以听到狗叫声” — 这句话将介绍敌人。

“您浑身发冷、不住颤抖,而且没有武器” — 这句话将添加复杂度。

就像创建名字和姓氏的文本文件一样,首先分别创建设置、目标、敌人和复杂度的文本文件。代码归档中附带了样例文件。在拥有这些文件后,生成场景的代码与生成名称的代码基本相同。

清单 6. 生成场景

$settings = explode("\n", file_get_contents('scenario.settings.txt')); 

$objectives = explode("\n", file_get_contents('scenario.objectives.txt')); 

$antagonists = explode("\n", file_get_contents('scenario.antagonists.txt')); 

$complicati**** = explode("\n", file_get_contents('scenario.complicati****.txt')); 

shuffle($settings); 

shuffle($objectives); 

shuffle($antagonists); 

shuffle($complicati****); 

echo $settings[0] . ' ' . $objectives[0] . ' ' . $antagonists[0] . ' ' 

. $complicati****[0] . "<br />\n";

我们可以通过添加新文本文件向场景中添加元素,也可能希望添加多重复杂度。添加到基本文本文件中的内容越多,场景随时间的变化就越多。

牌组创建器(Deck builder)和装备(shuffler)

如果您要玩纸牌并且要处理与纸牌相关的脚本,我们需要用装备中的工具整合一副牌组构建器。首先,让我们构建一副标准纸牌。需要构建两个数组 — 一个用于保存同花色的组牌,而另一个用于保存牌面。如果稍后需要添加新组牌或牌类型,则这样做将获得很好的灵活性。

清单 7. 构建一副标准扑克牌

$suits = array ( 

"Spades", "Hearts", "Clubs", "Diamonds" 

); 

$faces = array ( 

"Two", "Three", "Four", "Five", "Six", "Seven", "Eight", 

"Nine", "Ten", "Jack", "Queen", "King", "Ace" 

);

然后构建一副牌数组来保存所有纸牌值。只需使用一对 foreach 循环即可完成此操作。

清单 8. 构建一副牌数组

$deck = array(); 

foreach ($suits as $suit) { 

foreach ($faces as $face) { 

$deck[] = array ("face"=>$face, "suit"=>$suit); 

} 

}

在构建了一副扑克牌数组后,我们可以轻松地洗牌并随机抽出一张牌。

清单 9. 洗牌并随机抽出一张牌

shuffle($deck); 

$card = array_shift($deck); 

echo $card['face'] . ' of ' . $card['suit'];

现在,我们就获得了抽取多副牌或构建多层牌盒(multideck shoe)的捷径。

胜率计算器:发牌

由于构建扑克牌时会分别跟踪每张牌的牌面和花色,因此可以通过编程方式利用这副牌来计算得到特定牌的几率。首先每只手分别抽出五张牌。

清单 10. 每只手抽出五张牌

$hands = array(1 => array(), 2=>array()); 

for ($i = 0; $i < 5; $i++) { 

$hands[1][] = implode(" of ", array_shift($deck)); 

$hands[2][] = implode(" of ", array_shift($deck)); 

}

然后可以查看这副牌,看看剩余多少张牌以及抽到特定牌的机率是多少。查看剩余的牌数十分简单。只需要计算 $deck 数组中包含的元素数。要获得抽到特定牌的机率,我们需要一个函数来遍历整副牌并估算其余牌以查看是否匹配。

清单 11. 计算抽到特定牌的几率

function calculate_odds($draw, $deck) { 

$remaining = count($deck); 

$odds = 0; 

foreach ($deck as $card) { 

if ( ($draw['face'] == $card['face'] && $draw['suit'] == 

$card['suit'] ) || 

($draw['face'] == '' && $draw['suit'] == $card['suit'] ) || 

($draw['face'] == $card['face'] && $draw['suit'] == '' ) ) { 

$odds++; 

} 

} 

return $odds . ' in ' $remaining; 

}

现在可以选出尝试抽出的牌。为了简单起见,传入看上去类似某张牌的数组。我们可以查找特定的一张牌。

清单 12. 查找指定的一张牌

$draw = array('face' => 'Ace', 'suit' => 'Spades'); 

echo implode(" of ", $draw) . ' : ' . calculate_odds($draw, $deck);

或者可以查找指定牌面或花色的牌。

清单 13. 查找指定牌面或花色的牌

$draw = array('face' => '', 'suit' => 'Spades'); 

$draw = array('face' => 'Ace', 'suit' => '');

简单的扑克发牌器

现在已经得到牌组构建器和一些工具,可以帮助计算出抽出特定卡的机率,我们可以整合一个真正简单的发牌器来进行发牌。出于本例的目的,我们将构建一个可以抽出五张牌的发牌器。发牌器将从整副牌中提供五张牌。使用数字指定需要放弃哪些牌,并且发牌器将用一副牌中的其他牌替换这些牌。我们无需指定发牌限制或特殊规则,但是您可能会发现这些是非常有益的个人经验。

如上一节所示,生成并洗牌,然后每只手五张牌。按数组索引显示这些牌,以便可以指定返回哪些牌。您可以使用表示要替换哪些牌的复选框来完成此操作。

清单 14. 使用复选框表示要替换的牌

foreach ($hand as $index =>$card) {

echo "<input type='checkbox' name='card[" . $index . "]'>

" . $card['face'] . ' of ' . $card['suit'] . "<br />";

}

然后,计算输入 array $_POST['card'],查看哪些牌已被选择用于替换。

清单 15. 计算输入

$i = 0; 

while ($i < 5) { 

if (isset($_POST['card'][$i])) { 

$hand[$i] = array_shift($deck); 

} 

}

使用此脚本,您可以尝试找到处理特定一组牌的最佳方法。

Hangman 游戏

Hangman 实质上是一款猜字游戏。给定单词的长度,我们使用有限的几次机会猜这个单词。如果猜出了出现在该单词中的一个字母,则填充该字母出现的所有位置。在猜错若干次(通常为六次)后,您就输了比赛。要构建一个简陋的 hangman 游戏,我们需要从单词列表开始。现在,让我们把单词列表制作成一个简单的数组。

清单 16. 创建单词列表

$words = array ( 

"giants", 

"triangle", 

"particle", 

"birdhouse", 

"minimum", 

"flood" 

);

使用前面介绍的技术,我们可以把这些单词移动到外部单词列表文本文件中,然后根据需要导入。

在得到单词列表后,需要随机选出一个单词,将每个字母显示为空,然后开始猜测。我们需要在每次进行猜测时跟踪正确和错误的猜测。只需序列化猜测数组并在每次猜测时传递它们,就可实现跟踪目的。如果需要阻止人们通过查看页面源代码侥幸猜对,则需要执行一些更安全的操作。

构建数组以保存字母和正确/错误的猜测。对于正确的猜测,我们将用字母作为键并用句点作为值填充数组。

清单 17. 构建保存字母和猜测结果的数组

$letters = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o', 

'p','q','r','s','t','u','v','w','x','y','z'); 

$right = array_fill_keys($letters, '.'); 

$wrong = array();

现在需要一些代码来评估猜测并在完成猜字游戏的过程中显示该单词。

清单 18. 评估猜测并显示进度

if (stristr($word, $guess)) { 

$show = ''; 

$right[$guess] = $guess; 

$wordletters = str_split($word); 

foreach ($wordletters as $letter) { 

$show .= $right[$letter]; 

} 

} else { 

$show = ''; 

$wrong[$guess] = $guess; 

if (count($wrong) == 6) { 

$show = $word; 

} else { 

foreach ($wordletters as $letter) { 

$show .= $right[$letter]; 

} 

} 

}

在 源代码归档 中,可以看到如何序列化猜测数组并将该数组从一次猜测传递到另一次猜测中。

纵横字谜助手

我知道这样做不合适,但是有时在玩纵横拼字谜时,您不得不费劲地找出以 C 开头并以 T 结尾、包含五个字母的单词。使用为 Hangman 游戏构建的相同单词列表,我们可以轻松地搜索符合某个模式的单词。首先,找到一种传输单词的方法。为了简单起见,用句点替换缺少的字母:$guess = "c...t";。由于正则表达式将把句点处理为单个字符,因此我们可以轻松地遍历单词列表以查找匹配。

清单 19. 遍历单词列表

foreach ($words as $word) { 

if (preg_match("/^" . $_POST['guess'] . "$/",$word)) { 

echo $word . "<br />\n"; 

} 

}

根据单词列表的质量及猜测的准确度,我们应当能够得到合理的单词列表以用于可能的匹配。您必须自己决定 “表示 ‘不按规则玩' 的由五个字母组成的单词” 的谜底是 “chest” 还是 “cheat”。

米德里比斯

米德里比斯是一款文字游戏,玩家在游戏中得到一个简短的故事并用同一类型的不同单词替换主要类型的单词,从而创建同一个故事的更无聊的新版本。阅读以下文本:“I was walking in the park when I found a lake. I jumped in and swallowed too much water. I had to go to the hospital.” 开始用其他单词标记替换单词类型。开始和结束标记带有下划线用于阻止意外的字符串匹配。

清单 20. 用单词标记替换单词类型

$text = "I was _VERB_ing in the _PLACE_ when I found a _NOUN_.

I _VERB_ed in, and _VERB_ed too much _NOUN_. I had to go to the _PLACE_.";

接下来,创建几个基本单词列表。对于本例,我们也不会做得太复杂。

清单 21. 创建几个基本单词列表

$verbs = array('pump', 'jump', 'walk', 'swallow', 'crawl', 'wail', 'roll'); 

$places = array('park', 'hospital', 'arctic', 'ocean', 'grocery', 'basement', 

'attic', 'sewer'); 

$nouns = array('water', 'lake', 'spit', 'foot', 'worm', 

'dirt', 'river', 'wankel rotary engine');

现在可以重复地评估文本来根据需要替换标记。

清单 22. 评估文本

while (preg_match("/(_VERB_)|(_PLACE_)|(_NOUN_)/", $text, $matches)) { 

switch ($matches[0]) { 

case '_VERB_' : 

shuffle($verbs); 

$text = preg_replace($matches[0], current($verbs), $text, 1); 

break; 

case '_PLACE_' : 

shuffle($places); 

$text = preg_replace($matches[0], current($places), $text, 1); 

break; 

case '_NOUN_' : 

shuffle($nouns); 

$text = preg_replace($matches[0], current($nouns), $text, 1); 

break; 

} 

} 

echo $text;

很明显,这是一个简单而粗糙的示例。单词列表越精确,并且花在基本文本上的时间越多,结果就越好。我们已经使用了文本文件创建名称列表及基本单词列表。使用相同原则,我们可以创建按类型划分的单词列表并使用这些单词列表创建更加变化多端的米德里比斯游戏。

乐透机

全部选中乐透的六个正确号码 —— 退一步说 —— 在统计学上是不可能的。不过,许多人仍然花钱去玩,而且如果您喜欢号码,则查看趋势图可能很有趣。让我们构建一个脚本,该脚本将允许跟踪赢奖号码并在列表中提供选择次数最少的 6 个号码。

(免责声明:这不会帮助您中乐透奖,因此请不要花钱购买奖券。这只是为了娱乐)。

把赢奖的乐透选择保存到文本文件中。用逗号分隔各个号码并把每组号码放在单独一行中。使用换行符分隔文件内容并使用逗号分隔行后,可以得到类似清单 23 的内容。

清单 23. 把选择的赢奖乐透保存到文本文件中

$picks = array( 

array('6', '10', '18', '21', '34', '40'), 

array('2', '8', '13', '22', '30', '39'), 

array('3', '9', '14', '25', '31', '35'), 

array('11', '12', '16', '24', '36', '37'), 

array('4', '7', '17', '26', '32', '33') 

);

很明显,这不足以成为绘制统计数据的基本文件。但是它是一个开端,并且足以演示基本原理。

设置一个基本数组以保存选择范围。例如,如果选择 1 到 40 之间(例如,$numbers = array_fill(1,40,0);)的号码,则遍历我们的选择,递增相应的匹配值。

清单 24. 遍历选择

foreach ($picks as $pick) { 

foreach ($pick as $number) { 

$numbers[$number]++; 

} 

}

最后,根据值将号码排序。此操作应当会把最少选择的号码放在数组的前部。

清单 25. 根据值将号码排序

asort($numbers); 

$pick = array_slice($numbers,0,6,true); 

echo implode(',', array_keys($pick));

通过有规律地向包含中奖号码列表的文本文件添加实际的乐透中奖号码,可以发现选号的长期趋势。查看某些号码的出现频率十分有趣。

PHP 相关文章推荐
落伍首发 php+mysql 采用ajax技术的 省 市 地 3级联动无刷新菜单 源码
Dec 16 PHP
实例(Smarty+FCKeditor新闻系统)
Jan 02 PHP
浅析php header 跳转
Jun 17 PHP
关于crontab的使用详解
Jun 24 PHP
PHP Curl出现403错误的解决办法
May 29 PHP
PHP+FastCGI+Nginx配置PHP运行环境
Aug 07 PHP
配置Nginx+PHP的正确思路与过程
May 10 PHP
php生成与读取excel文件
Oct 14 PHP
深入解析PHP中SESSION反序列化机制
Mar 01 PHP
PHP基于curl实现模拟微信浏览器打开微信链接的方法示例
Feb 15 PHP
java解析json方法总结
May 16 PHP
php修改word的实例方法
Nov 17 PHP
PHPMyAdmin 快速配置方法
May 11 #PHP
PHP 在线翻译函数代码
May 07 #PHP
PHP SQLite类
May 07 #PHP
php结合飞信 免费天气预报短信
May 07 #PHP
PHP 身份证号验证函数
May 07 #PHP
php 购物车的例子
May 04 #PHP
PHP 错误之引号中使用变量
May 04 #PHP
You might like
纯真IP数据库的应用 IP地址转化成十进制
2009/06/14 PHP
php disk_free_space 返回目录可用空间
2010/05/10 PHP
浅谈PHP无限极分类原理
2019/03/14 PHP
JavaScript OOP类与继承
2009/11/15 Javascript
JavaScript中使用构造器创建对象无需new的情况说明
2012/03/01 Javascript
JavaScript包装对象使用介绍
2013/08/29 Javascript
巧用jquery解决下拉菜单被Div遮挡的相关问题
2014/02/13 Javascript
jQuery中mouseover事件用法实例
2014/12/26 Javascript
js中跨域方法原理详解
2015/07/19 Javascript
js+css实现的圆角边框TAB选项卡滑动门代码分享(2款)
2015/08/26 Javascript
BOM之navigator对象和用户代理检测
2017/02/10 Javascript
基于JavaScript实现的希尔排序算法分析
2017/04/14 Javascript
vue mounted组件的使用
2018/06/18 Javascript
记一次webapck4 配置文件无效的解决历程
2018/09/19 Javascript
jQuery实现的点击图片居中放大缩小功能示例
2019/01/16 jQuery
[51:17]完美世界DOTA2联赛循环赛Inki vs DeMonsTer 第二场 10月30日
2020/10/31 DOTA
python持久性管理pickle模块详细介绍
2015/02/18 Python
浅谈Python 的枚举 Enum
2017/06/12 Python
python 计算数组中每个数字出现多少次--“Bucket”桶的思想
2017/12/19 Python
Python爬虫天气预报实例详解(小白入门)
2018/01/24 Python
django文档学习之applications使用详解
2018/01/29 Python
python实现录屏功能(亲测好用)
2020/03/02 Python
手把手教你将Flask应用封装成Docker服务的实现
2020/08/19 Python
python 实现&quot;神经衰弱&quot;翻牌游戏
2020/11/09 Python
python制作一个简单的gui 数据库查询界面
2020/11/19 Python
python实现启动一个外部程序,并且不阻塞当前进程
2020/12/05 Python
css3选择器基本介绍
2014/12/15 HTML / CSS
英国和世界各地鲜花速递专家:Arena Flowers
2018/02/10 全球购物
2014自荐信的写作技巧
2014/01/28 职场文书
生物科学专业职业规划书范文
2014/02/11 职场文书
移交协议书
2014/08/19 职场文书
2016年五一国际劳动节活动总结
2016/04/06 职场文书
创业计划书之青年旅馆
2019/09/23 职场文书
golang 在windows中设置环境变量的操作
2021/04/29 Golang
javascript canvas实现雨滴效果
2021/06/09 Javascript
大型强子对撞机再次重启探索“第五种自然力”
2022/04/29 数码科技