JSFiddle - React, Tailwind, and code Playground

HTML

<!DOCTYPE html>
<html>
<head>
    <title>Крестики-нолики с Тетрисом</title>
    <style>
        canvas {
            border: 1px solid black;
        }
    </style>
</head>
<body>
    <canvas id="game-canvas" width="400" height="600"></canvas>
    <script>
        // Размеры поля
        const FIELD_WIDTH = 10;
        const FIELD_HEIGHT = 15;
        const CELL_SIZE = 40;

        // Направления движения
        const DIRECTIONS = {
            LEFT: 'left',
            RIGHT: 'right',
            DOWN: 'down'
        };

        // Состояние игры
        let gameState = {
            field: Array(FIELD_HEIGHT).fill().map(() => Array(FIELD_WIDTH).fill(null)),
            currentPiece: null,
            currentPlayer: 'X',
            gameOver: false
        };

        // Отрисовка игрового поля
        function drawField() {
            const canvas = document.getElementById('game-canvas');
            const ctx = canvas.getContext('2d');
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            for (let y = 0; y < FIELD_HEIGHT; y++) {
                for (let x = 0; x < FIELD_WIDTH; x++) {
                    const piece = gameState.field[y][x];
                    if (piece) {
                        ctx.fillStyle = piece === 'X' ? 'red' : 'blue';
                        ctx.fillRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
                    }
                }
            }

            if (gameState.currentPiece) {
                gameState.currentPiece.forEach(([x, y]) => {
                    ctx.fillStyle = gameState.currentPlayer === 'X' ? 'red' : 'blue';
                    ctx.fillRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
                });
            }
        }

        // Проверка на победу
        function checkWin() {
            const field = gameState.field;
            const currentPlayer = gameState.currentPlayer;

            // Проверка по горизонтали
            for (let y = 0; y <...