Monday, 12 August 2019

Web Tutorial: Tic-tac-toe (Part 2/2)

Now, we're going to handle placing "x" or "o" on the board. Modify the HTML. Each square should have the takeTurn() method set to run upon being clicked, with the number of the square and "o" passed in as arguments.
            <div id="board_container">
                <div id="square0" class="square" onclick="user.takeTurn(0, 'o');"></div>
                <div id="square1" class="square" onclick="user.takeTurn(1, 'o');"></div>
                <div id="square2" class="square" onclick="user.takeTurn(2, 'o');"></div>
                <div id="square3" class="square" onclick="user.takeTurn(3, 'o');"></div>
                <div id="square4" class="square" onclick="user.takeTurn(4, 'o');"></div>
                <div id="square5" class="square" onclick="user.takeTurn(5, 'o');"></div>
                <div id="square6" class="square" onclick="user.takeTurn(6, 'o');"></div>
                <div id="square7" class="square" onclick="user.takeTurn(7, 'o');"></div>
                <div id="square8" class="square" onclick="user.takeTurn(8, 'o');"></div>
            </div>


The method itself, of course, will cater to "x" as well. These If blocks are to ensure that it is the appropriate turn and that the game is started. We can't have "x" taking a turn when it's "o"'s turn, for instance. Also, that particular square on the board array must not be occupied, i.e, the value property has to be an empty string.
                takeTurn: function(square, mark)
                {
                    if (mark == game.turn && game.started)
                    {
                        if (game.board[square].value == "")
                        {

                        }
                    }
                },


First, set the value to whatever is appropriate ("x" or "o") determined by the value of mark.
                takeTurn: function(square, mark)
                {
                    if (mark == game.turn && game.started)
                    {
                        if (game.board[square].value == "")
                        {
                            game.board[square].value = mark;
                        }
                    }
                },


Then iterate through the entire winPatterns array and set the appropriate values as well. For instance, if we're setting the square a to "x", then this needs to be set for anything in winPatterns where the square is "a". This makes it easy to check later if victory conditions are met.
                takeTurn: function(square, mark)
                {
                    if (mark == game.turn && game.started)
                    {
                        if (game.board[square].value == "")
                        {
                            game.board[square].value = mark;

                            for (var i = 0; i < game.winPatterns.length; i++)
                            {
                                for (var j = 0; j < game.winPatterns[i].length; j++)
                                {
                                    if (game.winPatterns[i][j].square == game.board[square].square)
                                    {
                                        game.winPatterns[i][j].value = mark;
                                    }  
                                }
                            }
                        }
                    }
                },


And then we modify the class of the appropriate square, adding the value of mark as a CSS class.
                takeTurn: function(square, mark)
                {
                    if (mark == game.turn && game.started)
                    {
                        if (game.board[square].value == "")
                        {
                            game.board[square].value = mark;

                            for (var i = 0; i < game.winPatterns.length; i++)
                            {
                                for (var j = 0; j < game.winPatterns[i].length; j++)
                                {
                                    if (game.winPatterns[i][j].square == game.board[square].square)
                                    {
                                        game.winPatterns[i][j].value = mark;
                                    }  
                                }
                            }

                            document.getElementById("square" + square).className = "square " + mark;
                        }
                    }
                },


Let's modify the CSS some more. While the cursor property for square is pointer, we want to hammer home the point that any occupied square is not clickable (notice that in the takeTurn() method, clicking does nothing if the square is not empty), so we set the cursor property to default.
            .square
            {
                width: 100px;
                height: 100px;
                margin-right: 10px;
                margin-bottom: 10px;
                float: left;
                outline: 1px solid #444444;
                background-color: #FFFFFF;
                cursor: pointer;
            }

            .x, .o
            {
                cursor: default;
            }

            #user_x_container
            {
                color: #FF4400;
            }


