Application Android et IOS pour l'amicale des élèves
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Piece.ts 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. * Copyright (c) 2019 - 2020 Arnaud Vergnet.
  3. *
  4. * This file is part of Campus INSAT.
  5. *
  6. * Campus INSAT is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Campus INSAT is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. import ShapeL from '../Shapes/ShapeL';
  20. import ShapeI from '../Shapes/ShapeI';
  21. import ShapeJ from '../Shapes/ShapeJ';
  22. import ShapeO from '../Shapes/ShapeO';
  23. import ShapeS from '../Shapes/ShapeS';
  24. import ShapeT from '../Shapes/ShapeT';
  25. import ShapeZ from '../Shapes/ShapeZ';
  26. import type {CoordinatesType} from '../Shapes/BaseShape';
  27. import BaseShape from '../Shapes/BaseShape';
  28. import type {GridType} from '../components/GridComponent';
  29. /**
  30. * Class used as an abstraction layer for shapes.
  31. * Use this class to manipulate pieces rather than Shapes directly
  32. *
  33. */
  34. export default class Piece {
  35. shapes = [ShapeL, ShapeI, ShapeJ, ShapeO, ShapeS, ShapeT, ShapeZ];
  36. currentShape: BaseShape;
  37. theme: ReactNativePaper.Theme;
  38. /**
  39. * Initializes this piece's color and shape
  40. *
  41. * @param theme Object containing current theme
  42. */
  43. constructor(theme: ReactNativePaper.Theme) {
  44. this.currentShape = this.getRandomShape(theme);
  45. this.theme = theme;
  46. }
  47. /**
  48. * Gets a random shape object
  49. *
  50. * @param theme Object containing current theme
  51. */
  52. getRandomShape(theme: ReactNativePaper.Theme): BaseShape {
  53. return new this.shapes[Math.floor(Math.random() * 7)](theme);
  54. }
  55. /**
  56. * Removes the piece from the given grid
  57. *
  58. * @param grid The grid to remove the piece from
  59. */
  60. removeFromGrid(grid: GridType) {
  61. const pos: Array<CoordinatesType> = this.currentShape.getCellsCoordinates(
  62. true,
  63. );
  64. pos.forEach((coordinates: CoordinatesType) => {
  65. grid[coordinates.y][coordinates.x] = {
  66. color: this.theme.colors.tetrisBackground,
  67. isEmpty: true,
  68. key: grid[coordinates.y][coordinates.x].key,
  69. };
  70. });
  71. }
  72. /**
  73. * Adds this piece to the given grid
  74. *
  75. * @param grid The grid to add the piece to
  76. * @param isPreview Should we use this piece's current position to determine the cells?
  77. */
  78. toGrid(grid: GridType, isPreview: boolean) {
  79. const pos: Array<CoordinatesType> = this.currentShape.getCellsCoordinates(
  80. !isPreview,
  81. );
  82. pos.forEach((coordinates: CoordinatesType) => {
  83. grid[coordinates.y][coordinates.x] = {
  84. color: this.currentShape.getColor(),
  85. isEmpty: false,
  86. key: grid[coordinates.y][coordinates.x].key,
  87. };
  88. });
  89. }
  90. /**
  91. * Checks if the piece's current position is valid
  92. *
  93. * @param grid The current game grid
  94. * @param width The grid's width
  95. * @param height The grid's height
  96. * @return {boolean} If the position is valid
  97. */
  98. isPositionValid(grid: GridType, width: number, height: number): boolean {
  99. let isValid = true;
  100. const pos: Array<CoordinatesType> = this.currentShape.getCellsCoordinates(
  101. true,
  102. );
  103. for (let i = 0; i < pos.length; i += 1) {
  104. if (
  105. pos[i].x >= width ||
  106. pos[i].x < 0 ||
  107. pos[i].y >= height ||
  108. pos[i].y < 0 ||
  109. !grid[pos[i].y][pos[i].x].isEmpty
  110. ) {
  111. isValid = false;
  112. break;
  113. }
  114. }
  115. return isValid;
  116. }
  117. /**
  118. * Tries to move the piece by the given offset on the given grid
  119. *
  120. * @param x Position X offset
  121. * @param y Position Y offset
  122. * @param grid The grid to move the piece on
  123. * @param width The grid's width
  124. * @param height The grid's height
  125. * @param freezeCallback Callback to use if the piece should freeze itself
  126. * @return {boolean} True if the move was valid, false otherwise
  127. */
  128. tryMove(
  129. x: number,
  130. y: number,
  131. grid: GridType,
  132. width: number,
  133. height: number,
  134. freezeCallback: () => void,
  135. ): boolean {
  136. let newX = x;
  137. let newY = y;
  138. if (x > 1) {
  139. newX = 1;
  140. } // Prevent moving from more than one tile
  141. if (x < -1) {
  142. newX = -1;
  143. }
  144. if (y > 1) {
  145. newY = 1;
  146. }
  147. if (y < -1) {
  148. newY = -1;
  149. }
  150. if (x !== 0 && y !== 0) {
  151. newY = 0;
  152. } // Prevent diagonal movement
  153. this.removeFromGrid(grid);
  154. this.currentShape.move(newX, newY);
  155. const isValid = this.isPositionValid(grid, width, height);
  156. if (!isValid) {
  157. this.currentShape.move(-newX, -newY);
  158. }
  159. const shouldFreeze = !isValid && newY !== 0;
  160. this.toGrid(grid, false);
  161. if (shouldFreeze) {
  162. freezeCallback();
  163. }
  164. return isValid;
  165. }
  166. /**
  167. * Tries to rotate the piece
  168. *
  169. * @param grid The grid to rotate the piece on
  170. * @param width The grid's width
  171. * @param height The grid's height
  172. * @return {boolean} True if the rotation was valid, false otherwise
  173. */
  174. tryRotate(grid: GridType, width: number, height: number): boolean {
  175. this.removeFromGrid(grid);
  176. this.currentShape.rotate(true);
  177. if (!this.isPositionValid(grid, width, height)) {
  178. this.currentShape.rotate(false);
  179. this.toGrid(grid, false);
  180. return false;
  181. }
  182. this.toGrid(grid, false);
  183. return true;
  184. }
  185. /**
  186. * Gets this piece used cells coordinates
  187. *
  188. * @return {Array<CoordinatesType>} An array of coordinates
  189. */
  190. getCoordinates(): Array<CoordinatesType> {
  191. return this.currentShape.getCellsCoordinates(true);
  192. }
  193. getCurrentShape(): BaseShape {
  194. return this.currentShape;
  195. }
  196. }