Skip to main content
Participating Frequently
December 11, 2023
Question

Tetris Game AS3- I can't move the tetromino HORIZONTALLY, DOWN, ROTATE (need help!)

  • December 11, 2023
  • 1 reply
  • 703 views
package {
	import flash.display.Sprite;
	import flash.utils.Timer;
	import flash.events.TimerEvent;
	import flash.events.KeyboardEvent;
	import flash.display.DisplayObject;
	import flash.events.KeyboardEvent;
	import flash.ui.Keyboard;


	// The main class for the Tetris game, extends Sprite.
	public class Main extends Sprite {

		// Constant representing the size of a single tile (in pixels).
		private const TS: uint = 20;
		private const ROWS: uint = 20;
		private const COLS: uint = 10;

		// Array to numerically represent the game field.
		private var fieldArray: Array;

		// DisplayObject to graphically render the game field.
		private var fieldSprite: Sprite;

		// Arrays to store different types of tetrominoes and their colors.
		private var tetrominoes: Array = new Array();
		private var colors: Array = new Array();

		// Sprite to represent the current tetromino.
		private var tetromino: Sprite;

		// Variables to store the current and next tetromino types, current rotation,
		// and the current position of the tetromino on the game field.
		private var currentTetromino: uint;
		private var nextTetromino: uint;
		private var currentRotation: uint;
		private var tRow: int;
		private var tCol: int;
		private var gameOver: Boolean = false;


		// Timer to control the falling speed of tetrominoes.
		private var timeCount: Timer = new Timer(500);

		// Constructor function for the Main class.
		public function Main() {
			generateField();
			initTetrominoes();
			// Generate the random value for the next tetromino
			nextTetromino = Math.floor(Math.random() * 7);
			generateTetromino();
			stage.addEventListener(KeyboardEvent.KEY_DOWN, onKDown);
		}


		/**
		 * Function to generate the initial game field.
		 * The field is represented numerically by a 2D array (fieldArray)
		 * and visually by a Sprite (fieldSprite) added to the stage.
		 */
		private function generateField(): void {
			// Define alternating colors for the game field tiles.
			var colors: Array = new Array("0x444444", "0x555555");

			// Initialize the fieldArray to store numerical representation of the game field.
			fieldArray = new Array;

			// Create a new Sprite to visually represent the game field and add it to the stage.
			var fieldSprite: Sprite = new Sprite;
			addChild(fieldSprite);

			// Set the line style for the graphics of the fieldSprite.
			fieldSprite.graphics.lineStyle(0, 0x000000);

			// Set the position of fieldSprite to specific coordinates.
			//fieldSprite.x = 29.05;
			//fieldSprite.y = 18.05;

			// Loop through rows and columns to initialize fieldArray and draw the game field.
			for (var i: uint = 0; i < 20; i++) {
				// Initialize a new array for each row in fieldArray.
				fieldArray[i] = new Array;

				for (var j: uint = 0; j < 10; j++) {
					// Initialize each element in fieldArray to 0.
					fieldArray[i][j] = 0;

					// Use alternating colors to create a checkerboard pattern in the game field.
					fieldSprite.graphics.beginFill(colors[(j % 2 + i % 2) % 2]);

					// Draw a rectangle representing a tile in the game field.
					fieldSprite.graphics.drawRect(TS * j, TS * i, TS, TS);

					// End the fill for the current tile.
					fieldSprite.graphics.endFill();
				}
			}
		}


		// Function to initialize different types of tetrominoes and their colors.
		private function initTetrominoes(): void {
			// I
			tetrominoes[0] = [
				[
					[0, 0, 0, 0],
					[1, 1, 1, 1],
					[0, 0, 0, 0],
					[0, 0, 0, 0]
				],
				[
					[0, 1, 0, 0],
					[0, 1, 0, 0],
					[0, 1, 0, 0],
					[0, 1, 0, 0]
				]
			];
			colors[0] = 0x00FFFF;

			// T
			tetrominoes[1] = [
				[
					[0, 0, 0, 0],
					[1, 1, 1, 0],
					[0, 1, 0, 0],
					[0, 0, 0, 0]
				],
				[
					[0, 1, 0, 0],
					[1, 1, 0, 0],
					[0, 1, 0, 0],
					[0, 0, 0, 0]
				],
				[
					[0, 1, 0, 0],
					[1, 1, 1, 0],
					[0, 0, 0, 0],
					[0, 0, 0, 0]
				],
				[
					[0, 1, 0, 0],
					[0, 1, 1, 0],
					[0, 1, 0, 0],
					[0, 0, 0, 0]
				]
			];

			colors[1] = 0xAA00FF;

			// L
			tetrominoes[2] = [
				[
					[0, 0, 0, 0],
					[1, 1, 1, 0],
					[1, 0, 0, 0],
					[0, 0, 0, 0]
				],
				[
					[1, 1, 0, 0],
					[0, 1, 0, 0],
					[0, 1, 0, 0],
					[0, 0, 0, 0]
				],
				[
					[0, 0, 1, 0],
					[1, 1, 1, 0],
					[0, 0, 0, 0],
					[0, 0, 0, 0]
				],
				[
					[0, 1, 0, 0],
					[0, 1, 0, 0],
					[0, 1, 1, 0],
					[0, 0, 0, 0]
				]
			];

			colors[2] = 0xFFA500;

			// J
			tetrominoes[3] = [
				[
					[1, 0, 0, 0],
					[1, 1, 1, 0],
					[0, 0, 0, 0],
					[0, 0, 0, 0]
				],
				[
					[0, 1, 1, 0],
					[0, 1, 0, 0],
					[0, 1, 0, 0],
					[0, 0, 0, 0]
				],
				[
					[0, 0, 0, 0],
					[1, 1, 1, 0],
					[0, 0, 1, 0],
					[0, 0, 0, 0]
				],
				[
					[0, 1, 0, 0],
					[0, 1, 0, 0],
					[1, 1, 0, 0],
					[0, 0, 0, 0]
				]
			];
			colors[3] = 0x0000FF;

			// Z
			tetrominoes[4] = [
				[
					[0, 0, 0, 0],
					[1, 1, 0, 0],
					[0, 1, 1, 0],
					[0, 0, 0, 0]
				],
				[
					[0, 0, 1, 0],
					[0, 1, 1, 0],
					[0, 1, 0, 0],
					[0, 0, 0, 0]
				]
			];
			colors[4] = 0xFF0000;

			// S
			tetrominoes[5] = [
				[
					[0, 0, 0, 0],
					[0, 1, 1, 0],
					[1, 1, 0, 0],
					[0, 0, 0, 0]
				],
				[
					[0, 1, 0, 0],
					[0, 1, 1, 0],
					[0, 0, 1, 0],
					[0, 0, 0, 0]
				]
			];
			colors[5] = 0x00FF00;

			// O
			tetrominoes[6] = [
				[
					[0, 1, 1, 0],
					[0, 1, 1, 0],
					[0, 0, 0, 0],
					[0, 0, 0, 0]
				]
			];
			colors[6] = 0xFFFF00;
		}

		// Generate the current tetromino
		private function generateTetromino(): void {
			// Check if the game is not over
			if (!gameOver) {
				// Generate a new random tetromino
				currentTetromino = Math.floor(Math.random() * 7);
				// Set the initial rotation for the current tetromino
				currentRotation = 0;

				// Set the initial row for the current tetromino to the top
				tRow = 0;

				// Adjust starting position based on the tetromino shape and the width of the game field
				tCol = Math.floor((10 - tetrominoes[currentTetromino][0][0].length) / 2);

				// Draw the current tetromino on the stage
				drawTetromino();

				// Check if the tetromino can fit in its starting position
				if (canFit(tRow, tCol, currentRotation)) {
					// Add timer listener for automatic downward movement
					timeCount.addEventListener(TimerEvent.TIMER, onTime);
					timeCount.start();
				} else {
					// If tetromino cannot fit, it's game over
					gameOver = true;
				}
			}
		}

		// Draw the next tetromino
		private function drawNext(): void {
			// Remove the previous "next" sprite if it exists
			if (getChildByName("next") != null) {
				removeChild(getChildByName("next"));
			}

			// Create a new sprite for the next tetromino
			var next_t: Sprite = new Sprite();
			next_t.x = 300; // Set the x-coordinate for the display
			next_t.name = "next"; // Set the name for identification
			addChild(next_t); // Add the sprite to the display list

			// Draw the next tetromino
			next_t.graphics.lineStyle(0, 0x000000);
			for (var i: int = 0; i < tetrominoes[nextTetromino][0][0].length; i++) {
				for (var j: int = 0; j < tetrominoes[nextTetromino][0].length; j++) {
					if (tetrominoes[nextTetromino][0][j][i] == 1) {
						next_t.graphics.beginFill(colors[nextTetromino]);
						next_t.graphics.drawRect(TS * i, TS * j, TS, TS);
						next_t.graphics.endFill();
					}
				}
			}
		}

		// Add the onTime function for the timer listener
		private function onTime(e: TimerEvent): void {
			// Check if tetromino can fit one row down
			if (canFit(tRow + 1, tCol, currentRotation)) {
				// Move the tetromino down by one row
				tRow++;
				placeTetromino();
			} else {
				// If it can't move down, land the tetromino and generate a new one
				landTetromino();
				generateTetromino();
			}
		}

		// Function to draw the current tetromino on the stage.
		private function drawTetromino(): void {
			var ct: uint = currentTetromino;
			tetromino = new Sprite();
			addChild(tetromino);
			tetromino.graphics.lineStyle(0, 0xCFCCF7);

			// Loop to draw the individual tiles of the current tetromino.
			for (var i: int = 0; i < tetrominoes[ct][currentRotation].length; i++) {
				for (var j: int = 0; j < tetrominoes[ct][currentRotation][i].length; j++) {
					if (tetrominoes[ct][currentRotation][i][j] == 1) { // Fix: Check if the value is equal to 1
						tetromino.graphics.beginFill(colors[ct]);
						tetromino.graphics.drawRect(TS * j, TS * i, TS, TS);
						tetromino.graphics.endFill();
					}
				}
			}

			// Place the tetromino on the game field.
			placeTetromino();
		}

		// Function to place the current tetromino at its current position.
		private function placeTetromino(): void {
			tetromino.x = tCol * TS;
			tetromino.y = tRow * TS;
		}

		// Modify onKDown function to check for game over and handle horizontal movement
		private function onKDown(e: KeyboardEvent): void {
			// Check if the game is not over
			if (!gameOver) {
				switch (e.keyCode) {
					case 37: // LEFT arrow key
						if (canFit(tRow, tCol - 1, currentRotation)) {
							// Move the current tetromino to the left
							tCol--;
							placeTetromino();
						}
						break;
					case 38: // UP arrow key
						// Rotate the tetromino (existing code)
						var ct: uint = currentRotation;
						var rot: uint = (ct + 1) % tetrominoes[currentTetromino].length;
						if (canFit(tRow, tCol, rot)) {
							currentRotation = rot;
							removeChild(tetromino);
							drawTetromino();
							placeTetromino();
						}
						break;
					case 39: // RIGHT arrow key
						if (canFit(tRow, tCol + 1, currentRotation)) {
							// Move the current tetromino to the right
							tCol++;
							placeTetromino();
						}
						break;
					case 40: // DOWN arrow key
						if (canFit(tRow + 1, tCol, currentRotation)) {
							// Move the current tetromino down
							tRow++;
							placeTetromino();
						} else {
							landTetromino();
							generateTetromino();
						}
						break;
				}
			}
		}



		// Modify landTetromino function to remove the timer listener
		private function landTetromino(): void {
			var ct: uint = currentTetromino;
			var landed: Sprite;
			for (var i: int = 0; i < tetrominoes[ct][currentRotation].length; i++) {
				for (var j: int = 0; j < tetrominoes[ct][currentRotation][i].length; j++) {
					// Check for tetromino piece
					if (tetrominoes[ct][currentRotation][i][j] == 1) {
						// Create a new sprite for the landed piece
						landed = new Sprite();
						// Add the landed piece to the display list
						addChild(landed);
						// Draw the landed piece
						landed.graphics.lineStyle(0, 0x000000);
						landed.graphics.beginFill(colors[currentTetromino]);
						landed.graphics.drawRect(TS * (tCol + j), TS * (tRow + i), TS, TS);
						landed.graphics.endFill();
						// Set the name for easy identification
						landed.name = "r" + (tRow + i) + "c" + (tCol + j);
						// Update the fieldArray to mark the cell as occupied
						fieldArray[tRow + i][tCol + j] = 1;
					}
				}
			}

			// Remove the timer listener
			timeCount.removeEventListener(TimerEvent.TIMER, onTime);
			// Stop the timer
			timeCount.stop();

			// Continue with line checking and removal
			checkForLines();
		}

		private function rotateTetromino(): void {
			var ct: uint = currentRotation;
			var rot: uint = (ct + 1) % tetrominoes[currentTetromino].length;

			if (canFit(tRow, tCol, rot)) {
				currentRotation = rot;
				removeChild(tetromino);
				drawTetromino();
				placeTetromino();
			}
		}

		// Updated checkForLines function to manage remaining lines after removal
		private function checkForLines(): void {
			for (var i: int = 0; i < 20; i++) {
				if (fieldArray[i].indexOf(0) == -1) {
					// Remove the completed line
					for (var j: int = 0; j < 10; j++) {
						fieldArray[i][j] = 0;
						removeChild(getChildByName("r" + i + "c" + j));
					}

					// Move down tetrominoes above the cleared line
					for (j = i; j >= 0; j--) {
						for (var k: int = 0; k < 10; k++) {
							if (fieldArray[j][k] == 1) {
								fieldArray[j][k] = 0;
								fieldArray[j + 1][k] = 1;

								// Move down the corresponding DisplayObject
								getChildByName("r" + j + "c" + k).y += TS;
								// Update the name based on the new position
								getChildByName("r" + j + "c" + k).name = "r" + (j + 1) + "c" + k;
							}
						}
					}
				}
			}
		}

		// Function to remove a completed line
		private function removeLine(lineIndex: int): void {
			// Loop through each column of the fieldArray
			for (var j: int = 0; j < fieldArray[lineIndex].length; j++) {
				// Remove the cell from the display
				removeChild(getChildByName("r" + lineIndex + "c" + j));
			}
		}

		// Function to move down lines above the removed line
		private function moveLinesDown(removedLine: int): void {
			// Loop through each row above the removed line
			for (var i: int = removedLine; i > 0; i--) {
				// Copy the row above to the current row
				fieldArray[i] = fieldArray[i - 1].concat();

				// Loop through each column
				for (var j: int = 0; j < fieldArray[i].length; j++) {
					// Update the name of the moved cell
					var cell: DisplayObject = getChildByName("r" + (i - 1) + "c" + j);
					if (cell) cell.name = "r" + i + "c" + j;
				}
			}

			// Empty the top row
			fieldArray[0] = new Array(10).fill(0);
		}

		private function canFit(row: int, col: int, side: uint): Boolean {
			var ct: uint = currentTetromino;
			for (var i: int = 0; i < tetrominoes[ct][side].length; i++) {
				for (var j: int = 0; j < tetrominoes[ct][side][i].length; j++) {
					if (tetrominoes[ct][side][i][j] == 1) {
						// Check for boundaries
						if (col + j < 0 || col + j > 9) {
							return false;
						}
						// Check if the cell is occupied by another tetromino
						if (fieldArray[row + i][col + j] == 1) {
							return false;
						}
					}
				}
			}
			return true;
		}
	}
}
    This topic has been closed for replies.

    1 reply

    kglad
    Community Expert
    Community Expert
    December 11, 2023
    Participating Frequently
    December 11, 2023

    may i know how to delete the previous post?

     

    kglad
    Community Expert
    Community Expert
    December 11, 2023

    is the code there different than the code you posted here?