Use the before pseudoselector for x and o. We basically want it to fill up the inside of its parent, with maybe some padding. Font properties should be set to personal taste.
            .x, .o
            {
                cursor: default;
            }

            .x:before, .o:before
            {
                display: block;
                width: 80%;
                height: 80%;
                margin: 10% auto 0 auto;
                font-weight: bold;
                font-size: 3em;
                text-align: center;
            }

            #user_x_container
            {
                color: #FF4400;
            }


For x, the content is "X" and the color is orange. For o, content is "O" and color is green.
            .x, .o
            {
                cursor: default;
            }

            .x:before, .o:before
            {
                display: block;
                width: 80%;
                height: 80%;
                margin: 10% auto 0 auto;
                font-weight: bold;
                font-size: 3em;
                text-align: center;
            }

            .x:before
            {
                content: "X";
                color: #FF4400;
            }

            .o:before
            {
                content: "O";
                color: #44FF00;
            }

            #user_x_container
            {
                color: #FF4400;
            }


Go on, click on an empty square. An "O" should appear! Once it appears, mouse over that square and you should see your mouse cursor go back to default.


Back to the myTurn() method. We've established what happens when player "o" (the user) takes his or her turn. But what happens when the player "x" (the computer) takes its turn? We first set up a delay of one second.
                myTurn: function()
                {
                    document.getElementById("user_x_text").innerHTML = "";
                    document.getElementById("user_o_text").innerHTML = "";
                    document.getElementById("user_" + game.turn + "_text").innerHTML = "My turn!";

                    if (game.turn == "x")
                    {
                        setTimeout
                        (
                            function()
                            {

                            },
                            1000
                        )  
                    }
                }


Then we declare randomBlankSquare and set it to the returned value of the getRandomBlankSquare() method of the game object. Let's not bother about that method for now; but the plan is, it should return an integer which is the index of any one of the elements in the board array that has not yet been filled. And if all squares are filled, it returns null.
                        setTimeout
                        (
                            function()
                            {
                                var randomBlankSquare = game.getRandomBlankSquare();

                                if (randomBlankSquare == null)
                                {

                                }
                                else
                                {

                                }
                            },
                            1000
                        )  


If all squares are filled, display a message in the appropriate user's text box signifying that it's a draw. And set the started property of the game object to false to signify that the game is over.
                        setTimeout
                        (
                            function()
                            {
                                var randomBlankSquare = game.getRandomBlankSquare();

                                if (randomBlankSquare == null)
                                {
                                    document.getElementById("user_" + game.turn + "_text").innerHTML = "Looks like it's a draw.";
                                    game.started = false;
                                }
                                else
                                {

                                }
                            },
                            1000
                        )


And if there's a blank square, run the takeTurn() method of the user object, passing in the value of randomBlankSquare and "x". Which basically means player "x" places the mark on that square.
                        setTimeout
                        (
                            function()
                            {
                                var randomBlankSquare = game.getRandomBlankSquare();

                                if (randomBlankSquare == null)
                                {
                                    document.getElementById("user_" + game.turn + "_text").innerHTML = "Looks like it's a draw.";
                                    game.started = false;
                                }
                                else
                                {
                                    user.takeTurn(randomBlankSquare, "x");
                                }
                            },
                            1000
                        )


Wait... what happens if all squares are filled when it's player "o"'s turn?

Ah, I see the gears are turning! Logically, that never happens. The draw condition only happens when all 9 squares are filled. And that means 9 turns have to be taken. Player "o" always starts first... so if all 9 squares are filled, player "o" is always the one filling in the last square.

Back to the game!

We're going to fill in the getRandomBlankSquare() method. First, declare blanks. Set blanks to the array returned when you run the filter() method on the board array, returning only objects whose value properties are empty.
                getRandomBlankSquare: function()
                {
                    var blanks = this.board.filter(function (x) {return x.value == ""});
                },


If blanks is an empty array, return null.
                getRandomBlankSquare: function()
                {
                    var blanks = this.board.filter(function (x) {return x.value == ""});

                    if (blanks.length == 0)
                    {
                        return null;
                    }
                    else
                    {

                    }
                },


