Saturday, April 17, 2021
  • Setup menu at Appearance » Menus and assign menu to Top Bar Navigation
Advertisement
  • AI Development
    • Artificial Intelligence
    • Machine Learning
    • Neural Networks
    • Learn to Code
  • Data
    • Blockchain
    • Big Data
    • Data Science
  • IT Security
    • Internet Privacy
    • Internet Security
  • Marketing
    • Digital Marketing
    • Marketing Technology
  • Technology Companies
  • Crypto News
No Result
View All Result
NikolaNews
  • AI Development
    • Artificial Intelligence
    • Machine Learning
    • Neural Networks
    • Learn to Code
  • Data
    • Blockchain
    • Big Data
    • Data Science
  • IT Security
    • Internet Privacy
    • Internet Security
  • Marketing
    • Digital Marketing
    • Marketing Technology
  • Technology Companies
  • Crypto News
No Result
View All Result
NikolaNews
No Result
View All Result
Home Technology Companies

Start coding with the Rust language

January 10, 2019
in Technology Companies
IBM RFE Community, Release 23
586
SHARES
3.3k
VIEWS
Share on FacebookShare on Twitter

Beginner’s Guide to Rust

Put your Rust skills to use by building a simple Tic Tac Toe
game

You might also like

How AI helps Overwatch League process 410M data points to build power rankings – IBM Developer

A brief intro to Red Hat OpenShift for Node.js developers – IBM Developer

IBM joins Eclipse Adoptium and offers free certified JDKs with Eclipse OpenJ9 – IBM Developer


Content series:

This content is part # of # in the series: Beginner’s Guide to Rust

https://www.ibm.com/developerworks/library/?series_title_by=**auto**

Stay tuned for additional content in this series.

This content is part of the series:Beginner’s Guide to Rust

Stay tuned for additional content in this series.

As I mentioned in the first part of this series, I really love Rust. This
statically compiled language is memory safe and operating system agnostic,
so it can run on any computer. Rust gives you the speed and low-level
benefits of a systems language without the pesky garbage collection of
languages like C# and Java.

There’s no better way to learn a language than to actually start using it. This article helps you put Rust to use by showing you how to build a simple
Tic-Tac-Toe
game using the language. Follow along to build your own fun game.

Prerequisites

Start by reading the first part of this series, Beginner’s Guide to Rust. I show you how to install and run Rust, describe its core
functionality, and introduce you to the concepts you need to get started.
In this article, I won’t be describing every facet of the language, so
you’ll need to get a handle on the language’s basics.

Start the project

First, you need to set up your project. You can use Cargo to create the new
executable binary program from the terminal:

$ cd ~/Documents
$ cargo new tic_tac_toe –bin

In the tree program, your new tic_tac_toe directory looks like this:

$ cd tic_tac_toe
$ tree .
.
??? Cargo.toml
??? src
    ??? main.rs

The main.rs file should consist of the following lines:

fn main() {
    println!("Hello, world!");
}

Running the program is just as easy as creating it, as Listing 1 shows.

Listing 1. Running “Hello, World!”
$ cargo build
    Compiling …
     Finished …
$ cargo run
     Finished …
      Running …
Hello, world!

Now, you also need a file for the game module. Create this file by
executing the following command line:

$ touch ./src/game.rs

With the project and directories setup, you can dive into outlining the
game.

Plan out the game with types and
structs

The classic Tic-Tac-Toe game consists of two main components: a
board and turns for each player. The board is essentially an empty 3×3
array, and the turns indicate which player must make a move. To translate
this functionality, you must edit the game.rs file you made in the last
section (see Listing 2).

Listing 2. Game.rs modified for a board and player
turns
type Board = Vec<Vec<String>>;

enum Turn {
    Player,
    Bot,
}

pub struct Game {
    board: Board,
    current_turn: Turn,
}

You may have noticed the odd syntax here, but don’t worry: I describe that
as we go.

The board

