Step 1:產生秘密數字
把 Math.random 的 0 到 1 小數轉成 1 到 100 整數。
const sample = 0.42; const secret = Math.floor(sample * ) + ; console.log(secret);
這章結束你會把規格、變數、判斷、迴圈與函式組成一個真的可玩小遊戲。
開場 · 從規格開始
很多人第一次叫 AI 寫遊戲,會直接丟一句:「幫我做猜數字。」AI 可能真的吐出一大段程式,但你看不懂哪裡是規則、哪裡在記次數、哪裡判斷輸贏。這章我們反過來:先把遊戲規格寫成一句話,再拆成小步驟,一塊一塊組起來。你不需要變成工程師,但你會第一次感覺到:原來程式不是一團黑盒,而是一張可以檢查的食譜。
做遊戲之前,先不要急著寫 code。真正的第一步是規格(specification):把「這個東西要怎麼玩」說到沒有模糊空間。猜數字的規格可以寫成一句話:電腦先在 1 到 100 之間選一個秘密數字,玩家每次輸入一個猜測,電腦回答太大、太小或猜中,直到玩家猜中為止。
這句話看起來很普通,其實已經把程式的骨架藏好了。電腦要先選答案,表示我們需要一個地方記住答案。玩家會一直猜,表示某件事要重複。每次猜完要回應太大或太小,表示要用 ch06 學過的判斷(condition)。猜中時遊戲結束,表示要有勝利條件。
規格還要能被檢查。好的規格不只說「好玩」,而是能回答幾個具體問題:數字範圍是多少?玩家一次輸入什麼?錯了要得到什麼提示?什麼時候算結束?如果這些問題回答不出來,後面寫 code 時就會一直補洞,AI 實習生也會替你亂猜。
規格的功用不是寫給老師看的,是寫給未來的自己和 AI 實習生看的。你如果只說「做個好玩的猜數字」,AI 實習生會自己腦補規則,可能做成 1 到 10,可能做成限時,可能沒有計次。你如果先說清楚規格,後面檢查 code 時就有標準,不會被一大段看似厲害的程式牽著走。
把程式當食譜來看,規格就是菜名和成品描述:「我要一碗不辣、兩顆蛋、三分鐘能完成的番茄蛋花湯。」沒有這句話,後面的步驟再精美,也可能煮錯菜。寫小遊戲也是一樣,先定義成品,再拆步驟。
const gameSpec = {
range: '1 到 100',
rule: '玩家猜一個數字,電腦提示太大、太小或猜中',
win: '猜中秘密數字就獲勝'
};
console.log('遊戲範圍:' + gameSpec.range);
console.log('遊戲規則:' + gameSpec.rule);
console.log('勝利條件:' + gameSpec.win);
規格寫完,下一步是拆解。先問第一個問題:這個遊戲需要記住什麼?至少有兩件事。第一,秘密答案是多少。第二,玩家已經猜了幾次。這些會變動、會被程式拿來判斷的資料,就是狀態(state)。你可以把狀態想成遊戲桌上的計分板:答案藏在裁判手裡,猜測次數寫在白板上。
在 JavaScript 裡,我們用變數(variable)保存狀態。秘密答案在一局遊戲中通常不變,所以可以用 const secret = 42 想像它被鎖定。猜測次數會一直增加,所以用 let tries = 0。這不是語法炫技,而是把真實世界的遊戲物件放進抽屜:答案抽屜、次數抽屜、玩家猜測抽屜。
狀態和設定不太一樣。範圍 1 到 100 比較像設定,通常一局裡不會改;已猜次數是狀態,因為每猜一次就會變。分清楚這件事很實用:如果你把會變的東西當成不會變,遊戲就無法更新;如果你把不該變的答案一直重設,玩家永遠不可能穩定猜中。
第二個問題:程式需要判斷什麼?玩家猜的數字小於答案,要回「太小」;大於答案,要回「太大」;等於答案,要回「猜中」。這三條路就是分岔路。你在 ch06 已經看過 if、else if、else,這裡只是把它們放進真正的遊戲。
第三個問題:什麼事情會重複?每次玩家猜,程式都要計次、比較、回饋。只要還沒猜中,這個流程就會繼續。到這裡你會發現,一個遊戲並不是神秘的大東西,而是幾個老朋友的組合:變數負責記住,判斷負責分路,迴圈(loop)負責重複,函式(function)負責把一段流程包起來重用。
const secret = 42;
let tries = 0;
function showState(guess) {
tries = tries + 1;
console.log('第 ' + tries + ' 次猜:' + guess);
console.log('秘密答案仍然是:' + secret);
}
showState(30);
showState(50);
真的遊戲不能每次答案都固定 42,所以我們需要亂數(random number)。JavaScript 內建 Math.random(),它會產生一個大於等於 0、但小於 1 的小數,例如 0.13、0.72、0.999。注意,它不會直接給你 1 到 100,所以我們要把這個小數加工。
加工食譜是三步。第一步,把小數乘以 100,範圍就變成 0 到 99.999。第二步,用 Math.floor() 把小數無條件捨去,變成 0 到 99 的整數。第三步,加 1,範圍就變成 1 到 100。這行常見寫法是 Math.floor(Math.random() * 100) + 1。
這裡最容易出錯的是邊界。如果少了最後的 + 1,答案可能是 0,玩家照規則猜 1 到 100 就永遠猜不到 0。如果把乘數寫成 101,又可能把 101 放進答案範圍。邊界錯誤通常不會每次出現,所以更需要你先用規格檢查。
這行 code 對新手很容易像咒語,所以請拆開看。Math.random() 是拿到原料;* 100 是放大範圍;Math.floor 是切掉小數;+ 1 是把 0 到 99 平移成 1 到 100。你不需要背咒語,只要記得每一步在改變範圍。
這也是指揮 AI 的關鍵。當 AI 寫出亂數,你要能問:「這個範圍是不是包含 100?會不會出現 0?每一局是不是重新產生?」如果你能問出這些問題,就已經不是只會貼 prompt 的人,而是在檢查食譜有沒有少一匙鹽。
const samples = [0, 0.009, 0.42, 0.999];
for (const sample of samples) {
const secretNumber = Math.floor(sample * 100) + 1;
console.log(sample + ' 會變成 ' + secretNumber);
}
const realSecret = Math.floor(Math.random() * 100) + 1;
console.log('真正遊戲的秘密數字在範圍內:' + (realSecret >= 1 && realSecret <= 100));
有了秘密答案,下一步是處理玩家的猜測。頁面版遊戲會從輸入框拿到文字,但程式判斷大小時需要數字,所以要把輸入轉成數字。這裡會用到 Number()。例如 Number('35') 會變成數字 35;如果輸入不是合理數字,後面可以另外提醒玩家。
輸入處理也要照規格。空白、文字、0、101 都不是合法猜測,最好不要計入次數,因為玩家其實還沒有完成一次有效猜測。這種細節很像餐廳點餐:客人說不清楚餐點時,收銀機不應該先扣款,而是先請他重新確認。
核心判斷只有三條路。猜測小於答案,回「太小」;猜測大於答案,回「太大」;否則就是猜中。這裡的「否則」不是偷懶,而是因為數字比較只有三種結果:小於、大於、等於。前兩個都不是,剩下就是等於。
我們把這段流程包成函式,因為每次玩家猜都要做同一件事。函式像一張小食譜:給它猜測數字,它就回傳一句結果。當遊戲變大時,函式會讓你比較不亂。你可以先單獨測它,不必一次跑完整個遊戲。這叫逐步測試,也是本章最重要的工作習慣。
注意,判斷邏輯要用清楚的順序寫。不要同時在一行裡塞太多花招。給完全不會寫程式的人看的 code,第一要務不是短,而是讀得出食譜。未來你讓 AI 寫 code,也可以要求它「先用清楚版本,不要壓縮成一行」。
const answer = 42;
function checkGuess(guess) {
if (guess < answer) {
return '太小';
} else if (guess > answer) {
return '太大';
} else {
return '猜中';
}
}
console.log(checkGuess(20));
console.log(checkGuess(80));
console.log(checkGuess(42));
最後,把零件組起來。每猜一次,次數加一;檢查猜測;如果猜中,顯示「你用了幾次猜中」。如果還沒猜中,就給提示,讓玩家繼續。這時候你會看到 ch03 學過的迴圈、ch05 學過的變數、ch06 學過的判斷,都不是分開的考題,而是同一個遊戲裡的不同工具。
在頁面 UI 裡,玩家按一次按鈕,就等於執行一次「猜測流程」。在 console 範例裡,我們用一串預先準備好的猜測來模擬玩家:20、50、37。迴圈會一個一個拿出來試,直到猜中就停止。停止這件事很重要,因為遊戲已經完成目標,不應該繼續消耗後面的猜測。
組裝時請保留一張小檢查表:開始新局會不會產生新答案?猜錯會不會計次?不合法輸入會不會被擋下?猜中後按鈕會不會停用?重新開始會不會清空紀錄?這些問題都不是額外功課,而是遊戲能不能真的玩的基本驗收。
如果某一項驗收失敗,不要整包重寫。先回到對應零件:亂數錯就看產生答案那行,提示錯就看判斷,次數錯就看狀態更新。這樣修會比盯著整段程式發呆有效。先小步測試。
做完整遊戲時,不要期待一次寫完就完美。成熟做法是:先固定答案測判斷,再換成亂數;先在 console 測函式,再接到按鈕;先確認猜中會結束,再加上限制次數或嘲諷語。這就是「規格 → 拆解 → 逐步做 → 測試」的流程。它不是只適用猜數字,之後做待辦清單、記帳工具、個人網站互動,都會用同一套。
加料挑戰也要守規格。限制 7 次不是亂加一個變數而已,還要定義超過 7 次時遊戲怎麼結束。加入嘲諷語也不是每次都亂噴一句,而是要在太大、太小、接近答案時給不同回饋。你越會先把行為說清楚,AI 實習生越不容易自己亂發明。
const secret = 37;
const guesses = [20, 50, 37, 99];
let tries = 0;
for (const guess of guesses) {
tries = tries + 1;
if (guess < secret) {
console.log('第 ' + tries + ' 次:' + guess + ' 太小');
} else if (guess > secret) {
console.log('第 ' + tries + ' 次:' + guess + ' 太大');
} else {
console.log('第 ' + tries + ' 次:' + guess + ' 猜中,遊戲結束');
break;
}
}
照順序補上空格,每一步跑通才會解鎖下一步;最後把你的遊戲玩到猜中。
Step 1:產生秘密數字
把 Math.random 的 0 到 1 小數轉成 1 到 100 整數。
const sample = 0.42; const secret = Math.floor(sample * ) + ; console.log(secret);
Step 2:判斷太大太小
三條路徑都要對:太小、太大、猜中。
const answer = 42;
function checkGuess(guess) {
if (guess < answer) {
return '';
} else if (guess > answer) {
return '';
} else {
return '';
}
}
console.log(checkGuess(10));
console.log(checkGuess(99));
console.log(checkGuess(42));
Step 3:每猜一次就計次
次數不是記猜了什麼,而是每執行一次猜測流程就加 1。
let tries = 0;
function makeGuess(guess) {
tries = tries + ;
return '第 ' + tries + ' 次猜:' + guess;
}
console.log(makeGuess(30));
console.log(makeGuess(60));
Step 4:組裝成一局遊戲
猜中後要停止迴圈,遊戲才真的結束。
const secret = 37;
const guesses = [20, 50, 37];
let tries = 0;
for (const guess of guesses) {
tries = tries + 1;
if (guess < secret) {
console.log('太小');
} else if (guess > secret) {
console.log('太大');
} else {
console.log('第 ' + tries + ' 次猜中');
;
}
}
UI 版猜數字
範圍 1-100 · 有效猜測才計次 · 猜中後按鈕會停用
純 vibe coder 會把「幫我做猜數字」丟給 AI,然後祈禱結果能跑。你現在會先寫規格,列出要記住的狀態,再要求 AI 分步完成。拿到 code 後,你也能檢查亂數範圍、判斷方向、計次與結束條件。這些動作會直接降低 AI 腦補規則和藏 bug 的機率。
下一章預告
下一章我們會故意把程式弄壞,學會讀紅字、抓 bug,讓錯誤變成線索。