TicTacToe: Difference between revisions
Line 150: | Line 150: | ||
When done, delete the code lines under the function btn11.onclick(). | When done, delete the code lines under the function btn11.onclick(). | ||
==== The function init_board() ==== | ===== The function init_board() ===== | ||
Now that we understand what strings are, let's go back to resetting the Tic Tac Toe board before the game starts. | Now that we understand what strings are, let's go back to resetting the Tic Tac Toe board before the game starts. |
Revision as of 15:14, 27 April 2016
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
Installing AppStudio
If you haven't already installed AppStudio, download it from here [1] and follow the installation instructions here [2].
Creating a New Project
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.
We're assuming someone has downloaded and installed AppStudio onto your system already. If not, visit the website to get it.
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 (JavaScript), and choose the form size. We chose the 320x460 size, so it will fit on a phone. When done, click "Create". You'll see something like this:
In the Design Screen (5), you will see an empty form, called Form1. In the Project Explorer (6) on the right, you will find Form1 under "Project properties and Global Code". Click on Form1. Under the Properties Grid (7), 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 and the form tab.
Designing the Start Page
Now let's start adding controls to the Start form. From the Toolbox (4), drag and drop a Label in the middle of the Design Screen (5). In the Properties Grid (7), 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 (4), find Button under jQuery Mobile. 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 (6), 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 Grid (7), change its id to frmPlay. In the Project Explorer (6), there are now two forms; frmStart and frmPlay.
Designing the Game Form
Let's now design the Tic Tac Toe board. From the toolbox, drag and drop another 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 like so:
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 Mobile 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 Grid (7) 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.
Programming the Controls
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!
Programming the buttons on the Start Page
Go back to AppStudio. Right click on the Play button btnPlay and click on Go To Event>onclick. This will take you to the Code 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 form 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 "end of the command".
Deploy the app again. Now try clicking on the Play button. You should see that the app changes to the Play form!
Programming the buttons on 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.
Programming the Tic Tac Toe board buttons
Global and Local Variables
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 (example: the var statement declaring the variable "player" above), the computer reserves a space in memory and calls it "player". Every time you modify the value of the variable 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 word "global", a variable is global if it is declared outside of any function with a var statement. It is also global if it is not declared at all. This means that any function is able to access this place in memory. If you declare a variable inside a function, the variable is only "visible" to that function; only that function is able to access that place in memory. Such variable would be called a local variable.
Resetting the Board before a New Game
We can now use the player variable to keep track of whose turn it is to play. At the start of a new game, we want to reset our board and game variables. For example, we want to clear any moves that might be there from the last game and reset the player variable to 1.
Characters and Strings
But before we do that let's talk about characters and strings. A character is any symbol, such as a letter, a number, punctuation marks, or other characters on the ASCII chart (see the full chart here [3]). A string is a sequence of characters. In code, we tell the computer that a certain sequence of characters is a string (as opposed to a variable name) by enclosing it in double quotation marks. For example the variable str has a string value "app":
var str = "app";
Go back to the Design Screen (5) on frmPlay by clicking on the Design Screen icon as shown below.
Click on any of the Tic Tac Toe board's buttons, say btn11. Look at the properties window. You can see that the property Height has a numerical value: 70. Scroll down the properties list and find the value property. This property is expecting a string value. Set the value property to the letter X. You see that the letter X appears on the button in your design screen.
But what if we only want the X to appear when the button is clicked? We can do that by editing the value property of btn11 through code. Go back to the properties pane of btn11 and delete the letter X from the value property field.
Right click on btn11 Go To Event>onclick.
Type the following line:
btn11.value = "app";
As you might have guessed, this line tells the computer to change the value of the Value property to the string "app". Generally, if we want to edit an object's properties through code, we must first tell the computer which object we want edit. Recall that we have set the button id's to btn11, btn12, ...., btn33. Here, we want to edit btn11's Value property, so we type btn11, followed by a ".". When you type a dot following an object's name, AppStudio understands that you are trying to edit one of the object's properties and it automatically shows a list of properties the object has. As you start typing the property name, only functions and properties that match what you've typed appear on the list. If you're not sure about the name of a 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 AppStudio's Wiki pages pages.
After you've typed the line above, try running the app by clicking on the Run button. In your web browser, click on btn11. The word "app" should appear on the button when clicked.
Go back to the code, and remove the quotation marks around the word app:
btn11.value = app;
Try running the app again and clicking on btn11. You probably got an error message: Uncaught ReferenceError: app is not defined. This is because the computer no longer doesn't understand that the word app is a string. It thinks it is a variable name because there are no quotation marks around the word app. But we never gave a variabe with the name app any value! So the computer doesn't know what to set btn11's Value property to.
Go back to the code, and now add the following line:
var app = "app"; btn11.value = app;
What do you expect to happen? Now we have a variable with the name app. This variable has a string value "app". On the next line, we set btn11's value to the value of the variable app; that is "app". Try running the code. Upon clicking on btn11, the string app should appear on the button! Of course, we could have set the value of the variable app to any string we want!
var app = "hello, there! 1234"; btn11.value = app;
Re-run the app to see the results. When done, delete the code lines under the function btn11.onclick().
The function init_board()
Now that we understand what strings are, let's go back to resetting the Tic Tac Toe board before the game starts.
Switch to the Design Screen (5). 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 appears. For example, in our app, when we click on the Play button from frmStart, frmPlay appears and the function frmPlay.onshow() will be executed automatically. When the frmPlay appears, it is the start of a new game and we want it to be Player 1's turn. So we set the variable player to 1. We also want to make sure that the Tic Tac Toe board is completely empty. That is, if there are any X's and O's on the board, we want to replace them with empty strings. So we set each button's value property to an empty string 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.
Next, we set the global variable player to 1. Ignore the next three lines for now. We will get to them later.
To test this piece of code, go to the Design Screen (5). Click on btn11 and change it's property Value to "xxx" through the Properties pane. Run the app, click on btnStart. You might be able to catch the value of btn11 changing from "xxx" to no text. You now know when the function frmPlay.onshow() executes and you know that it resets our board. Go back to the design screen, and delete the "xxx" from btn11's Value property.
Now that we have a way to know which player's turn it is, and we have reset our board, we now want to program the board's blocks.
Keeping track of moves on the board
Think about what we want to do when 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.
X's and O's
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. Recall 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.
Next Player's turn
After a player is happy with the move they made, 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.
Addition
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 math, 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;
Return Values of Functions
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.
Recall that 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().
Handling invalid moves
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.
Has anyone won the game yet?
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 first 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.
What if it's a tie?
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().
Test your app!
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 [4].