Otherwise, run the getRandomNumber() method, entering 0 and the length of blanks minus 1 as the minimum and maximum values.
                getRandomBlankSquare: function()
                {
                    var blanks = this.board.filter(function (x) {return x.value == ""});

                    if (blanks.length == 0)
                    {
                        return null;
                    }
                    else
                    {
                        var rand = this.getRandomNumber(0, blanks.length - 1);
                    }
                },


Next up is the getRandomNumber() method, and that's self-explanatory. We've been using that ad nauseam in web tutorials.
                getRandomNumber: function(min, max)
                {
                    return Math.floor((Math.random() * (min - max + 1)) + max);
                },


So now that you have a random number, run the findIndex() method on the board array. Compare the square property of the random element of blanks against the square property of each element in board, and return the matching index! Thus, if the square property is "a", then you will return 0. If it is "f", you return 5.
                getRandomBlankSquare: function()
                {
                    var blanks = this.board.filter(function (x) {return x.value == ""});

                    if (blanks.length == 0)
                    {
                        return null;
                    }
                    else
                    {
                        var rand = this.getRandomNumber(0, blanks.length - 1);
                        return this.board.findIndex(function (x) {return x.square == blanks[rand].square});  
                    }
                },


OK, now take a turn on the board! The computer should respond with "My Turn!" and place an "X" on the board, then make it your turn again!


For the next step, remember that once a turn is taken, we want to check for a victory condition. That's where the checkWin() method of the game object comes in. Pass in mark as an argument.
                takeTurn: function(square, mark)
                {
                    if (mark == game.turn && game.started)
                    {
                        if (game.board[square].value == "")
                        {
                            game.board[square].value = mark;

                            for (var i = 0; i < game.winPatterns.length; i++)
                            {
                                for (var j = 0; j < game.winPatterns[i].length; j++)
                                {
                                    if (game.winPatterns[i][j].square == game.board[square].square)
                                    {
                                        game.winPatterns[i][j].value = mark;
                                    }  
                                }
                            }

                            document.getElementById("square" + square).className = "square " + mark;

                            if (game.checkWin(mark))
                            {

                            }
                            else
                            {

                            }  
                        }
                    }
                },


If it's not a victory condition, run the nextTurn() method of the game object, signifying that the game goes on.
                            if (game.checkWin(mark))
                            {

                            }
                            else
                            {
                                game.nextTurn();
                            }  


If the game is won, put a victory message in the appropriate user's div. And then set the started property of the game object to false, because the game is over.
                            if (game.checkWin(mark))
                            {
                                document.getElementById("user_" + mark + "_text").innerHTML = "I win!";
                                game.started = false;
                            }
                            else
                            {
                                game.nextTurn();
                            }


The next thing to do is fill out the checkWin() method. By default, it returns false.
                checkWin: function(mark)
                {
                    return false;
                }  


Declare the findSquare variable and iterate through the winPatterns array using a For loop.
                checkWin: function(mark)
                {
                    var findSquare;

                    for (var i = 0; i < this.winPatterns.length; i++)
                    {
  
                    }

                    return false;
                }  


For each array inside the winPatterns array, check each object. If all three of the value properties of the array objects are set to mark, that means victory has been achieved. So return true.
                checkWin: function(mark)
                {
                    var findSquare;

                    for (var i = 0; i < this.winPatterns.length; i++)
                    {
                        if (this.winPatterns[i][0].value == mark && this.winPatterns[i][1].value == mark && this.winPatterns[i][2].value == mark)
                        {
                            return true;
                        }  
                    }

                    return false;
                }  


