TicTacToe

From NSB App Studio
Revision as of 21:31, 11 April 2016 by Aelsayed (talk | contribs)
Jump to navigation Jump to search

In this tutorial, we walk you through making a Tic Tac Toe game from scratch. You will find this tutorial useful if you are new to programming and new to AppStudio. This tutorial uses JavaScript. You can download the source file for the completed app here File:Tictactoe.nsx

Let's start!

Like most games, we want to have a welcome page or a main menu page that takes us to the game. To do that, we can have multiple forms. A form is a container for controls such as buttons and drop down menus. We can use one form for our start page. Read more about forms here [1]

Start AppStudio. Go to the File menu then choose New Project. From now on, we will adopt the notation File > New Project to denote choosing the option New Project from the File Menu. Choose the language, and choose the form size. We chose the 320x460 size. When done, click "Create"

In the design window, you will see an empty form, called Form1. In the Project Explorer pane on the right, you will find Form1 under "Project properties and Global Code". Click on Form1. Under the Properties pane, you will find all the properties of the form listed. Note that the designHeight and designWidth properties are set to 460 and 320. Scroll down the properties list and find the "id" property under "Common". Change the form id to "frmStart". Note that the name of the form changed in the Project Explorer pane and the form tab.

Now let's start adding controls to the Start form. From the toolsbox, drag and drop a label in the middle of the form. Change the id property to lblStartMenu and set its TextContent to "Tic Tac Toe". Set the width, height and fontSize properties to suitable values.

Next, let us add a Play button. From the toolbox, find the jQuery button. Drag and drop the button to below the Tic Tac Toe label. Set its id property to btnPlay and its value to Play. Under the Project Explorer pane, you will find lblStartMenu and btnPlay under frmStart.

After setting up the Start form, we can now move to designing the Game form. Find the "Plus" sign next to the Start form tab. Click on it. This will create a new form, Form2. From the Properties window, change its id to frmPlay. In the Project Explorer pane, there are now two forms; frmStart and frmPlay.

Let's now design the Tic Tac Toe board. From the toolbox, drag and drop a jQuery button. Set its id to btn11 to denote the button in the first row, first column. Set its height and width to 70 to make it a square. Erase its value to remove any text. Set its corners to "Round" instead of Square.

Now right-click on the button from the design menu, and choose Copy. Right-click on the form and click Paste. In the Project Explorer pane, you will find two buttons under the form frmPlay. One with the name btn11 and the other with the name btn11Copy. Change its name to btn12 and place it to the right of btn11. To use the arrow keys to move the controls on the form, press alt + arrowKey.

Make more copies of the button to form a 3x3 board. Remember to change the names of the buttons. When you are done, you should have something like this.

Optionally, you can put the buttons into groups to give them the shape you see in the image above. To do so, set the groupBegin property of buttons btn11, btn12, btn13 to "Start vertical". And set the groupEnd property of buttons btn31, btn32, btn33 to "Yes".

Next, let's add a label to indicate which player's turn it is. Add a label to the Play form. Change its name to lblTurn. Set its initial textContent to "Player 1's turn".

Next, let's add some control buttons. Drag and drop three jQuery buttons below the Tic Tac Toe board. We chose to make them square buttons and to use icons instead of text to denote their usage. To choose an Icon, click on the Icon property from the properties pane and choose a suitable icon. If you don't want any text, set the IconPos property to "none". We used the leftmost button to get to the next turn. We use the middle button to reset the game to start from the beginning. And finally, the rightmost button to go back to the main menu. We set their ids to btnNext, btnReset and btnMenu, respectively. We choose the Icons as in the image below.

With that, we are done designing our app. But we have yet to program the controls.

Go to Run>Start in Desktop Browser to try out the app. Alternatively, you can click on the Run button shown below.

Your default web browser will open. You will see the Start Form. You can try clicking the Play button, but of course, clicking on the Play button won't take you to the game as we have not programmed any of the controls yet!

Go back to AppStudio. Right click on the Play button btnPlay and click on Go To Event>onclick. This will take you to the coding window of frmStart. You will also see the onclick function header. A function is a piece of code that executes when called or triggered. In this case, the event of clicking on a the Play button triggers this code. When clicking on this button, we want the app to take us to the Play form. We can simply write, ChangeForm(frmPlay). ChangeForm() is a function that AppStudio provides that allows us to switch from one form to another in Run time. You can tell that a code word is a function when it is followed by round brackets like in ChangeForm(). We use those brackets to pass in values that the function requires to execute. We call these values function parameters. For example, ChangeForm() requires the name of the function to change to. Thus, it takes in one parameter, a function name. In JavaScript, we follow the ChangeForm() line by a semicolon as shown in the picture below. A semicolon simply tells the computer "execute this command".

