Skip to main content

tetris.golf.html

A Tetris game for t0.vc

gist link


Hover mouse over canvas to allow keyboard input.
Use arrow keys to play.
Someone else can add touch controls (see window.Tetris).
<canvas id="tetris" style="border: solid 1px #000; background: #eee" width="200" height="400"></canvas>
<script>
(($Window, $Document, _m_addEventListener, _m_preventDefault, _m_forEach, _prop_$canvas, _prop_score, _prop_board, _prop_piece, _const_COLS, _const_ROWS, _const_SHAPES, _const_COLORS, _const_ROW_SCORE) => {
    class Tetris {
        static I = {};
        constructor(selector) {
            if (Tetris.I[selector])
                return Tetris.I[selector];
            Tetris.I[selector] = this;

            this[_prop_$canvas] = $Document.querySelector(selector);
            this.ctx = this[_prop_$canvas].getContext("2d");
            this.ms = 350;
            this.reset();
            this._L();

        }
        _L() {
            this.drop(0);
            this.draw();
            setTimeout(() => this._L(), this.ms);
        }
        reset() {
            this[_prop_score] = 0
            this[_prop_board] = Array.from({ length: _const_ROWS }, () => Array(_const_COLS).fill(0));
            this._N();
        };
        _N(index) {
            index = Math.floor(Math.random() * _const_SHAPES.length);
            this[_prop_piece] = { m: _const_SHAPES[index], c: index + 1, x: 4, y: 0 };
        };
        draw() {
            this.ctx.clearRect(0, 0, this[_prop_$canvas].width, this[_prop_$canvas].height);
            this[_prop_board][_m_forEach]((row, y) => row[_m_forEach]((color, x) => {
                if (color) this._B(x, y, color);
            }));
            const { x, y, m, c } = this[_prop_piece];
            m[_m_forEach]((row, j) => row[_m_forEach]((value, i) => {
                if (value) this._B(x + i, y + j, c);
            }));

            this.ctx.fillStyle = _const_COLORS[0];
            this.ctx.fillText(`${_prop_score}: ${this[_prop_score]}`, 1, 10);
        }
        _B(x, y, color, size) {
            size = _const_ROWS;
            this.ctx.fillStyle = _const_COLORS[color];
            this.ctx.strokeStyle = _const_COLORS[0];
            this.ctx.fillRect(x * size, y * size, size, size);
            this.ctx.strokeRect(x * size, y * size, size, size);
        }
        _C() {
            const { x, y, m } = this[_prop_piece];
            return m.some((row, dy) => row.some((value, dx) =>
                value && (this[_prop_board][y + dy]?.[x + dx] || y + dy >= _const_ROWS)
            ));
        }

        drop(mult, count) {
            this[_prop_piece].y++;
            this[_prop_score] += 1 + 2 * (mult || 0);
            if (this._C()) {
                this[_prop_piece].y--;
                const { x, y, c, m } = this[_prop_piece];
                m[_m_forEach]((row, j) => row[_m_forEach]((value, i) => {
                    if (value) this[_prop_board][y + j][x + i] = c;
                }));
                count = 0;
                this[_prop_board] = this[_prop_board].filter((row, _, __, full) => (full = row.every(cell => cell), count += full, !full));
                this[_prop_score] += _const_ROW_SCORE[count];
                while (this[_prop_board].length < _const_ROWS) this[_prop_board].unshift(Array(_const_COLS).fill(0));
                this._N();
                if (this._C()) this.reset();
            }
        }
        move(dir) {
            this[_prop_piece].x += dir;
            if (this[_prop_piece].x < 0 || this[_prop_piece].x + this[_prop_piece].m[0].length > _const_COLS || this._C()) {
                this[_prop_piece].x -= dir;
            }
        }

        rot8(temp, old) {
            temp = this[_prop_piece].m[0].map((_, i) => this[_prop_piece].m.map(row => row[i]).reverse());
            old = this[_prop_piece].m;
            this[_prop_piece].m = temp;
            if (this._C()) this[_prop_piece].m = old;
        }

        play(k, e) {
            if (k === "ArrowLeft") { e[_m_preventDefault](); this.move(-1); }
            if (k === "ArrowRight") { e[_m_preventDefault](); this.move(1); }
            if (k === "ArrowDown") { e[_m_preventDefault](); this.drop(1); }
            if (k === "ArrowUp") { e[_m_preventDefault](); this.rot8(); }
        }
    }
    $Window.Tetris = Tetris;
    $Document[_m_addEventListener]("DOMContentLoaded", (game, keydown, on, toggle) => {
        on = false;
        game = new Tetris('#tetris');
        keydown = (e) => game.play(e.key, e);
        toggle = (o) => $Document[o ? (game.reset(), _m_addEventListener) : 'removeEventListener']("keydown", keydown);
        game.$canvas[_m_addEventListener]('mouseenter', () => toggle((on = true, on)));
        game.$canvas[_m_addEventListener]('mouseout', () => toggle((on = false, on)));
    });
})(
    window,
    document,
    'addEventListener',
    'preventDefault',
    'forEach',
    '$canvas',
    'score',
    'board',
    'piece',
    10,
    20,
    [
        [[1, 1, 1, 1]],
        [[1, 1], [1, 1]],
        [[0, 1, 0], [1, 1, 1]],
        [[1, 1, 0], [0, 1, 1]],
        [[0, 1, 1], [1, 1, 0]],
        [[1, 0, 0], [1, 1, 1]],
        [[0, 0, 1], [1, 1, 1]]
    ],
    ['#000', "#0FF", "#FF0", "#80F", "#0F0", "#F00", "#F80", "#00F"],
    [0, 100, 300, 500, 800]
);
</script>