But of course, we need a visual indicator. So iterate through that array. Use the findIndex() method to get the index of the squares that have been identified as meeting the victory condition, and set the index to the value of findSquare. Then use findSquare to set add the CSS class win to that element.
                checkWin: function(mark)
                {
                    var findSquare;

                    for (var i = 0; i < this.winPatterns.length; i++)
                    {
                        if (this.winPatterns[i][0].value == mark && this.winPatterns[i][1].value == mark && this.winPatterns[i][2].value == mark)
                        {
                            for (var j = 0; j < 3; j++)
                            {
                                var winPatterns = this.winPatterns;
                                findSquare = this.board.findIndex(function (x) {return x.square == winPatterns[i][j].square});
                                document.getElementById("square" + findSquare).className += " win";                      
                            }

                            return true;
                        }  
                    }

                    return false;
                }  


Here's the styling for win. We basically set the background color to yellow.
            .o:before
            {
                content: "O";
                color: #44FF00;
            }

            .win
            {
                background-color: #FFFF00;
            }

            #user_x_container
            {
                color: #FF4400;
            }


Try it now. When you achieve victory, it should look like this. And clicking will not achieve anything more.


If it's a draw, it should look like this.


Well, that was fun...

... and brought back memories of having a pencil, paper and being a stupid kid.

xoxo,
T___T

Saturday, 10 August 2019

Web Tutorial: Tic-tac-toe (Part 1/2)

Aloha!

Today, we will be doing something mind-numbingly simple... apparently. It's a Noughts And Crosses game on the web, also known as "Tic-tac-toe" in local parlance. 

Rules of the game, for everyone who hasn't played this as a kid (which is absolutely nobody, but let's do it anyway, just to spell out the requirements):

1. There is a 3 by 3 square board, totalling nine squares forming a larger square.



2. Each of the opponents take turns placing their mark (either "x" or "o") on the board.

X
O



3. Once either opponent manages to get three squares in a row with "x" or "o" (vertically, horizontally or diagonally), the game is won.

X O
X O
X



4. You can't place an "x" or "o" in any occupied square.
5. If there are no more vacant squares and no one has won the game, it ends in a draw.

Clear? Let's begin.

Set up some basic HTML structure. We'll just do the easy part first.

<!DOCTYPE html>
<html>
    <head>
        <title>Tic-tac-toe</title>

        <style>

        </style>

        <script>

        </script>
    </head>

    <body>

    </body>
</html>



Add a header tag and two buttons. Their ids are btnPlay and btnQuit respectively, and they're styled with the CSS class btn. One says "PLAY" and the other says "QUIT".

<!DOCTYPE html>
<html>
    <head>
        <title>Tic-tac-toe</title>

        <style>

        </style>

        <script>

        </script>
    </head>

    <body>
        <h1>Let's play Tic-tac-toe!</h1>
        <button id="btnPlay" class="btn">PLAY</button>
        <button id="btnQuit" class="btn">QUIT</button>
    </body>
</html>



So far so good. This is just the beginning.




Now for some CSS! It's a kiddy game, so let's use a kiddy font for the game... the hideously cheerful Comic Sans MS. I'm going to include some styling for the header and buttons, but honestly, they don't matter much in the grand scheme of things.

<style>
            body
            {
                font-family: "Comic Sans MS";
            }

            h1
            {
                width: 30em;
                text-align: center;
                margin: 0 auto 0 auto;
            }

            .btn
            {
                font-family: "Comic Sans MS";
                font-size: 2em;
                display: block;
                width: 5em;
                height: 1.5em;
                border-radius: 0.5em;
                border: 0px solid;
                background-color: #FF4400;
                color: #FFFFFF;
                margin: 1em auto 0 auto;
            }

            .btn:hover
            {
                background-color: #FFFF00;
                color: #FF4400;
            }
</style>



Bueno!




Add a div. The id is game_container. Within it, add three other divs. They are user_x_container, board_container and user_o_container. Style user_x_container and user_o_container using the player CSS class.

    <body>
        <h1>Let's play Tic-tac-toe!</h1>
        <button id="btnPlay" class="btn">PLAY</button>
        <button id="btnQuit" class="btn">QUIT</button>
    
        <div id="game_container">
            <div id="user_x_container" class="player">

            </div>

            <div id="board_container">

            </div>

            <div id="user_o_container" class="player">

            </div>
        </div>
    </body>