To translate the game board, you use the type keyword to alias
the name Board to be synonymous with the type
Vec<Vec<String>>. Now, Board is a
simple type for a two-dimensional vector of strings. I would use a char
here because the only values in the array will be x,
o, or a number indicating an open position.

The turns

A turn simply indicates which player must choose a spot, so an
enum structure works perfectly. On each turn, simply match
the Turn variant to make the appropriate method calls.

The game

Finally, you must create a Game object that holds the board
and the current turn being played. But wait! Where are the methods for the
Game struct? Never fear: That’s next.

Implement the game

What methods make up a Tic-Tac-Toe game? Well, there are turns. On
each turn, the board is displayed, a player makes a move, the board is
displayed again, and the win condition is checked. If the game was won,
the game announces which player won and asks him or her to play again. If
no one won the game, then the game switches the current player and plays
the next turn. Obviously, there are finer issues inside each move,
depending on the player, but you can just dive in from here.

First, you create a construction that is nested in an impl
block, as shown in Listing 3.

Listing 3. The game construction
impl Game {
    pub fn new() -> Game {
        let first_row = vec![
            String::from("1"), String::from("2"), 
            String::from("3")];

        let second_row = vec![
            String::from("4"), String::from("5"), 
            String::from("6")];

        let third_row = vec![
            String::from("7"), String::from("8"), 
            String::from("9")];

        Game {
            board: vec![first_row, second_row, third_row],
            current_turn: Turn::Player,
        }
    }
}

The static method new creates and returns a Game
struct. This is a standard name for an object constructor in Rust.

You must bind the board member variable with a 2d
vector of String objects. Instead of leaving each location
blank, notice that I filled them with a number indicating the available
positions for each move. Next, bind the current_turn member
variable to the value of Turn::Player. This line means that
every game has the player move first.

How do you play the game?

The first method serves as a map for the program. You add this method
inside the impl Grid block (along with the rest
of the methods in this section). Listing 4 shows the method.

Listing 4. A map of the game program
pub fn play_game(&mut self) {
    let mut finished = false;

    while !finished {
        self.play_turn();

        if self.game_is_won() {
            self.print_board();

            match self.current_turn {
                Turn::Player => println!("You won!"),
                Turn::Bot => println!("You lost!"),
            };

            finished = Self::player_is_finished();

            self.reset();
        }

        self.current_turn = self.get_next_turn();
    }
}

It’s easy to see the flow of the game. Using an infinite loop, you move
from one turn to the next, alternating the current_turn. For
this reason, you use a mutable borrow on self, because the
game’s internal state changes with each turn.

That enum is already paying off because if the game is won,
the information about who won the game is embedded. You then let the
player know that he or she either won or lost. In addition, you reset the
board to its original state, which is helpful if the user wants to play
again.

Notice that this will be the only pub method other than
new. This means that play_game and
new are the only methods that another library has access to
when using Game objects. All other methods, static or
otherwise, are private.

Turning the tides

The first helper method used in the play_game method is
play_turn. Listing 5 shows this nifty little function.

Listing 5. The play_turn function
fn play_turn(&mut self) {
    self.print_board();

    let (token, valid_move) = match self.current_turn {
        Turn::Player => (
            String::from("X"), self.get_player_move()),
        Turn::Bot => (
            String::from("O"), self.get_bot_move()),
    };

    let (row, col) = Self::to_board_location(valid_move);

    self.board[row][col] = token;
}

This one is tricky. First, you print the board so that the user knows which
positions are available (useful even when it’s the bot’s turn). Next,
depending on the variant of current_turn, you assign the
variables token and valid_move by using tuple
deconstruction and match.

token is either the String X or O
for the player or bot, respectively. valid_move is the
integer 1 through 9, who’s spot on the board isn’t occupied. This variable
is then converted to the respective row and column for the board, using
the to_board_location static method. (Self, with
a capital “S,” returns a type of self—in this case,
Game.)

