ในการสร้างเกม Tic-Tac-Toe เราจะต้องตั้งค่า Rust บนเครื่องของเรา
ติดตั้งสนิมและสนิม
ขั้นแรก คุณจะต้อง "ติดตั้ง Rust" ฉันแนะนำให้ใช้ Rustup เพื่อติดตั้ง Rust ได้อย่างง่ายดายและอัปเดตอยู่เสมอ
รันคำสั่งนี้เพื่อติดตั้ง Rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
สิ่งนี้จะติดตั้ง Rustup และ Rust เวอร์ชันเสถียรล่าสุด
สร้างโครงการ Rust ใหม่ด้วย Cargo
เมื่อติดตั้ง Rust แล้ว เราสามารถสร้างโปรเจ็กต์ Rust ใหม่โดยใช้ Cargo ซึ่งเป็นตัวจัดการแพ็คเกจของ Rust
รันคำสั่งนี้:
cargo new tic-tac-toe
สิ่งนี้จะสร้างโฟลเดอร์ชื่อ tic-tac-toe
พร้อมไฟล์เริ่มต้นบางไฟล์ ไฟล์ที่สำคัญที่สุดสองไฟล์คือ:
src/main.rs
- นี่คือไฟล์ต้นฉบับหลักที่โค้ดของเราจะไปCargo.toml
- นี่คือไฟล์ Manifest ของ Cargo ที่ใช้จัดการการขึ้นต่อกัน ฯลฯ
เราทุกคนพร้อมแล้วและพร้อมที่จะเริ่มสร้างเกม Tic-Tac-Toe ใน Rust! ในส่วนถัดไป เราจะออกแบบโครงสร้างพื้นฐานของเกม
ออกแบบเกม
ในการออกแบบเกม Tic-Tac-Toe เราจำเป็นต้องกำหนดโครงสร้างและแจงนับบางส่วนเพื่อเป็นตัวแทนของกระดานและผู้เล่น
struct TicTacToe { board: Vec<Vec<char>>, next_player: char, }
- โครงสร้าง
TicTacToe
จะมีเวกเตอร์ 2 มิติเพื่อเป็นตัวแทนของบอร์ด รวมถึงช่องสำหรับติดตามผู้เล่นคนต่อไป (อาจเป็น 'X' หรือ 'O')
enum Player { X, O }
Player
enum จะแสดงถึงตาของผู้เล่นคนใด - 'X' หรือ 'O'
fn get_open_spots(board: &Vec<Vec<char>>) -> Vec<(usize, usize)> { // Logic to get a list of open spots on the board }
- ฟังก์ชัน
get_open_spots()
จะสแกนเวกเตอร์บอร์ดและส่งคืนรายการสิ่งอันดับของดัชนีแถวและคอลัมน์ที่เปิดอยู่ สิ่งนี้จะมีประโยชน์เมื่อให้ผู้เล่นย้ายเพื่อตรวจสอบอินพุต
นอกจากนี้เรายังต้องการให้ฟังก์ชันตรวจสอบการชนะ (3 รายการในแนวนอน แนวตั้ง หรือแนวทแยง) และตรวจสอบว่ามีการเสมอกันหรือไม่ (ไม่มีจุดเปิดบนกระดานอีกต่อไป) ฟังก์ชั่นเหล่านี้จะเป็นกุญแจสำคัญในการนำตรรกะของเกมไปใช้อย่างเต็มรูปแบบ
ด้วยโครงสร้างที่กำหนดเพื่อเป็นตัวแทนของกระดานเกม การแจงนับสำหรับผู้เล่น และฟังก์ชันเพื่อรับจุดเปิดและตรวจสอบผลลัพธ์ของเกม เราได้ออกแบบพื้นฐานของเกม Tic-Tac-Toe ใน Rust! ในส่วนถัดไป เราจะดูวิธีแสดงกระดานให้ผู้เล่นเห็น
แสดงกระดาน
เพื่อแสดงสถานะปัจจุบันของบอร์ด เราจะกำหนดฟังก์ชัน print_board()
:
fn print_board(board: &Vec<Vec<char>>) { for row in 0..3 { for col in 0..3 { print!("{} ", board[row][col]); } println!(); } }
ฟังก์ชั่นนี้จะวนซ้ำแต่ละแถวและคอลัมน์ของกระดานและพิมพ์อักขระที่ตำแหน่งนั้น (ทั้ง 'X', 'O' หรือ '_')
เราสามารถเรียกใช้ฟังก์ชันนี้หลังจากการเคลื่อนไหวแต่ละครั้งเพื่อพิมพ์สถานะบอร์ดที่อัปเดตสำหรับผู้เล่น:
// Make a move make_move(board, player); // Print updated board state print_board(&board);
สิ่งนี้จะช่วยให้มองเห็นกระดานได้ชัดเจนสำหรับผู้เล่นหลังจากแต่ละเทิร์น บอร์ดอาจมีลักษณะดังนี้:
_ X O X _ _ O _ _
เมื่อเกมดำเนินไปและมีการเคลื่อนไหวมากขึ้น สถานะของบอร์ดจะอัปเดตต่อไปสำหรับผู้เล่นจนกว่าจะมีคนได้ 3 อันติดต่อกันหรือเต็ม 9 ช่องทั้งหมด (เสมอกัน)
การแสดงสถานะของกระดานด้วยภาพเป็นส่วนสำคัญของการนำเกม Tic-Tac-Toe ไปใช้ ต่อไป เราจะดูการรับข้อมูลจากผู้เล่นเพื่อวาง X หรือ O บนกระดาน
รับอินพุตของผู้เล่น
หากต้องการรับข้อมูลจากผู้เล่นในการเคลื่อนไหว เราจะกำหนดฟังก์ชัน get_move()
มันจะแจ้งผู้เล่นที่ถึงเทิร์นสำหรับแถวและคอลัมน์ที่ต้องการวางเครื่องหมาย (X หรือ O)
fn get_move(board: &mut TicTacToe) -> (usize, usize) { println!("Player {}", board.next_player); let mut row = String::new(); let mut col = String::new(); io::stdin().read_line(&mut row) .expect("Failed to read line"); io::stdin().read_line(&mut col) .expect("Failed to read line"); let row: usize = row.trim().parse() .expect("Please enter a valid row number"); let col: usize = col.trim().parse() .expect("Please enter a valid column number"); (row, col) }
ฟังก์ชั่นนี้จะพิมพ์พรอมต์สำหรับผู้เล่นปัจจุบัน (X หรือ O) จากนั้นจะอ่านอินพุตสองบรรทัดจากคอนโซลสำหรับแถวและคอลัมน์ อินพุตจะถูกแยกวิเคราะห์จากสตริงเพื่อใช้จำนวนเต็มและส่งกลับเป็นสิ่งทูเพิล
เราตรวจสอบอินพุตเพื่อให้แน่ใจว่าผู้ใช้ป้อนตัวเลขที่ถูกต้องสำหรับแถวและคอลัมน์ หากป้อนข้อมูลไม่ถูกต้อง ข้อความแสดงข้อผิดพลาดจะถูกพิมพ์และผู้ใช้จะได้รับแจ้งอีกครั้ง
ฟังก์ชัน get_move() ถูกใช้ภายใน Game Loop ของเราเพื่อรับการเคลื่อนไหวถัดไปจากผู้เล่นปัจจุบันในแต่ละเทิร์น ด้วยการจัดการการตรวจสอบอินพุตและการจัดการข้อผิดพลาด เรามอบประสบการณ์ผู้ใช้ที่ราบรื่นสำหรับเกม Tic-Tac-Toe ของเรา
ตรวจสอบการชนะหรือเสมอกัน
เพื่อตรวจสอบว่ามีคนชนะเกมหรือจบลงด้วยการเสมอกัน เราจำเป็นต้องตรวจสอบสถานะของกระดานหลังแต่ละกระบวนท่า เราจะกำหนดฟังก์ชัน check_win()
เพื่อทำสิ่งนี้
fn check_win(board: &Vec<Vec<char>>) -> Option<char> { // Check for a win in the rows for row in 0..3 { if board[row][0] == board[row][1] && board[row][1] == board[row][2] { return Some(board[row][0]); } } // Check for a win in the columns for col in 0..3 { if board[0][col] == board[1][col] && board[1][col] == board[2][col] { return Some(board[0][col]); } } // Check for a win in the diagonals if board[0][0] == board[1][1] && board[1][1] == board[2][2] { return Some(board[0][0]); } if board[0][2] == board[1][1] && board[1][1] == board[2][0] { return Some(board[0][2]); } // Check for a tie let mut empty_spots = 0; for row in 0..3 { for col in 0..3 { if board[row][col] == ' ' { empty_spots += 1; } } } if empty_spots == 0 { return Some(' '); } // No winner or tie yet None }
ฟังก์ชั่นนี้จะตรวจสอบเงื่อนไขการชนะที่เป็นไปได้ทั้งหมด — แถว คอลัมน์ และเส้นทแยงมุม นอกจากนี้ยังนับจำนวนจุดว่างที่เหลืออยู่บนกระดานเพื่อตรวจสอบการเสมอกัน หากตรงตามเงื่อนไขการชนะ ระบบจะส่งคืนผู้เล่นที่ชนะ (Some('X')
หรือ Some('O')
) หากเต็มทุกจุด จะส่งกลับ Some(' ')
เพื่อระบุว่าเสมอกัน มิฉะนั้นจะส่งกลับ None
เพื่อแสดงว่าเกมควรดำเนินต่อไป
เราใช้ฟังก์ชันนี้หลังจากการเคลื่อนไหวแต่ละครั้งเพื่อตรวจสอบว่าเกมจบลงแล้วหรือไม่ หาก check_win()
ส่งกลับค่าใดค่าหนึ่ง เราสามารถแสดงข้อความแสดงความยินดีหรือผูกเน็คไทที่เหมาะสมแก่ผู้เล่นได้
เคลื่อนไหว
ในการดำเนินการ เราจะกำหนดฟังก์ชัน make_move
ที่รับแถวและคอลัมน์จากอินพุตของผู้เล่น และวาง X
หรือ O
บนกระดาน แล้วสลับผู้เล่นคนถัดไปหลังจากนั้น
fn make_move(&mut self, row: usize, col: usize) { assert!(row < 3 && col < 3, "Invalid move"); assert!(self.board[row][col] == '-', "Invalid move, space taken"); if self.next_player == 'X' { self.board[row][col] = 'X'; self.next_player = 'O'; } else { self.board[row][col] = 'O'; self.next_player = 'X'; } }
ก่อนอื่นเรายืนยันว่าการย้ายนั้นถูกต้องโดยตรวจสอบว่าแถวและคอลัมน์อยู่ในขอบเขตและพื้นที่ว่าง
จากนั้นเราวาง X
หรือ O
ไว้บนกระดาน ขึ้นอยู่กับตาของใคร เรายังเปลี่ยนฟิลด์ next_player
เป็นผู้เล่นอื่นด้วย เพื่อที่พวกเขาจะได้ไปต่อ
ฟังก์ชันนี้เปลี่ยนโครงสร้าง TicTacToe โดยการอัพเดตบอร์ดและฟิลด์ next_player ตอนนี้เรามีตรรกะทั้งหมดในการรับข้อมูล เคลื่อนไหว ตรวจสอบชัยชนะ และเล่นเกมต่อ!
ส่วนถัดไปจะใช้ Game Loop หลักเพื่อเชื่อมโยงทั้งหมดเข้าด้วยกัน
ห่วงเกม
เพื่อใช้งาน Game Loop หลัก เราจะ:
- กำหนดฟังก์ชัน
game_loop()
- มีวงวนที่ดำเนินต่อไปในขณะที่ไม่มีผู้ชนะหรือเสมอกัน
- ภายในวง:
- เรียกใช้ฟังก์ชัน
get_player_move()
เพื่อรับตัวเลือกแถวและคอลัมน์ของผู้เล่น - เรียกใช้ฟังก์ชัน
make_move()
เพื่อวางX
หรือO
บนกระดาน - เรียกใช้ฟังก์ชัน
check_for_win()
เพื่อตรวจสอบว่ามีผู้ชนะหรือเสมอกัน - หากส่งคืน
Some(X)
หรือSome(O))
เราก็ได้ผู้ชนะ! หลุดออกจากวงแล้วพิมพ์ข้อความแสดงความยินดี - หากส่งคืน
None
ให้ดำเนินการวนซ้ำต่อไป - สลับ
next_player
จากX
เป็นO
หรือกลับกันเพื่อเปลี่ยนรอบ
นี่คือตัวอย่างการใช้งาน:
fn game_loop() { loop { let (row, col) = get_player_move(); // Get player move make_move(row, col); // Make the move match check_for_win() { // Check if there is a winner or tie Some(player) => { // If there is a winner println!("Player {} wins!", player); break; } None => { // If no winner, continue the loop set_next_player!(); // Switch players } } } }
วงนี้จะดำเนินต่อไปจนกว่าจะมีผู้ชนะ จากนั้นจะมีการพิมพ์ข้อความแสดงความยินดีและออกจากวง หากไม่มีช่องสี่เหลี่ยมว่างเหลืออยู่ การเรียก check_for_win()
จะตัดสินว่าเสมอกัน และการวนซ้ำก็จะออกเช่นกัน
ข้อความแสดงความยินดี
เมื่อเรามีผู้ชนะหรือเสมอกัน เราต้องการแสดงข้อความที่เหมาะสมแก่ผู้เล่น เราสามารถเพิ่มตรรกะต่อไปนี้ให้กับ Game Loop ของเราได้:
let mut winner = None; loop { // Get player input and make move winner = check_for_win(&board); if winner == Some('X') || winner == Some('O') { break; } if board.iter().all(|row| row.iter().all(|&cell| cell == 'X' || cell == 'O')) { println!("It's a tie!"); break; } } match winner { Some('X') => println!("Congratulations X! You win!"), Some('O') => println!("Congratulations O! You win!"), None => {} // Do nothing, we already printed tie message }
เราตรวจสอบชัยชนะหลังจากการเคลื่อนไหวแต่ละครั้งโดยการเรียกใช้ฟังก์ชัน check_for_win()
ของเรา หากเรามีผู้ชนะ เราจะออกจากวงและพิมพ์ข้อความแสดงความยินดีโดยจับคู่กับผู้เล่นที่ชนะ มิฉะนั้น เราจะตรวจสอบว่าบอร์ดเต็มหรือไม่โดยวนซ้ำแถวและคอลัมน์ทั้งหมด - หากเป็นเช่นนั้น เราจะพิมพ์ข้อความผูกและออกจากลูป
นี่เป็นข้อความปิดท้ายที่ดีที่จะแสดงต่อผู้เล่นของเราเมื่อเกมจบลง ไม่ว่าจะเพราะชนะหรือเสมอกัน! ผู้ใช้จะรู้สึกถึงความสำเร็จเมื่อเห็นข้อความแสดงความยินดี และพอใจที่พวกเขาสามารถปฏิบัติตามและสร้างโปรแกรม Rust เพื่อเล่น Tic-Tac-Toe!
บทสรุป
ในบทความนี้ เราได้สร้างเกม Tic-Tac-Toe ใน Rust ตั้งแต่เริ่มต้น! เราเริ่มต้นด้วยการจัดตั้งโครงการ Rust ใหม่กับ Cargo จากนั้นเราออกแบบเกมโดยกำหนดโครงสร้าง TicTacToe
เพื่อแสดงสถานะของบอร์ดและเขียนฟังก์ชันเพื่อพิมพ์บอร์ด รับข้อมูลจากผู้เล่น ตรวจสอบชัยชนะ และทำการเคลื่อนไหว
struct TicTacToe { board: Vec<Vec<char>>, next_player: char }
เราสร้างเกมลูปเพื่อรับข้อมูลซ้ำแล้วซ้ำอีกและทำการเคลื่อนไหวจนกว่าจะชนะหรือเสมอกัน
loop { // Get input and make move check_win(); // Check for win or tie if let Some(winner) = check_win() { println!("{} is the winner!", winner); break; } if check_tie() { println!("It's a tie!"); break; } }
ในที่สุด เราได้เพิ่มตรรกะในการพิมพ์ข้อความแสดงความยินดีหากมีผู้ชนะ หรือข้อความการเสมอกันหากจบลงด้วยการเสมอกัน
ระบบประเภทที่เข้มงวด โมเดลการเป็นเจ้าของ และการมุ่งเน้นไปที่ความปลอดภัยทำให้เราสามารถสร้าง Tic-Tac-Toe ได้โดยไม่ต้องกังวลกับการเคลื่อนไหวที่ไม่ถูกต้อง การเข้าถึงนอกขอบเขต หรือปัญหานามแฝงที่ไม่แน่นอน ฉันหวังว่าสิ่งนี้จะช่วยให้คุณเห็นจุดแข็งของ Rust และเป็นแรงบันดาลใจให้คุณสร้างสิ่งที่สนุกสนานใน Rust!
โปรดปรบมือให้กับบทความนี้หากคุณชอบและพบว่าเนื้อหามีประโยชน์! ฉันขอขอบคุณข้อเสนอแนะหรือข้อเสนอแนะสำหรับหัวข้อในอนาคตเช่นกัน โปรดแจ้งให้เราทราบหากคุณมีคำถามอื่นๆ เกี่ยวกับการพัฒนาเกม Rust
ฉันหวังว่าบทความนี้จะเป็นประโยชน์กับคุณ! หากคุณพบว่ามีประโยชน์ โปรดสนับสนุนฉันด้วยการ 1) คลิกปรบมือ และ 2) แบ่งปันเรื่องราวไปยังเครือข่ายของคุณ โปรดแจ้งให้เราทราบหากคุณมีคำถามใดๆ เกี่ยวกับเนื้อหาที่ครอบคลุม
โปรดติดต่อฉันได้ที่ coderhack.com(at)xiv.in