Deploy the app again. Now try clicking on the Play button. You should see that the app changes to the Play form!

Go back to AppStudio. Go to the Play form. Let us program the "Go to Main Menu" button. You should know how to do this! Simply, right-click on btnMenu, choose Go to Event>onclick. In the function btnMenu.onclick write ChangeForm(frmStart);

Deploy the app, test the Play and the Return to Main Menu buttons. You should be able to go back and forth between the two forms now.

We now want to program the buttons of the Tic Tac Toe board. Upon clicking any empty button, we want to write an X or an O on it, depending on whose turn is it to play. We need a way to keep track of which Player to play. For that, we create a global variable. At the top of the Coding tab of frmPlay, write the following line:

var player;

Let's understand what that means. A variable is a place holder. Upon declaring a variable, the computer reserves a space in memory and calls it "player". Every time you read or write to player, the computer access this place in memory, and modifies its value. For example, if you were to write

player = 1;

the computer accesses the place it had reserved for player in memory, and writes a 1 there. This place in memory will always have the value 1 until you modify it or close your program.

As for the world "global", a variable is global if it is declared outside of any function. That means that any function is able to access this place in memory. If you declare a variable inside a function, the variable is only "visibile" by that function; only that function is able to access that place in memory. Such variable would be called a local variable.

We can use the player variable to keep track of whose turn it is to play. Go back to the design window on frmPlay by clicking on the desing window Icon as shown below.

Right click on any empty space on the form and click on Go to Event>onshow. As evident by its name, this function executes when frmPlay loads. When the function first loads, we want it to be Player 1's turn. So we set the variable player to 1. We also want to make sure that initially, the Tic Tac Toe board is completely empty. So we set each button's value property to empty like so:

As you can see in the picture, the frmPlay.onshow function calls another function called init_board(). To understand the code in init_board(), first, note that the computer (or phone processor!) executes code sequentially. Meaning that it executes the first line in the function init_board() before moving on to the second line, then third, etc. In init board, we first set each of the buttons values to an empty text string. To change a property of a control through code, we first refer to the object name. Recall that we have set the button id's to btn11, btn12, ...., btn33. To set an object's property, first write its name, for example, btn11, followed by a ".". When you write a dot following an object's name, AppStudio understands that you are trying to access one of the function properties and it automatically shows a list of properties and functions you can call on that object. As you start typing the property name, only functions and properties that match what you typed appear on the list. If you're not sure about the name of the property, you can simply go through that menu to find it. If you're not sure what a property or function does, you can refer to NS Basic's Wiki pages pages. Here, we want to set the buttons' value property to an empty string. So we type: btn11.value = ""; where the empty double quotes refer to an empty string.

Next, we set the global variable player to 1. Ignore the next three lines for now. We will get to them later.

Now that we have a way to know which player's turn it is, we now want to program the board's blocks.

Think about what we want to do a player clicks on one of the buttons. When it is Player 1's turn, we want to allow him to choose where to make a move. He may click on a button to place an X there, change their mind and want to place it somewhere else before its Player 2's turn. Thus we want to allow the player change his mind, by re-clicking on the button he wishes to remove a move from. Of course, once the player clicks on the nextTurn button, he cannot change his mind! Neither can he change his mind on a move he's made on a previous turn!

For that reason, we want to create a set of variables that allow us to keep track of the state of the board, so we know which blocks already have moves, and which blocks are empty. At the top of the coding window, declare the variable, last_state. Unlike the player variable, we want last_state to hold the place of 9 variables, one for each block on the board.

In the function init_board, we initialize the variable last_state to an empty 3x3 array of variables like so:

last_state = [["","",""],["","",""],["","",""]];

This means that last state is a two dimensional array of variables: a 3x3 two-dimensional array. The line of code above places an empty string in each of the 9 positions, as initially, all the positions are empty. Each of the array variables can be addressed as shown in the image below. For example, the top left block is last_state[0][0]. The center block is last_state[1][1].