Let’s see that board

Now that you have set up the play_turn, you need a method for
printing. Listing 6 shows that method.

Listing 6. Printing the game board
fn print_board(&self) {
    let separator = "+---+---+---+";

    println!("n{}", separator);

    for row in &self.board {
        println!("| {} |n{}", row.join(" | "), separator);
    }

    print!("n");
}

In this method, you use a for loop to print an ASCII
representation of the rows on the board. The temporary variable
row is a reference to each vector in the board. Using the
join method, you can turn row into a
String and print that new value with an appended separator
String.

With printing functionality now working, you can finally move on to getting
the valid moves for the player and the bot.

Player, it’s your turn

So far, this program is a series of hard-coded returns with no input from
the player. Listing 7 changes that.

Listing 7. Setting up turn taking
fn get_player_move(&self) -> u32 {
    loop {
        let mut player_input = String::new();

        println!(
            "nPlease enter your move (an integer between 
            1 and 9): ");

        match io::stdin().read_line(&mut player_input) {
            Err(_) => println!(
                "Error reading input, try again!"),
            Ok(_) => match self.validate(&player_input) {
                Err(err) => println!("{}", err),
                Ok(num) => return num,
            },
        }
    }
}

The heart of this method boils down to this: It loops infinitely unless the
player provides a valid move for the game.

The first match expression after the user prompt attempts to read a user’s
input into a String—player_input—and checks if
an error occurs in doing so. The io module provides this
functionality; you must import this module at the top of the
game.rs file. Its stdin().read_line method
(stdin() returns a handle object to the current standard
input). Here’s my import of the io module:

use std::io;

It is also important to note that the read_line method, while
mutating a given String, also returns an enum
called Result. I didn’t talk about Result in my
introductory article, so I touch on it next.

The Result enum

Result is what’s known as an algebraic type. It’s an
enum with two variants: Ok and Err.
Each variant can hold data, like String or i32.

In the read_line case, the Result returned is a
special version from the io module, which means that
Err is a special io::Error variant. In contrast,
Ok is the same as the original Result variant
and, in this case, holds an integer that represents the number of bytes
read. Result is a useful enum that helps ensure
that you’re handling all possible errors at compile time instead of
runtime.

Another sibling enum that’s pervasive in Rust is
Option. Instead of Ok and Err, its
variants are None (which holds no data) and Some
(which does). Option is useful in the way that
nullptr in C++ or None in Python is
useful.

What’s the difference between Option and Result
and when should you use them? Here are my go-to answers. First, if you
expect that a function can return nothing, then use Option.
Use Result for functions that you expect to succeed at all
times but that can fail, meaning that the error must be caught. Got it?
Great. Back to the get_player_move method.

Back to the game

I left off at reading the input from the player. If an error reading the
user’s input occurs, the program notifies the user and asks him or her for
another input. If no error occurs, then the program reaches the second
match expression. Notice the use of the underscores
(_): They tell Rust that you’re not binding the data inside
the Result‘s Ok or Err variants,
which you do in the second match expression.

This match expression checks if the player_input
variable is valid. If it isn’t, the code returns an error (which the game
alerts the player to), and asks the player for a valid input. If
player_input is valid, then that input, converted into an
integer using the validate method, is returned.

Validate your code

With the core of the game written, it’s a good to write a
validate function. Listing 8 shows
the code.

