export default class TicTacToe {
constructor() {
// create three js groups
this.board = new THREE.Group();
this.circles = new THREE.Group();
this.crosses = new THREE.Group();
this.winLine = new THREE.Group();
this.boardLines = new THREE.Group();
this.hiddenTiles = new THREE.Group();
// add groups to the board
this.board.add(this.circles);
this.board.add(this.crosses);
this.board.add(this.winLine);
this.board.add(this.boardLines);
this.board.add(this.hiddenTiles);
// handle additional data
this.currentPlayer = "o";
this.boardCopy = [
["1", "2", "3"],
["4", "5", "6"],
["7", "8", "9"],
];
this._createBoard();
}
_createBoard() {
// vertical board lines
this.boardLines.add(this._boardLine(4, 64, 4, -12, 0));
this.boardLines.add(this._boardLine(4, 64, 4, 12, 0));
// horizontal board lines
this.boardLines.add(this._boardLine(64, 4, 4, 0, -12));
this.boardLines.add(this._boardLine(64, 4, 4, 0, 12));
// hidden tiles - top row
this.hiddenTiles.add(this._hiddenTile(-24, 24));
this.hiddenTiles.add(this._hiddenTile(0, 24));
this.hiddenTiles.add(this._hiddenTile(24, 24));
// hidden tiles - middle row
this.hiddenTiles.add(this._hiddenTile(-24, 0));
this.hiddenTiles.add(this._hiddenTile(0, 0));
this.hiddenTiles.add(this._hiddenTile(24, 0));
// hidden tiles - bottom row
this.hiddenTiles.add(this._hiddenTile(-24, -24));
this.hiddenTiles.add(this._hiddenTile(0, -24));
this.hiddenTiles.add(this._hiddenTile(24, -24));
// NOTE: Rotate backward for thumbnail.
// this.board.rotation.x = -Math.PI / 8;
}
_boardLine(x, y, z, xOffset, yOffset) {
const boardLineGeometry = new THREE.BoxGeometry(x, y, z);
const boardLineMaterial = new THREE.MeshNormalMaterial();
const boardLine = new THREE.Mesh(boardLineGeometry, boardLineMaterial);
boardLine.position.x = xOffset;
boardLine.position.y = yOffset;
boardLine.scale.x = 0;
boardLine.scale.y = 0;
boardLine.scale.z = 0;
return boardLine;
}
_hiddenTile(xOffset, yOffset) {
const hiddenTileGeometry = new THREE.BoxGeometry(12, 12, 1);
// NOTE: Create hidden mesh for ray casting.
const hiddenTileMaterial = new THREE.MeshNormalMaterial();
// const hiddenTileMaterial = new THREE.MeshBasicMaterial({ color: "black" });
const hiddenTile = new THREE.Mesh(hiddenTileGeometry, hiddenTileMaterial);
hiddenTile.position.x = xOffset;
hiddenTile.position.y = yOffset;
return hiddenTile;
}
_updateBoardCopy(xOffset, yOffset) {
let i, j;
if (xOffset < 0) {
j = 0;
} else if (xOffset === 0) {
j = 1;
} else {
j = 2;
}
if (yOffset < 0) {
i = 2;
} else if (yOffset === 0) {
i = 1;
} else {
i = 0;
}
if (this.currentPlayer === "o") {
this.boardCopy[i][j] = "o";
} else {
this.boardCopy[i][j] = "x";
}
console.log(this.boardCopy);
}
checkWinConditions() {
let strike;
for (let n = 0; n < 3; n++) {
if (this._checkRowWin(n)) {
strike = this._getStrike(64, 2, 4);
strike.position.y = this._getOffsetY(n);
this.winLine.add(strike);
}
if (this._checkColumnWin(n)) {
strike = this._getStrike(2, 64, 4);
strike.position.x = this._getOffsetX(n);
this.winLine.add(strike);
}
}
if (this._topLeftToBottomRightWin()) {
strike = this._getStrike(90, 2, 4);
strike.rotation.z = -Math.PI / 4;
this.winLine.add(strike);
}
if (this._bottomLeftToTopRightWin()) {
strike = this._getStrike(90, 2, 4);
strike.rotation.z = Math.PI / 4;
this.winLine.add(strike);
}
}
_getStrike(x, y, z) {
const strikeGeometry = new THREE.BoxGeometry(x, y, z);
const strikeMaterial = new THREE.MeshNormalMaterial();
const strike = new THREE.Mesh(strikeGeometry, strikeMaterial);
strike.scale.x = 0;
strike.scale.y = 0;
strike.scale.z = 0;
return strike;
}
_checkRowWin(i) {
return (
this.boardCopy[i][0] === this.boardCopy[i][1] &&
this.boardCopy[i][1] === this.boardCopy[i][2]
);
}
_checkColumnWin(j) {
return (
this.boardCopy[0][j] === this.boardCopy[1][j] &&
this.boardCopy[1][j] === this.boardCopy[2][j]
);
}
_topLeftToBottomRightWin() {
return (
this.boardCopy[0][0] === this.boardCopy[1][1] &&
this.boardCopy[1][1] === this.boardCopy[2][2]
);
}
_bottomLeftToTopRightWin() {
return (
this.boardCopy[2][0] === this.boardCopy[1][1] &&
this.boardCopy[1][1] === this.boardCopy[0][2]
);
}
_getOffsetX(n) {
if (n === 0) {
return -24;
} else if (n === 1) {
return 0;
} else {
return 24;
}
}
_getOffsetY(n) {
if (n === 0) {
return 24;
} else if (n === 1) {
return 0;
} else {
return -24;
}
}
// NOTE: This function was initially supposed to draw a cool
// wavy line but I decided to remove that from the project scope.
// drawLine(x, y, z, xOffset, yOffset) {
// const boardLineGeometry = new THREE.BoxGeometry(x, y, z, 100, 10);
// const boardLineMaterial = new THREE.MeshNormalMaterial({ wireframe: true });
// const boardLine = new THREE.Mesh(boardLineGeometry, boardLineMaterial);
// boardLine.position.x = xOffset;
// boardLine.position.y = yOffset;
// boardLine.scale.x = 1;
// boardLine.scale.y = 1;
// boardLine.scale.z = 1;
// // console.log(boardLineGeometry);
// // boardLine.geometry.vertices.map((v) => (v.z = Math.sin(v.x)));
// this.winLine.add(boardLine);
// }
addCrossOrCircle(xOffset, yOffset) {
if (this.currentPlayer === "x") {
this._addCross(xOffset, yOffset);
this._updateBoardCopy(xOffset, yOffset);
this.currentPlayer = "o";
} else {
this._addCircle(xOffset, yOffset);
this._updateBoardCopy(xOffset, yOffset);
this.currentPlayer = "x";
}
}
_addCross(xOffset, yOffset) {
const cross = new THREE.Group();
const crossGeometry = new THREE.BoxGeometry(12, 4, 4);
const crossMaterial = new THREE.MeshNormalMaterial();
const cross1 = new THREE.Mesh(crossGeometry, crossMaterial);
const cross2 = new THREE.Mesh(crossGeometry, crossMaterial);
cross1.rotation.z = Math.PI / 4;
cross2.rotation.z = -Math.PI / 4;
cross.add(cross1, cross2);
cross.position.x = xOffset;
cross.position.y = yOffset;
cross.scale.x = 0;
cross.scale.y = 0;
cross.scale.z = 0;
this.crosses.add(cross);
}
_addCircle(xOffset, yOffset) {
const r = 6;
const height = 4;
const cylinderGeometry = new THREE.CylinderGeometry(r, r, height, 100);
const cylinderMaterial = new THREE.MeshNormalMaterial();
const cylinder = new THREE.Mesh(cylinderGeometry, cylinderMaterial);
cylinder.position.x = xOffset;
cylinder.position.y = yOffset;
cylinder.rotation.x = Math.PI / 2;
cylinder.scale.x = 0;
cylinder.scale.y = 0;
cylinder.scale.z = 0;
this.circles.add(cylinder);
}
}