Note that we could have created 9 individual variables instead of one 3x3 array of variables, but this would have been tedious! Now, lets start programming the board blocks.

Right-click on btn11. Click on Go To Event> onclick. Write the following code, as shown in the picture below.

As you can see, the function btn11.onclick calls the function changeState(0,0,btn11). That is, it passes three parameters to it. If you look at the definition of the function changeState() you see that it takes three parameters: i, j, and btn. This means that when we call changeState from btn11.onclick, we set i = 0, j = 0, and btn = btn11.

Before we get to the code in changeState(), let's talk about if statements. If statements allow the computer/phone to make decision whether to execute a certain piece of code, given a condition. Each if statement comes with a condition. If the condition evalues to true, it executes the piece of code inside its brackets. If it is false, it executes the piece of code under else, if any exists. In the case of changeState()'s if statement, the condition is whether last_state[0][0] (that is the variable in the zero-th row, zero-th column in the 2d array last_state) is an empty string. Note the use of two equal signs, instead of one. In JavaScript, two equal signs are used to check equality, whereas one equal sign is used to change a variable's value.

In changeState() We check the last_state array at position 0,0 to make sure that it didn't already contain a move, as players cannot place moves on top of ech others moves! If the position is empty, we want to allow the player to toggle between placing a move at that position or removing a move from it! Of course, we will only allow the player to make at most one move at a time. But until they decide, we want to allow them to change their mind! If the button had an empty string, we place an X or an O, depending on which player is currently playing. And if it had an X or an O, we set its value to an empty string.

Make sure to call the function changeState in each of the 9 onclick functions of each of the buttons. Make sure to call changeState with the suitable parameters for each button. Note that arrays are 0-based, meaning that the block in the first row, in the first column is in position last_state[0][0]. The block to its right is at last_state[0][1], as shown in the picture below.

After a player is set on a move, they click on btnNext. This button should first check if the move that the player made was valid. If it was, it gives the turn to the next player. If not, it restores the board to the last valid state and asks the player to try again.

A move is invalid if the player makes more than one move at once or doesn't make any moves at all!

Before we get into the details of programming btnNext, we first need to discuss two ideas; addition, and return values of functions.

Suppose that in the function btnNext.onclick we have the following statements:

var count = 1; var g = 2;

which declare two local variables, count and g and initializes them to 1 and 2, respectively. Then, supposose we write the following statement:

var add = count + g;

What value do you think will be stored in the variable add? That's right, 3! Let's another example:

count = count + 1;

In maths, this statement would be meaningless, as you'd subtract count from both sides and be left with 0 = value! However, programming doesn't work like that! Equations in programming always go from right to left. That is, the expression on the right hand side is first evaluated and the result is stored in the variable on the left hand side. Thus, this statement simply adds one to the value that was previously stored in the variable count. In other words, we can rewrite the equation in pseudo code as follows:

new value of count = old value of count + 1

If count was initialized to 1, then the final value of count after executing this statement would be 2.

For that reason, as you might have already guessed, writing this statement as follows would be incorrect:

count + 1 = count;

Now that we understand how addition works, let's discuss the second concept: return values of functions.

As we have seen, functions are triggered by certain events or called by other functions. We already used code to call functions in frmPlay.show and in btnxx.onclick. Let's go into more detail about how that works.

Computers (or phone processors) execute code sequentially. That means that upon executing a function, the computer executes the first line in that function first before executing the next line. So what happens when a function calls another function?

Suppose function A calls function B, that each of these functions contain 10 lines of code, and that function A calls function B on line 2. Then the computer will start executing line 1 of function A, then line 2. In line 2, function B is called, so the computer goes to function B and executes lines 1-10 of function B before it returns to function A. Upon returning, it executes lines 3-10 of function A.

Sometimes functions have return values, which means that they return a value that they might have computed (or that they otherwise have access to) to the function that called them. In the above example, if function B has a return value, it returns it to function A. Function A can capture that value in a variable by calling function B as follows:

