在 Unix 系統中,Shell 指令碼不僅能用於系統管理,也能創造有趣的遊戲體驗。本文將示範如何利用 Shell 指令碼實作單詞重組和猜字遊戲,並解析其核心程式碼邏輯,例如單詞的隨機選取、字母的打亂與比對、以及遊戲流程的控制等。透過這些案例,我們可以瞭解 Shell 指令碼在遊戲開發中的應用,以及如何結合系統功能與程式邏輯來豐富遊戲體驗。此外,我們也將探討如何改進這些遊戲,例如加入圖形介面、更精細的計分系統等,以提升遊戲的趣味性和互動性。
解謎遊戲:單詞重組與猜字遊戲
在Unix系統中,我們可以利用shell指令碼創造出有趣的遊戲。本文將介紹兩個經典的文字遊戲:單詞重組(unscramble)與猜字遊戲(hangman)。這兩個遊戲不僅能娛樂使用者,也展現了shell指令碼的強大功能。
單詞重組遊戲
單詞重組遊戲的目標是讓玩家猜測一個被打亂順序的單詞。遊戲會隨機從一個單詞列表中挑選一個單詞,將其字母順序打亂後呈現給玩家。
遊戲實作
#!/bin/bash
# unscramble - 一個簡單的單詞重組遊戲
wordlib="/usr/lib/games/long-words.txt"
# 檢查單詞函式庫是否存在
if [ ! -r "$wordlib" ]; then
echo "錯誤:缺少單詞函式庫 $wordlib" >&2
echo "請從 http://www.intuitive.com/wicked/examples/long-words.txt 下載並儲存為 $wordlib" >&2
exit 1
fi
total=0; correct=0
until [ "$guess" = "quit" ]; do
match=$(randomquote $wordlib)
scrambled=""
for ((i=0; i<${#match}; i++)); do
letter=${match:$i:1}
if [ $RANDOM % 2 -eq 0 ]; then
scrambled="$letter$scrambled"
else
scrambled="$scrambled$letter"
fi
done
echo "你需要重新組合:$scrambled"
guess="??"; guesses=0
total=$((total + 1))
while [ "$guess" != "$match" -a "$guess" != "quit" -a "$guess" != "next" ]; do
read -p "你的猜測(quit|next): " guess
if [ "$guess" = "$match" ]; then
guesses=$((guesses + 1))
echo "*** 你猜對了!嘗試次數 = ${guesses} ***"
correct=$((correct + 1))
elif [ "$guess" = "next" -o "$guess" = "quit" ]; then
echo "正確答案是 \"$match\"。你的嘗試次數:$guesses"
else
echo "不正確,請再試一次。"
guesses=$((guesses + 1))
fi
done
done
echo "遊戲結束。你正確猜出了 $correct 個單詞,總共 $total 個單詞。"
exit 0
程式碼解析
- 單詞函式庫檢查:首先檢查單詞函式庫檔案是否存在且可讀。
- 單詞隨機選取:使用
randomquote函式從單詞函式庫中隨機選取一個單詞。 - 單詞打亂:透過迴圈將選取的單詞字母逐一加入到打亂的單詞中,每次隨機決定是加在開頭還是結尾。
- 遊戲迴圈:外層迴圈控制遊戲的繼續或離開,內層迴圈處理玩家的猜測。
- 結果統計:統計玩家正確猜出的單詞數量和總遊玩次數。
猜字遊戲
猜字遊戲的目標是讓玩家猜測一個隱藏的單詞。玩家每次猜測一個字母,如果字母存在於單詞中,則顯示該字母;否則,減少一次錯誤機會。
遊戲實作
#!/bin/bash
# hangman - 一個簡單的猜字遊戲
wordlib="/usr/lib/games/long-words.txt"
empty="\."
games=0
# 檢查單詞函式庫是否存在
if [ ! -r "$wordlib" ]; then
echo "錯誤:缺少單詞函式庫 $wordlib" >&2
echo "請從 http://www.intuitive.com/wicked/examples/long-words.txt 下載並儲存為 $wordlib" >&2
exit 1
fi
while [ "$guess" != "quit" ]; do
match=$(randomquote $wordlib)
if [ $games -gt 0 ]; then
echo "*** 新遊戲!***"
fi
games=$((games + 1))
guessed=""; guess=""; bad=${1:-6}
partial=$(echo $match | sed "s/[^$empty${guessed}]/-/g")
while [ "$guess" != "$match" -a "$guess" != "quit" ]; do
echo ""
if [ ! -z "$guessed" ]; then
echo -n "已猜字母:$guessed, "
fi
echo "距離絞刑架的步數:$bad,目前單詞:$partial"
read -p "猜一個字母: " guess
# 猜測處理邏輯...
done
done
程式碼解析
- 單詞函式庫檢查:與單詞重組遊戲相同,首先檢查單詞函式庫的存在性和可讀性。
- 新遊戲開始:每次開始新遊戲時,隨機選取一個單詞,並初始化相關變數。
- 猜測迴圈:內層迴圈處理玩家的猜測,並根據猜測結果更新遊戲狀態。
- 結果顯示:顯示目前已猜出的字母、錯誤次數和目前單詞的狀態。
Hangman 遊戲指令碼解析與實作
Hangman 是一款經典的猜字遊戲,玩家需要猜測一個隱藏的單字。本文將深入解析一個使用 Shell Script 實作的 Hangman 遊戲,並探討其運作原理和改進方法。
遊戲指令碼結構
該指令碼的主要結構包括以下幾個部分:
- 單字選擇:從一個包含 2882 個不同單字的列表中隨機選擇一個單字。
- 猜測迴圈:玩家可以重複猜測字母,直到猜出單字或用完允許的錯誤次數。
- 猜測驗證:對玩家的猜測進行多重驗證,包括檢查是否為單個字母、是否為小寫字母、是否已經猜過等。
- 遊戲狀態更新:根據玩家的猜測結果更新遊戲狀態,包括顯示已猜出的字母和剩餘的錯誤次數。
關鍵程式碼解析
猜測驗證
elif [ $(echo $guess | wc -c | sed 's/[^[:digit:]]//g') -ne 2 ] ; then
echo "Uh oh: You can only guess a single letter at a time"
elif [ ! -z "$(echo $guess | sed 's/[[:lower:]]//g')" ] ; then
echo "Uh oh: Please only use lowercase letters for your guesses"
elif [ -z "$(echo $guess | sed "s/[$empty$guessed]//g")" ] ; then
echo "Uh oh: You have already tried $guess"
內容解密:
- 檢查輸入長度:使用
wc -c統計輸入字元數,由於使用者按下 ENTER 鍵會產生換行符,因此正確的單字元輸入長度應為 2。使用sed移除非數字字元,以避免wc輸出中的前導空白符幹擾判斷。 - 檢查是否為小寫字母:使用
sed移除輸入中的所有小寫字母,若結果非空,則表示輸入包含非小寫字元。 - 檢查是否重複猜測:將輸入與已猜字母進行比對,若移除已猜字母後結果為空,則表示該字母已被猜過。
遊戲狀態更新
elif [ "$(echo $match | sed "s/$guess/-/g")" != "$match" ] ; then
guessed="$guessed$guess"
partial="$(echo $match | sed "s/[^$empty${guessed}]/-/g")"
if [ "$partial" = "$match" ] ; then
echo "** You've been pardoned!! Well done! The word was \"$match\"."
guess="$match"
else
echo "* Great! The letter \"$guess\" appears in the word!"
fi
內容解密:
- 檢查字母是否存在於單字中:使用
sed將單字中的猜測字母替換為-,若結果與原單字不同,則表示猜測字母存在於單字中。 - 更新已猜字母列表:將猜測字母新增到
guessed變數中。 - 更新部分猜出的單字:使用
sed將單字中未被猜出的字母替換為-,並將結果儲存在partial變數中。 - 檢查是否已猜出完整單字:若
partial與match相同,則表示玩家已猜出完整單字。
改進建議
- 增加圖形介面:可以使用預定義的文字圖形來展示遊戲的進展,例如吊死鬼的不同狀態。
- 避免重複單字:可以儲存已經使用過的單字,並在選擇新單字時進行檢查,以避免重複。
- 排序已猜字母列表:可以使用
sed和sort命令對已猜字母列表進行排序,以提高可讀性。
州首府猜謎遊戲與質數檢查指令碼解析
州首府猜謎遊戲
遊戲概述
該指令碼實作了一個簡單的州首府猜謎遊戲。遊戲從一個包含美國50個州及其首府的檔案中隨機選取一個州,並要求使用者輸入對應的首府。
程式碼解析
#!/bin/bash
# states--A state capital guessing game. Requires the state capitals data file state.capitals.txt.
db="/usr/lib/games/state.capitals.txt" # 格式為 State[tab]City.
if [ ! -r "$db" ] ; then
echo "$0: 無法讀取 $db。" >&2
echo "請取得 state.capitals.txt 並儲存為 $db。" >&2
exit 1
fi
內容解密:
- 指令碼首先檢查資料檔案是否存在且可讀。
$db變數儲存了資料檔案的路徑。- 如果檔案不可讀,指令碼輸出錯誤資訊並離開。
guesses=0; correct=0; total=0
while [ "$guess" != "quit" ] ; do
thiskey="$(randomquote $db)"
state="$(echo $thiskey | cut -d\ -f1 | sed 's/-/ /g')"
city="$(echo $thiskey | cut -d\ -f2 | sed 's/-/ /g')"
match="$(echo $city | tr '[:upper:]' '[:lower:]')"
# ...
done
內容解密:
- 指令碼進入主迴圈,直到使用者輸入 “quit”。
randomquote命令從資料檔案中隨機選取一行。- 使用
cut和sed命令解析出州名和首府名,並將首府名轉換為小寫以便於比較。
遊戲互動邏輯
- 使用者被提示輸入所選州的首府。
- 如果輸入正確,指令碼輸出肯定回應並計分。
- 如果輸入錯誤,指令碼提示使用者繼續嘗試或跳過。
改進方向
- 增加模糊匹配功能,以容忍拼寫錯誤。
- 新增提示功能,例如顯示正確答案的第一個字母。
質數檢查指令碼
功能概述
該指令碼檢查一個給定的數字是否為質數。它使用試除法,從2開始檢查直到數字的一半是否有任何除數。
程式碼解析
#!/bin/bash
# isprime--檢查一個數字是否為質數。
counter=2
remainder=1
if [ $# -eq 0 ] ; then
echo "用法: isprime NUMBER" >&2
exit 1
fi
number=$1
# ...
內容解密:
- 指令碼檢查是否提供了輸入數字。
- 初始化
$counter和$remainder變數。
while [ $counter -le $(expr $number / 2) -a $remainder -ne 0 ]
do
remainder=$(expr $number % $counter)
counter=$(expr $counter + 1)
done
內容解密:
- 指令碼進入迴圈,檢查從2到
$number一半的所有數字是否能整除$number。 - 如果找到一個除數,則
$remainder為0,迴圈結束。
結果判斷
- 如果迴圈結束時
$remainder為0,則$number不是質數。 - 否則,
$number是質數。
改進方向
- 最佳化演算法,例如只檢查到
$number的平方根。 - 增加對輸入的驗證,例如檢查是否為正整數。
擲骰子與紙牌遊戲的 Shell 指令碼實作
擲骰子指令碼解析
在桌上遊戲中,擲骰子是一個常見的隨機數生成方式。本文將介紹一個用於模擬擲骰子的 Shell 指令碼,該指令碼允許使用者指定骰子的數量和型別。
程式碼實作
#!/bin/bash
# rolldice--解析使用者輸入的骰子資訊並模擬擲骰結果
rolldie() {
dice=$1
dicecount=1
sum=0
# 解析骰子描述
if [ -z "$(echo $dice | grep 'd')" ]; then
quantity=1
sides=$dice
else
quantity=$(echo $dice | cut -dd -f1)
if [ -z "$quantity" ]; then
quantity=1
fi
sides=$(echo $dice | cut -dd -f2)
fi
echo ""
echo "擲 $quantity 個 $sides 面骰子"
# 模擬擲骰子
while [ $dicecount -le $quantity ]; do
roll=$(( ( $RANDOM % $sides ) + 1 ))
sum=$(( $sum + $roll ))
echo " 第 $dicecount 次擲骰結果 = $roll"
dicecount=$(( $dicecount + 1 ))
done
echo "總和:$sum"
}
while [ $# -gt 0 ]; do
rolldie $1
sumtotal=$(( $sumtotal + $sum ))
shift
done
echo ""
echo "所有骰子的總和:$sumtotal"
echo ""
exit 0
#### 內容解密:
rolldie函式:負責解析單一骰子描述並模擬擲骰結果。- 使用
grep和cut命令解析輸入的骰子描述,如2d6表示兩個六面骰子。 - 利用
$RANDOM生成隨機數,模擬擲骰結果。
- 使用
主迴圈:遍歷所有輸入的骰子描述,呼叫
rolldie函式並累加總和。- 使用
shift命令處理下一個輸入引數。
- 使用
輸出結果:顯示每次擲骰的結果和總和。
- 最終輸出所有骰子的總和。
Acey Deucey 紙牌遊戲指令碼解析
Acey Deucey 是一種簡單的紙牌遊戲,玩家需要猜測下一張牌是否在兩張已知牌之間。本文將介紹一個實作該遊戲的 Shell 指令碼。
程式碼實作(部分)
#!/bin/bash
# aceyduecey:實作 Acey Deucey 紙牌遊戲
function initializeDeck {
# 初始化牌堆積
card=1
while [ $card -le 52 ]; do
deck[$card]=$card
card=$(( $card + 1 ))
done
}
function shuffleDeck {
# 洗牌(實際上是隨機抽取牌)
count=1
while [ $count != 53 ]; do
pickCard
newdeck[$count]=$picked
count=$(( $count + 1 ))
done
}
function pickCard {
# 從牌堆積中隨機抽取一張牌
local errcount randomcard
threshold=10
errcount=0
while [ $errcount -lt $threshold ]; do
randomcard=$(( ( $RANDOM % 52 ) + 1 ))
errcount=$(( $errcount + 1 ))
if [ ${deck[$randomcard]} -ne 0 ]; then
picked=${deck[$randomcard]}
deck[$picked]=0 # 標記為已抽取
return $picked
fi
done
# 若隨機抽取失敗,則順序查詢可用牌
randomcard=1
while [ ${newdeck[$randomcard]} -eq 0 ]; do
randomcard=$(( $randomcard + 1 ))
done
picked=$randomcard
deck[$picked]=0 # 標記為已抽取
return $picked
}
#### 內容解密:
initializeDeck函式:初始化一副牌,共 52 張。- 使用陣列
deck表示牌堆積。
- 使用陣列
shuffleDeck函式:洗牌操作,實際上是隨機抽取牌並存入新陣列newdeck。- 使用
pickCard函式隨機抽取牌。
- 使用
pickCard函式:從牌堆積中隨機抽取一張牌。- 若隨機抽取失敗(即重複抽取同一張牌),則順序查詢下一張可用牌。
未來改進方向
- 最佳化輸出格式:對於擲骰子指令碼,可以調整輸出格式,使其更符合使用者需求。
- 擴充套件遊戲功能:對於 Acey Deucey 紙牌遊戲,可以增加更多遊戲規則或功能,如計分系統或多輪遊戲支援。
圖表說明
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title Shell指令碼實作經典文字遊戲
package "NumPy 陣列操作" {
package "陣列建立" {
component [ndarray] as arr
component [zeros/ones] as init
component [arange/linspace] as range
}
package "陣列操作" {
component [索引切片] as slice
component [形狀變換 reshape] as reshape
component [堆疊 stack/concat] as stack
component [廣播 broadcasting] as broadcast
}
package "數學運算" {
component [元素運算] as element
component [矩陣運算] as matrix
component [統計函數] as stats
component [線性代數] as linalg
}
}
arr --> slice : 存取元素
arr --> reshape : 改變形狀
arr --> broadcast : 自動擴展
arr --> element : +, -, *, /
arr --> matrix : dot, matmul
arr --> stats : mean, std, sum
arr --> linalg : inv, eig, svd
note right of broadcast
不同形狀陣列
自動對齊運算
end note
@enduml
此圖示展示了 Acey Deucey 紙牌遊戲的基本流程。從初始化牌堆積到判定勝負,每一步驟都清晰地標示出來。