Building a Tetramino Fitting Tool


After finishing with Tetris recreation we've decided that we need a tool to check if we can fill with tetris figures (Tetrimino) a specific form, that can be a canvas for a level, where players will be constructing stuff from these fegires, so below you can see the way it could be done. We will also do a second post about adding a convertor to obj and finding and fixing bugs.

Building a Tetrimino Shape Tool: First Steps

Stage 1: Setting Up the Basic Canvas and Grid

The first step in developing our Tetrimino shape tool was to create a canvas that would represent the grid. We used the HTML5 canvas element, which allowed us to draw a grid with set dimensions and prepare it for interaction. The grid serves as the foundation where all Tetrimino shapes will be placed.

This was achieved by drawing a grid on the canvas using JavaScript. We needed to ensure that each grid cell was of equal size and that the whole grid fit within the dimensions of the canvas.

        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');
        const gridSize = 20;
        const drawGrid = () => {
            ctx.strokeStyle = '#888';
            for (let y = 0; y < canvas.height; y += gridSize) {
                for (let x = 0; x < canvas.width; x += gridSize) {
                    ctx.strokeRect(x, y, gridSize, gridSize);
                }
            }
        };
    

Stage 2: Creating Tetrimino Shapes

With the grid in place, the next step was to define the Tetrimino shapes. Tetriminos are geometric shapes composed of squares, and each shape corresponds to a specific arrangement of squares. We defined these shapes in a matrix format, where 1 represents a filled square and 0 represents an empty space.

The shapes are drawn on the grid, and each Tetrimino type has a distinct color. For example, the "I" shape consists of a straight line of four squares, while the "T" shape is composed of three squares in a row with a square in the center.

        const tetriminos = {
            I: { shape: [[1, 1, 1, 1]], color: 'cyan' },
            O: { shape: [[1, 1], [1, 1]], color: 'yellow' },
            T: { shape: [[0, 1, 0], [1, 1, 1]], color: 'purple' },
            L: { shape: [[1, 0, 0], [1, 1, 1]], color: 'orange' },
            J: { shape: [[0, 0, 1], [1, 1, 1]], color: 'blue' },
            S: { shape: [[0, 1, 1], [1, 1, 0]], color: 'green' },
            Z: { shape: [[1, 1, 0], [0, 1, 1]], color: 'red' }
        };
    

Stage 3: Adding Tetriminos to the Grid

Once the shapes were defined, the next task was to allow users to place them onto the grid. We created buttons for each Tetrimino type, and when clicked, the corresponding shape would appear on the grid. The shape could then be dragged and dropped to different positions on the canvas.

This introduced some challenges in managing the coordinates and ensuring the shapes aligned with the grid cells. To solve this, we made sure all movement of the Tetriminos was done in steps that correspond to the grid size, preventing freeform movement.

        const addTetrimino = (type) => {
            const newShape = { shape: tetriminos[type].shape, color: tetriminos[type].color, x: 0, y: 0 };
            tetriminoObjects.push(newShape);
            updateCanvas();
        };
        const updateCanvas = () => {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            drawGrid();
            tetriminoObjects.forEach(obj => drawTetrimino(obj.shape, obj.x, obj.y, obj.color));
        };
    

Stage 4: Rotating the Shapes

Once users could place Tetriminos on the grid, the next feature we added was rotation. Players needed to be able to rotate the Tetriminos to fit them properly on the grid. We achieved this by implementing a 90-degree rotation function, which would shift the matrix of the shape and redraw it on the canvas.

We introduced a new interaction mode where users could select a shape and rotate it using the spacebar or a dedicated button. This allowed the shapes to be manipulated while staying aligned with the grid.

        const rotateMatrix = (matrix) => matrix[0].map((_, colIndex) => matrix.map(row => row[colIndex]).reverse());
        const rotateActiveShape = () => {
            if (selectedShape) {
                selectedShape.shape = rotateMatrix(selectedShape.shape);
                updateCanvas();
            }
        };
    

These initial stages laid the groundwork for the tool, allowing us to set up the canvas, define Tetrimino shapes, and give users the ability to add, move, and rotate the shapes on a grid. While these steps seemed simple at first glance, they posed interesting challenges that required creative problem-solving, particularly in ensuring that all movements and rotations aligned correctly with the grid.