From 46024f5c2d85e87d8bf85dace2bbc483add7769b Mon Sep 17 00:00:00 2001
From: Ziloka <50429450+ziloka@users.noreply.github.com>
Date: Fri, 20 Oct 2023 19:57:23 -0400
Subject: [PATCH] updating scripts/wallkicks script
---
scripts/wallkicks/index.js | 123 +-
scripts/wallkicks/input.txt | 4275 ++++++++++++++++++++++++++++++++++-
src/bin/client.rs | 22 -
tests/wallkicks.rs | 19 -
4 files changed, 4349 insertions(+), 90 deletions(-)
diff --git a/scripts/wallkicks/index.js b/scripts/wallkicks/index.js
index fc2f5ab..21244e1 100644
--- a/scripts/wallkicks/index.js
+++ b/scripts/wallkicks/index.js
@@ -3,56 +3,83 @@
// given HTML table of tetrominos convert it in array representation
// https://harddrop.com/wiki/SRS#Wall_Kick_Illustration
-
-import * as cheerio from "https://esm.sh/cheerio";
+import * as cheerio from "https://esm.sh/cheerio@0.22.0";
const $ = cheerio.load(await Deno.readTextFile("input.txt"));
-const HEIGHT = 7;
-const WIDTH = 5;
-
-// 35 elements in total
-const board = Array.from({length: HEIGHT}, ()=> (Array.from({length: WIDTH}, () => "N")));
-const active_piece_initial = [];
-const active_piece_destination = [];
-// parse as if bottom left is (0, 0)
-// increments left-right, bottom-up
-$("div img").each((i, e) => {
- const name = $(e).attr("alt");
- // "relative position"
- const y = Math.floor(i / WIDTH);
- const x = i % WIDTH;
-
- // position in array
- const row = HEIGHT - y - 1;
- const column = x;
-
- // (6, 0), (6, 1) (6, 2), ...
- // (5, 7), (5, 6) (5, 5), ...
- // console.log(`x: ${x}, y: ${y} | column: ${column} row: ${row}`);
-
- if (/(L|J|T|S|Z|I)Tet\.png/.test(name)) { // initial tetromino position
- // 1, 3
- active_piece_initial.push(` vec2(${column}., ${row}.)`)
- } else {
- switch (name) {
- case "GTet.png": // garbage block
- // console.log(`${x} ${y}`);
- board[row][column] = "G";
- break;
- case "-Tet.png": // dest tetromino position
- active_piece_destination.push(` vec2(${column}., ${row}.)`)
- break;
- case "Tet.png": // empty block
- break;
- default:
- console.log(`missing case for: ${name}`);
- break;
+// {
+// const $ = cheerio.load(`
+//
+// This is first row. |
+// This is second row. |
+// This is third row. |
+// This is fourth row. |
+//
+// `);
+// $("tr:nth-last-child(2n)").toArray().map((e) => $(e).find("tr td").each((_, e) => {
+// console.log($(e).text());
+// }));
+// }
+
+
+function generate_test_case(table, init_rot_indx, dest_rot_index) {
+ const HEIGHT = 7;
+ const WIDTH = 5;
+
+ // 35 elements in total
+ const board = Array.from({length: HEIGHT}, () => (Array.from({length: WIDTH}, () => "N")));
+ const active_piece_initial = [];
+ const active_piece_destination = [];
+ // parse as if bottom left is (0, 0)
+ // increments left-right, bottom-up
+ $(table).find("div img").each((i, e) => {
+ const name = $(e).attr("alt");
+ // "relative position"
+ const y = Math.floor(i / WIDTH);
+ const x = i % WIDTH;
+
+ // position in array
+ const row = HEIGHT - y - 1;
+ const column = x;
+
+ // (6, 0), (6, 1) (6, 2), ...
+ // (5, 7), (5, 6) (5, 5), ...
+ // console.log(`x: ${x}, y: ${y} | column: ${column} row: ${row}`);
+
+ if (/(L|J|T|S|Z|I)Tet\.png/.test(name)) { // initial tetromino position
+ // 1, 3
+ active_piece_initial.push(` vec2(${column}., ${row}.)`)
+ } else {
+ switch (name) {
+ case "GTet.png": // garbage block
+ // console.log(`${x} ${y}`);
+ board[row][column] = "G";
+ break;
+ case "-Tet.png": // dest tetromino position
+ active_piece_destination.push(` vec2(${column}., ${row}.)`)
+ break;
+ case "Tet.png": // empty block
+ break;
+ default:
+ console.log(`missing case for: ${name}`);
+ break;
+ }
}
- }
-});
+ });
+
+ const format = (arr) => JSON.stringify(arr).replace(/"/g, '').replace(/\[/g, "vec![");
-const format = (arr) => JSON.stringify(arr).replace(/"/g, '').replace(/\[/g, "vec![");
+ console.log(`// ${init_rot_indx}->${dest_rot_index}`);
+ console.log(`generate(${format(board)}, TETROMINO_TYPE, ${format(active_piece_initial)}, ${init_rot_indx}, ${format(active_piece_destination)})\n`);
+}
-console.log(`board: ${format(board)}`);
-console.log(`initial: ${format(active_piece_initial)}`);
-console.log(`dest: ${format(active_piece_destination)}`);
+// document.querySelector("table[style=\"text-align:center;\"]").querySelector("tr[align=\"center\"]").querySelector("th").textContent
+// document.querySelector("table[style=\"text-align:center;\"]").querySelector("tr[align=\"center\"]").querySelectorAll(":nth-last-child(-n+2 of td[width=\"74\"])");
+$("table[style=\"text-align:center;\"]").each((i, tetrominoTests) => {
+ if ([1, 3].includes(i)) return;
+ $(tetrominoTests).find("tr[align=\"center\"]").each((_, row) => {
+ const [init_rot_indx, dest_rot_indx] = $(row).find("th").text().trim().split("⇒");
+ $(row).find("td[width=\"74\"]:nth-last-child(-n+2)").each((_, table) => {
+ generate_test_case(table, init_rot_indx, dest_rot_indx);
+ });
+ });
+});
\ No newline at end of file
diff --git a/scripts/wallkicks/input.txt b/scripts/wallkicks/input.txt
index 6bc5290..2f6f16f 100644
--- a/scripts/wallkicks/input.txt
+++ b/scripts/wallkicks/input.txt
@@ -1,4 +1,866 @@
+The Super Rotation System, also known as SRS and Standard Rotation System is the current Tetris Guideline standard for how tetrominoes behave, defining where and how the tetrominoes spawn, how they rotate, and what wall kicks they may perform. SRS traces its routes back to 1991 when BPS introduced its signature third and fourth rotation states for the S, Z, and I tetrominoes in the game Tetris 2 + BomBliss. Two years later, in the game Tetris Battle Gaiden, BPS altered the spawn orientation of the T, L, and J tetrominoes so that they spawned flat-side first. It was not until the 2001 game, Tetris Worlds, that the wall kick system was introduced, and SRS took its final form. Henk Rogers, in his effort to unify all new Tetris games into the Tetris Guideline, required Arika to include a form of SRS in their 2005 game, Tetris The Grand Master 3 Terror-Instinct, where it is called "World" rule, in reference to Tetris Worlds.
+
+
+Spawn Orientation and Location
+ The 4 rotation states of all 7 tetrominoes. Starting with the spawn state on the left, the 4 rotation states resulting from successive clockwise rotations are shown in order. The circles merely help to illustrate rotation centers and do not appear in-game.
+The spawn orientations are included in the diagram on the right.
+
+- All tetrominoes spawn horizontally and wholly above the playfield.
+- The I and O tetrominoes spawn centrally, and the other, 3-cell wide tetrominoes spawn rounded to the left.
+- The J, L and T spawn flat-side first.
+- In Tetris Worlds, the tetrominoes spawn in rows 22 and 23 (or just row 22 in the case of the "I" tetromino), however, in later games the tetrominoes spawn 1 row lower.
+Basic Rotation
+The basic rotation states are shown in the diagram on the right.
+Some points to note:
+
+- When unobstructed, the tetrominoes all appear to rotate purely about a single point. These apparent rotation centers are shown as circles in the diagram.
+- It is a pure rotation in a mathematical sense, as opposed to the combination of rotation and translation found in other systems such as Sega Rotation and Atari Rotation.
+- As a direct consequence, the J, L, S, T and Z tetrominoes have 1 of their 4 states (the spawn state) in a "floating" position where they are not in contact with the bottom of their bounding box.
+- This allows the bounding box to descend below the surface of the stack (or the floor of the playing field) making it impossible for the tetrominoes to be rotated without the aid of floor kicks.
+- The S, Z and I tetrominoes have two horizontally oriented states and two vertically oriented states. It can be argued that having two vertical states leads to faster finesse.
+- For the "I" and "O" tetrominoes, the apparent rotation center is at the intersection of gridlines, whereas for the "J", "L", "S", "T" and "Z" tetrominoes, the rotation center coincides with the center of one of the four constituent minos.
+Wall Kicks
+When the player attempts to rotate a tetromino, but the position it would normally occupy after basic rotation is obstructed, (either by the wall or floor of the playfield, or by the stack), the game will attempt to "kick" the tetromino into an alternative position nearby.
+Some points to note:
+
+- When a rotation is attempted, 5 positions are sequentially tested (inclusive of basic rotation); if none are available, the rotation fails completely.
+- Which positions are tested is determined by the initial rotation state, and the desired final rotation state. Because it is possible to rotate both clockwise and counter-clockwise, for each of the 4 initial states there are 2 final states. Therefore there are a total of 8 possible rotations for each tetromino and 8 sets of wall kick data need to be described.
+- The positions are commonly described as a sequence of ( x, y) kick values representing translations relative to basic rotation; a convention of positive x rightwards, positive y upwards is used, e.g. (-1,+2) would indicate a kick of 1 cell left and 2 cells up.
+- The J, L, S, T and Z tetrominoes all share the same kick values, the I tetromino has its own set of kick values, and the O tetromino does not kick.
+- Several different conventions are commonly used for the naming of the rotation states. On this page, the following convention will be used:
+
- 0 = spawn state
+- R = state resulting from a clockwise rotation ("right") from spawn
+- L = state resulting from a counter-clockwise ("left") rotation from spawn
+- 2 = state resulting from 2 successive rotations in either direction from spawn.
+
+
+
+J, L, S, T, Z Tetromino Wall Kick Data
+
+
+
+ |
+Test 1
+ |
+Test 2
+ |
+Test 3
+ |
+Test 4
+ |
+Test 5
+ |
+
+
+
+0->R |
+( 0, 0) |
+(-1, 0) |
+(-1,+1) |
+( 0,-2) |
+(-1,-2)
+ |
+
+R->0 |
+( 0, 0) |
+(+1, 0) |
+(+1,-1) |
+( 0,+2) |
+(+1,+2)
+ |
+
+
+
+R->2 |
+( 0, 0) |
+(+1, 0) |
+(+1,-1) |
+( 0,+2) |
+(+1,+2)
+ |
+
+2->R |
+( 0, 0) |
+(-1, 0) |
+(-1,+1) |
+( 0,-2) |
+(-1,-2)
+ |
+
+
+
+2->L |
+( 0, 0) |
+(+1, 0) |
+(+1,+1) |
+( 0,-2) |
+(+1,-2)
+ |
+
+L->2 |
+( 0, 0) |
+(-1, 0) |
+(-1,-1) |
+( 0,+2) |
+(-1,+2)
+ |
+
+
+
+L->0 |
+( 0, 0) |
+(-1, 0) |
+(-1,-1) |
+( 0,+2) |
+(-1,+2)
+ |
+
+0->L |
+( 0, 0) |
+(+1, 0) |
+(+1,+1) |
+( 0,-2) |
+(+1,-2)
+ |
+
+
+
+I Tetromino Wall Kick Data
+
+
+
+ |
+Test 1
+ |
+Test 2
+ |
+Test 3
+ |
+Test 4
+ |
+Test 5
+ |
+
+
+
+0->R |
+( 0, 0) |
+(-2, 0) |
+(+1, 0) |
+(-2,-1) |
+(+1,+2)
+ |
+
+R->0 |
+( 0, 0) |
+(+2, 0) |
+(-1, 0) |
+(+2,+1) |
+(-1,-2)
+ |
+
+
+
+R->2 |
+( 0, 0) |
+(-1, 0) |
+(+2, 0) |
+(-1,+2) |
+(+2,-1)
+ |
+
+2->R |
+( 0, 0) |
+(+1, 0) |
+(-2, 0) |
+(+1,-2) |
+(-2,+1)
+ |
+
+
+
+2->L |
+( 0, 0) |
+(+2, 0) |
+(-1, 0) |
+(+2,+1) |
+(-1,-2)
+ |
+
+L->2 |
+( 0, 0) |
+(-2, 0) |
+(+1, 0) |
+(-2,-1) |
+(+1,+2)
+ |
+
+
+
+L->0 |
+( 0, 0) |
+(+1, 0) |
+(-2, 0) |
+(+1,-2) |
+(-2,+1)
+ |
+
+0->L |
+( 0, 0) |
+(-1, 0) |
+(+2, 0) |
+(-1,+2) |
+(+2,-1)
+ |
+
+A wall kick example:
+The desired rotation is 0->L, and from the table above, the wall kick test order is ( 0, 0), (+1, 0), (+1,+1), ( 0,-2), (+1,-2).
+
+
+
+
+
+
+ 1. Initial position.
+Attempt to rotate 0->L.
+
+ |
+
+
+ 2. Test 1, ( 0, 0) fails.
+(Basic rotation fails.)
+
+ |
+
+
+ 3. Test 2, (+1, 0) fails.
+
+ |
+
+
+
+
+
+ 4. Test 3, (+1,+1) fails.
+
+ |
+
+
+ 5. Test 4, ( 0,-2) fails.
+
+ |
+
+
+ 6. Final position.
+Test 5, (+1,-2) succeeds.
+
+ |
+Arika SRS
+In their games Tetris The Grand Master 3 Terror-Instinct and Tetris The Grand Master Ace, Arika were required to include a form of SRS as the default rotation system, in order to conform more closely to Henk Rogers' Tetris Guideline. Arika's implementation of SRS uses the exact same wall kick data for the J, L, S, T and Z tetrominoes as the Guideline's standard; however, the I tetromino uses the wall kick data shown below:
+
+
+
+Arika I Tetromino Wall Kick Data
+
+
+
+ |
+Test 1
+ |
+Test 2
+ |
+Test 3
+ |
+Test 4
+ |
+Test 5
+ |
+
+
+
+0->R |
+( 0, 0) |
+(-2, 0) |
+(+1, 0) |
+(+1,+2) |
+(-2,-1)
+ |
+
+R->0 |
+( 0, 0) |
+(+2, 0) |
+(-1, 0) |
+(+2,+1) |
+(-1,-2)
+ |
+
+
+
+R->2 |
+( 0, 0) |
+(-1, 0) |
+(+2, 0) |
+(-1,+2) |
+(+2,-1)
+ |
+
+2->R |
+( 0, 0) |
+(-2, 0) |
+(+1, 0) |
+(-2,+1) |
+(+1,-1)
+ |
+
+
+
+2->L |
+( 0, 0) |
+(+2, 0) |
+(-1, 0) |
+(+2,+1) |
+(-1,-1)
+ |
+
+L->2 |
+( 0, 0) |
+(+1, 0) |
+(-2, 0) |
+(+1,+2) |
+(-2,-1)
+ |
+
+
+
+L->0 |
+( 0, 0) |
+(-2, 0) |
+(+1, 0) |
+(-2,+1) |
+(+1,-2)
+ |
+
+0->L |
+( 0, 0) |
+(+2, 0) |
+(-1, 0) |
+(-1,+2) |
+(+2,-1)
+ |
+
+The logic behind Arika's modifications is that the I wall kicks are now symmetric about the y-axis when rotating from or to a horizontal orientation. One noticeable consequence of this is illustrated in the following example:
+
+
+
+
+
+
+ |
+From the dotted position, it is possible to clear 4 lines with both Guideline and Arika SRS by rotating clockwise.
+ |
+
+
+
+
+
+ |
+In the symmetric position, only Arika SRS allows the clearing of 4 lines by rotating counter-clockwise.
+ |
+
+
+
+
+
+ |
+Arika SRS also allows for this position to be achieved by rotating clockwise. However, with Guideline SRS, this is the only position achievable, regardless of which direction the player rotates.
+ |
+How Guideline SRS Really Works
+ The internal true rotations used in Guideline SRS; offsets are applied to these.
+
+Instead of directly assigning a set of ( x, y) kick translations to each of the 8 possible rotations, TTC actually employed a different approach, and instead assigned a set of ( x, y) "offset" values to the 4 rotation states. The kick translations are then derived by taking the difference between pairs of offset data. When rotating from A to B, subtracting B's values from A's will give the kick translation for the rotation one way; and subtracting A's values from B's will give the kick translation for rotating back the other way.
+
There is another complexity to TTC's implementation: the derived translations are relative to a different datum. So far on this page, kick translations have been defined relative to "basic rotation", but TTC uses what forum user nightmareci has named "true rotation". "True rotation" is still a mathematical pure rotation with no translation involved; however, the rotation center always coincides with the center of one of the four constituent minos. (Recall that the apparent rotation center of the I and O tetrominoes in basic rotation coincided with the intersection of gridlines). This means that for "true rotation", the rotation center for the O piece is not at the geometric center, so the piece will have a "wobble" when rotated. The first kick translation has to be used to correct for this wobble.
+
+
+
+J, L, S, T, Z Tetromino Offset Data
+
+
+
+ |
+Offset 1
+ |
+Offset 2
+ |
+Offset 3
+ |
+Offset 4
+ |
+Offset 5
+ |
+
+0
+ |
+( 0, 0) |
+( 0, 0) |
+( 0, 0) |
+( 0, 0) |
+( 0, 0)
+ |
+
+R
+ |
+( 0, 0) |
+(+1, 0) |
+(+1,-1) |
+( 0,+2) |
+(+1,+2)
+ |
+
+2
+ |
+( 0, 0) |
+( 0, 0) |
+( 0, 0) |
+( 0, 0) |
+( 0, 0)
+ |
+
+L
+ |
+( 0, 0) |
+(-1, 0) |
+(-1,-1) |
+( 0,+2) |
+(-1,+2)
+ |
+
+
+
+I Tetromino Offset Data
+
+
+
+ |
+Offset 1
+ |
+Offset 2
+ |
+Offset 3
+ |
+Offset 4
+ |
+Offset 5
+ |
+
+0
+ |
+( 0, 0) |
+(-1, 0) |
+(+2, 0) |
+(-1, 0) |
+(+2, 0)
+ |
+
+R
+ |
+(-1, 0) |
+( 0, 0) |
+( 0, 0) |
+( 0,+1) |
+( 0,-2)
+ |
+
+2
+ |
+(-1,+1) |
+(+1,+1) |
+(-2,+1) |
+(+1, 0) |
+(-2, 0)
+ |
+
+L
+ |
+( 0,+1) |
+( 0,+1) |
+( 0,+1) |
+( 0,-1) |
+( 0,+2)
+ |
+
+
+
+O Tetromino Offset Data
+
+
+
+ |
+Offset 1
+ |
+Offset 2
+ |
+Offset 3
+ |
+Offset 4
+ |
+Offset 5
+ |
+
+0
+ |
+( 0, 0) |
+No further offset data required
+ |
+
+R
+ |
+( 0,-1)
+ |
+
+2
+ |
+(-1,-1)
+ |
+
+L
+ |
+(-1, 0)
+ |
+
+An example of deriving kick translations from the offsets:
+
The offsets for J, rotation state 0 are: ( 0, 0), ( 0, 0), ( 0, 0), ( 0, 0), ( 0, 0).
+The offsets for J, rotation state R are: ( 0, 0), (+1, 0), (+1,-1), ( 0,+2), (+1,+2).
+
( 0, 0) - ( 0, 0) = ( 0, 0),
+( 0, 0) - (+1, 0) = (-1, 0),
+( 0, 0) - (+1,-1) = (-1,+1),
+( 0, 0) - ( 0,+2) = ( 0,-2),
+( 0, 0) - (+1,+2) = (-1,-2).
+
Therefore, the kick translations for the J rotation 0->R, relative to "true rotation" (which is conveniently the same as "basic rotation" for the J tetromino), are: ( 0, 0), (-1, 0), (-1,+1), ( 0,-2), (-1,-2).
+
+Wall Kick Illustration
+SRS wall kicks are symmetric for all pieces but the I piece. That means for the mirrored playfield and mirrored piece ( J ↔ L piece, S ↔ Z piece, L ↔ R rotation state), the equivalent kick (same y value, opposite sign for x value) will appear. Thus for all pieces but the I piece, the kick system can be completely described by just examining clockwise rotation.
+
+
+
+
+
+
+ |
+Kick Tests
+ |
+Useful Kicks
+ |
+
+0⇒R
+ |
+( 0, 0)
+
+ |
+(-1, 0)
+
+ |
+(-1,+1)
+
+ |
+( 0,-2)
+
+ |
+(-1,-2)
+
+ |
+(-1, 0)
+
+ |
+(-1,-2)
+
+ |
+
+R⇒2
+ |
+( 0, 0)
+
+ |
+(+1, 0)
+
+ |
+(+1,-1)
+
+ |
+( 0,+2)
+
+ |
+(+1,+2)
+
+ |
+(+1,-1)
+
@@ -8,4 +870,3415 @@
- |
\ No newline at end of file
+
+ |
+(+1, 0)
+
+ |
+
+2⇒L
+ |
+( 0, 0)
+
+ |
+(+1, 0)
+
+ |
+(+1,+1)
+
+ |
+( 0,-2)
+
+ |
+(+1,-2)
+
+ |
+(+1,-2)
+
+ |
+
+ |
+
+L⇒0
+ |
+( 0, 0)
+
+ |
+(-1, 0)
+
+ |
+(-1,-1)
+
+ |
+( 0,+2)
+
+ |
+(-1,+2)
+
+ |
+(-1,-1)
+
+ |
+
+ |
+
+
+
+
+
+
+ |
+Kick Tests
+ |
+Useful Kicks
+ |
+
+0⇒R
+ |
+( 0, 0)
+
+ |
+(-1, 0)
+
+ |
+(-1,+1)
+
+ |
+(0,-2)
+
+ |
+(-1,-2)
+
+ |
+(-1, 0)
+
+ |
+( 0,-2)
+
+ |
+
+R⇒2
+ |
+( 0, 0)
+
+ |
+(+1, 0)
+
+ |
+(+1,-1)
+
+ |
+( 0,+2)
+
+ |
+(+1,+2)
+
+ |
+(+1,-1)
+
+ |
+(+1, 0)
+
+ |
+
+2⇒L
+ |
+( 0, 0)
+
+ |
+(+1, 0)
+
+ |
+(+1,+1)
+
+ |
+( 0,-2)
+
+ |
+(+1,-2)
+
+ |
+(+1,-2)
+
+ |
+
+ |
+
+L⇒0
+ |
+( 0, 0)
+
+ |
+(-1, 0)
+
+ |
+(-1,-1)
+
+ |
+( 0,+2)
+
+ |
+(-1,+2)
+
+ |
+(-1,-1)
+
+ |
+(-1, 0)
+
+ |
+
+
+
+
+
+
+ |
+Kick Tests
+ |
+Useful Kicks
+ |
+
+0⇒R
+ |
+( 0, 0)
+
+ |
+(-1, 0)
+
+ |
+(-1,+1)
+
+ |
+not possible
+
+ |
+(-1,-2)
+
+ |
+(-1,-2)
+
+ |
+(-1, 0)
+
+ |
+
+R⇒2
+ |
+( 0, 0)
+
+ |
+(+1, 0)
+
+ |
+(+1,-1)
+
+ |
+( 0,+2)
+
+ |
+(+1,+2)
+
+ |
+( 0, 0)
+
+ |
+(+1,-1)
+
+ |
+
+2⇒L
+ |
+( 0, 0)
+
+ |
+(+1, 0)
+
+ |
+not possible
+
+ |
+( 0,-2)
+
+ |
+(+1,-2)
+
+ |
+(+1,-2)
+
+ |
+( 0,-2)
+
+ |
+
+L⇒0
+ |
+( 0, 0)
+
+ |
+(-1, 0)
+
+ |
+(-1,-1)
+
+ |
+( 0,+2)
+
+ |
+(-1,+2)
+
+ |
+(-1,-1)
+
+ |
+(-1, 0)
+
+ |
+
+
+
+
+
+
+ |
+Kick Tests
+ |
+Useful Kicks
+ |
+
+0⇒R
+ |
+( 0, 0)
+
+ |
+(-1, 0)
+
+ |
+(-1,+1)
+
+ |
+(0,-2)
+
+ |
+(-1,-2)
+
+ |
+(-1,-2)
+
+ |
+(0,-2)
+
+ |
+
+R⇒2
+ |
+( 0, 0)
+
+ |
+(+1, 0)
+
+ |
+(+1,-1)
+
+ |
+( 0,+2)
+
+ |
+(+1,+2)
+
+ |
+(+1,-1)
+
+ |
+(+1, 0)
+
+ |
+
+2⇒L
+ |
+( 0, 0)
+
+ |
+(+1, 0)
+
+ |
+(+1,+1)
+
+ |
+( 0,-2)
+
+ |
+(+1,-2)
+
+ |
+( 0,-2)
+
+ |
+
+ |
+
+L⇒0
+ |
+( 0, 0)
+
+ |
+(-1, 0)
+
+ |
+(-1,-1)
+
+ |
+( 0,+2)
+
+ |
+(-1,+2)
+
+ |
+(-1, 0)
+
+ |
+
+ |
+
+
+
+
+
+
+ |
+Kick Tests
+ |
+Useful Kicks
+ |
+
+0⇒R
+ |
+( 0, 0)
+
+ |
+(-1, 0)
+
+ |
+(-1,+1)
+
+ |
+( 0,-2)
+
+ |
+(-1,-2)
+
+ |
+( 0,-2)
+
+ |
+( 0, 0)
+
+ |
+
+R⇒2
+ |
+( 0, 0)
+
+ |
+(+1, 0)
+
+ |
+(+1,-1)
+
+ |
+( 0,+2)
+
+ |
+(+1,+2)
+
+ |
+(+1,-1)
+
+ |
+(+1, 0)
+
+ |
+
+2⇒L
+ |
+( 0, 0)
+
+ |
+(+1, 0)
+
+ |
+(+1,+1)
+
+ |
+( 0,-2)
+
+ |
+(+1,-2)
+
+ |
+(+1,-2)
+
+ |
+
+ |
+
+L⇒0
+ |
+( 0, 0)
+
+ |
+(-1, 0)
+
+ |
+(-1,-1)
+
+ |
+( 0,+2)
+
+ |
+(-1,+2)
+
+ |
+( 0, 0)
+
+ |
+
+ |
+
+
+
+
+
+
+ |
+Kick Tests
+ |
+Useful Kicks
+ |
+
+0⇒R
+ |
+( 0, 0)
+
+ |
+(-2, 0)
+
+ |
+(+1, 0)
+
+ |
+(-2,-1)
+
+ |
+(+1,+2)
+
+ |
+(-2,-1)
+
+ |
+
+R⇒2
+ |
+( 0, 0)
+
+ |
+(-1, 0)
+
+ |
+(+2, 0)
+
+ |
+(-1,+2)
+
+ |
+(+2,-1)
+
+ |
+(+2,-1)
+
+ |
+
+2⇒L
+ |
+( 0, 0)
+
+ |
+(+2, 0)
+
+ |
+(-1, 0)
+
+ |
+(+2,+1)
+
+ |
+(-1,-2)
+
+ |
+
+ |
+
+L⇒0
+ |
+( 0, 0)
+
+ |
+(+1, 0)
+
+ |
+(-2, 0)
+
+ |
+(+1,-2)
+
+ |
+(-2,+1)
+
+ |
+(+1,-2)
+
+ |
+
+
+
+
+
+
+ |
+Kick Tests
+ |
+Useful Kicks
+ |
+
+0⇒L
+ |
+( 0, 0)
+
+ |
+(-1, 0)
+
+ |
+(+2, 0)
+
+ |
+(-1,+2)
+
+ |
+(+2,-1)
+
+ |
+
+ |
+
+L⇒2
+ |
+( 0, 0)
+
+ |
+(-2, 0)
+
+ |
+(+1, 0)
+
+ |
+(-2,-1)
+
+ |
+(+1,+2)
+
+ |
+(-2,-1)
+
+ |
+
+2⇒R
+ |
+( 0, 0)
+
+ |
+(+1, 0)
+
+ |
+(-2, 0)
+
+ |
+(+1,-2)
+
+ |
+(-2,+1)
+
+ |
+(+1,-2)
+
+ |
+
+R⇒0
+ |
+( 0, 0)
+
+ |
+(+2, 0)
+
+ |
+(-1, 0)
+
+ |
+(+2,+1)
+
+ |
+(-1,-2)
+
+ |
+(-1,-2)
+
+ |
+
+
+Criticism
+The Super Rotation System, and with it associated Move Reset Lock delay, is criticized by a large amount of players. SRS is suitable for multiplayer but causes problems in modes with significant gravity effect such as Marathon. In the following we will list advantages and disadvantages of using SRS.
+
pro:
+
+- Pieces rotate very smoothly under no gravity. The piece rotates to the position where an unexperienced player would expect it.
+
+
+
+
+ |
+SRS: Smooth rotation under 0 gravity
+ |
+
+ |
+
+
+ |
+TGM: Weird "Up" rotation state
+ |
+
+ |
+
+
+ |
+MTC: Center of rotation lies outside piece
+ |
+
+ |
+
+
+ |
+Atari: Piece aligned to top-left corner
+ |
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+- SRS uses kicks that may move a piece to an otherwise unreachable location.
+
+
+
+
+ |
+
+ |
+
+
+ |
+
+
+
+ |
+
+
+ |
+- Move Reset and kicks allow quite interesting multiplayer rules and T-spin setups. Players often have to make gaps narrower such that certain filled cells serve as leverage point and the piece is kicked in downward direction.
+
+
+
+
+ |
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+
+ |
+Red cell marks leverage point.
+ |
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+- The four rotation states of S, Z and I pieces allow to utilize Finesse and make some moves possible that don't require kicks.
+
+
+
+
+ |
+Possible in SRS. Not possible in TGM.
+ |
+
+ |
+
+
+ |
+Rotate counterclockw. when moving towards left.
+ |
+
+ |
+
+
+ |
+Rotate clockwise when moving towards right.
+ |
+
+
+
+ |
+
+
+ |
+
+
+ |
+- Pretty much every spin can be reversed. That means for every kick exists a kick in the other direction.
+
+
+
+
+ |
+
+
+ |
+last second decision: e.g. for 4-wide
+ |
+
contra:
+
+- SRS uses Up spawning direction (pieces spawn flatside down). If a non-rotated piece hits a perfectly even ground, it can't be rotated anymore without an upward kick being used. Note that this problem wouldn't exist, if the pieces spawned in Down direction or if the Up rotation state would be moved 1 row downwards (like in TGM).
+
+
+
+
+ |
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+
+
+
+ |
+
+
+ |
+
+
+ |
+- Upward kicks will most likely kick the piece also towards the left or the right. That means the piece ends up in an unfamiliar column. This is extremely confusing for S and Z pieces, where clockwise rotation with kick will behave like counterclockwise rotation without kick and vice versa.
+
+
+
+
+ |
+
+
+ |
+Clockwise rotation before S piece hits the ground.
+ |
+
+ |
+
+
+ |
+
+
+ |
+Countercw. rotation before S piece hits the ground.
+ |
+
+
+
+ |
+
+
+ |
+Clockwise rotation after S piece hits hits the ground.
+ |
+
+ |
+
+
+ |
+
+
+ |
+Countercw. rotation after S piece hits hits the ground.
+ |
+- Move Reset and upward kicks makes survival pretty easy under high gravity. As a result, most gravity-related modes will end up in 20G gravity, that means the piece will always touch the ground (stack) resulting in the occurence of the two aforementioned problems. Even under this hardened condition, survival is easy for advanced players - unless the lock delay is reduced to a ridiculous small amount in which case the player must mash buttons (in contrast to hold buttons as usual) to prevent the piece from locking.
+- When playing under low gravity, upward kicks can be used to prevent a piece from locking ("stalling") for a long time. This is bad in multiplayer (trolls preventing a game from finishing or players abusing it when hurry up garbage arrives).
+
+
+
+
+ |
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+- The kick system is often counterintuitive because half of the kicks are shifting the center of the piece by two rows or columns.
+
+
+
+
+ |
+
+ |
+Triple clears with S, Z or T ? Center is kicked 2 rows down.
+ |
+
+ |
+
+
+ |
+- However, under this circumstance, Z simply can't rotate clockwise (and S mirrored). This may cause misdrops in 20G gameplay, such as using "World" rule in TGM3.
+
+- Some rather intuitive kicks are not checked. For example, a kick by 1 row down (and not to the side) is never checked.
+
+
+
+
+ |
+Possible in TGM.
+ |
+
+ |
+
+
+ |
+Possible in TGM.
+ |
+
+ |
+
+
+ |
+Possible in C2.
+ |
+
+ |
+
+
+ |
+Possible in C2.
+ |
+
+ |
+
+
+ |
+Possible in C2.
+ |
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+- The rotation direction matters for some common S and Z kicks. Rotating clockwise twice is the key to success in some cases whereas rotating counterclockwise twice is the key to success in other cases.
+
+
+
+
+ |
+Kick only works when rotating clockwise twice.
+ |
+
+ |
+
+
+ |
+Kick only works when rotating countercw. twice.
+ |
+- The kick system for the I piece is not symmetric. That means, sometimes the I piece can be kicked in a certain place while this is not possible in the mirrored playfield.
+
+
+
+
+ |
+Kick is possible by rotating countercw twice.
+ |
+
+ |
+
+
+ |
+Mirrored field. Kick is not possible.
+ |
+- The center of the I piece is located one row below the center of the O piece. With a very high stack, it is often troublesome to drop the I piece vertically next to the right wall. In this case, the I piece has the tendency to kick in the wrong direction, and players have to rotate 3 times to reach the desired place.
+
+
+
+
+ |
+
+
+ |
+Piece kicked left when rotated from Up direction.
+ |
+
+ |
+
+
+ |
+
+
+ |
+Piece kicked right when rotated from Down direction.
+ |
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+External links
+
+
+
+
+
diff --git a/src/bin/client.rs b/src/bin/client.rs
index 213a96e..148e80f 100644
--- a/src/bin/client.rs
+++ b/src/bin/client.rs
@@ -31,28 +31,6 @@ async fn main() {
let mut open_settings = false;
let mut game = Game::new();
- // let mut negative_1x_0y = vec![vec![G,N,N,G,G],vec![G,N,G,G,G],vec![G,N,G,G,G],vec![G,N,N,N,N],vec![G,G,N,N,N],vec![N,N,N,N,N],vec![N,N,N,N,N]];
- // game.board = Box::new(Board::import(negative_1x_0y, 0));
- // game.board.active_piece.tetromino = tetris::tetris::consts::Tetromino::L;
- // game.board.active_piece.dots = vec![ vec2(3., 4.), vec2(1., 3.), vec2(2., 3.), vec2(3., 3.)];
- // game.board.active_piece.rotation_index = 0;
- // game.board.rotate_tetromino_90(true, true);
- // let dest = vec![vec2(1., 2.), vec2(1., 1.), vec2(1., 0.), vec2(2., 0.)];
- // dbg!(&game.board.active_piece.dots);
-
- // assert!(
- // &game.board.active_piece.dots.iter().all(|item| vec![
- // vec2(1., 2.),
- // vec2(1., 1.),
- // vec2(1., 0.),
- // vec2(2., 0.)
- // ]
- // .contains(item)),
- // "expected = {:?}\nfound = {:?}",
- // vec![vec2(1., 2.), vec2(1., 1.), vec2(1., 0.), vec2(2., 0.)],
- // &game.board.active_piece.dots
- // );
-
loop {
let block_size_temp = (screen_height() / (game.board.positions.len() as f32 * 1.25))
.min(screen_width() / (game.board.positions[0].len() as f32 * 1.25));
diff --git a/tests/wallkicks.rs b/tests/wallkicks.rs
index 684ca27..1a6bc92 100644
--- a/tests/wallkicks.rs
+++ b/tests/wallkicks.rs
@@ -5,23 +5,6 @@ use tetris::tetris::{
consts::{vec2, Tetromino, Vec2},
};
-macro_rules! print_matrix {
- ($matrix: expr) => {
- let mut board_string = String::new();
- for row in $matrix.iter() {
- for element in row.iter().rev() {
- if let Some(_) = element {
- board_string.insert_str(0, "x");
- } else {
- board_string.insert_str(0, " ");
- }
- }
- board_string.insert_str(0, "\n");
- }
- println!("{}", board_string);
- };
-}
-
// make sure the first element in the initial_pos is the origin cell
fn generate(
matrix: Vec>>,
@@ -73,8 +56,6 @@ fn tetromino_j_clockwise_wallkicks() {
);
// R-> 2 (+1, -1)
-
-
let positive_1x_0y = vec![
vec![0, 0, 0, 0, 0],
vec![0, 0, 0, 0, 0],