Gomoku ★★★ (Tic Tac Toe)
There are many Gomoku (5 in a row) game projects on the Internet.
The scripts of these projects are freely available. Having analyzed them, I created my own
game code based on algorithms taken from the Internet. I have a simple script. The rules of
the game are almost the same, that is, the winner is the one who collects 5 letters in a row
(either vertically, horizontally, or diagonally). In case of winning, a winner's line is drawn
connecting 5 letters. I would like experienced programmers to point out my mistakes.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Gomoku game 5 in a row</title>
<style>
body {
text-align: center;
}
#canvas {
border: 2px solid green;
background-color: lightblue;
}
</style>
</head>
<body>
<h1>Gomoku game 5 in a row. Javascript simple game Canvas.</h1>
<p id="gameStateEl"></p>
<p id="gameStateE2"></p>
<canvas id="mycanvas"></canvas>
<div>
<button id="ReStart"type="button"
onclick="ReStart();">ReStart</button>
</div>
<script>
const canvas = document.getElementById('mycanvas');
const ctx = canvas.getContext("2d");
const CELL_SIZE = 50;
const ROWS = 15, COLS = 15;
const CELL_COUNT = ROWS * COLS;
const PLAY_COLOR = "teal";
var cellCol = PLAY_COLOR;
//Positions on the board
const boardPosition =
{x: 0, y: 0, w: COLS * CELL_SIZE, h: ROWS * CELL_SIZE};
canvas.width = boardPosition.x + boardPosition.w;
canvas.height = boardPosition.y + boardPosition.h;
var count_1 = 0;
var count_2 = 0;
var count_3 = 0;
const cells = [];
var num = ROWS + 5;
//Determine whether the current click is X or O
var coords = [];
//Save the clicked coordinates
record = [];
var isWin = false, a1, a2, a3, a4, a5;
//Define the unplaced state of the chessboard as 0
for(var i = 0; i<num; i++){
var arr = [];
for(var j = 0; j < num; j++){
arr.push(0);
}
record.push(arr);
}
var isChangePlayer = false;
var first_X = 0;
var first_Y = 0;
var last_X = 0;
var last_Y = 0;
function setFont() {
ctx.font = "60px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
}
//The initBoard method initializes the game board by
//setting the height and width of the board,
//creating an array to represent the board, and filling the array with zeros.
function initBoard() {
var cellIdx = 0;
cellCol = PLAY_COLOR;
//game State elements
gameStateEl.textContent = "Player_1 no moves.";
gameStateE2.textContent = "Player_2 no moves.";
}
//Drawing board background
function drawBoardBackground() {
ctx.beginPath();
//Nicknames for ease of reading
const bP = boardPosition;
/* BM67 local alias */
ctx.rect(bP.x, bP.y, bP.w, bP.h);
ctx.fillStyle = "rgba(0,122,0, 0.2)";
ctx.strokeStyle = "fuchsia";
ctx.fill();
ctx.beginPath();
for (let i = 0; i <= COLS; i++) {
ctx.moveTo(bP.x, bP.y + i * CELL_SIZE);
ctx.lineTo(bP.x + bP.w, bP.y + i * CELL_SIZE);
ctx.moveTo(bP.x + i * CELL_SIZE, bP.y);
ctx.lineTo(bP.x + i * CELL_SIZE, bP.y + bP.h);
}
ctx.stroke();
}
//draw a cell
function drawCell(cellIdx) {
let val = "";
const x = (cellIdx % COLS) * CELL_SIZE;
const y = (cellIdx / COLS | 0) * CELL_SIZE;
ctx.fillStyle = "blue";
ctx.shadowColor = '#000';
ctx.fillRect(x, y, CELL_SIZE, CELL_SIZE);
ctx.fillStyle = cellCol;
ctx.shadowBlur = 4;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 2;
ctx.fillRect(x + 5, y + 5, CELL_SIZE - 10, CELL_SIZE - 10);
ctx.fillText(val, x + CELL_SIZE * 0.5, y + CELL_SIZE * 0.5);
}
//let's draw a game
function drawGame() {
setFont();
var cellIdx = 0;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
drawBoardBackground();
while (cellIdx < CELL_COUNT) { drawCell(cellIdx ++); }
}
//Cell pressed
function cellClicked(clickIdx) {
/* BM67 returns true if board needs redraw */
const x = clickIdx % COLS;
const y = clickIdx / COLS | 0;
if(coords.length === 0){
draw_X(x, y ,ctx);
isChangePlayer = true;
addCoords(x, y, 1);
}else{
if(verdictCoords(x, y)){
if(!isChangePlayer){
draw_X(x, y);
addCoords(x, y, 1);
isChangePlayer = true;
}else {
draw_O(x, y);
addCoords(x, y, 2);
isChangePlayer = false;
}
console.log('isVictory() = ' + isVictory());
if(isVictory() && isChangePlayer === true){
drawLine(x, y);
my_color = "blue";
<!-- alert("GAME OVER --- blue"); -->
//game over
console.log("GAME OVER --- Winner blue");
gameStateEl.textContent = "Player_1 Winner blue "
+ count_1 + " moves.";
}
if(isVictory() && isChangePlayer === false){
drawLine(x, y);
my_color = "red";
<!-- alert("GAME OVER --- red"); -->
//game over
console.log("GAME OVER --- Winner red" );
gameStateE2.textContent = "Player_2 Winner red "
+ count_2 + " moves.";
}
}
}
}
//Adding an onClick handler to a canvas element
canvas.addEventListener("mousedown", function (e) {
//Nicknames for ease of reading
const bP = boardPosition;
/* BM67 local alias */
const x = Math.floor((e.offsetX - bP.x) / CELL_SIZE); /* 0 - 3 on board cell */
const y = Math.floor((e.offsetY - bP.y) / CELL_SIZE); /* 0 - 3 on board cell */
if(isChangePlayer === false){
count_1 += 1;
gameStateEl.textContent = "Player_1 - "
+ count_1 + " moves.";
}
if(isChangePlayer === true){
count_2 += 1;
gameStateE2.textContent = "Player_2 - "
+ count_2 + " moves.";
}
if (cellClicked(x + y * COLS)) {
moveCount += 1;
drawGame();
}
if (isVictory() === true) {
count_3 += 1;
}
if (count_3 === 2) {
ReStart();
}
console.log('count_3 = ' + count_3);
});
//Draw a X
function draw_X(x, y){
editRecord(x, y, 1);
ctx.fillStyle = "blue";
ctx.beginPath();
ctx.font = "40px Verdana";
ctx.fillText("X", x * CELL_SIZE + 25, y * CELL_SIZE + 27);
ctx.closePath();
ctx.fill();
}
//Draw a O
function draw_O(x, y){
editRecord(x, y, 2);
ctx.fillStyle = "red";
ctx.beginPath();
ctx.font = "40px Verdana";
ctx.fillText("O", x * CELL_SIZE + 25, y * CELL_SIZE + 27);
ctx.closePath();
ctx.fill();
}
//Let's draw the winner's line.
function drawLine(x, y){
editRecord(x, y, 1);
ctx.beginPath();
ctx.moveTo(first_X * CELL_SIZE + 25, first_Y * CELL_SIZE + 25);
ctx.lineTo(last_X * CELL_SIZE + 25, last_Y * CELL_SIZE + 25);
if(isChangePlayer === true){
ctx.strokeStyle = "blue";
}
else{
ctx.strokeStyle = "red";
}
ctx.lineWidth = 11;
ctx.stroke();
ctx.closePath();
}
//Add mark to clicked
function addCoords(x, y, num){
coords.push({
x: x,
y: y,
num: num
})
}
//Edit the marks on the board,
//1 represents the red move, 2 represents the black move
function editRecord(x, y, num){
record.forEach(function(item, index){
if(index === y){
item.forEach(function(item, index){
if(x === index){
record[y][x] = num;
}
})
}
})
}
//Determine whether the current position is placed
function verdictCoords(x, y){
var isTrue = true;
coords.forEach(function(item, index){
if(item.x === x && item.y === y){
isTrue = false;
}
})
return isTrue
}
//Determine whether the mark on the board
//meets the victory condition
function isVictory(){
var len = record.length;
for(var i = 0; i < record.length; i++){
for(var j=0; j<record[i].length; j++){
if(record[i][j] !==0 ){
if(j < len && i - 1 < len){
//Determine the horizontal direction
a1 = record[i][j];
a2 = record[i][j + 1];
a3 = record[i][j + 2];
a4 = record[i][j + 3];
a5 = record[i][j + 4];
if(isEqual(a1, a2, a3, a4, a5)){
first_X = j;
first_Y = i;
last_X = j + 4;
last_Y = i;
console.log('first_X = ' + first_X);
console.log('first_Y = ' + first_Y);
console.log('last_X = ' + last_X);
console.log('last_Y = ' + last_Y);
isWin = true;
}
//Determine the direction of the forward slash
a1 = record[i][j];
a2 = record[i + 1][j + 1];
a3 = record[i + 2][j + 2];
a4 = record[i + 3][j + 3];
a5 = record[i + 4][j + 4];
if(isEqual(a1, a2, a3, a4, a5)){
first_X = j;
first_Y = i;
last_X = j + 4;
last_Y = i + 4;
console.log('first_X = ' + first_X);
console.log('first_Y = ' + first_Y);
console.log('last_X = ' + last_X);
console.log('last_Y = ' + last_Y);
isWin = true;
}
//Judging the vertical direction
a1 = record[i][j];
a2 = record[i + 1][j];
a3 = record[i + 2][j];
a4 = record[i + 3][j];
a5 = record[i + 4][j];
if(isEqual(a1, a2, a3, a4, a5)){
first_X = j;
first_Y = i;
last_X = j;
last_Y = i + 4;
console.log('first_X = ' + first_X);
console.log('first_Y = ' + first_Y);
console.log('last_X = ' + last_X);
console.log('last_Y = ' + last_Y);
isWin = true;
}
//Determine the direction of the backslash
a1 = record[i][j];
a2 = record[i + 1][j - 1];
a3 = record[i + 2][j - 2];
a4 = record[i + 3][j - 3];
a5 = record[i + 4][j - 4];
if(isEqual(a1, a2, a3, a4, a5)){
first_X = j;
first_Y = i;
last_X = j - 4;
last_Y = i + 4;
console.log('first_X = ' + first_X);
console.log('first_Y = ' + first_Y);
console.log('last_X = ' + last_X);
console.log('last_Y = ' + last_Y);
isWin = true;
}
}
}
}
}
return isWin;
}
isVictory();
//Determine whether the adjacent 5 numbers are equal,
//if they are equal, it means that the winner has been divided
function isEqual(a1, a2, a3, a4, a5){
if(a1 == a2 && a2==a3 && a3==a4 && a4 == a5){
return true;
}else {
return false;
}
}
initBoard();
drawGame();
function ReStart() {
document.location.reload();
}
</script>
</body>
</html>