|
@@ -1,419 +0,0 @@
|
|
|
-//Javascript Connect 4 Game
|
|
|
-
|
|
|
-function start_game() {
|
|
|
- "use strict";
|
|
|
-
|
|
|
- let button = document.getElementById("button");
|
|
|
- let h1 = document.getElementById("h1");
|
|
|
- let h3 = document.getElementById('h3');
|
|
|
- h3.remove()
|
|
|
- h1.remove();
|
|
|
- button.remove();
|
|
|
- const DELAY_COMP = 0.5;
|
|
|
- const GRID_CIRCLE = 0.7;
|
|
|
- const GRID_COLS = 7;
|
|
|
- const GRID_ROWS = 6;
|
|
|
- const MARGIN = 0.02;
|
|
|
- const COLOR_BACKGROUND = "mintcream";
|
|
|
- const COLOR_COMP = "yellow";
|
|
|
- const COLOR_COMP_DRK = "olive";
|
|
|
- const COLOR_FRAME = "dodgerblue";
|
|
|
- const COLOR_FRAME_BUTT = "royalblue";
|
|
|
- const COLOR_PLAY = "red";
|
|
|
- const COLOR_PLAY_DRK = "darkred";
|
|
|
- const COLOR_TIE = "darkgrey";
|
|
|
- const COLOR_TIE_DRK = "black";
|
|
|
- const COLOR_WIN = "black";
|
|
|
- const TEXT_COMP = "Computer";
|
|
|
- const TEXT_PLAY = "YOU";
|
|
|
- const TEXT_TIE = "DRAW";
|
|
|
- const TEXT_WIN = "WON!";
|
|
|
- class Cell {
|
|
|
- constructor(left, top, w, h, row, col) {
|
|
|
- this.bot = top + h;
|
|
|
- this.left = left;
|
|
|
- this.right = left + w;
|
|
|
- this.top = top;
|
|
|
- this.w = w;
|
|
|
- this.h = h;
|
|
|
- this.row = row;
|
|
|
- this.col = col;
|
|
|
- this.cx = left + w / 2;
|
|
|
- this.cy = top + h / 2;
|
|
|
- this.r = w * GRID_CIRCLE / 2;
|
|
|
- this.highlight = null;
|
|
|
- this.owner = null;
|
|
|
- this.winner = false;
|
|
|
- }
|
|
|
-
|
|
|
- contains(x, y) {
|
|
|
- return x > this.left && x < this.right && y > this.top && y < this.bot;
|
|
|
- }
|
|
|
- draw(/** @type {CanvasRenderingContext2D} */ ctx) {
|
|
|
- let color = this.owner == null ? COLOR_BACKGROUND : this.owner ? COLOR_PLAY : COLOR_COMP;
|
|
|
- ctx.fillStyle = color;
|
|
|
- ctx.beginPath();
|
|
|
- ctx.arc(this.cx, this.cy, this.r, 0, Math.PI * 2);
|
|
|
- ctx.fill();
|
|
|
- if (this.winner || this.highlight != null) {
|
|
|
- color = this.winner ? COLOR_WIN : this.highlight ? COLOR_PLAY : COLOR_COMP;
|
|
|
- ctx.lineWidth = this.r / 4;
|
|
|
- ctx.strokeStyle = color;
|
|
|
- ctx.beginPath();
|
|
|
- ctx.arc(this.cx, this.cy, this.r, 0, Math.PI * 2);
|
|
|
- ctx.stroke();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- var canv = document.createElement("canvas");
|
|
|
- document.body.appendChild(canv);
|
|
|
- var ctx = canv.getContext("2d");
|
|
|
- var gameOver, gameTied, grid = [], playersTurn, timeComp;
|
|
|
- var height, width, margin;
|
|
|
- setDimensions();
|
|
|
- canv.addEventListener("click", click);
|
|
|
- canv.addEventListener("mousemove", highlightGrid);
|
|
|
- window.addEventListener("resize", setDimensions);
|
|
|
- var timeDelta, timeLast;
|
|
|
- requestAnimationFrame(loop);
|
|
|
-
|
|
|
- function loop(timeNow) {
|
|
|
- if (!timeLast) {
|
|
|
- timeLast = timeNow;
|
|
|
- }
|
|
|
- timeDelta = (timeNow - timeLast) / 1000; // seconds
|
|
|
- timeLast = timeNow;
|
|
|
-
|
|
|
- goComputer(timeDelta);
|
|
|
-
|
|
|
- drawBackground();
|
|
|
- drawGrid();
|
|
|
- drawText();
|
|
|
-
|
|
|
- requestAnimationFrame(loop);
|
|
|
- }
|
|
|
-
|
|
|
- function checkWin(row, col) {
|
|
|
-
|
|
|
- let diagL = [], diagR = [], horiz = [], vert = [];
|
|
|
- for (let i = 0; i < GRID_ROWS; i++) {
|
|
|
- for (let j = 0; j < GRID_COLS; j++) {
|
|
|
-
|
|
|
- if (i == row) {
|
|
|
- horiz.push(grid[i][j]);
|
|
|
- }
|
|
|
-
|
|
|
- if (j == col) {
|
|
|
- vert.push(grid[i][j]);
|
|
|
- }
|
|
|
-
|
|
|
- if (i - j == row - col) {
|
|
|
- diagL.push(grid[i][j]);
|
|
|
- }
|
|
|
-
|
|
|
- if (i + j == row + col) {
|
|
|
- diagR.push(grid[i][j]);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return connect4(diagL) || connect4(diagR) || connect4(horiz) || connect4(vert);
|
|
|
- }
|
|
|
-
|
|
|
- function connect4(cells = []) {
|
|
|
- let count = 0, lastOwner = null;
|
|
|
- let winningCells = [];
|
|
|
- for (let i = 0; i < cells.length; i++) {
|
|
|
-
|
|
|
- if (cells[i].owner == null) {
|
|
|
- count = 0;
|
|
|
- winningCells = [];
|
|
|
- }
|
|
|
-
|
|
|
- else if (cells[i].owner == lastOwner) {
|
|
|
- count++;
|
|
|
- winningCells.push(cells[i]);
|
|
|
- }
|
|
|
-
|
|
|
- else {
|
|
|
- count = 1;
|
|
|
- winningCells = [];
|
|
|
- winningCells.push(cells[i]);
|
|
|
- }
|
|
|
-
|
|
|
- lastOwner = cells[i].owner;
|
|
|
-
|
|
|
- if (count == 4) {
|
|
|
- for (let cell of winningCells) {
|
|
|
- cell.winner = true;
|
|
|
- }
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- function click(ev) {
|
|
|
-
|
|
|
- if (gameOver) {
|
|
|
- newGame();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (!playersTurn) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- selectCell();
|
|
|
- }
|
|
|
-
|
|
|
- function createGrid() {
|
|
|
- grid = [];
|
|
|
-
|
|
|
- let cell, marginX, marginY;
|
|
|
-
|
|
|
- if ((width - margin * 2) * GRID_ROWS / GRID_COLS < height - margin * 2) {
|
|
|
- cell = (width - margin * 2) / GRID_COLS;
|
|
|
- marginX = margin;
|
|
|
- marginY = (height - cell * GRID_ROWS) / 2;
|
|
|
- }
|
|
|
-
|
|
|
- else {
|
|
|
- cell = (height - margin * 2) / GRID_ROWS;
|
|
|
- marginX = (width - cell * GRID_COLS) / 2;
|
|
|
- marginY = margin;
|
|
|
- }
|
|
|
-
|
|
|
- for (let i = 0; i < GRID_ROWS; i++) {
|
|
|
- grid[i] = [];
|
|
|
- for (let j = 0; j < GRID_COLS; j++) {
|
|
|
- let left = marginX + j * cell;
|
|
|
- let top = marginY + i * cell;
|
|
|
- grid[i][j] = new Cell(left, top, cell, cell, i, j);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- function drawBackground() {
|
|
|
- ctx.fillStyle = COLOR_BACKGROUND;
|
|
|
- ctx.fillRect(0, 0, width, height);
|
|
|
- }
|
|
|
-
|
|
|
- function drawGrid() {
|
|
|
-
|
|
|
- let cell = grid[0][0];
|
|
|
- let fh = cell.h * GRID_ROWS;
|
|
|
- let fw = cell.w * GRID_COLS;
|
|
|
- ctx.fillStyle = COLOR_FRAME;
|
|
|
- ctx.fillRect(cell.left, cell.top, fw, fh);
|
|
|
- ctx.fillStyle = COLOR_FRAME_BUTT;
|
|
|
- ctx.fillRect(cell.left - margin / 2, cell.top + fh - margin / 2, fw + margin, margin);
|
|
|
-
|
|
|
- for (let row of grid) {
|
|
|
- for (let cell of row) {
|
|
|
- cell.draw(ctx);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- function drawText() {
|
|
|
- if (!gameOver) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- let size = grid[0][0].h;
|
|
|
- ctx.fillStyle = gameTied ? COLOR_TIE : playersTurn ? COLOR_PLAY : COLOR_COMP;
|
|
|
- ctx.font = size + "px dejavu sans mono";
|
|
|
- ctx.lineJoin = "round";
|
|
|
- ctx.lineWidth = size / 10;
|
|
|
- ctx.strokeStyle = gameTied ? COLOR_TIE_DRK : playersTurn ? COLOR_PLAY_DRK : COLOR_COMP_DRK;
|
|
|
- ctx.textAlign = "center";
|
|
|
- ctx.textBaseline = "middle";
|
|
|
-
|
|
|
- let offset = size * 0.55;
|
|
|
- let text = gameTied ? TEXT_TIE : playersTurn ? TEXT_PLAY : TEXT_COMP;
|
|
|
- if (gameTied) {
|
|
|
- ctx.strokeText(text, width / 2, height / 2);
|
|
|
- ctx.fillText(text, width / 2, height / 2);
|
|
|
- } else {
|
|
|
- ctx.strokeText(text, width / 2, height / 2 - offset);
|
|
|
- ctx.fillText(text, width / 2, height / 2 - offset);
|
|
|
- ctx.strokeText(TEXT_WIN, width / 2, height / 2 + offset);
|
|
|
- ctx.fillText(TEXT_WIN, width / 2, height / 2 + offset);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- function goComputer(delta) {
|
|
|
- if (playersTurn || gameOver) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (timeComp > 0) {
|
|
|
- timeComp -= delta;
|
|
|
- if (timeComp <= 0) {
|
|
|
- selectCell();
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- let options = [];
|
|
|
- options[0] = [];
|
|
|
- options[1] = [];
|
|
|
- options[2] = [];
|
|
|
- options[3] = [];
|
|
|
-
|
|
|
- let cell;
|
|
|
- for (let i = 0; i < GRID_COLS; i++) {
|
|
|
- cell = highlightCell(grid[0][i].cx, grid[0][i].cy);
|
|
|
-
|
|
|
- if (cell == null) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- cell.owner = playersTurn;
|
|
|
- if (checkWin(cell.row, cell.col)) {
|
|
|
- options[0].push(i);
|
|
|
- } else {
|
|
|
-
|
|
|
- cell.owner = !playersTurn;
|
|
|
- if (checkWin(cell.row, cell.col)) {
|
|
|
- options[1].push(i);
|
|
|
- } else {
|
|
|
- cell.owner = playersTurn;
|
|
|
-
|
|
|
- if (cell.row > 0) {
|
|
|
- grid[cell.row - 1][cell.col].owner = !playersTurn;
|
|
|
-
|
|
|
- if (checkWin(cell.row - 1, cell.col)) {
|
|
|
- options[3].push(i);
|
|
|
- }
|
|
|
-
|
|
|
- else {
|
|
|
- options[2].push(i);
|
|
|
- }
|
|
|
-
|
|
|
- grid[cell.row - 1][cell.col].owner = null;
|
|
|
- }
|
|
|
-
|
|
|
- else {
|
|
|
- options[2].push(i);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- cell.highlight = null;
|
|
|
- cell.owner = null;
|
|
|
- }
|
|
|
-
|
|
|
- for (let row of grid) {
|
|
|
- for (let cell of row) {
|
|
|
- cell.winner = false;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- let col;
|
|
|
- if (options[0].length > 0) {
|
|
|
- col = options[0][Math.floor(Math.random() * options[0].length)];
|
|
|
- } else if (options[1].length > 0) {
|
|
|
- col = options[1][Math.floor(Math.random() * options[1].length)];
|
|
|
- } else if (options[2].length > 0) {
|
|
|
- col = options[2][Math.floor(Math.random() * options[2].length)];
|
|
|
- } else if (options[3].length > 0) {
|
|
|
- col = options[3][Math.floor(Math.random() * options[3].length)];
|
|
|
- }
|
|
|
-
|
|
|
- highlightCell(grid[0][col].cx, grid[0][col].cy);
|
|
|
-
|
|
|
- timeComp = DELAY_COMP;
|
|
|
- }
|
|
|
-
|
|
|
- function highlightCell(x, y) {
|
|
|
- let col = null;
|
|
|
- for (let row of grid) {
|
|
|
- for (let cell of row) {
|
|
|
-
|
|
|
- cell.highlight = null;
|
|
|
-
|
|
|
- if (cell.contains(x, y)) {
|
|
|
- col = cell.col;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (col == null) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- for (let i = GRID_ROWS - 1; i >= 0; i--) {
|
|
|
- if (grid[i][col].owner == null) {
|
|
|
- grid[i][col].highlight = playersTurn;
|
|
|
- return grid[i][col];
|
|
|
- }
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- function highlightGrid(/** @type {MouseEvent} */ ev) {
|
|
|
- if (!playersTurn || gameOver) {
|
|
|
- return;
|
|
|
- }
|
|
|
- highlightCell(ev.clientX, ev.clientY);
|
|
|
- }
|
|
|
-
|
|
|
- function newGame() {
|
|
|
- playersTurn = Math.random() < 0.5;
|
|
|
- gameOver = false;
|
|
|
- gameTied = false;
|
|
|
- createGrid();
|
|
|
- }
|
|
|
-
|
|
|
- function selectCell() {
|
|
|
- let highlighting = false;
|
|
|
- OUTER: for (let row of grid) {
|
|
|
- for (let cell of row) {
|
|
|
- if (cell.highlight != null) {
|
|
|
- highlighting = true;
|
|
|
- cell.highlight = null;
|
|
|
- cell.owner = playersTurn;
|
|
|
- if (checkWin(cell.row, cell.col)) {
|
|
|
- gameOver = true;
|
|
|
- }
|
|
|
- break OUTER;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!highlighting) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (!gameOver) {
|
|
|
- gameTied = true;
|
|
|
- OUTER: for (let row of grid) {
|
|
|
- for (let cell of row) {
|
|
|
- if (cell.owner == null) {
|
|
|
- gameTied = false;
|
|
|
- break OUTER;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (gameTied) {
|
|
|
- gameOver = true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!gameOver) {
|
|
|
- playersTurn = !playersTurn;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- function setDimensions() {
|
|
|
- height = window.innerHeight;
|
|
|
- width = window.innerWidth;
|
|
|
- canv.height = height;
|
|
|
- canv.width = width;
|
|
|
- margin = MARGIN * Math.min(height, width);
|
|
|
- newGame();
|
|
|
- }
|
|
|
-}
|