Building CLI Tic Tac Toe Game with Node JS
It will be fun! Also help you to build up some programming basic like Class, Function, Loop etc.
Introduction
One thing that makes developers other than ordinary people out there is a command line. The moment someone opens up the terminal and types out some commands, they instantly transform into a hacker (at least they seem to be hackers). It makes me want to build it myself.
Why Tic Tac Toe, you think? Well, it's simple enough to build but requires user input to make a game playable.
The tutorial is divided into three different sections. In the first section, you will learn how to set the environment to build a CLI App. After that, we develop our tic tac toe platform. Finally, we will publish it to NPM and see if we can play it on our terminal.
And our goal is to make a playable two-player Tic Tac Toe game from CLI. You can try the final result by executing this command.
npx @alvinend/tic-tac-toe-cli
Setting Up Environment
Let's start setting up our environment. For prerequisite, if you want to publish it on NPM. Make sure you have NPM and are logged in.
Initiate NPM
Since we want to use Nodejs and publish our application in NPM, it's good to initiate it with NPM.
npm init --scope=@alvinend
I want my application to be accessible via npx @alvinend/tic-tac-toe-cli.
To do that, we can put scoped option --scope=@user-name
and replace your "user-name with your username. More Info: docs.npmjs.com/cli/v8/using-npm/scope.
Install Package
Here is the list of the package we need:
npm i chalk figlet inquirer
chalk
Basically add color toconsole.log
figlet
Make BIG text in a terminalinquirer
Enable us to ask a question in terminal
Building Tic Tac Toe
Home
Start simple, writing what this application is all about, a tic tac toe game. But it's boring to console log the title. I wanted to go flashy. Fortunately, with Figlet, I could do that!
We start by making a file named index.js
.
// index.js
console.log(figlet.textSync('Tic Tac Toe'))
Output:
And maybe there is someone who does not know tic tac toe's rule. So I added it.
// index.js
console.log(`
===============================================================================================================
${chalk.yellow('How To Play')}
${chalk.yellow('1. Have the first player go first')}
${chalk.yellow('2. Have the second player go second.')}
${chalk.yellow('3. Keep alternating moves until one of the players has drawn a row of three symbols or until no one can win.')}
${chalk.yellow('Any Question? Google it!')}
===============================================================================================================
`)
With chalk.js, we can color some text to make it less "terminal" like. Output:
And we are done. The first step is to be CLI Developer.
Player
First, we make a class that defines Player, setting order
the variable which tells if the player is first or second to make a move and name to record the player's name.
// player.js
export class Player {
constructor(order) {
this.order = order
this.name = `Player ${order}`
}
}
We want the player to put their name in, so we add a function in that class to ask the player what their name is. Using inquirer
, we can do that easily.
// player.js
async init() {
const ans = await inquirer.prompt({
name: 'name',
type: 'input',
message: 'What is your name?',
default: `Player ${this.order}`
})
if (ans.name) {
this.name = ans.name
}
}
Finally, we want a function that lets players choose which spot they want to mark. Tic Tac Toe has nine spots, so we make an array of it.
// player.js
const MOVE_LIST = [
'0 (Top Left)',
'1 (Top Middle)',
'2 (Top Right)',
'3 (Middle Left)',
'4 (Middle Middle)',
'5 (Middle Right)',
'6 (Bottom Left)',
'7 (Bottom Middle)',
'8 (Bottom Right)',
]
But we don't want players to choose spots with a mark on it. So we make a function that asks the player to select only empty spots.
// player.js
async calcMove(board) {
const legalMoveList = MOVE_LIST.filter((_, index) => !board.table[index])
const ans = await inquirer.prompt({
name: 'move',
type: 'list',
message: `${this.name} Chose Your Move.`,
choices: legalMoveList
})
return MOVE_LIST.findIndex(move => move === ans.move)
}
That is it for players!
Board
Board is a place where the player can put marks on it. As said before, Tic Tac Toe contains nine spots. We start by making a class that has board information in it. 0
are empty spots, 1
are spots with player order one's mark (O
), and 2
are spots with player order two's mark (X
).
// board.js
export class Board {
constructor() {
this.table = [0, 0, 0, 0, 0, 0, 0, 0, 0]
}
}
If we want to make a board, we should print it like a board. Rather than printing the board's array, it makes sense to print a 2D board with marks on it.
// board.js
const MARKS = ['', 'O', 'X']
print() {
const arr = []
for (let i = 0; i < 3; i++) {
let row = []
for (let j = 0; j < 3; j++) {
row.push(MARKS[this.table[i * 3 + j]])
}
arr.push(row.join(' | '))
}
console.log(arr.join('\n') + '\n')
}
Now we get into building the game rule. First, we want to enable the player to play it. And to do that, the player needs to put their marks on board. So we make the function of it.
// board.js
async place(player) {
const pos = await player.calcMove(this)
if (this.table[pos] == 0) {
this.table[pos] = player.order
return true
}
console.log('Placement Error, Already someone there')
return false
}
A game is not a game if there is no winner. In Tic Tac Toe, you can win by putting three marks vertically, horizontally, or diagonally. There are many ways to do this. Here is my way. Feel free to make your version of it.
// board.js
checkIsWin(player) {
const checkList = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
]
for (let i = 0; i < checkList.length; i++) {
const check = checkList[i]
let count = 0
for (let i = 0; i < check.length; i++) {
if (this.table[check[i]] === player.order) {
count = count + 1
}
if (count === 3) {
return true
}
}
}
return false
}
And... we are done. We built a functional board class of Tic Tac Toe.
Match
We built Player and Board. The last thing we need is for those players to use Board and play with it.
- Declare board and player
- Loop to maximum move that Tic Tac Toe game can do (nine times)
- For each loop, the player places a mark on a spot, print the board, and check the winner
- Game end a player win or game reaches maximum move count
// match.js
import { Board } from "./board.js"
import { Player } from "./player.js"
export const startGame = async (board, firstPlayer, secondPlayer) => {
// Init Board and Player
board = new Board()
firstPlayer = new Player(1)
await firstPlayer.init()
secondPlayer = new Player(2)
await secondPlayer.init()
// For recording turn
let turn = 0
for (let i = 0; i < 9; i++) {
const inTurnPlayer = (turn % 2 + 1 === 1) ? firstPlayer : secondPlayer
await board.place(inTurnPlayer)
board.print()
// Check If has winner
if (board.checkIsWin(inTurnPlayer)) {
console.log(`${inTurnPlayer.name} won!`)
return
}
turn++
}
console.log('Draw!')
}
Call that function in index.js
, and we are done!
// index.js
#!/usr/bin/env node
import chalk from "chalk";
import figlet from "figlet";
import { startGame } from "./match.js";
console.log(figlet.textSync('Tic Tac Toe'))
console.log(`
===============================================================================================================
${chalk.yellow('How To Play')}
${chalk.yellow('1. Have the first player go first')}
${chalk.yellow('2. Have the second player go second.')}
${chalk.yellow('3. Keep alternating moves until one of the players has drawn a row of three symbols or until no one can win.')}
${chalk.yellow('Any Question? Google it!')}
===============================================================================================================
`)
startGame()
Publish to NPM
Publishing CLI is as easy as entering one line of command in the terminal.
npm publish
If this is your first time, you might run into errors, but almost everybody uses and publishes npm's packages, google, and you are set to go.
Ending
Congratulations, you published your own CLI application! In this article, I focused on building our own CLI and Tic Tac Toe. I hope it gives you a grasp of how npm CLI works.
If you are interested, here is my link to the complete code on this application
Did you find this article valuable?
Support Alvin Endratno by becoming a sponsor. Any amount is appreciated!