Untuk membuat game Tic-Tac-Toe ini, kita perlu menyiapkan Rust di mesin kita.

Instal Rust dan Rustup

Pertama, Anda harus “menginstal Rust”. Saya sarankan menggunakan Rustup untuk menginstal Rust dengan mudah dan selalu memperbaruinya.

Jalankan perintah ini untuk menginstal Rustup:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Ini akan menginstal Rustup dan Rust versi stabil terbaru.

Buat proyek Rust baru dengan Cargo

Sekarang Rust sudah terinstal, kita dapat membuat proyek Rust baru menggunakan Cargo, manajer paket Rust.

Jalankan perintah ini:

cargo new tic-tac-toe

Ini akan membuat folder bernama tic-tac-toe dengan beberapa file starter. Dua file terpenting adalah:

  • src/main.rs - Ini adalah file sumber utama tempat kode kita akan ditempatkan
  • Cargo.toml - Ini adalah file manifes Cargo yang digunakan untuk mengelola dependensi, dll.

Kami sudah siap dan siap untuk mulai membuat game Tic-Tac-Toe di Rust! Di bagian selanjutnya kita akan merancang struktur dasar permainan.

Rancang permainannya

Untuk merancang permainan Tic-Tac-Toe, kita perlu mendefinisikan beberapa struct dan enum untuk mewakili papan dan pemain.

struct TicTacToe {
    board: Vec<Vec<char>>,
    next_player: char, 
}
  • Struct TicTacToe akan berisi vektor 2D untuk mewakili papan, serta bidang untuk melacak pemain berikutnya (baik 'X' atau 'O').
enum Player {
    X,
    O 
}
  • Player enum akan mewakili giliran pemain mana - 'X' atau 'O'.
fn get_open_spots(board: &Vec<Vec<char>>) -> Vec<(usize, usize)> {
    // Logic to get a list of open spots on the board
}
  • Fungsi get_open_spots() akan memindai vektor papan dan mengembalikan daftar tupel indeks baris dan kolom terbuka. Ini akan berguna ketika pemain bergerak untuk memvalidasi input.

Kami juga menginginkan fungsi untuk memeriksa kemenangan (3 berturut-turut secara horizontal, vertikal atau diagonal) dan untuk menentukan apakah ada hasil seri (tidak ada lagi tempat terbuka di papan). Fungsi-fungsi ini akan menjadi kunci untuk mengimplementasikan logika permainan secara penuh.

Dengan struct yang didefinisikan untuk mewakili papan permainan, enum untuk para pemain, dan berfungsi untuk mendapatkan tempat terbuka dan memeriksa hasil permainan, kami telah merancang dasar-dasar permainan Tic-Tac-Toe di Rust! Di bagian selanjutnya, kita akan melihat cara menampilkan papan kepada pemain.

Perlihatkan papannya

Untuk menampilkan status papan saat ini, kita akan mendefinisikan fungsi print_board():

fn print_board(board: &Vec<Vec<char>>) {
    for row in 0..3 {
        for col in 0..3 {
            print!("{} ", board[row][col]);
        }
        println!();
    }
}

Fungsi ini akan mengulangi setiap baris dan kolom papan dan mencetak karakter pada posisi tersebut (baik 'X', 'O' atau '_').

Kita dapat memanggil fungsi ini setelah setiap gerakan untuk mencetak status papan yang diperbarui untuk para pemain:

// Make a move
make_move(board, player);

// Print updated board state
print_board(&board);

Ini membantu memberikan pandangan yang jelas tentang papan bagi para pemain setelah setiap giliran. Papannya mungkin terlihat seperti ini:

_ X O 
X _ _
O _ _

Saat permainan berlangsung dan semakin banyak gerakan yang dilakukan, status papan akan terus diperbarui untuk para pemain sampai seseorang mendapatkan 3 berturut-turut atau semua 9 kotak terisi (seri).

Memberikan representasi visual tentang keadaan papan adalah bagian penting dari setiap implementasi permainan Tic-Tac-Toe. Selanjutnya, kita akan melihat masukan dari para pemain untuk memberi tanda X atau O di papan.

Dapatkan Masukan Pemain

Untuk mendapatkan masukan dari pemain untuk pergerakan mereka, kita akan mendefinisikan fungsi get_move(). Ini akan meminta pemain yang mendapat giliran untuk baris dan kolom di mana mereka ingin menempatkan penandanya (X atau 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)
}

Fungsi ini mencetak prompt untuk pemutar saat ini (X atau O). Kemudian membaca dua baris masukan dari konsol untuk baris dan kolom. Input diurai dari string untuk menggunakan bilangan bulat dan dikembalikan sebagai tupel.

Kami memvalidasi input untuk memastikan pengguna memasukkan nomor yang valid untuk baris dan kolom. Jika input yang dimasukkan tidak valid, pesan kesalahan akan dicetak dan pengguna diminta kembali.

Fungsi get_move() digunakan dalam game loop kita untuk mendapatkan langkah selanjutnya dari pemain saat ini di setiap giliran. Dengan menangani validasi input dan penanganan kesalahan, kami memberikan pengalaman pengguna yang lancar untuk game Tic-Tac-Toe kami.

Periksa kemenangan atau seri

Untuk menentukan apakah seseorang memenangkan permainan atau berakhir seri, kita perlu memeriksa status papan setelah setiap gerakan. Kami akan mendefinisikan fungsi check_win() untuk melakukan ini.

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 
}

