Bejeweled AS3 engine with array rotation

Read all posts about "" game

I wanted to have a look at a Bejeweled game featuring the 90 degrees array rotation I explained yesterday.

So I took the script made by Brook Jordan in the complete Bejeweled game in less than 2KB – legible version and added some quick and dirty code to make it work:

package {
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.events.Event;
	import flash.text.TextField;
 
	public class Main extends Sprite {
		private var gems_array:Array=new Array();
		private var aGem:Sprite;
		private var selectorBox:Sprite=new Sprite();
		private var selectorRow:int=-10;
		private var selectorColumn:int=-10;
		private var red:uint = 0xFF0000;
		private var green:uint = 0xFF00;
		private var blue:uint = 0xFF;
		private var yellow:uint = 0xFFFF00;
		private var cyan:uint = 0xFFFF;
		private var magenta:uint = 0xFF00FF;
		private var white:uint = 0xFFFFFF;
		private var colours_array:Array=new Array(red,green,blue,yellow,cyan,magenta,white);
		private var clickPossible:Boolean=false;
		private var score_txt:TextField=new TextField();
		private var hint_txt:TextField=new TextField();
		private var score:uint=0;
		private var inaRow:uint=0;
		private var match:Boolean = true;
		private var rotate:Boolean=false;
		private var gemCanvas:Sprite=new Sprite();
 
		public function Main() {
			// Game initiation
			// Create and style score text
			addChild(score_txt);
			score_txt.textColor=0xFFFFFF;
			score_txt.x=500;
			// Create and style hint text
			addChild(hint_txt);
			hint_txt.textColor=0xFFFFFF;
			hint_txt.x=550;
			// Create Gems in rows and columns
			addChild(gemCanvas);
			gemCanvas.x=240;
			gemCanvas.y=240;
			for (var i:uint=0; i<8; i++) {
				gems_array[i]=new Array();
				for (var j:uint=0; j<8; j++) {
					do {
						gems_array[i][j]=Math.floor(Math.random()*7);
					}
						while (rowLineLength(i,j)>2 || columnLineLength(i,j)>2);
					aGem=new Sprite();
					aGem.graphics.beginFill(colours_array[gems_array[i][j]]);
					aGem.graphics.drawCircle(30,30,29);
					aGem.graphics.endFill();
					aGem.name=i+"_"+j;
					aGem.x=j*60-240;
					aGem.y=i*60-240;
					gemCanvas.addChild(aGem);
				}
			}
			// Create and style selector box
			addChild(selectorBox);
			selectorBox.graphics.lineStyle(2,red,1);
			selectorBox.graphics.drawRect(0,0,60,60);
			selectorBox.visible=false;
			// Listen for user input
			stage.addEventListener(MouseEvent.CLICK,onClick);
 
			addEventListener(Event.ENTER_FRAME,everyFrame);
		}
 
		// Every frame...
		private function everyFrame(e:Event):void {
			//Assume that gems are not falling
			var gemsAreFalling:Boolean=false;
			// Check each gem for space below it
			for (var i:int=6; i>=0; i--) {
				for (var j:uint=0; j<8; j++) {
					// If a spot contains a gem, and has an empty space below...
					if (gems_array[i][j] != -1 && gems_array[i+1][j]==-1) {
						// Set gems falling
						gemsAreFalling=true;
						gems_array[i+1][j]=gems_array[i][j];
						gems_array[i][j]=-1;
						gemCanvas.getChildByName(i+"_"+j).y+=60;
						gemCanvas.getChildByName(i+"_"+j).name=(i+1)+"_"+j;
						break;
					}
				}
				// If a gem is falling
				if (gemsAreFalling) {
					// don't allow any more to start falling
					break;
				}
			}
			// If no gems are falling
			if (! gemsAreFalling) {
				// Assume no new gems are needed
				var needNewGem:Boolean=false;
				// but check all spaces...
				for (i=7; i>=0; i--) {
					for (j=0; j<8; j++) {
						// and if a spot is empty
						if (gems_array[i][j]==-1) {
							// now we know we need a new gem
							needNewGem=true;
							// pick a random color for the gem
							gems_array[0][j]=Math.floor(Math.random()*7);
							// create the gem
							aGem=new Sprite();
							aGem.graphics.beginFill(colours_array[gems_array[0][j]]);
							aGem.graphics.drawCircle(30,30,29);
							aGem.graphics.endFill();
							// ID it
							aGem.name="0_"+j;
							// position it
							aGem.x=j*60-240;
							aGem.y=-240;
							// show it
							gemCanvas.addChild(aGem);
							// stop creating new gems
							break;
						}
					}
					// if a new gem was created, stop checking
					if (needNewGem) {
						break;
					}
				}
				// If no new gems were needed...
				if (! needNewGem) {
					// assume no more/new lines are on the board
					var moreLinesAvailable:Boolean=false;
					// check all gems
					for (i=7; i>=0; i--) {
						for (j=0; j<8; j++) {
							// if a line is found
							if (rowLineLength(i,j)>2 || columnLineLength(i,j)>2) {
								// then we know more lines are available
								moreLinesAvailable=true;
								// creat a new array, set the gem type of the line, and where it is
								var lineGems:Array=[i+"_"+j];
								var gemType:uint=gems_array[i][j];
								var linePosition:int;
								// check t's a horizontal line...
								if (rowLineLength(i,j)>2) {
									// if so, find our how long it is and put all the line's gems into the array
									linePosition=j;
									while (sameGemIsHere(gemType,i,linePosition-1)) {
										linePosition--;
										lineGems.push(i+"_"+linePosition);
									}
									linePosition=j;
									while (sameGemIsHere(gemType,i,linePosition+1)) {
										linePosition++;
										lineGems.push(i+"_"+linePosition);
									}
								}
								// check t's a vertical line...
								if (columnLineLength(i,j)>2) {
									// if so, find our how long it is and put all the line's gems into the array
									linePosition=i;
									while (sameGemIsHere(gemType,linePosition-1,j)) {
										linePosition--;
										lineGems.push(linePosition+"_"+j);
									}
									linePosition=i;
									while (sameGemIsHere(gemType,linePosition+1,j)) {
										linePosition++;
										lineGems.push(linePosition+"_"+j);
									}
								}
								// for all gems in the line...
								for (i=0; i0){
									gemCanvas.removeChildAt(0);
								}
								for (i=0; i<8; i++) {
									for (j=0; j<8; j++) {
										aGem=new Sprite();
										aGem.graphics.beginFill(colours_array[gems_array[i][j]]);
										aGem.graphics.drawCircle(30,30,29);
										aGem.graphics.endFill();
										aGem.name=i+"_"+j;
										aGem.x=j*60-240;
										aGem.y=i*60-240;
										gemCanvas.addChild(aGem);
									}
								}
							}
						}
						else{
							// allow new moves to be made
							clickPossible=true;
							// remove score multiplier
							inaRow=0;
						}
					}
				}
			}
			// display new score
			score_txt.text=score.toString();
		}
		// When the user clicks
		private function onClick(e:MouseEvent):void {
			// If a click is allowed
			if (clickPossible) {
				// If the click is within the game area...
				if (mouseX<480&&mouseX>0&&mouseY<480&&mouseY>0) {
					// Find which row and column were clicked
					var clickedRow:uint=Math.floor(mouseY/60);
					var clickedColumn:uint=Math.floor(mouseX/60);
					// Check if the clicked gem is adjacent to the selector
					// If not...
					if (!(((clickedRow==selectorRow+1 || clickedRow==selectorRow-1)&&clickedColumn==selectorColumn)||((clickedColumn==selectorColumn+1 || clickedColumn==selectorColumn-1) && clickedRow==selectorRow))) {
						// Find row and colum the selector should move to
						selectorRow=clickedRow;
						selectorColumn=clickedColumn;
						// Move it to the chosen position
						selectorBox.x=60*selectorColumn;
						selectorBox.y=60*selectorRow;
						// If hidden, show it.
						selectorBox.visible=true;
					}
					// If it is not next to it...
					else {
						// Swap the gems;
						swapGems(selectorRow,selectorColumn,clickedRow,clickedColumn);
						// If they make a line...
						if (rowLineLength(selectorRow,selectorColumn)>2 || columnLineLength(selectorRow,selectorColumn)>2||rowLineLength(clickedRow,clickedColumn)>2 || columnLineLength(clickedRow,clickedColumn)>2) {
							// remove the hint text
							hint_txt.text="";
							// dis-allow a new move until cascade has ended (removes glitches)
							clickPossible=false;
							// move and rename the gems
							gemCanvas.getChildByName(selectorRow+"_"+selectorColumn).x=clickedColumn*60-240;
							gemCanvas.getChildByName(selectorRow+"_"+selectorColumn).y=clickedRow*60-240;
							gemCanvas.getChildByName(selectorRow+"_"+selectorColumn).name="t";
							gemCanvas.getChildByName(clickedRow+"_"+clickedColumn).x=selectorColumn*60-240;
							gemCanvas.getChildByName(clickedRow+"_"+clickedColumn).y=selectorRow*60-240;
							gemCanvas.getChildByName(clickedRow+"_"+clickedColumn).name=selectorRow+"_"+selectorColumn;
							gemCanvas.getChildByName("t").name=clickedRow+"_"+clickedColumn;
							match = true;
							rotate = true;
						}
						// If not...
						else {
							// Switch them back
							swapGems(selectorRow,selectorColumn,clickedRow,clickedColumn);
							match = false;
						}
						if (match) {
							// Move the selector position to default
							selectorRow=-10;
							selectorColumn=-10;
							// and hide it
							selectorBox.visible=false;
						}
						else {
							// Set the selector position
							selectorRow=clickedRow;
							selectorColumn=clickedColumn;
							// Move the box into position
							selectorBox.x=60*selectorColumn;
							selectorBox.y=60*selectorRow;
							match = false;
							// If hidden, show it.
							selectorBox.visible=true;
						}
					}
				}
				// If the click is outside the game area
				else {
					// For gems in all rows...
					for (var i:uint=0; i<8; i++) {
						// and columns...
						for (var j:uint=0; j<8; j++) {
							// if they're not too close to the side... 
							if (i<7) {
								// swap them horizontally
								swapGems(i,j,i+1,j);
								// check if they form a line
								if ((rowLineLength(i,j)>2||columnLineLength(i,j)>2||rowLineLength(i+1,j)>2||columnLineLength(i+1,j)>2)) {
									// if so, name the move made
									selectorBox.x = j*60;
									selectorBox.y = i*60;
									selectorBox.visible = true;
									hint_txt.text = (i+1).toString()+","+(j+1).toString()+"->"+(i+2).toString()+","+(j+1).toString();
								}
								// swap the gems back
								swapGems(i,j,i+1,j);
							}
							// then if they're not to close to the bottom...
							if (j<7) {
								// swap it vertically
								swapGems(i,j,i,j+1);
								// check if it forms a line
								if ((rowLineLength(i,j)>2||columnLineLength(i,j)>2||rowLineLength(i,j+1)>2||columnLineLength(i,j+1)>2) ) {
									// if so, name it
									selectorBox.x = j*60;
									selectorBox.y = i*60;
									selectorBox.visible = true;
									hint_txt.text = (i+1).toString()+","+(j+1).toString()+"->"+(i+1).toString()+","+(j+2).toString();
								}
								// swap the gems back
								swapGems(i,j,i,j+1);
							}
						}
					}
				}
			}
		}
		//Swap given gems
		private function swapGems(fromRow:uint,fromColumn:uint,toRow:uint,toColumn:uint):void {
			//Save the original position
			var originalPosition:uint=gems_array[fromRow][fromColumn];
			//Move original gem to new position
			gems_array[fromRow][fromColumn]=gems_array[toRow][toColumn];
			//move second gem to saved, original gem's position
			gems_array[toRow][toColumn]=originalPosition;
		}
		//Find out if there us a horizontal line
		private function rowLineLength(row:uint,column:uint):uint {
			var gemType:uint=gems_array[row][column];
			var lineLength:uint=1;
			var checkColumn:int=column;
			//check how far left it extends
			while (sameGemIsHere(gemType,row,checkColumn-1)) {
				checkColumn--;
				lineLength++;
			}
			checkColumn=column;
			//check how far right it extends
			while (sameGemIsHere(gemType,row,checkColumn+1)) {
				checkColumn++;
				lineLength++;
			}
			// return total line length
			return (lineLength);
		}
		//Find out if there us a vertical line
		private function columnLineLength(row:uint,column:uint):uint {
			var gemType:uint=gems_array[row][column];
			var lineLength:uint=1;
			var checkRow:int=row;
			//check how low it extends
			while (sameGemIsHere(gemType,checkRow-1,column)) {
				checkRow--;
				lineLength++;
			}
			//check how high it extends
			checkRow=row;
			while (sameGemIsHere(gemType,checkRow+1,column)) {
				checkRow++;
				lineLength++;
			}
			// return total line length
			return (lineLength);
		}
		private function sameGemIsHere(gemType:uint,row:int,column:int):Boolean {
			//Check there are gems in the chosen row
			if (gems_array[row]==null) {
				return false;
			}
			//If there are, check if there is a gem in the chosen slot
			if (gems_array[row][column]==null) {
				return false;
			}
			//If there is, check if it's the same as the chosen gem type
			return gemType==gems_array[row][column];
		}
		private function rotateClockwise(a:Array):void {
			var n:int=a.length;
			for (var i:int=0; i

My code is written mainly at lines 197-218

gemCanvas.rotation+=5;
if(gemCanvas.rotation%90==0){
	rotate=false;
	gemCanvas.rotation=0;
	rotateClockwise(gems_array);
	while(gemCanvas.numChildren>0){
		gemCanvas.removeChildAt(0);
	}
	for (i=0; i<8; i++) {
		for (j=0; j<8; j++) {
			aGem=new Sprite();
			aGem.graphics.beginFill(colours_array[gems_array[i][j]]);
			aGem.graphics.drawCircle(30,30,29);
			aGem.graphics.endFill();
			aGem.name=i+"_"+j;
			aGem.x=j*60-240;
			aGem.y=i*60-240;
			gemCanvas.addChild(aGem);
		}
	}
}

To save coding time, I only rotate the two-dimensional array, then remove all jewels and recreate them in their new position.

This is the result:

I wonder if it could be interesting to create a real game out of it. Any idea?

No need to download anything, replace the source code of the original prototype with the one on this page.

Get the most popular Phaser 3 book

Through 202 pages, 32 source code examples and an Android Studio project you will learn how to build cross platform HTML5 games and create a complete game along the way.

Get the book

214 GAME PROTOTYPES EXPLAINED WITH SOURCE CODE
// 1+2=3
// 100 rounds
// 10000000
// 2 Cars
// 2048
// A Blocky Christmas
// A Jumping Block
// A Life of Logic
// Angry Birds
// Angry Birds Space
// Artillery
// Astro-PANIC!
// Avoider
// Back to Square One
// Ball Game
// Ball vs Ball
// Ball: Revamped
// Balloon Invasion
// BallPusher
// Ballz
// Bar Balance
// Bejeweled
// Biggification
// Block it
// Blockage
// Bloons
// Boids
// Bombuzal
// Boom Dots
// Bouncing Ball
// Bouncing Ball 2
// Bouncy Light
// BoxHead
// Breakout
// Bricks
// Bubble Chaos
// Bubbles 2
// Card Game
// Castle Ramble
// Chronotron
// Circle Chain
// Circle Path
// Circle Race
// Circular endless runner
// Cirplosion
// CLOCKS - The Game
// Color Hit
// Color Jump
// ColorFill
// Columns
// Concentration
// Crossy Road
// Crush the Castle
// Cube Jump
// CubesOut
// Dash N Blast
// Dashy Panda
// Deflection
// Diamond Digger Saga
// Don't touch the spikes
// Dots
// Down The Mountain
// Drag and Match
// Draw Game
// Drop Wizard
// DROP'd
// Dudeski
// Dungeon Raid
// Educational Game
// Elasticity
// Endless Runner
// Erase Box
// Eskiv
// Farm Heroes Saga
// Filler
// Flappy Bird
// Fling
// Flipping Legend
// Floaty Light
// Fuse Ballz
// GearTaker
// Gem Sweeper
// Globe
// Goat Rider
// Gold Miner
// Grindstone
// GuessNext
// Helicopter
// Hero Emblems
// Hero Slide
// Hexagonal Tiles
// HookPod
// Hop Hop Hop Underwater
// Horizontal Endless Runner
// Hundreds
// Hungry Hero
// Hurry it's Christmas
// InkTd
// Iromeku
// Jet Set Willy
// Jigsaw Game
// Knife Hit
// Knightfall
// Legends of Runeterra
// Lep's World
// Line Rider
// Lumines
// Magick
// MagOrMin
// Mass Attack
// Math Game
// Maze
// Meeblings
// Memdot
// Metro Siberia Underground
// Mike Dangers
// Mikey Hooks
// Nano War
// Nodes
// o:anquan
// One Button Game
// One Tap RPG
// Ononmin
// Pacco
// Perfect Square!
// Perfectionism
// Phyballs
// Pixel Purge
// PixelField
// Planet Revenge
// Plants Vs Zombies
// Platform
// Platform game
// Plus+Plus
// Pocket Snap
// Poker
// Pool
// Pop the Lock
// Pop to Save
// Poux
// Pudi
// Pumpkin Story
// Puppet Bird
// Pyramids of Ra
// qomp
// Quick Switch
// Racing
// Radical
// Rebuild Chile
// Renju
// Rise Above
// Risky Road
// Roguelike
// Roly Poly
// Run Around
// Rush Hour
// SameGame
// SamePhysics
// Save the Totem
// Security
// Serious Scramblers
// Shrink it
// Sling
// Slingy
// Snowflakes
// Sokoban
// Space Checkers
// Space is Key
// Spellfall
// Spinny Gun
// Splitter
// Spring Ninja
// Sproing
// Stabilize!
// Stack
// Stick Hero
// String Avoider
// Stringy
// Sudoku
// Super Mario Bros
// Surfingers
// Survival Horror
// Talesworth Adventure
// Tetris
// The Impossible Line
// The Moops - Combos of Joy
// The Next Arrow
// Threes
// Tic Tac Toe
// Timberman
// Tiny Wings
// Tipsy Tower
// Toony
// Totem Destroyer
// Tower Defense
// Trick Shot
// Tunnelball
// Turn
// Turnellio
// TwinSpin
// vvvvvv
// Warp Shift
// Way of an Idea
// Whack a Creep
// Wheel of Fortune
// Where's my Water
// Wish Upon a Star
// Word Game
// Wordle
// Worms
// Yanga
// Yeah Bunny
// Zhed
// zNumbers