php 的25個經典小遊戲
阿新 • • 發佈:2019-02-02
PHP是一種易於使用、易於學習且可以廣泛獲取的程式語言。它非常適合開發在各種遊戲中可以使用的簡單指令碼。無論是一個人玩簡單的使用紙和筆的遊戲,還是同一群人玩複雜的桌面角色扮演遊戲,或者任意型別的聯機遊戲,本系列都提供了適合您的內容。“用PHP
可以編寫的 30 個遊戲指令碼” 系列中的每篇文章都將分別用不到 300 詞的文字介紹 10 個指令碼(3d10 表示 “擲三個10面的骰子”),這些介紹性文字甚至對於開發新手來說都十分簡單,而且對於經驗豐富的遊戲玩家來說也十分有用。本系列的目的在於為您提供可以修改的內容來滿足自身的需求,以便您可以在下一次遊戲交流會上通過展示您的筆記本來給朋友和玩家們留下深刻印象。
開始之前
作為一名遊戲專家/設計者和開發人員,我經常發現自己在執行、規劃和玩遊戲時,很少編寫有用的實用程式和指令碼。有時我需要快速想出創意。其他時候,我只需要編出一大堆非玩家角色(Non-PlayerCharacter,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 中包括了 “任務混合器(missionblender)”。任務混合器可用於在快速滾動骰子時整合完整任務。讓我們整合自己的場景生成器。
考慮以下場景:您醒來後發現自己迷失於叢林中。您知道自己必須趕去紐約,但是不知道原因。您可以聽到附近的狗叫聲及清晰的敵方搜尋者的聲音。您渾身發冷、不住顫抖,而且沒有武器。該場景中的每一句話都介紹場景的特定方面:
“您醒來後發現自己迷失於叢林中” — 這句話將建立設定。
“您知道自己必須趕去紐約” — 這句話將描述目標。
“您可以聽到狗叫聲” — 這句話將介紹敵人。
“您渾身發冷、不住顫抖,而且沒有武器” — 這句話將新增複雜度。
就像建立名字和姓氏的文字檔案一樣,首先分別建立設定、目標、敵人和複雜度的文字檔案。程式碼歸檔中附帶了樣例檔案。在擁有這些檔案後,生成場景的程式碼與生成名稱的程式碼基本相同。
清單 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] . "
\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 "
" . $card['face'] . ' of ' . $card['suit'] . "
";
}
然後,計算輸入 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 . "
\n";
}
}
根據單詞列表的質量及猜測的準確度,我們應當能夠得到合理的單詞列表以用於可能的匹配。您必須自己決定 “表示 ‘不按規則玩’的由五個字母組成的單詞” 的謎底是 “chest” 還是 “cheat”。
米德里比斯
米德里比斯是一款文字遊戲,玩家在遊戲中得到一個簡短的故事並用同一型別的不同單詞替換主要型別的單詞,從而建立同一個故事的更無聊的新版本。閱讀以下文字:“Iwas walking in the park when I found a lake. I jumped in andswallowed 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));
通過有規律地向包含中獎號碼列表的文字檔案新增實際的樂透中獎號碼,可以發現選號的長期趨勢。檢視某些號碼的出現頻率十分有趣。
開始之前
作為一名遊戲專家/設計者和開發人員,我經常發現自己在執行、規劃和玩遊戲時,很少編寫有用的實用程式和指令碼。有時我需要快速想出創意。其他時候,我只需要編出一大堆非玩家角色(Non-PlayerCharacter,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 中包括了 “任務混合器(missionblender)”。任務混合器可用於在快速滾動骰子時整合完整任務。讓我們整合自己的場景生成器。
考慮以下場景:您醒來後發現自己迷失於叢林中。您知道自己必須趕去紐約,但是不知道原因。您可以聽到附近的狗叫聲及清晰的敵方搜尋者的聲音。您渾身發冷、不住顫抖,而且沒有武器。該場景中的每一句話都介紹場景的特定方面:
“您醒來後發現自己迷失於叢林中” — 這句話將建立設定。
“您知道自己必須趕去紐約” — 這句話將描述目標。
“您可以聽到狗叫聲” — 這句話將介紹敵人。
“您渾身發冷、不住顫抖,而且沒有武器” — 這句話將新增複雜度。
就像建立名字和姓氏的文字檔案一樣,首先分別建立設定、目標、敵人和複雜度的文字檔案。程式碼歸檔中附帶了樣例檔案。在擁有這些檔案後,生成場景的程式碼與生成名稱的程式碼基本相同。
清單 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] . "
\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 "
" . $card['face'] . ' of ' . $card['suit'] . "
";
}
然後,計算輸入 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 . "
\n";
}
}
根據單詞列表的質量及猜測的準確度,我們應當能夠得到合理的單詞列表以用於可能的匹配。您必須自己決定 “表示 ‘不按規則玩’的由五個字母組成的單詞” 的謎底是 “chest” 還是 “cheat”。
米德里比斯
米德里比斯是一款文字遊戲,玩家在遊戲中得到一個簡短的故事並用同一型別的不同單詞替換主要型別的單詞,從而建立同一個故事的更無聊的新版本。閱讀以下文字:“Iwas walking in the park when I found a lake. I jumped in andswallowed 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));
通過有規律地向包含中獎號碼列表的文字檔案新增實際的樂透中獎號碼,可以發現選號的長期趨勢。檢視某些號碼的出現頻率十分有趣。