Within user_x_container and user_o_container, add divs styled using token and text. The divs styled using text should have ids of user_x_text and user_o_text, respectively. Also add smiley face symbols in the divs styled with token.

        <div id="game_container">
            <div id="user_x_container" class="player">
                <div class="token">&#9786;</div>
                <div id="user_x_text" class="text"></div>
            </div>

            <div id="board_container">

            </div>

            <div id="user_o_container" class="player">
                <div class="token">&#9786;</div>
                <div id="user_o_text" class="text"></div>
            </div>
        </div>



That's all you will see right now - two tiny smiley faces on the left.




Use different colors for user_x_container and user_o_container. I'm going with orange and green.

<style>
            .btn:hover
            {
                background-color: #FFFF00;
                color: #FF4400;
            }
           
            #user_x_container
            {
                color: #FF4400;
            }

            #user_o_container
            {
                color: #44FF00;
            }
</style>



For player, set the width and height, and use the margin property to align the divs to the middle of the screen. For token, this is mostly cosmetic. Set the font size to something large, limit the width, and float left. Since we want the smiley faces to align nicely, set the text-align property to center. text aligns left as well, and takes up 70% width. I've made some margin adjustments so that any text we put in there won't be hideously misaligned.

<style>
            #user_o_container
            {
                color: #44FF00;
            }

            .player
            {
                width: 600px;
                height: 150px;
                margin: 0 auto 0 auto;
            }

            .token
            {
                width: 20%;
                font-size: 5em;
                float: left;
                text-align: center;
                padding: 10px;
            }

            .text
            {
                width: 70%;
                font-size: 2em;
                float: left;
                text-align: left;
                padding: 10px;
                margin-top: 1.5em;
            }
</style>



And it's taking shape...




Now let's set the board. Add nine divs within board_container, all with a class of square. Set the ids from 0 to 8.

            <div id="board_container">
                <div id="square0" class="square"></div>
                <div id="square1" class="square"></div>
                <div id="square2" class="square"></div>
                <div id="square3" class="square"></div>
                <div id="square4" class="square"></div>
                <div id="square5" class="square"></div>
                <div id="square6" class="square"></div>
                <div id="square7" class="square"></div>
                <div id="square8" class="square"></div>
            </div>



board_container will be 330 by 330 pixels, and set in the middle of the screen. We give it a light grey background, and some padding to the left and top. square is a 100 by 100 pixel square, with a right and bottom margin of 10 pixels to separate it from the other divs, so we get some nice spacing between squares. Give it a deep grey outline and a white background. And finally, set the cursor property to pointer.

And... this really should go without saying, but, adjust colors to personal taste.

            .btn:hover
            {
                background-color: #FFFF00;
                color: #FF4400;
            }            body

            #board_container
            {
                width: 330px;
                height: 330px;
                margin: 0 auto 0 auto;
                background-color: #AAAAAA;
                padding-top: 10px;
                padding-left: 10px;
            }

            .square
            {
                width: 100px;
                height: 100px;
                margin-right: 10px;
                margin-bottom: 10px;
                float: left;
                outline: 1px solid #444444;
                background-color: #FFFFFF;
                cursor: pointer;
            }

            #user_x_container
            {
                color: #FF4400;
            }



OK! You have a board.




Time to JavaScript the hell out of this guy...

We still start with two objects - game and user.

<script>
var game =
{

};

var user =
{

};
</script>