Listing 8. The validate function
fn validate(&self, input: &str) -> Result<u32, String> {
    match input.trim().parse::<u32>() {
        Err(_) => Err(
            String::from(
                "Please input a valid unsigned integer!")),
        Ok(number) => {
            if self.is_valid_move(number) {
                Ok(number)
            } else {
                Err(
                    String::from(
                        "Please input a number, between 
                        1 and 9, not already chosen!"))
            }
        }
    }
}

Running through this output line by line, here’s the gist of the
method.

First, the program is returning a Result enum. I haven’t
covered type templates, but basically, you’re stating that the
Ok variant of the Result must hold a
u32 integer and the Err variant must hold a
String. Why a Result return here? Well, the
method is expected to pass and throws an error only if the given input is:

  • Not an integer;
  • Not a valid location because of occupancy; or
  • Not a valid location because the integer isn’t 1–9.

Next, the program attempts to transform the input into a
u32 by using input‘s parse method.
The turbofish, ::<type> is a special aspect of some
functions that tells them what type to return. In this case, it’s
simultaneously telling parse to try to convert
input to a u32 and setting the
Result‘s Ok variant to hold a u32.
If input cannot be converted, the code returns an error
indicating that the input was not an unsigned integer.
However, if it’s successfully converted, then the code passes the
input through another helper function:
is_valid_move.

Why is there another helper function for validating? From the earlier list
of possible errors, number 1 is specific to the user. The bot will always
give an integer. That’s why you use validate only to validate
the player’s response. is_valid_move checks the other two
possible errors.

Listing 9 shows the last piece of the validation
code.

Listing 9. A bit more validation
fn is_valid_move(&self, unchecked_move: u32) -> bool {
    match unchecked_move {
        1...9 => {
            let (row, col) = Self::to_board_location(
                unchecked_move);

            match self.board[row][col].as_str() {
                "X" | "O" => false,
                 _ => true,
            }
        }
        _ => false,
    }
}

Simple enough. If the given unchecked_move is not between 1
and 9 (inclusive), then it’s not a valid move. Otherwise, the code is
forced to check whether the move has already been made. Like before in
play_turn, you transform unchecked_move into the
respective row and column on the board. You then can check if that
location is on the board. If the location is X or
O, then the move is invalid.

On to the bot

Before moving on to writing the method to get the bot’s move, create the
to_board_location static method that Listing 10 shows.

Listing 10. The to_board_location method
fn to_board_location(game_move: u32) -> (usize, usize) {
    let row = (game_move - 1) / 3;
    let col = (game_move - 1) % 3;

    (row as usize, col as usize)
}

This method is a bit of a cheat because you know that when
to_board_location is called in validate and
play_turn, the argument game_move is an integer
between 1 and 9 (inclusive). You set this method as static because the
math has no ties to a Game object. A Tic-Tac-Toe
board is always 3×3.

Chatter bot

Your code can get a move from a player, but consider the bot. First, the
bot’s move should be a random number, which means that you need to import
the third-party crate rand. Second, you keep generating this
random move until it reaches a valid location by using the
is_valid_move method. Then, the game must notify the player
what move the bot made and return the move.

You import and install that rand crate in a file called
Cargo.toml, with rand as a dependency. Listing 11 shows the file.

Listing 11. Cargo.toml
[package]
name = "tic_tac_toe"
version = "0.1.0"
authors = ["Dylan Hicks <dirtgrub.dylanhicks@gmail.com>"]

[dependencies]
rand = "0.4"

The main.js file tells Cargo that you want to use this dependency. I put
this command at the top of the file:

extern crate rand;

Then, put this command at the top of the game.rs file, above the
io import:

use rand;

With the rand crate to generate a random number, you need a
method to get a move from the bot. Listing 12
shows that method.

Listing 12. The bot_move method
fn get_bot_move(&self) -> u32 {
    let mut bot_move: u32 = rand::random::<u32>() % 9 + 1;

    while !self.is_valid_move(bot_move) {
        bot_move = rand::random::<u32>() % 9 + 1;
    }

    println!("Bot played moved at: {}", bot_move);

    bot_move
}

That was painless, right?

That method finishes off the play_turn method dependencies.
Now, you need to make a method to check if the game was won.

We are the champions

Now, you’re going to play a little fast and loose with the Boolean algebra
(Listing 13).

Listing 13. A bit of Boolean algebra
fn game_is_won(&self) -> bool {
    let mut all_same_row = false;
    let mut all_same_col = false;

    for index in 0..3 {
        all_same_row |= 
            self.board[index][0] == self.board[index][1]
            && self.board[index][1] == self.board[index][2];
        all_same_col |= 
            self.board[0][index] == self.board[1][index]
            && self.board[1][index] == self.board[2][index];
    }

    let all_same_diag_1 =
        self.board[0][0] == self.board[1][1] 
        && self.board[1][1] == self.board[2][2];
    let all_same_diag_2 =
        self.board[0][2] == self.board[1][1] 
        && self.board[1][1] == self.board[2][0];

        (all_same_row || all_same_col || all_same_diag_1 || 
         all_same_diag_2)
}

During the for loop, you simultaneously check the rows and
columns to see if the win condition for Tic-Tac-Toe has been met
(that is, three Xs or Os in a row). You do this with |=,
which is like +=, but instead of the addition operator it
uses the or operator. Then, you check if the two diagonals are all the
same character. Finally, you return whether any of the win conditions have
been met by using some Boolean algebra. Three more methods and you’re
done.

Would you like to play again?

If you go back and look at the play_game method in Listing 4, you see that the code keeps looping
until finished is true. This happens only if the
method player_is_finished is true. This method
should be based on the player’s response: either yes or no (Listing 14).

Listing 14. The player_is_finished method
fn player_is_finished() -> bool {
    let mut player_input = String::new();

    println!("Are you finished playing (y/n)?:");

    match io::stdin().read_line(&mut player_input) {
        Ok(_) => {
            let temp = player_input.to_lowercase();

            temp.trim() == "y" || temp.trim() == "yes"
        }
            Err(_) => false,
    }
}

When I originally wrote this method, I decided that it was best if I just
handled the “yes” case of the player’s input, meaning that all other input
returns false. Again, this is a static method because it has
no use for any of the data that self carries.

A hard reset fixes all

One of the last methods used in play_game is
reset, shown in Listing 15.

Listing 15. The reset method
fn reset(&mut self) {
    self.current_turn = Turn::Player;
    self.board = vec![
        vec![
            String::from("1"), String::from("2"),  
            String::from("3")],
        vec![
            String::from("4"), String::from("5"), 
            String::from("6")],
        vec![
            String::from("7"), String::from("8"), 
            String::from("9")],
    ];
}

All this method does is set the game’s member variables back to their
defaults.

The final method that you need to complete the game is
get_next_turn, shown in Listing 16.

Listing 16. The get_next_turn method
fn get_next_turn(&self) -> Turn {
    match self.current_turn {
        Turn::Player => Turn::Bot,
        Turn::Bot => Turn::Player,
    }
}

This method simply checks which turn self is on and returns
the opposite.

Run and compile the game

With the game.rs module finished, main.rs is now at the point that you can
compile and play the game (Listing 17).

Listing 17. Compile the game
extern crate rand;

mod game;

use game::Game;

fn main() {
    println!("Welcome to Tic-Tac-Toe!");

    let mut game = Game::new();

    game.play_game();
}

That’s it. You just declared that the game module exists in this project
with mod and brought the Game object into scope
with use. Then, you created a game object with
Game::new() and told the object to play the game. Now, run it
with Cargo (Listing 18).

Listing 18. Run the game
$ cargo run
   Compiling tic_tac_toe v0.1.0 …
    Finished dev [unoptimized + debuginfo] …
     Running …
Welcome to Tic-Tac-Toe!

+---+---+---+
| 1 | 2 | 3 |
+---+---+---+
| 4 | 5 | 6 |
+---+---+---+
| 7 | 8 | 9 |
+---+---+---+


Please enter your move (an integer between 1 and 9):
…

Final thoughts

As you learned throughout this tutorial, Rust is a versatile language that
has the ease of use of Java, C#, or Python but the speed and
power of C or C++. Not only is this code
compiled and fast, but all memory and error concerns are handled at
compile time instead of runtime, cutting down the human errors possible in
the code.

Next steps

  • To see the code I created for this article, please visit my
    GitHub repo

Downloadable resources

Credit: Source link

Previous Post

Alibaba Blinks: Building an open source, data-driven cloud empire in real-time

Next Post

Object.fromEntries

Related Posts

Six courses to build your technology skills in 2021 – IBM Developer
Technology Companies

How AI helps Overwatch League process 410M data points to build power rankings – IBM Developer

April 15, 2021
Six courses to build your technology skills in 2021 – IBM Developer
Technology Companies

A brief intro to Red Hat OpenShift for Node.js developers – IBM Developer

April 15, 2021
Six courses to build your technology skills in 2021 – IBM Developer
Technology Companies

IBM joins Eclipse Adoptium and offers free certified JDKs with Eclipse OpenJ9 – IBM Developer

April 14, 2021
Six courses to build your technology skills in 2021 – IBM Developer
Technology Companies

Every stroke matters – IBM Developer

April 14, 2021
Six courses to build your technology skills in 2021 – IBM Developer
Technology Companies

Day 1 inside the digital ops center – IBM Developer

April 10, 2021
Next Post
How to Change the WordPress Admin Login Logo

Object.fromEntries

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recommended

Plasticity in Deep Learning: Dynamic Adaptations for AI Self-Driving Cars

Plasticity in Deep Learning: Dynamic Adaptations for AI Self-Driving Cars

January 6, 2019
Microsoft, Google Use Artificial Intelligence to Fight Hackers

Microsoft, Google Use Artificial Intelligence to Fight Hackers

January 6, 2019

Categories

  • Artificial Intelligence
  • Big Data
  • Blockchain
  • Crypto News
  • Data Science
  • Digital Marketing
  • Internet Privacy
  • Internet Security
  • Learn to Code
  • Machine Learning
  • Marketing Technology
  • Neural Networks
  • Technology Companies

Don't miss it

ML Scaling Requires Upgraded Data Management Plan
Machine Learning

ML Scaling Requires Upgraded Data Management Plan

April 17, 2021
SolarWinds cybersecurity spending tops $3 million in Q4, sees $20 million to $25 million in 2021
Internet Security

SolarWinds: US and UK blame Russian intelligence service hackers for major cyberattack

April 17, 2021
Machine learning can be your best bet to transform your career
Machine Learning

Machine learning can be your best bet to transform your career

April 17, 2021
AI and Human Rights, A Story About Equality | by bundleIQ | Mar, 2021
Neural Networks

AI and Human Rights, A Story About Equality | by bundleIQ | Mar, 2021

April 17, 2021
Monitor Your SEO Placement with SEObase
Learn to Code

Monitor Your SEO Placement with SEObase

April 17, 2021
Google Project Zero testing 30-day grace period on bug details to boost user patching
Internet Security

Google Project Zero testing 30-day grace period on bug details to boost user patching

April 17, 2021
NikolaNews

NikolaNews.com is an online News Portal which aims to share news about blockchain, AI, Big Data, and Data Privacy and more!

What’s New Here?

  • ML Scaling Requires Upgraded Data Management Plan April 17, 2021
  • SolarWinds: US and UK blame Russian intelligence service hackers for major cyberattack April 17, 2021
  • Machine learning can be your best bet to transform your career April 17, 2021
  • AI and Human Rights, A Story About Equality | by bundleIQ | Mar, 2021 April 17, 2021

Subscribe to get more!

© 2019 NikolaNews.com - Global Tech Updates

No Result
View All Result
  • AI Development
    • Artificial Intelligence
    • Machine Learning
    • Neural Networks
    • Learn to Code
  • Data
    • Blockchain
    • Big Data
    • Data Science
  • IT Security
    • Internet Privacy
    • Internet Security
  • Marketing
    • Digital Marketing
    • Marketing Technology
  • Technology Companies
  • Crypto News

© 2019 NikolaNews.com - Global Tech Updates