function A(){ //line 1 var result = B(); // lines 2-8.... }

In JavaScript, you can give a function a return value by using the keyword "return". For example, function B can look something like this

function B(){ return 1; }

From this code, we know that after the execution of line 2 of function A, the variable result will have the value 1. Return values are useful, for example, when the called function calculates a value that the caller function wants to use. For example, consider the following piece of code:

function A(){ var value = 5; var result; result = isBig(5); if(result == 1) NSB.Print("the value is bigger than 100!"); else NSB.Print("the value is smaller than 100!"); }

function isBig(number){ if(number < 100) return 0; else return 1; }

Now that we understand how return values of functions work, we can return to programming our "Next" button.

Right click on btnNext, click on Go To Event> onclick. Upon clicking on that button, we first want to find where the player placed his move. We create a global variable, newMove to store the value of the new move. We create a function find_new_move(). The purpose of this funcion is to where the new move was placed and to count how many new moves were placed. To make sure that only one move was made.

In find_new_move(), we write the following code. Initially, we create a local variable called count, which we initialize to 0. We then call another function, check_move() 9 times, for each of the buttons, to check if a move was made in that block. The function check_move returns a 1 if a new move was made in that block, and a 0 otherwise. We add the return value from check_move() to count. Recall how equations are evaluated in programming: first the right hand side expression is evaluated, then the result is saved in the left hand side of the equation. In this case, the function check_move() is executed, and its return value is saved in the variable count on the left hand side of the equation. After all 9 calls are executed, count would have the total number of moves made in total. find_new_move() can now return the value of count to its caller function btnNext.onclick. For btnNext.onclick to capture the return value of find_new_move(), we create a variable count. It is okay to call that variable count as well since it is local to the function btnNext.onclick and the other variable named count is local to the function find_new_move().

Now that we have the number of new moves made on the board. We need to take action. If the player made more than one move, we cannot accept that! We must inform the player that he'd made too many moves and remove his moves! If the player made no moves, we must inform him that he should make a move before getting its the next player's turn. If he made exactly one move, we record that move in our last_state array, we check if the player has won, if not, we give the other player the turn. See the function btnNext.onclick below

In reset_board() we reset remove all the moves the player made last. That is, we reset the board to the last valid state of the board saved in our last_state array. That is, we simply set the property value of the buttons to the last_state entry that corresponds to that button.

In player_won() we check if any player has won. That is, they placed three moves in the same row, column on diagonal. If so, we return true. If not, we return false. See the picture below.

Note the use of the "||" and "&&" operands. These are "OR" and "AND" operands used in if statements. For example, the first statement reads:

if last_state[0][0] (row 0, column 0) is not an empty string AND last_state[0][0] is equal to last_staet[0][1] (row 0, column 1) AND last_state[0][0] is equal to last_state[0][2], that means that the player has placed moves on all three blocks on the top row. In that case, the player wins, and we return 1 to indicate that the player has won. Because there are other ways to win, we list all of them, encapsulate each one by round brackets, and separate them by ORs.

Let's look at the if statement in btnNext.onclick. The if condition is evaluated by evaluating each of the expressions on the right and left hand sides of the operand "==" then checking if the equality holds. In this case, play_won() is executed and the return value of the function is compared to "false". If the return value is false, the code under the if statement is executed. Starting with next_turn().

As you might expect, next_turn() just updates the variable player and the label that says whose turn it is. See picture below.

Back to btnNext.onclick after the next_turn() function returns, we want to check if the all the blocks are full and there's a tie. To do that, we declare a global variable at the top of our coding file, we declare a variable moves_count. In our init_board function, we had initialized moves_count to 0. In btnNext.onclick, if the player has not won, we add one to moves_count. Note that following lines of code are equivalent:

moves_count++; moves_count += 1; moves_count = moves_count + 1;

finally we check if moves_count is 9. If so, and we know nobody has won, then it must be a tie! We inform the players that there's a tie, reset the board, and change to the Start form!

if however, player_won() returns true, the code under the if statement if(player_won() == false) will not execute, instead, the code under the else would execute: We inform the players which player won, we reset the board, and return to the start Form.

Return to btnMenu.onclick function, before we return to the Start form, we probably want to initialize the board, so we simply make a call to init_board() before the line ChangeForm(frmStart).

Now, we're just left with programming the middle button, btnReset. Right-click on btnReset Go to Event> onclick. Simply add one line. A call to init_board().

We are done now with programming the controls of our app. We can now play with the app! First try running the app on the browser. Try to find any bugs with the app and find where you might have made a mistake in the code.

When you've tested your code, you are finally ready to deploy your app! Click on Run>Deploy.

AppStudio will give you a link. Open your phone browser and enter the link you were given. Use the app on your phone!

For future apps, you can read about app publishing options here [2].