Robots/JavaScript

From Rosetta Code

Code

const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');

const Grid = {
    Player: "@",
    Robot: "+",
    Scrap: "*",
    Mark: "~"
};

const dirs = [[-1, 1], [0, 1], [1, 1], [-1, 0], [1, 0], [-1, -1], [0, -1], [1, -1]];

let nRows = 38;
let nCols = 50;
let grid = [];
let playerRow, playerCol, score, hiScore, level;
let gameOver = true;

function initGrid() {
    grid = Array(nRows).fill().map(() => Array(nCols).fill(null));
    teleport();

    const numRobots = 7 * level;
    for (let i = 0; i < numRobots;) {
        const r = Math.floor(Math.random() * nRows);
        const c = Math.floor(Math.random() * nCols);
        if (grid[r][c] === null) {
            grid[r][c] = Grid.Robot;
            i++;
        }
    }
}

function startNewGame() {
    level = 1;
    if (score > hiScore) hiScore = score;
    score = 0;
    initGrid();
    gameOver = false;
    draw();
}

function movePlayer(r, c) {
    if (!withinBounds(r, c) || grid[r][c] !== null) {
        gameOver = true;
    } else {
        if (playerRow !== undefined && playerCol !== undefined) {
            grid[playerRow][playerCol] = null;
        }
        playerRow = r;
        playerCol = c;
        grid[r][c] = Grid.Player;
    }
    return !gameOver;
}

function move(dr, dc) {
    if (playerRow === undefined || playerCol === undefined) return;
    const r = playerRow + dr;
    const c = playerCol + dc;

    if (!withinBounds(r, c)) return;
    if (!movePlayer(r, c)) return;

    for (let rr = 0; rr < nRows; rr++) {
        for (let cc = 0; cc < nCols; cc++) {
            if (grid[rr][cc] === Grid.Robot) {
                const nc = (c === cc ? 0 : (c - cc) / Math.abs(c - cc)) + cc;
                const nr = (r === rr ? 0 : (r - rr) / Math.abs(r - rr)) + rr;

                if (!withinBounds(nr, nc)) continue;

                grid[rr][cc] = null;

                if (grid[nr][nc] === Grid.Player) {
                    gameOver = true;
                    return;
                } else if (grid[nr][nc] !== null) {
                    score++;
                    if (grid[nr][nc] !== Grid.Scrap) score++;
                    grid[nr][nc] = Grid.Scrap;
                } else {
                    grid[nr][nc] = Grid.Mark;
                }
            }
        }
    }

    let robotsLeft = 0;
    for (let rr = 0; rr < nRows; rr++) {
        for (let cc = 0; cc < nCols; cc++) {
            if (grid[rr][cc] === Grid.Mark) grid[rr][cc] = Grid.Robot;
            if (grid[rr][cc] === Grid.Robot) robotsLeft++;
        }
    }

    if (robotsLeft === 0) {
        level++;
        initGrid();
    }
}

function teleport() {
    movePlayer(Math.floor(Math.random() * nRows), Math.floor(Math.random() * nCols));
}

function withinBounds(r, c) {
    return c >= 0 && c < nCols && r >= 0 && r < nRows;
}

function drawBorder() {
    ctx.strokeStyle = 'lightgray';
    ctx.setLineDash([5, 5]);
    ctx.strokeRect(22, 20, canvas.width - 41, canvas.height - 72);
    ctx.setLineDash([]);
}

function drawGrid() {
    ctx.font = '18px SansSerif';
    ctx.fillStyle = 'black';
    for (let r = 0; r < nRows; r++) {
        for (let c = 0; c < nCols; c++) {
            if (grid[r][c] !== null) {
                ctx.fillText(grid[r][c], 24 + c * 15, 36 + r * 15);
            }
        }
    }
}

function drawStartScreen() {
    ctx.fillStyle = 'gray';
    ctx.font = 'bold 48px SansSerif';
    ctx.fillText('robots', 315, 280);

    ctx.font = '18px SansSerif';
    ctx.fillText('(use arrow keys to move player)', 270, 350);
    ctx.fillText('(press T to teleport)', 300, 380);
    ctx.fillText('(click to start)', 328, 410);
}

function drawScore() {
    ctx.fillStyle = 'gray';
    ctx.font = '18px SansSerif';
    ctx.fillText(`hiscore   ${hiScore}    score   ${score}`, 30, canvas.height - 17);
}

function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = 'white';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    drawBorder();
    drawScore();
    if (gameOver) {
        drawStartScreen();
    } else {
        drawGrid();
    }
}

canvas.addEventListener('mousedown', () => {
    if (gameOver) {
        startNewGame();
    }
});

document.addEventListener('keydown', (e) => {
    if (gameOver) return;

    switch(e.key) {
        case 'ArrowUp':
            move(-1, 0);
            break;
        case 'ArrowDown':
            move(1, 0);
            break;
        case 'ArrowLeft':
            move(0, -1);
            break;
        case 'ArrowRight':
            move(0, 1);
            break;
        case 't':
        case 'T':
            teleport();
            break;
    }
    draw();
});

hiScore = 0;
score = 0;
initGrid();
draw();