game has four properties (a string, a boolean and two arrays) and seven methods (use these names for now; I'll explain as we go through them. user has two methods - takeTurn() and myTurn().

<script>
var game =
{
                turn: "x",
                started: true,
                board:
                [

                ],
                winPatterns:
                [

                ],
                init: function()
                {

                },
                play: function()
                {

                },
                quit: function()
                {

                },
                getRandomNumber: function(min, max)
                {

                },
                getRandomBlankSquare: function()
                {

                },
                nextTurn: function()
                {

                },
                checkWin: function(mark)
                {

                }               
};

var user =
{
                takeTurn: function(square, mark)
                {

                },
                myTurn: function()
                {

                }
};
</script>



For the game object, turn is a string used to determine whether player "x" or "o" gets to make their turn. Kind of like a traffic controller, if you will, and set to "x" by default. started is a flag that determines if the game has begun. By default, it's true.

board is a series of objects - each with square and value properties. The square property corresponds to the layout of the board. value is meant to determine if the square is currently occupied by "x" or "o".

a b c
d e f
g h i




var game =
{
                turn: "x",
                started: true,
                board:
                [
                    {square: "a", value: ""},
                    {square: "b", value: ""},
                    {square: "c", value: ""},
                    {square: "d", value: ""},
                    {square: "e", value: ""},
                    {square: "f", value: ""},
                    {square: "g", value: ""},
                    {square: "h", value: ""},
                    {square: "i", value: ""}
                ],
                winPatterns:
                [

                ],
                init: function()
                {

                },
                play: function()
                {

                },
                quit: function()
                {

                },
                getRandomNumber: function(min, max)
                {

                },
                getRandomBlankSquare: function()
                {

                },
                nextTurn: function()
                {

                },
                checkWin: function(mark)
                {

                }               
};



winPatterns is an array of an array of objects that help you determine if victory has been achieved. Take, for example, the first array that has been filled in for you. "abc" is a straight line right at the top of the board.

var game =
{
                turn: "x",
                started: true,
                board:
                [
                    {square: "a", value: ""},
                    {square: "b", value: ""},
                    {square: "c", value: ""},
                    {square: "d", value: ""},
                    {square: "e", value: ""},
                    {square: "f", value: ""},
                    {square: "g", value: ""},
                    {square: "h", value: ""},
                    {square: "i", value: ""}
                ],
                winPatterns:
                [
                    [
                        {square: "a", value: ""},
                        {square: "b", value: ""},
                        {square: "c", value: ""}
                    ]
                ],
                init: function()
                {

                },
                play: function()
                {

                },
                quit: function()
                {

                },
                getRandomNumber: function(min, max)
                {

                },
                getRandomBlankSquare: function()
                {

                },
                nextTurn: function()
                {

                },
                checkWin: function(mark)
                {

                }               
};



And the rest, you can figure out yourself. There are eight possible winning combinations.

                winPatterns:
                [
                    [
                        {square: "a", value: ""},
                        {square: "b", value: ""},
                        {square: "c", value: ""}
                    ],
                    [
                        {square: "a", value: ""},
                        {square: "d", value: ""},
                        {square: "g", value: ""}
                    ],
                    [
                        {square: "a", value: ""},
                        {square: "e", value: ""},
                        {square: "i", value: ""}
                    ],
                    [
                        {square: "b", value: ""},
                        {square: "e", value: ""},
                        {square: "h", value: ""}
                    ],
                    [
                        {square: "c", value: ""},
                        {square: "f", value: ""},
                        {square: "i", value: ""}
                    ],
                    [
                        {square: "c", value: ""},
                        {square: "e", value: ""},
                        {square: "g", value: ""}
                    ],
                    [
                        {square: "d", value: ""},
                        {square: "e", value: ""},
                        {square: "f", value: ""}
                    ],
                    [
                        {square: "g", value: ""},
                        {square: "h", value: ""},
                        {square: "i", value: ""}
                    ]
                ]



Now let's set create the init() method for the game object. This initializes the properties of the game object. We start with setting turn to "x" and started to true.

                init: function()
                {
                    this.started = true;
                    this.turn = "x";
                },



And then we iterate through the board and winPatterns arrays, and reset all values to an empty string.

                init: function()
                {
                    this.started = true;
                    this.turn = "x";

                    for (var i = 0; i < this.board.length; i++)
                    {
                        this.board[i].value = "";
                    }

                    for (var i = 0; i < this.winPatterns.length; i++)
                    {
                        for (var j = 0; j < this.winPatterns[i].length; j++)
                        {
                            this.winPatterns[i][j].value = "";
                        }
                    }
                },



After that, go through all the squares in the board and reset the class to square.

                init: function()
                {
                    this.started = true;
                    this.turn = "x";

                    for (var i = 0; i < this.board.length; i++)
                    {
                        this.board[i].value = "";
                    }

                    for (var i = 0; i < this.winPatterns.length; i++)
                    {
                        for (var j = 0; j < this.winPatterns[i].length; j++)
                        {
                            this.winPatterns[i][j].value = "";
                        }
                    }

                    for (var i = 0; i < 9; i++)
                    {
                        document.getElementById("square" + i).className = "square";
                    }
                },



Then clear the innerHTML property of all elements styled using the text CSS class.

                init: function()
                {
                    this.started = true;
                    this.turn = "x";

                    for (var i = 0; i < this.board.length; i++)
                    {
                        this.board[i].value = "";
                    }

                    for (var i = 0; i < this.winPatterns.length; i++)
                    {
                        for (var j = 0; j < this.winPatterns[i].length; j++)
                        {
                            this.winPatterns[i][j].value = "";
                        }
                    }

                    for (var i = 0; i < 9; i++)
                    {
                        document.getElementById("square" + i).className = "square";
                    }

                    var texts = document.getElementsByClassName("text");
                    for (var i = 0; i < texts.length; i++)
                    {
                        texts.innerHTML = "";
                    }
                },



For the play() method, hide the "PLAY" button and show the "QUIT" button. Then show game_container and run the nextTurn() method.

For quit(), you do almost the exact opposite - show the "PLAY" button and hide the "QUIT" button. Then hide game_container. But before all that, run the init() method.

                play: function()
                {
                    document.getElementById("btnPlay").style.display = "none";
                    document.getElementById("btnQuit").style.display = "block";
                    document.getElementById("game_container").style.display = "block";
                    this.nextTurn();
                },
                quit: function()
                {
                    this.init();
                    document.getElementById("btnQuit").style.display = "none";
                    document.getElementById("btnPlay").style.display = "block";
                    document.getElementById("game_container").style.display = "none";
                },



Now make sure the buttons run those.

        <button id="btnPlay" class="btn" onclick="game.play()">PLAY</button>
        <button id="btnQuit" class="btn" onclick="game.quit()">QUIT</button>



For there to be anything noticeable when you click the buttons, you first need to set this CSS to hide the "QUIT" button and game_container. Now, refresh. Click the "PLAY" button. It should disappear, only to be replaced by the "QUIT" button. And then the board should appear as well.

            .btn:hover
            {
                background-color: #FFFF00;
                color: #FF4400;
            }

            #btnQuit, #game_container
            {
                display: none;
            }

            #board_container
            {
                width: 330px;
                height: 330px;
                margin: 0 auto 0 auto;
                background-color: #AAAAAA;
                padding-top: 10px;
                padding-left: 10px;
            }



The nextTurn() method will set the turn property to "x" if it's currently "o", and vice versa. Then it will run the myTurn() method of the user object.

                nextTurn: function()
                {
                    this.turn =  (this.turn == "x" ? "o" : "x");
                    user.myTurn();
                },



Let's create myTurn(), then. This will manipulate the DOM to display whose turn it is on the board. First we clear the user_x_text and user_o_text divs, then fill the appropriate div with the string "My turn!".

                myTurn: function()
                {
                    document.getElementById("user_x_text").innerHTML = "";
                    document.getElementById("user_o_text").innerHTML = "";
                    document.getElementById("user_" + game.turn + "_text").innerHTML = "My turn!";
                }



If is the computer's turn, i.e. "x", then we do more stuff. Let's just leave the If block here for now.

                myTurn: function()
                {
                    document.getElementById("user_x_text").innerHTML = "";
                    document.getElementById("user_o_text").innerHTML = "";
                    document.getElementById("user_" + game.turn + "_text").innerHTML = "My turn!";

                    if (game.turn == "x")
                    {

                    }
                }



Now test this. When you refresh, you should now only see the "PLAY" button.




When you click the "PLAY" button, does "My turn!" come on for player "o"?




Next

The groundwork has been laid. The next codes we write will handle the meat of the game.

Sunday, 4 August 2019

Much Ado About FaceApp

A great deal has been said about FaceApp, which for some inexplicable reason is probably the hottest deepfake-for-fun app on the market right now, and how Russians are using the data collected to gather information on our faces.


For some background, FaceApp is one of those mobile apps people use to upload facial photographs (usually their own) and perform digital surgery on, to see how they'll look as older or younger people, as the opposite sex, with facial hair, and so on. It's good silly fun, though it was kind of disturbing to see how eerily like dear old Dad I looked when I aged my own photo. And how much more handsome my wife looks like as a man, than me.

At some point, I even thought about writing an app review for FaceApp... but what the hell, it's pretty pointless. It doesn't do anything (for free, that is) that Snapchat already does better, except its interface is a whole lot less annoying than Snapchat's because you don't have a whole lot of other functionality to learn. I had fun with it... and one of the things I did was to play around with Thanos's face!

The original
Interesting.
Aging filter.
Er...
OK, ew,  that is without doubt
the ugliest transsexual
I've ever laid eyes on.

So, all that aside, there was some growing concern that FaceApp's servers were storing all the data that was being uploaded on them, despite their claims that data was being cleared.  And that this contravenes privacy and all that jazz. It came to the point where FaceApp had to show this message whenever you start it up.


And I know they only recently added it because I was playing with FaceApp way before all this commotion began.

Facial Recognition

On that note, facial recognition has made some progress as a technology. Now some mobile phone cameras can detect the race, gender and approximate age of a person simply by scanning the user's face. That's some awesome shit, right? We can even use facial recognition to lock and unlock phones.

Wait. Uh-oh...

That means that deepfake technology could conceivably simulate your facial features enough to bypass any security measures you may have implemented using your facial features. That certainly throws a spanner in the works, yes?

To continue that train of thought, if FaceApp had records of your facial features, it could bypass any security setting depending on facial recognition. Now, that's pretty scary. Think of your face as a key, and FaceApp as a key duplication machine.

Nah, I'm just kidding. Mobile phones have come a long way from the days where they could be easily fooled by photographs. Honestly, if that were still an issue, the last thing you should lose any sleep over is FaceApp.

Should we be worried anyway?

Maybe. If you're paranoid about people knowing what you look like, and being tracked as to your whereabouts. Personally, I'm a nobody and not worth the effort, so I think I'm pretty safe.

But you know what? FaceApp doesn't have anything that you probably already have uploaded to Social Media. Yes, all your very best selfies on Facebook, Instagram, God knows what else. If you're not a Social Media user, yes, FaceApp might represent a threat. But to the world at large? Unbunch your underpants, children. FaceApp is the least of your worries. It's not the only app of its kind, and it's not even the best of its kind.

And considering you can upload not just your own face, but photos of other people, there's absolutely nothing you can do to prevent others from uploading your face onto FaceApp. Worrying at this point seems like an exercise in futility.

The cool (and morbid) thing about all this is...

The photos of you that were uploaded, could be added to the data bank that helps train the learning algorithm to synthesize faces. Think about it - long after you're dead and gone, part of you is going to exist for ever and ever. In perpetuity. Not just in some moldering Facebook In Memorandium page, but in an app that people actually access frequently.

Probably not the kind of immortality most people wish for, but well, damn.

Talk about taking something at face value!
T___T