Fungsi ini memeriksa semua kemungkinan kondisi kemenangan — baris, kolom, dan diagonal. Ini juga menghitung jumlah tempat kosong yang tersisa di papan untuk memeriksa hasil seri. Jika ada kondisi menang yang terpenuhi, maka pemain yang menang akan dikembalikan (Some('X') atau Some('O')). Jika semua tempat terisi, ia mengembalikan Some(' ') untuk menunjukkan seri. Jika tidak, ia akan mengembalikan None untuk menunjukkan bahwa permainan harus dilanjutkan.

Kami menggunakan fungsi ini setelah setiap gerakan untuk memeriksa apakah permainan telah berakhir. Jika check_win() mengembalikan nilai tertentu, kami dapat menampilkan pesan ucapan selamat atau ikatan yang sesuai kepada para pemain.

Bergerak

Untuk bergerak, kita akan mendefinisikan fungsi make_move yang mengambil baris dan kolom dari masukan pemain dan menempatkan X atau O di papan, mengganti pemain berikutnya setelahnya.

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';
    }
}

Pertama-tama kita tegaskan bahwa perpindahan tersebut valid dengan memeriksa apakah baris dan kolom berada dalam batas dan ruangnya kosong.

Kemudian kita letakkan X atau O di papan tergantung giliran siapa. Kami juga mengalihkan bidang next_player ke pemain lain sehingga mereka akan melanjutkan.

Fungsi ini mengubah struct TicTacToe dengan memperbarui bidang board dan next_player. Sekarang kita memiliki semua logika untuk mendapatkan masukan, melakukan gerakan, memeriksa kemenangan, dan melanjutkan permainan!

Bagian selanjutnya akan mengimplementasikan game loop utama untuk menyatukan semuanya.

Lingkaran permainan

Untuk mengimplementasikan game loop utama, kami akan:

  • Tentukan fungsi game_loop()
  • Miliki putaran yang berlanjut sementara tidak ada pemenang atau seri
  • Di dalam lingkaran:
  • Panggil fungsi get_player_move() untuk mendapatkan pilihan baris dan kolom pemain
  • Panggil fungsi make_move() untuk menempatkan X atau O di papan
  • Panggil fungsi check_for_win() untuk memeriksa apakah ada pemenang atau seri
  • Jika Some(X) atau Some(O)) dikembalikan, kami memiliki pemenang! Keluar dari lingkaran dan cetak pesan ucapan selamat
  • Jika None dikembalikan, lanjutkan perulangan
  • Ganti next_player dari X ke O atau sebaliknya untuk berpindah belokan

Berikut ini contoh penerapannya:

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
            }
        }
    }
}

Perulangan ini akan berlanjut hingga ada pemenang, dan pada saat itulah pesan ucapan selamat dicetak dan perulangan keluar. Jika tidak ada lagi kotak kosong yang tersisa, pemanggilan check_for_win() akan menentukan hasil seri, dan perulangan juga akan keluar.

Pesan ucapan selamat

Setelah kami mendapatkan pemenang atau seri, kami ingin menampilkan pesan yang sesuai kepada para pemain. Kita dapat menambahkan logika berikut ke game loop kita:

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 
}

Kami memeriksa kemenangan setelah setiap gerakan dengan memanggil fungsi check_for_win() kami. Jika kita memiliki pemenang, kita keluar dari loop dan mencetak pesan ucapan selamat dengan mencocokkan pemain yang menang. Jika tidak, kami memeriksa apakah papan sudah penuh dengan mengulangi semua baris dan kolom - jika demikian, kami mencetak pesan dasi dan keluar dari loop.

Ini memberi kami pesan akhir yang bagus untuk ditampilkan kepada pemain kami setelah pertandingan selesai, baik karena menang atau seri! Pengguna akan merasakan pencapaian melihat pesan ucapan selamat mereka, dan puas karena dapat mengikuti dan membuat program Rust untuk memainkan Tic-Tac-Toe!

Kesimpulan

Pada artikel ini, kami membuat game Tic-Tac-Toe di Rust dari awal! Kami memulai dengan menyiapkan proyek Rust baru dengan Cargo. Kemudian kami merancang permainan dengan mendefinisikan TicTacToe struct untuk mewakili status papan dan menulis fungsi untuk mencetak papan, mendapatkan masukan pemain, memeriksa kemenangan, dan melakukan gerakan.

struct TicTacToe {
    board: Vec<Vec<char>>,
    next_player: char 
}

Kami membuat game loop untuk berulang kali mendapatkan masukan dan melakukan gerakan hingga terjadi kemenangan atau seri.

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;
    }
}

Terakhir, kami menambahkan logika untuk mencetak pesan ucapan selamat jika ada pemenang atau pesan seri jika berakhir seri.

Sistem tipe Rust yang ketat, model kepemilikan, dan fokus pada keselamatan memungkinkan kami membuat Tic-Tac-Toe tanpa mengkhawatirkan perpindahan yang tidak valid, akses di luar batas, atau masalah aliasing yang dapat berubah. Saya harap ini memberi Anda gambaran sekilas tentang kekuatan Rust dan menginspirasi Anda untuk membangun sesuatu yang menyenangkan di Rust!

Silakan bertepuk tangan untuk artikel ini jika Anda menikmatinya dan merasa kontennya bermanfaat! Saya juga menghargai masukan atau saran untuk topik mendatang. Beri tahu saya jika Anda memiliki pertanyaan lain tentang pengembangan game Rust.

Saya harap artikel ini bermanfaat bagi Anda! Jika menurut Anda ini bermanfaat, silakan dukung saya dengan 1) klik beberapa tepuk tangan dan 2) bagikan cerita ini ke jaringan Anda. Beri tahu saya jika Anda memiliki pertanyaan tentang konten yang dicakup.

Jangan ragu untuk menghubungi saya di coderhack.com(at)xiv.in