- Given a sequence of tetrominos, this program will compute and display a way of dropping them into a grid such that the height of the resulting tetromino stack is minimised.
- The result displays which columns each tetromino should be dropped in, and the angle at which the tetrominos should be rotated before being dropped. The user can also specify to use the default rotations of tetrominos only.
- Dimensions of the grid are determined by the
GRID_HEIGHTandGRID_WIDTHmacros ingrid.h. - Maximum allowed length for a tetromino sequence is determined by the
MAX_SEQUENCE_SIZEmacro intetromino.h.
- The best permutation (a column/rotation value for each tetromino) is calculated by trying all possible permutations and saving the one which produced the shortest stack. The order in which the permutations are tried is shown below:
- A tetromino can be dropped into
GRID_WIDTH + 1 - TETROMINO_WIDTHcolumns, whereTETROMINO_WIDTHis the width of a tetromino in a specific rotation (0, 90, 180, or 270 degrees). If the tetromino hasrrotations, (assuming its width is the same in all rotations) the number of permutations for that tetromino becomesr * (GRID_WIDTH + 1 - TETROMINO_WIDTH). Therefore, the number of permutations for a sequence of lengthnbecomes(r * (GRID_WIDTH + 1 - TETROMINO_WIDTH)) ** n. - In order to handle the exponentially growing number of permutations, certain optimisations are implemented:
- Divide and Conquer: The search space of all permutations is divided and assigned to
solverunits which independently try the permutations assigned to them. Each solver unit runs on a seperate solver thread for concurrent operation. Multi-threading is supported for Windows and Linux, otherwise a single solver unit is used which runs on the main thread. The number of solver units is determined by theNUMBER_OF_SOLVERSmacro insolver.h(defaults to 16 for Windows/Linux). - Efficient Collision Detection: When dropping tetrominos into a grid, the state of the grid is stored and updated using the column heights of the grid/tetromino, instead of scanning the values in each cell of the pattern.
- Grid State Restoration: When trying a permutation, the grid state obtained after dropping each tetromino is individually saved. Given that the next permutation changes the column/rotation of piece
n, restore the grid state from the previous permutation before dropping piecento avoid dropping these pieces again. This significantly reduces the number of collision detection calculations - Search Tree Pruning: During solving, given that the current lowest stack height is
m, and the length of the sequence isn, if dropping the firstp (p < n)pieces gives a stack height>= m, skip all permutations which have the prefix of the firstppieces at their current orientation:
- Divide and Conquer: The search space of all permutations is divided and assigned to
- Overflow detection: If the number of permutations for a sequence is greater than 2^64, an overflow in the 64-bit permutation counter is detected and the solving operation is aborted as all permutations cannot be tried.
- Debug mode: Creates an environment where the user can drop tetrominos into a grid one by one, in the specified column/rotation.
- Tests: The program solves the testcase tetromino sequences in
test.cand compares the solutions with the testcase solutions. Used during development and for verifying correct compilation - VSCode Build File:
.vscode/tasks.jsoncontains the build configuration settings for compiling the code in this repository using VSCode.
If you need other shapes which are specific to your problem domain, you can add custom tetrominos:
- In
tetromino.cdefine atetrominoarray containing atetrominostruct for each rotation of your custom tetromino. In thePatternattribute enter a'_'for blank cells and in others enter a differentcharwhich will represent your tetromino. For each rotation of your tetromino, a width/height value and a column heights array must also be supplied. Maximum dimensions for a tetromino are 4x4. - Declare your tetromino array in
tetromino.h. - Change the
getTetrominoandgetRotationsfunctions intetromino.c, and thegetSequencefunction ininput_utils.cto add a case for your new tetromino.

