閱讀本篇文章你需要有編程邏輯的基礎(chǔ),本篇文章將使用 Rust 編程語言的基本的條件控制語句和隨機數(shù)生成語句來實現(xiàn)一個兩位數(shù)的基礎(chǔ)數(shù)學(xué)加法計算命令行小游戲程序。這可能是 The Book 原書中的 猜數(shù)字 的演進版本,Rust 程序會生成兩個數(shù)的數(shù)字,用戶自己則計算答案輸入進去,然后由程序來判題。
準(zhǔn)備工作
在編寫程序之前你的電腦必須先安裝好 Rust 的基礎(chǔ)開發(fā)環(huán)境,并且有 Cargo 支持,如果你是其他編程語言轉(zhuǎn)過來的例如 C、 Java 、Python 、JavaScript 、Swift 等,此程序?qū)芎唵尾簧婕皬?fù)雜的操作。首先明白什么控制臺輸出、什么變量、知道什么是數(shù)學(xué)中的四則運算,和編程中條件控制語句和循環(huán)語句,這是看得懂本程序的前提,這個程序很簡單大概 100 行代碼左右,我很經(jīng)量讓小白也能看得懂,整體的邏輯圖如下:
在編寫程序之前我們需要使用 cargo 創(chuàng)建一個名字為 math_game 的項目,使用命令如下:
$:?cargo?new?math_game??????????????????????????????? ?????Created?binary?(application)?`math_game`?package
到此創(chuàng)建完成了,打開項目目錄下面的 src/main.rs 源文件并且開始編寫程序主體邏輯代碼。
主體邏輯
首先我們要定義幾個全局的基礎(chǔ)變量,這些變量是幫助為我們來控制程序主體的邏輯的變量,具體的變量的作用已經(jīng)在上方的注釋中說明了。值得注意的是 Rust 中默認聲明一個變量是不可變的,如果想在運行的過程中改變其值需要再變量定義的時候使用 mut 關(guān)鍵字,mut 關(guān)鍵字對應(yīng)著 Mutable 的意思,如下:
fn?main()?{ ????//?當(dāng)前回合數(shù) ????let?mut?count?=?0; ????//?需要幾輪游戲 ????let?rounds?=?10; ????//?用了存儲分數(shù) ????let?mut?score?=?0; ????//?生成兩個隨機數(shù)?默認值都為?0 ????let?mut?addend:?u32?=?0; ????let?mut?adding:?u32?=?0; }
接下來我們要定義兩個函數(shù),分別來處理隨機數(shù)生成和并將隨機數(shù)轉(zhuǎn)成數(shù)學(xué)公式展示給用戶,下面定義一個函數(shù),在 Rust 中定義一個函數(shù)要使用 fn 關(guān)鍵字。在 Rust 因為特殊的內(nèi)存管理方式,所有權(quán)規(guī)則,默認在函數(shù)之間傳遞變量可能會導(dǎo)致變量的所有權(quán)被轉(zhuǎn)移,所以這里在函數(shù)簽名中采用的是可變借用的方式,具體什么是所有權(quán)規(guī)則和借用規(guī)則這不是本篇文章的重點,對應(yīng) Rust 初學(xué)者來說應(yīng)該關(guān)注著怎么寫一個完整的小程序然后慢慢深入,函數(shù)體如下:
fn?next_math(addend:?&mut?u32,?adding:?&mut?u32)?->?u32?{ ????*addend?=?0; ????*adding?=?0; ????//?直接返回2個數(shù)的和方便結(jié)果判斷 ????*addend?+?*adding }
上面定義一個名為 next_math 的函數(shù),參數(shù)列表分別為外部傳入的是存儲被加數(shù)和加數(shù)的變量,返回值則是這兩個數(shù)的之和。目前具體的隨機數(shù)生成邏輯還沒有寫,如果需要隨機數(shù)生成這時我們就使用第三方的 Crates,什么是 Crate ?Crate 可以認為是第三方開發(fā)者實現(xiàn)的一些類庫,類似于 Java 中的 jar 包,而 Crates 則是一個存放這些工具庫的中心倉庫,類似于 Java 中央倉庫 Maven。使用第三方的 rand 庫只需要在項目的根目錄中 cargo.toml 添加對應(yīng)的依賴名稱:
[dependencies] rand = "0.8.3"
添加完成之后,需要使用 cargo update 更新一下項目,更新完成之后如果沒有錯誤,即可去代碼中完善 next_math 函數(shù)的邏輯,如下:
fn?next_math(addend:?&mut?u32,?adding:?&mut?u32)?->?u32?{ ????*addend?=?rand::thread_rng().gen_range(1..=100); ????*adding?=?rand::thread_rng().gen_range(1..=100); ????//?直接返回2個數(shù)的和方便結(jié)果判斷 ????*addend?+?*adding }
在函數(shù)中使用 rand::thread_rng().gen_range(1..=100) 即可生成 1 至 100 之間自然數(shù),如果需要生成更大的數(shù)字只需要調(diào)整范圍即可,最后如果不寫 return 表示最后一行的結(jié)果作為返回值,返回值類型為 u32 類型,這里返回值充當(dāng)著兩數(shù)之和并且返回給上層調(diào)用程序使用。
接下來要編寫一個提問函數(shù) question 向用戶展示計算公式要求計算公式,傳入剛才通過 next_math 函數(shù)生成的數(shù)字,并且傳入題目的索引編號,如下:
//?向用戶展示計算公式要求計算公式 fn?question(addend:?&mut?u32,?adding:?&mut?u32,?index:?&mut?u32)?{ ????println!(":?第{index}題為?{addend}?+?{adding}?=???請輸入正確結(jié)果?",?index?=?*index?+?1,?addend?=?addend,?adding?=?adding); }
這里我們使用了 println! 這是一個 Rust 內(nèi)置的宏,可以將字符串格式化輸出,在前段字符串中的 {} 則為后面所需要傳入的變量名稱占位符。接下來我們可以編寫程序主體邏輯了,要讓用戶不斷輸入信息,必須使用循環(huán)控制語句來編寫,在 Rust 中有多種循環(huán)語句,這里我們使用的是 while 循環(huán)語句,當(dāng)條件滿足時會進入循環(huán)體執(zhí)行里面邏輯,當(dāng)不滿足時則跳出了循環(huán)體執(zhí)行剩下的代碼邏輯,代碼邏輯如下:
use?std::Ordering; use?rand::Rng; use?std::io; fn?main()?{ ????//?當(dāng)前回合數(shù) ????let?mut?count?=?0; ????//?需要幾輪游戲 ????let?rounds?=?10; ????//?用了存儲分數(shù) ????let?mut?score?=?0; ????//?生成兩個隨機數(shù)?默認值都為?0 ????let?mut?addend:?u32?=?0; ????let?mut?adding:?u32?=?0; ????println!(":?讓我們開始游戲吧!?一輪游戲為10個回合,每題10分,總分100分!"); ????//?這里我們使用?while?制造一個循環(huán) ????while?count??num, ????????????//?如果非法設(shè)置默認值 ????????????Err(_)?=>?0, ????????}; ????????match?guess.cmp(&sum)?{ ????????????Ordering::Less?=>?{ ????????????????println!(":?你的答案太小了!正確答案是?{sum}!",?sum?=?sum); ????????????} ????????????Ordering::Greater?=>?{ ????????????????println!(":?你的答案太大了!正確答案是?{sum}!",?sum?=?sum); ????????????} ????????????Ordering::Equal?=>?{ ????????????????score?+=?10; ????????????????println!(":?恭喜你!答案正確!加10分!"); ????????????} ????????}; ????????//?添加輪數(shù)將其加一 ????????count?+=?1; ????} ????println!(":?本輪游戲結(jié)束!你的分數(shù)為?{score}?!{exp}", ?????????????score?=?score, ?????????????exp?=?if?score?>=?60 ?????????????{?"成績合格!"?} ?????????????else ?????????????{?"成績不合格!"?}) } fn?next_math(addend:?&mut?u32,?adding:?&mut?u32)?->?u32?{ ????*addend?=?rand::thread_rng().gen_range(1..=100); ????*adding?=?rand::thread_rng().gen_range(1..=100); ????//?直接返回2個數(shù)的和方便結(jié)果判斷 ????*addend?+?*adding } //?向用戶展示計算公式要求計算結(jié)構(gòu) fn?question(addend:?&mut?u32,?adding:?&mut?u32,?index:?&mut?u32)?{ ????println!(":?第{index}題為?{addend}?+?{adding}?=???請輸入正確結(jié)果?",?index?=?*index?+?1,?addend?=?addend,?adding?=?adding); }
這里跟要關(guān)注的是主體代碼邏輯里面函數(shù)作用,要讓用戶輸入信息那就必須使用系統(tǒng)調(diào)用從控制臺中讀入輸入的數(shù)據(jù),使用兩段代碼邏輯完成,如下:
?
?
????????//?用來存儲用戶控制臺輸入的變量 ????????let?mut?guess?=?String::new(); ????????//?從控制臺上讀入輸入字符串 ????????io::stdin() ????????????.read_line(&mut?guess) ????????????.expect(":?你能不能好好輸!請輸入正數(shù)!");
第一段中的 guess 為用來存儲臨時用戶輸入的字符串信息,第二段邏輯則是通過標(biāo)準(zhǔn)庫中的 io 包從標(biāo)準(zhǔn)輸入中讀入一行輸入數(shù)據(jù),而 expect 則防止用戶輸入的信息是非法,如果非法程序就提示該錯誤信息,非常容易理解。
????????//?解析輸入的值 ????????let?guess:?u32?=?match?guess.trim().parse()?{ ????????????Ok(num)?=>?num, ????????????//?如果非法設(shè)置默認值 ????????????Err(_)?=>?0, ????????}; ????????match?guess.cmp(&sum)?{ ????????????Ordering::Less?=>?{ ????????????????println!(":?你的答案太小了!正確答案是?{sum}!",?sum?=?sum); ????????????} ????????????Ordering::Greater?=>?{ ????????????????println!(":?你的答案太大了!正確答案是?{sum}!",?sum?=?sum); ????????????} ????????????Ordering::Equal?=>?{ ????????????????score?+=?10; ????????????????println!(":?恭喜你!答案正確!加10分!"); ????????????} ????????};
最后我們得到用戶輸入的字符串,這時我們需要將字符串轉(zhuǎn)成數(shù)字,將字符串轉(zhuǎn)成數(shù)字的方式有很多種,這里使用的具有 Rust 特性的模式匹配的方式,如果字符串轉(zhuǎn)換成功將執(zhí)行 Ok() 返回正常的數(shù)字,而如果字符串不能成功轉(zhuǎn)換成數(shù)字則會走 Err(_) ,這里如果轉(zhuǎn)換錯誤就使用默認值 0 作為用戶輸入的結(jié)果。
最后我們再次使用了模式匹配,將用戶輸入的結(jié)果和計算機計算的結(jié)果進行比對,在程序的頭部我們增加了另一個 use 聲明,從標(biāo)準(zhǔn)庫引入了一個叫做 std::Ordering 的類型到作用域中。Ordering 也是一個枚舉,不過它的成員是 Less、Greater 和 Equal。這是比較兩個值時可能出現(xiàn)的三種結(jié)果,接著,底部的五行新代碼使用了 Ordering 類型,cmp 方法用來比較兩個值并可以在任何可比較的值上調(diào)用。它獲取一個被比較值的引用:這里是把 guess 與 sum 做比較。然后它會返回一個剛才通過 use 引入作用域的 Ordering 枚舉的成員。使用一個 match 表達式,根據(jù)對 guess 和 sum 調(diào)用 cmp 返回的 Ordering 成員來決定接下來做什么。
有了上面的解釋就很容易明白這段代碼的邏輯了,用戶輸入正確的答案就將分數(shù)加 10 分,如果不正??梢愿嬖V時候離準(zhǔn)確的答案的差值或者大小,這里的使用的大小,最后完成整個程序的邏輯并且將游戲輪數(shù)加 1 回合,當(dāng)循環(huán)體執(zhí)行完成打印分數(shù)信息并且展示到控制臺中。
?println!(
????????":?本輪游戲結(jié)束!你的分數(shù)為?{score}?!{exp}", ????????score?=?score, ????????exp?=?if?score?>=?60?{ ????????????"成績合格!" ????????}?else?{ ????????????"成績不合格!" ????????} ????)
因為 Rust 沒有三目表達式,這里我采用的是if 語句塊來控制具體的分數(shù)合格信息的輸出。
小 結(jié)
至此我們就完成一個 Rust 小游戲的編寫工作,代碼其實一點都不復(fù)雜,主要是將思路轉(zhuǎn)換成具體代碼實現(xiàn)。
編輯:黃飛
?
評論
查看更多