返回文章列表

Shell指令碼實作經典文字遊戲

本文介紹如何使用 Shell 指令碼實作單詞重組和猜字遊戲,並解析程式碼邏輯,同時探討 Shell 指令碼在遊戲開發中的應用和優勢。兩種遊戲都使用了隨機數生成和字串處理技術,展現了 Shell 指令碼在處理文字和邏輯方面的能力。

遊戲開發 Shell指令碼

在 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

程式碼解析

  1. 單詞函式庫檢查:首先檢查單詞函式庫檔案是否存在且可讀。
  2. 單詞隨機選取:使用randomquote函式從單詞函式庫中隨機選取一個單詞。
  3. 單詞打亂:透過迴圈將選取的單詞字母逐一加入到打亂的單詞中,每次隨機決定是加在開頭還是結尾。
  4. 遊戲迴圈:外層迴圈控制遊戲的繼續或離開,內層迴圈處理玩家的猜測。
  5. 結果統計:統計玩家正確猜出的單詞數量和總遊玩次數。

猜字遊戲

猜字遊戲的目標是讓玩家猜測一個隱藏的單詞。玩家每次猜測一個字母,如果字母存在於單詞中,則顯示該字母;否則,減少一次錯誤機會。

遊戲實作

#!/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

程式碼解析

  1. 單詞函式庫檢查:與單詞重組遊戲相同,首先檢查單詞函式庫的存在性和可讀性。
  2. 新遊戲開始:每次開始新遊戲時,隨機選取一個單詞,並初始化相關變數。
  3. 猜測迴圈:內層迴圈處理玩家的猜測,並根據猜測結果更新遊戲狀態。
  4. 結果顯示:顯示目前已猜出的字母、錯誤次數和目前單詞的狀態。

Hangman 遊戲指令碼解析與實作

Hangman 是一款經典的猜字遊戲,玩家需要猜測一個隱藏的單字。本文將深入解析一個使用 Shell Script 實作的 Hangman 遊戲,並探討其運作原理和改進方法。

遊戲指令碼結構

該指令碼的主要結構包括以下幾個部分:

  1. 單字選擇:從一個包含 2882 個不同單字的列表中隨機選擇一個單字。
  2. 猜測迴圈:玩家可以重複猜測字母,直到猜出單字或用完允許的錯誤次數。
  3. 猜測驗證:對玩家的猜測進行多重驗證,包括檢查是否為單個字母、是否為小寫字母、是否已經猜過等。
  4. 遊戲狀態更新:根據玩家的猜測結果更新遊戲狀態,包括顯示已猜出的字母和剩餘的錯誤次數。

關鍵程式碼解析

猜測驗證

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"

內容解密:

  1. 檢查輸入長度:使用 wc -c 統計輸入字元數,由於使用者按下 ENTER 鍵會產生換行符,因此正確的單字元輸入長度應為 2。使用 sed 移除非數字字元,以避免 wc 輸出中的前導空白符幹擾判斷。
  2. 檢查是否為小寫字母:使用 sed 移除輸入中的所有小寫字母,若結果非空,則表示輸入包含非小寫字元。
  3. 檢查是否重複猜測:將輸入與已猜字母進行比對,若移除已猜字母後結果為空,則表示該字母已被猜過。

遊戲狀態更新

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

內容解密:

  1. 檢查字母是否存在於單字中:使用 sed 將單字中的猜測字母替換為 -,若結果與原單字不同,則表示猜測字母存在於單字中。
  2. 更新已猜字母列表:將猜測字母新增到 guessed 變數中。
  3. 更新部分猜出的單字:使用 sed 將單字中未被猜出的字母替換為 -,並將結果儲存在 partial 變數中。
  4. 檢查是否已猜出完整單字:若 partialmatch 相同,則表示玩家已猜出完整單字。

改進建議

  1. 增加圖形介面:可以使用預定義的文字圖形來展示遊戲的進展,例如吊死鬼的不同狀態。
  2. 避免重複單字:可以儲存已經使用過的單字,並在選擇新單字時進行檢查,以避免重複。
  3. 排序已猜字母列表:可以使用 sedsort 命令對已猜字母列表進行排序,以提高可讀性。

州首府猜謎遊戲與質數檢查指令碼解析

州首府猜謎遊戲

遊戲概述

該指令碼實作了一個簡單的州首府猜謎遊戲。遊戲從一個包含美國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

內容解密:

  1. 指令碼首先檢查資料檔案是否存在且可讀。
  2. $db 變數儲存了資料檔案的路徑。
  3. 如果檔案不可讀,指令碼輸出錯誤資訊並離開。
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

內容解密:

  1. 指令碼進入主迴圈,直到使用者輸入 “quit”。
  2. randomquote 命令從資料檔案中隨機選取一行。
  3. 使用 cutsed 命令解析出州名和首府名,並將首府名轉換為小寫以便於比較。

遊戲互動邏輯

  • 使用者被提示輸入所選州的首府。
  • 如果輸入正確,指令碼輸出肯定回應並計分。
  • 如果輸入錯誤,指令碼提示使用者繼續嘗試或跳過。

改進方向

  1. 增加模糊匹配功能,以容忍拼寫錯誤。
  2. 新增提示功能,例如顯示正確答案的第一個字母。

質數檢查指令碼

功能概述

該指令碼檢查一個給定的數字是否為質數。它使用試除法,從2開始檢查直到數字的一半是否有任何除數。

程式碼解析

#!/bin/bash
# isprime--檢查一個數字是否為質數。
counter=2
remainder=1
if [ $# -eq 0 ] ; then
    echo "用法: isprime NUMBER" >&2
    exit 1
fi
number=$1
# ...

內容解密:

  1. 指令碼檢查是否提供了輸入數字。
  2. 初始化 $counter$remainder 變數。
while [ $counter -le $(expr $number / 2) -a $remainder -ne 0 ]
do
    remainder=$(expr $number % $counter)
    counter=$(expr $counter + 1)
done

內容解密:

  1. 指令碼進入迴圈,檢查從2到 $number 一半的所有數字是否能整除 $number
  2. 如果找到一個除數,則 $remainder 為0,迴圈結束。

結果判斷

  • 如果迴圈結束時 $remainder 為0,則 $number 不是質數。
  • 否則,$number 是質數。

改進方向

  1. 最佳化演算法,例如只檢查到 $number 的平方根。
  2. 增加對輸入的驗證,例如檢查是否為正整數。

擲骰子與紙牌遊戲的 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

#### 內容解密:

  1. rolldie 函式:負責解析單一骰子描述並模擬擲骰結果。

    • 使用 grepcut 命令解析輸入的骰子描述,如 2d6 表示兩個六面骰子。
    • 利用 $RANDOM 生成隨機數,模擬擲骰結果。
  2. 主迴圈:遍歷所有輸入的骰子描述,呼叫 rolldie 函式並累加總和。

    • 使用 shift 命令處理下一個輸入引數。
  3. 輸出結果:顯示每次擲骰的結果和總和。

    • 最終輸出所有骰子的總和。

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
}

#### 內容解密:

  1. initializeDeck 函式:初始化一副牌,共 52 張。

    • 使用陣列 deck 表示牌堆積。
  2. shuffleDeck 函式:洗牌操作,實際上是隨機抽取牌並存入新陣列 newdeck

    • 使用 pickCard 函式隨機抽取牌。
  3. 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 紙牌遊戲的基本流程。從初始化牌堆積到判定勝負,每一步驟都清晰地標示出來。