mis en place la PR de simon sur les couleurs

This commit is contained in:
2025-02-28 18:42:04 +01:00
parent f0f391ade6
commit 26f501f7e8
29 changed files with 713 additions and 665 deletions

View File

@@ -28,8 +28,8 @@ Moving and soft dropping can be held but hard dropping, holding and rotating nee
In a general sense, we try to be kind to the players. Some of the following mechanics are just standards in a lot of stacker games, while other have been added because of this game using polyominos of high sizes and thus being way harder to play.
When a piece touches the ground, there is a short time period before she automatically locks. This period is called _lock delay_. Lock delay is reset everytime the piece move. To not allow for infinite stalling, there is another longer period called _forced lock delay_ that does not reset when moving the piece.
The player as a hold box in which they can temporarly store a piece. In this game, we allow the player to swap between the held piece and the active piece as much as they want. Once again to not allow for infinite stalling, forced lock delay does not reset when holding a piece.
When a piece touches the ground, there is a short time period before it automatically locks. This period is called _lock delay_. Lock delay is reset everytime the piece move. To not allow for infinite stalling, there is another longer period called _forced lock delay_ that does not reset when moving the piece.
The player has a hold box in which they can temporarly store a piece. In this game, we allow the player to swap between the held piece and the active piece as much as they want. Once again to not allow for infinite stalling, forced lock delay does not reset when holding a piece.
If either holding or rotating happens during frames where no piece is in the board, they will be memorized and immediately applied upon spawning the next piece. This can sometime prevent the player from loosing when the default spawn would have lost the game. This is called IRS and IHS, for Instant Rotation/Hold System.
IRS and IHS will fail if they actually loose the player the game when it would have not happened otherwise. In the same sense, holding always fails if it would loose the game.
@@ -42,26 +42,26 @@ This concept works very well for games with up to tetrominos or pentominos, but
Since this game uses polyomino of high sizes which are very unplayable, we will try to be complaisant to the player and allow as much kicking spots as possible, while trying not to make him feel like the piece is going through walls. To solve this problem, this game introduce a new Rotation System called **AutoRS**, which does not have a predetermined list of spots to fit the piece but instead adapt to its shape. Its algorithm goes as follow:
1. Before rotating, mark every cell containing the piece or touching the piece, we will call the set of all theses cells the ``safeCells``
1. Before rotating, mark every position containing the piece or touching the piece, we will call the set of all theses positions the ``safePositions``
2. Rotate the piece, if it fit stop the algorithm
3. Try fitting the piece, going from the center to the sides, that means we try to move the piece 1 cell right, then 1 cell left, then 2 cell right, etc. until it fit (and then stop the algorithm), if at one point a position doesn't touch one of the ``safeCells`` we stop trying in this direction
4. Move the piece one line down, and repeat step 3 again, until we hit a line were the first position (shifted by 0 cells horizontally) touched none of the ``safeCells``
3. Try fitting the piece, going from the center to the sides, that means we try to move the piece 1 position right, then 1 position left, then 2 position right, etc. until it fit (and then stop the algorithm), if at one point a position doesn't touch one of the ``safePositions`` we stop trying in this direction
4. Move the piece one line down, and repeat step 3 again, until we hit a line were the first position (shifted by 0 positions horizontally) touched none of the ``safePositions``
5. Do the same as step 4 but now we move the piece one line up every time
6. Cancel the rotation
## Detecting spins
Another common mechanic of stacker games that goes alongside kicking is spinning. A spin is a special move (a move is calculated once a piece has been locked to the board) which usually happen when the last move a piece did was a kick or a rotation and the piece is locked in place, but the rules varies a lot from game to game.
Another common mechanic of stacker games that goes alongside kicking is spinning. A spin is a special move (a move is calculated once a piece has been locked to the board) which usually happen when the last move a piece did was a kick or a rotation and the piece is locked in place or is locked in certain corners, but the rules varies a lot from game to game.
Since we deal with a great deal of different size and shapes, the rules for spin dectection have been simplified greatly:
Since we work with a great deal of different size and shapes, the rules for spin dectection have been simplified greatly:
- A move is a _spin_ if the piece is locked in place, that is it can't be moved one cell up, down, left or right without hitting a wall
- A move is a _spin_ if the piece is locked in place, that is it can't be moved one position up, down, left or right without hitting a wall
- A move is a _mini spin_ if the move isn't a spin and the last action of the piece was a kick (dropping down because of gravity counts as an action)
## Score calculation
- For every cell soft dropped, add 1 to the score
- For every cell hard dropped, add 2 to the score
- For every position soft dropped, add 1 to the score
- For every position hard dropped, add 2 to the score
- When clearing one line, add 100 to the score, 200 for 2 lines, 400 for 3 lines, 800 for 4 lines, 1600 for 5 lines, etc.
- If the line clear is a spin, count the score like a normal clear of 2x more line (200 for 1-line spin, 800 for 2, 3200 for 3, etc.)
- When performing a spin, a mini spin, or clearing 4 or more lines, B2B is activated, every subsequent line clear that is a spin, a mini spin, or clear 4 or more lines, scores twice as much

