Advertisement
Nickpips

TETRISGAME

Feb 25th, 2022
230
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.09 KB | None | 0 0
  1. import { GameState, GameTransition, AbstractGame, GameInputTensor } from './AbstractGame';
  2. import { NESTetrisGame } from 'tetris-game';
  3.  
  4. const BOARD_WIDTH = NESTetrisGame.;
  5. const BOARD_HEIGHT = 10;
  6. const NUM_FALLING_STAR_ACTIONS = 3;
  7.  
  8. enum FallingStarActionType {
  9. ACTION_LEFT,
  10. ACTION_NONE,
  11. ACTION_RIGHT,
  12. };
  13.  
  14. enum FallingStarItemType {
  15. NONE,
  16. STAR,
  17. ENEMY,
  18. };
  19.  
  20. class FallingStarState extends GameState {
  21. nesTetrisGame: NESTetrisGame;
  22. totalScore: number;
  23. gameOver: Boolean;
  24.  
  25. toTensor(): GameInputTensor {
  26. let inputTensor: number[][][] = [];
  27. for(let y = 0; y < this.nesTetrisGame.height; y++) {
  28. inputTensor.push([]);
  29. for(let x = 0; x < this.nesTetrisGame.width; x++) {
  30. inputTensor[y].push(new Array(1 + 7 + 7));
  31.  
  32. // Tetris board channel
  33. inputTensor[y][x][0] = this.nesTetrisGame.board.getSquareTaken(x, y) ? 1.0 : 0.0;
  34.  
  35. // Current piece channels
  36. let channelStart = 1;
  37. for(let i = 0; i < 7; i++) {
  38. if (i + 1 == this.nesTetrisGame.current_piece.abstractTetrisPiece.pieceID) {
  39. inputTensor[y][x][channelStart+i] = 1.0;
  40. } else {
  41. inputTensor[y][x][channelStart+i] = 0.0;
  42. }
  43. }
  44.  
  45. // Next piece channels
  46. channelStart = 1 + 7;
  47. for(let i = 0; i < 7; i++) {
  48. if (i + 1 == this.nesTetrisGame.next_piece.abstractTetrisPiece.pieceID) {
  49. inputTensor[y][x][channelStart+i] = 1.0;
  50. } else {
  51. inputTensor[y][x][channelStart+i] = 0.0;
  52. }
  53. }
  54. }
  55. }
  56. return inputTensor;
  57. }
  58.  
  59. toString() {
  60. let ret = "";
  61. for(let y = 0; y < BOARD_HEIGHT; y++) {
  62. let rowStr = "[";
  63. for(let x = 0; x < BOARD_WIDTH; x++) {
  64. if (this.gameOver && y > this.lazeredFrom && x == this.playerLocation) {
  65. rowStr += "*";
  66. } else if (y == BOARD_HEIGHT - 1 && x == this.playerLocation && !this.gameOver) {
  67. rowStr += "P";
  68. } else {
  69. if (this.board[y][x] == FallingStarItemType.STAR) {
  70. rowStr += "O";
  71. } else if (this.board[y][x] == FallingStarItemType.ENEMY) {
  72. rowStr += "X";
  73. } else {
  74. rowStr += " ";
  75. }
  76. }
  77. }
  78. ret += rowStr + "]\n";
  79. }
  80. return ret;
  81. }
  82. };
  83.  
  84. class FallingStarTransition extends GameTransition {
  85. immediateReward: number | null;
  86. probability: number;
  87. gameState: FallingStarState;
  88. }
  89.  
  90. function getFallingStarAction(action_number: number): FallingStarActionType {
  91. if (action_number == 0) {
  92. return FallingStarActionType.ACTION_LEFT;
  93. } else if (action_number == 1) {
  94. return FallingStarActionType.ACTION_NONE;
  95. } else {
  96. return FallingStarActionType.ACTION_RIGHT;
  97. }
  98. }
  99.  
  100. class FallingStarAbstractGame extends AbstractGame {
  101. getInitialState(): FallingStarState {
  102. let ret = new FallingStarState();
  103. ret.board = [];
  104. for(let y = 0; y < BOARD_HEIGHT; y++) {
  105. ret.board.push([]);
  106. for(let x = 0; x < BOARD_WIDTH; x++) {
  107. ret.board[y].push(FallingStarItemType.NONE);
  108. }
  109. }
  110. ret.playerLocation = Math.floor(BOARD_WIDTH / 2);
  111. ret.frame = 0;
  112. ret.currentlyTeleporting = false;
  113. ret.totalNumRewards = 0;
  114. ret.totalScore = 0;
  115. ret.lazeredFrom = -1;
  116. ret.gameOver = false;
  117. return ret;
  118. }
  119.  
  120. duplicateState(state: FallingStarState): FallingStarState {
  121. let ret = new FallingStarState();
  122. ret.board = [];
  123. for(let y = 0; y < BOARD_HEIGHT; y++) {
  124. ret.board.push([]);
  125. for(let x = 0; x < BOARD_WIDTH; x++) {
  126. ret.board[y].push(state.board[y][x]);
  127. }
  128. }
  129. ret.playerLocation = state.playerLocation;
  130. ret.frame = state.frame;
  131. ret.currentlyTeleporting = state.currentlyTeleporting;
  132. ret.totalNumRewards = state.totalNumRewards;
  133. ret.totalScore = state.totalScore;
  134. ret.lazeredFrom = state.lazeredFrom;
  135. ret.gameOver = state.gameOver;
  136. return ret;
  137. }
  138.  
  139. getGameEnded(state: FallingStarState): Boolean {
  140. return state.gameOver;
  141. }
  142.  
  143. getTotalScore(state: FallingStarState): number {
  144. return state.totalScore;
  145. }
  146.  
  147. getNumActions(): number {
  148. return NUM_FALLING_STAR_ACTIONS;
  149. }
  150.  
  151. getValidActions(state: FallingStarState): Boolean[] {
  152. if (this.getGameEnded(state)) {
  153. throw new Error("Game is over!");
  154. }
  155.  
  156. let validMoves = [true, true, true];
  157. if (state.playerLocation == 0) {
  158. validMoves[0] = false;
  159. } else if (state.playerLocation == BOARD_WIDTH - 1) {
  160. validMoves[2] = false;
  161. }
  162. return validMoves;
  163. }
  164.  
  165. getNextStates(state: FallingStarState, actionNumber: number): FallingStarTransition[] {
  166. if (!this.getValidActions(state)[actionNumber]) {
  167. throw new Error("Invalid action: " + actionNumber);
  168. }
  169. if (this.getGameEnded(state)) {
  170. throw new Error("Game is over!");
  171. }
  172.  
  173. // Duplicate the state
  174. let newState = this.duplicateState(state);
  175.  
  176. // Progress the frame
  177. newState.frame++;
  178.  
  179. // Move the current player
  180. let fallingStarAction = getFallingStarAction(actionNumber);
  181. if (fallingStarAction == FallingStarActionType.ACTION_LEFT) {
  182. newState.playerLocation--;
  183. } else if (fallingStarAction == FallingStarActionType.ACTION_RIGHT) {
  184. newState.playerLocation++;
  185. }
  186.  
  187. // Lower the board down
  188. for(let y = BOARD_HEIGHT - 1; y >= 0; y--) {
  189. for(let x = 0; x < BOARD_WIDTH; x++) {
  190. if (y == 0) {
  191. newState.board[y][x] = FallingStarItemType.NONE;
  192. } else {
  193. newState.board[y][x] = newState.board[y-1][x];
  194. }
  195. }
  196. }
  197.  
  198. // Check if a score was made, by intersecting a star
  199. let immediateReward: number | null = null;
  200. // Mark immediate reward, if it was possible to receive one
  201. for(let i = 0; i < BOARD_WIDTH; i++) {
  202. if (newState.board[BOARD_HEIGHT - 1][i] == FallingStarItemType.STAR) {
  203. immediateReward = 0.0;
  204. }
  205. }
  206. // Mark as 1.0, if the reward was received
  207. if (newState.board[BOARD_HEIGHT - 1][newState.playerLocation] == FallingStarItemType.STAR) {
  208. immediateReward = 1.0;
  209. }
  210.  
  211. // Check if the game is over, by intersecting an enemy
  212. if (newState.board[BOARD_HEIGHT - 1][newState.playerLocation] == FallingStarItemType.ENEMY) {
  213. newState.lazeredFrom = BOARD_HEIGHT - 1;
  214. newState.gameOver = true;
  215. }
  216.  
  217. if (immediateReward != null) {
  218. // newTotalScore = (prevTotalScore * prevNum + reward) / (prevNum + 1)
  219. // newTotalScore = prevTotalScore * ((prevNum + 1) - 1) / (prevNum + 1) + reward / (prevNum + 1)
  220. // newTotalScore = prevTotalScore * (1 - 1 / (prevNum + 1)) + reward / (prevNum + 1)
  221. // newTotalScore = prevTotalScore - prevTotalScore / (prevNum + 1) + reward / (prevNum + 1)
  222. // newTotalScore = prevTotalScore + (reward - prevTotalScore) / (prevNum + 1)
  223. newState.totalScore += (immediateReward - newState.totalScore) / (newState.totalNumRewards + 1);
  224. newState.totalNumRewards++;
  225. }
  226. let possibleTransitions: FallingStarTransition[] = [];
  227.  
  228. // Try spawning a new Star
  229. if (newState.frame % 5 == 0 && !newState.gameOver) {
  230. // Spawn a new star, in a deterministic location
  231. let newStarLocation = ((newState.frame / 5) * 2 + 3) % BOARD_WIDTH;
  232. newState.board[0][newStarLocation] = FallingStarItemType.STAR;
  233. }
  234.  
  235. // Else, try spawning a new enemy
  236. else if (newState.frame % 4 == 0 && !newState.gameOver) {
  237. // Spawn a new enemy, in a deterministic location, not over the player
  238. let newEnemyLocation = ((newState.frame / 4) * 3 + 2) % BOARD_WIDTH;
  239. if (newEnemyLocation == newState.playerLocation) {
  240. newEnemyLocation = (newEnemyLocation + 3) % BOARD_WIDTH;
  241. }
  242. newState.board[0][newEnemyLocation] = FallingStarItemType.ENEMY;
  243. }
  244.  
  245. // Consider the gamestate of an enemy lazering the player from above
  246. let remainingProbability = 1.0;
  247. for(let y = BOARD_HEIGHT - 2; y >= 1; y--) {
  248. // Only consider the lowest enemy above the player
  249. if (newState.board[y][newState.playerLocation] == FallingStarItemType.ENEMY && !newState.gameOver) {
  250. let probabilityOfDying = y / (BOARD_HEIGHT - 1);
  251. let badGameState = this.duplicateState(newState);
  252. badGameState.lazeredFrom = y;
  253. badGameState.gameOver = true;
  254. possibleTransitions.push({
  255. immediateReward: immediateReward,
  256. probability: probabilityOfDying,
  257. gameState: badGameState,
  258. });
  259. remainingProbability -= probabilityOfDying;
  260. break;
  261. }
  262. }
  263.  
  264. // Push the probability that the player doesn't get lazered from above
  265. possibleTransitions.push({
  266. immediateReward: immediateReward,
  267. probability: remainingProbability,
  268. gameState: newState,
  269. });
  270.  
  271. return possibleTransitions;
  272. }
  273. };
  274.  
  275. export {
  276. FallingStarState,
  277. FallingStarAbstractGame,
  278. };
  279.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement