Products

Tic Tac Toe

The Tic tac toe is a classical game where two players take turns marking the spaces in a three-by-three grid with X or O. The player who succeeds in placing three of their marks in a horizontal, vertical, or diagonal row is the winner.

This tutorial will explain how to create a basic classical version of this game using LemonadeJS.

See this example on codesandbox

Working example

<html>
<script src="https://lemonadejs.com/v5/lemonade.js"></script>
<div id='root'></div>
<script>
function Tictactoe() {
    let text = [
        'can start the game',
        'turn to play',
        'won the game',
    ];

    // Check the board to see if there is a winner
    const isWinner = () => {
        return [
            [0,1,2], [3,4,5], [6,7,8], // Rows
            [0,3,6], [1,4,7], [2,5,8], // Columns
            [0,4,8], [2,4,6]           // Diagonals
        ].some(([a,b,c]) =>
            this.board[a].player &&
            this.board[a].player === this.board[b].player &&
            this.board[b].player === this.board[c].player
        );
    }

    /**
     * Click event handler
     * @param {MouseEvent} e
     */
    const click = (e) => {
        // Valid item to be clicked - only SPAN
        if (e.target.tagName === 'SPAN') {
            if (this.winner) {
                alert(this.title.textContent);
            } else {
                // No one picked the position yet
                if (! e.target.textContent) {
                    // Get the position of the element clicked
                    let index = Array.prototype.indexOf.call(e.target.parentNode.children, e.target);
                    // Selected the board
                    this.board[index].player = this.player;
                    // Switch the text element to refers the player's turn.
                    this.text = text[1];
                    if (isWinner()) {
                        // Switch the text element to refers the winner.
                        this.text = text[2];
                        this.winner = true;
                        // We have a winner
                        alert(this.title.textContent);
                    } else {
                        // Switch player's turn.
                        this.player = this.player === 'o' ? 'x' : 'o';
                    }
                }
            }
        }
    }

    /**
     * Reset the game variables
     */
    const reset = () => {
        // Player 0 (o) and Player 1 (x)
        this.player = 'o';
        // Update the instruction to the user
        this.text = text[0];
        // Property to define if already reached a winner.
        this.winner = false;
        // Reset the board with the 9 positions
        this.board = [{},{},{},{},{},{},{},{},{}];
    }

    // Start the game
    reset();

    // Game template
    return render => render`<div class="tictactoe">
        <h4 :ref="this.title">${this.player} ${this.text}</h4>
        <div :loop="${this.board}" class="board" onclick="${click}">
            <span>{{this.player}}</span>
        </div><br/>
        <input type="button" onclick=${reset} value="Reset the game" />
    </div>`
}

lemonade.render(Tictactoe, document.getElementById('root'));
</script>
import lemonade from 'lemonadejs';

export default function Tictactoe() {
    let text = [
        'can start the game',
        'turn to play',
        'won the game',
    ];

    // Check the board to see if there is a winner
    const isWinner = () => {
        return [
            [0,1,2], [3,4,5], [6,7,8], // Rows
            [0,3,6], [1,4,7], [2,5,8], // Columns
            [0,4,8], [2,4,6]           // Diagonals
        ].some(([a,b,c]) =>
            this.board[a].player &&
            this.board[a].player === this.board[b].player &&
            this.board[b].player === this.board[c].player
        );
    }

    /**
     * Click event handler
     * @param {MouseEvent} e
     */
    const click = (e) => {
        // Valid item to be clicked - only SPAN
        if (e.target.tagName === 'SPAN') {
            if (this.winner) {
                alert(this.title.textContent);
            } else {
                // No one picked the position yet
                if (! e.target.textContent) {
                    // Get the position of the element clicked
                    let index = Array.prototype.indexOf.call(e.target.parentNode.children, e.target);
                    // Selected the board
                    this.board[index].player = this.player;
                    // Switch the text element to refers the player's turn.
                    this.text = text[1];
                    if (isWinner()) {
                        // Switch the text element to refers the winner.
                        this.text = text[2];
                        this.winner = true;
                        // We have a winner
                        alert(this.title.textContent);
                    } else {
                        // Switch player's turn.
                        this.player = this.player === 'o' ? 'x' : 'o';
                    }
                }
            }
        }
    }

    /**
     * Reset the game variables
     */
    const reset = () => {
        // Player 0 (o) and Player 1 (x)
        this.player = 'o';
        // Update the instruction to the user
        this.text = text[0];
        // Property to define if already reached a winner.
        this.winner = false;
        // Reset the board with the 9 positions
        this.board = [{},{},{},{},{},{},{},{},{}];
    }

    // Start the game
    reset();

    // Game template
    return render => render`<div class="tictactoe">
        <h4 :ref="this.title">${this.player} ${this.text}</h4>
        <div :loop="${this.board}" class="board" onclick="${click}">
            <span>{{this.player}}</span>
        </div><br/>
        <input type="button" onclick=${reset} value="Reset the game" />
    </div>`
}

Style for this example

.tictactoe {
    max-width: 244px;
}

.tictactoe .title {
    padding: 20px;
    text-align: center;
}

.tictactoe .board {
    display: grid;
    gap: 1px;
    grid-template-columns: repeat(3, 1fr);
}

.tictactoe .board > span {
    margin: 2px;
    width: 80px;
    height: 80px;
    background-color: #ddd;
    line-height: 80px;
    text-align: center;
    font-size: 22px;
}

.tictactoe button {
    width: 100%;
}