View File

@@ -2,9 +2,9 @@
## What are polyominos ?
In this game, pieces are represented as a polyomino with a color.
In this game, pieces are represented as a polyomino and a block type.
Polyominos are mathematical objects consisting of multiple edge-touching squares.
There must be a path from every cell to every other cell, going from square to square only through the sides and not the corners.
There must be a path from every square to every other square, going from square to square only through the sides and not the corners.
Polyominos can be classified in 3 ways:
- Fixed polyominos : only translation is allowed
@@ -13,33 +13,36 @@ Polyominos can be classified in 3 ways:
For more detailed informations about polyominos, check the [Wikipedia page](https://en.wikipedia.org/wiki/Polyomino).
Most stacker game uses one-sided polyominos, which results in 7 polyominos of size 4, also known as tetrominos.
Most stacker game uses all one-sided polyominos of size 4 (called tetrominos), which results in 7 distinct polyominos.
In this game too, one-sided polyominos will be used since we will only allow to move and rotate the pieces.
Internally, Polyominos are represented as a set of Cell.
This means the cells can be in any order but can't be duplicates.
A cell is simply a position on a 2D grid, so a polyomino is determined by the position of its cells.
This means however that 2 polyominos of same shape but different positions will be interpreted as different polyominos.
To solve this, we normalize the position of the polyominos so that their left-most column is at x=0 and their bottom-most row at y=0.
Internally, polyominos are represented as a set of positions on a 2D grid.
This means that 2 polyominos of same shape but at different places will be interpreted as different polyominos.
To solve this, when doing equality checks, we normalize the polyominos so that their left-most column is at x=0 and their bottom-most row at y=0.
Even if there is only 7 one-sided 4-minos, there is already more than 9,000 one-sided 10-minos.
Since the aim of this game is to be playable with polyominos of any size, listing all polyominos manually isn't viable.
We will need a way to:
We will need a way to automatically:
1. Generate all one-sided polyominos of size n
2. Set their spawn positions and rotation centers
Aditionally, for this game we will also a want a way to separate the polyominos into multiple categories, specifically to allow removing those with holes who are more unplayable.
Aditionally, for this game we will also a want a way to separate the polyominos into multiple categories, specifically to allow removing those with holes who are harder to play with.
## Ordering the polyominos
For practical reasons, we want to be able to sort all polyominos of the same size.
This is done very simply:
But to sort objects we need a way to compare them.
When a polyomino is created, an attribute named its length is computed. This is simply the max between its width and its height. Thus the polyomino can be inscribed in a box of size ``length``.
We will now assume that our polyominos are always inscribed in a box of origin (0,0) and size ``length`` (which should always be the case under normal circumstances).
We can now compare polyominos using this method:
- If one polyomino has an inferior length than another, it is deemed inferior
- If two polyomino have the same length, we check all the cells of their square, from left to right, and repeating from up to bottom, for the first position where a polyomino has a cell that another doesn't, the polyomino with the cell is deemed inferior
- If two polyomino have the same length, we check all the positions of their box, from left to right, and repeating from up to bottom, for the first position where a polyomino has a square and the another doesn't, the present polyomino which has a square is deemed inferior
Once the polyomino are ordered, it is very simple to attribute them a color, we can simply iterate through the list while looping over the color list.
A nice side-effect is that, once the polyomino are ordered, it is very simple to attribute them a block type, we can simply iterate through the list while looping over the block list.
## 1. Generating polyominos
@@ -47,51 +50,50 @@ The method used to generate polyominos is similar to the [inductive method](http
The algorithm is the following:
1. Add a single cell at position (0,0), and number it with 0
1. Add a single square at position (0,0), and number it with 0
2. Call the generator function
1. If we get a polyomino of the size we want:
1. We rotate it in its 4 possible rotations and sort them
2. If the polyomino was generated in its lowest rotation, we add it to the list, else we discard it
3. Stop this instance of the function (the function is recursive, see step 2.3.2)
2. Else we number each adjacent cell to the polyomino with a number higher than the last numbered cell, unless:
1. If a cell was already numbered then we don't touch it
2. If a cell is on top of the polyomino then we don't number it
3. If a cell is below y=0, or at exactly x=0 and y<0, then we don't number it
3. For each cell with a higher number than the last added one:
1. We add this cell to the polyomino
2. Else we number each adjacent square to the polyomino with a number higher than the last numbered square, unless:
1. If a square was already numbered then we don't touch it
2. If a square is on top of the polyomino then we don't number it
3. If a square is below y=0, or at exactly x=0 and y<0, then we don't number it
3. For each square with a higher number than the last added one:
1. We add this square to the polyomino
2. We call the generator function (recursive function!)
3. We remove this cell from the polyomino
3. We remove this square from the polyomino
3. Return the list of polyominos
The exact number of one-sided polyominos up to size 12 (and higher, but we only generated up to size 12) is known, and this method generated exactly theses numbers, without duplicates.
The exact number of one-sided polyominos up to size 15 (and higher, but we only generated up to size 15) is known, and this method generated exactly theses numbers, without duplicates.
By marking cells and adding only ones that have a higher number than the last one everytime, we generate each fixed polyomino exactly n times. By ignoring the cells below y=0, or at exactly x=0 and y<0, we generate each fixed polyomino exactly 1 time.
By marking squares and adding only ones that have a higher number than the last one everytime, we generate each fixed polyomino exactly n times. By ignoring the squares below y=0, or at exactly x=0 and y<0, we generate each fixed polyomino exactly 1 time.
An one-sided polyomino has 4 rotations, some of which can be the same if the polyomino has symmetries. 2 rotations that are the same corresponds to 1 fixed polyomino, we will refer to theses 2 rotation as 1 "unique" rotation.
Because we generate every fixed polyomino exactly 1 time, that means each "unique" rotation of an one-sided polyomino is generated exactly one-time, which includes its lowest rotation. That's how we can get away with only checking the rotation of the polyomino and not comparing it to the rest of the generated polyominos.
## 2. Setting the spawn position of polyominos
When a polyomino is created, an attribute named its length is computed. This is simply the max between its width and its height. Thus the polyomino can be inscribed in a square of size ``length``.
So now we can assume the polyomino is always inscribed in a square of origin (0,0) and size ``length`` (which should always be the case under normal circumstances). This make the rotation center very easy to find as it is simply the center of this square, and rotating is as simple as rotating a matrix:
Since we assume the polyomino is always inscribed in a box at origin (0,0), we can use formulae close to matrix rotations to rotate the piece:
- Clockwise (CW) rotation : ``x, y = y, (length - 1) - x``
- 180° rotation : ``x, y = (length - 1) - x, (length - 1) - y``
- Counter-clockwise (CCW) rotation : ``x, y = (length - 1) - y, x``
_Note we set the origin at the bottom-left corner instead of the up-left corner in a matrix, so the formulae aren't exactly the same._
_Note: we set the origin at the bottom-left corner instead of the up-left corner in a matrix, so the formulae aren't exactly the same._
The second challenge comes in finding a normalized spawn position for pieces. To do this, we first need to find which rotation the piece needs to spawn on, and then center it in the middle of its square.
For the rotation, **we want to find the side which is both the widest and the flattest**.
The second challenge comes in finding a normalized spawn position for pieces. To do this, we first need to find which rotation the piece needs to spawn on, and then center it in the middle of its box.
The very arbitrary rules used in this game for finding the spawn rotation is the following: **we want to find the side which is both the widest and the flattest**.
**Widest** means that we prefer if the piece is oriented horizontally rather than vertically.
**Flattest** means we prefer the side with the most cell at the bottom of the piece.
**Flattest** means we prefer the side with the most square at the bottom of the piece.
The current algorithm for doing so is the following:
1. Check if the polyomino has more lines horizontally or vertically, this will determine either 2 or 4 sides which are widest than the others, and the others will be discarded
2. For each potential side check the number of cell on the first line, if one has more than every others, then this side is choosed, else we only keep the sides that tied and repeat for the next line
3. If we still have at least 2 sides tied, we do the same again but check the flatness of the side to the left instead, this will make the pieces overall have more cells to their left at spawn
4. If there is still no winner, we sort the remaining sides (by simulating having them selectionned and then sort the resulting polyominos) and keep the lowest
1. Check if the polyomino has more lines horizontally or vertically, this will determine either 2 or 4 sides which are wider than the others, and the others will be discarded
2. For each potential side check the number of square on the first line, if one has more than every others, then this side is choosed, else we only keep the sides that tied and repeat for the next line
3. If we still have at least 2 sides tied, we do the same again but check the flatness of the side to the left instead, this will make the pieces overall have more squares to their left at spawn
4. If there is still no winner, we sort the remaining sides (by simulating having them selectionned and then sorting the resulting polyominos) and keep the lowest
5. We rotate the piece so that the chosen side ends up at the bottom of the polyomino
6. We center the polyomino inside its square, since we chose the widest side it will always fill the width of the square, but for the height it can be asymmetric, in that case we place it one line closer to the top than the bottom
6. We center the polyomino inside its box, since we chose the widest side it will always fill the width of the square, but for the height it can be asymmetric, in that case we place it one line closer to the top than the bottom
_Note we could actually just skip straight to step 4 because it always give the same orientation, but we want the spawn rotations to follow somewhat of a logic. Step 1 to 3 actually already works for 99% of polyominos and they constrain step 4 too by preselecting certain sides._
@@ -99,10 +101,10 @@ _Note we could actually just skip straight to step 4 because it always give the
For this game, we want the polyominos to be broken down into 3 categories:
- Convex: this is said of a polyomino where every row and column is formed of at most one continous line of cells
- Convex: this is said of a polyomino where every row and column is formed of at most one continous line of squares
- Holeless: the polyominos which are neither convex nor have a hole
- Others: the polyominos who have a hole (thoses are by definition not convex)
To check for convexity, we simply iterate trough each row and column, and check cell by cell if they are all contiguous.
To check for convexity, we simply iterate trough each row and column, and check if all the squares are contiguous.
To check for holes, we list every empty cells starting from the exterior of the square of the polyomino, then add every adjacent empty cell recursively. If the cell has an hole then there is at least one empty cell we could not attaign, and since we know the size of the square and of the polyomino, we can compute wheter we have the right number of empty cells.
To check for holes, we list every empty squares starting from the exterior of the box of the polyomino, then add every adjacent empty square recursively. If the polyomino has an hole then there is at least one empty square we could not attaign, and since we know the size of the square and of the polyomino, we can compute wheter we have the right number of empty squares.

View File

@@ -6,7 +6,7 @@ If you don't know what a polyomino is, check [this other file](Pieces_representa
Generating polyominos of size n is exponential in regard to n. Because of this, we will store the pieces beforehand and load them upon launching the game.
We want the pieces to be always sorted in the same order, always attributed the same color, and always set at the same spawn position, no matter how they were generated. We also want them to be separated in 3 categories : convex, not convex but wihtout a hole, and with a hole. Theses problematics are already resolved internally, but will be calculated before storage as to not need extra calculcations upon load.
We want the pieces to be always sorted in the same order, always attributed the same block type, and always set at the same spawn position, no matter how they were generated. We also want them to be separated in 3 categories : convex, not convex but without a hole, and with a hole. Theses problematics are already resolved internally, but will be calculated before storage as to not need extra calculcations upon load.
## How is it stored
@@ -14,7 +14,8 @@ Pieces are stored in binary files. Each file simply contains every polyomino of
Each piece is stored as follows:
- 1 byte for the length of the piece
- 1 byte for the other characteristics of the piece: ``ABCCCCCC`` where A indicates if the piece is convex, B indicates if it has a hole, and C is the color number of the piece
- 1 byte for each cell: ``XXXXYYYY`` where X is the x coordinate of the cell and Y is the y coordinate of the cell
- 1 byte for the other characteristics of the piece: ``ABCCCCCC`` where A indicates if the piece is convex, B indicates if it has a hole, and C is the block number of the piece
- 1 byte for each position: ``XXXXYYYY`` where X is the x coordinate of the position and Y is the y coordinate of the position
The current implementation only allows to generate polyominos up to size 16, but can be upgraded by storing coordinates on 8 bits instead of 4. It has been choosen to use pieces only up to size 15 for this game.
The current implementation only allows to generate polyominos up to size 16, but can be upgraded by storing coordinates on 8 bits instead of 4.
It has been currently choosen to use pieces only up to size 15 for this game.