Complete Roguelike Flash game in less than 2KB
Talking about Roguelike game, Actionscript 3, Flash and Game development.
If you are a regular reader, you should know sometimes I try to create Flash game in less than 2KB.
After Sokoban, Bejeweled and a math game with an original concept, it’s time to show you my Roguelike game in less than 2KB, here it is:

The roguelike is a sub-genre of role-playing video games, characterized by random level generation, tile-based graphics and permanent death. Roguelikes descend from the 1980 game Rogue, particularly mirroring Rogue’s character- or sprite-based graphics, turn-based gameplay that gives the player the time to plan each move, and high fantasy setting (source).
Let’s see what I was able to do in only 2KB:
* Random level generation
* Level progression
* Rogue style graphics
* Player energy
* Player score
* Basic enemy AI
* Game over screen
Have a look at it:
You control the green “9” (which also represent your energy) with arrow keys and must kill all red “M” (the monsters).
You kill a monster when you are in a cell adjacent to the monster and move towards it.
A monster hurts you when you end your turn in a cell adjacent to the monster.
But the most interesting thing of the script, unlike my previous 2KB experiments, it I managed to have a quite legible code.
package {
import flash.display.Sprite;
import flash.text.TextField;
import flash.events.KeyboardEvent;
[SWF(backgroundColor="0x222222")]
public class Main extends Sprite {
private var rows:Number=38;
private var cols:Number=52;
private var monsters:Number=30;
private var map:Array;
private var emptyRatio:Number=0.85;
private var heroRow:Number;
private var heroCol:Number;
private var enemyRange:Number=10;
private var energy:Number=9;
private var score:Number=0;
private var level:Number=1;
private var gameOver:Boolean=false;
private var gameOverSprite:Sprite;
public function Main() {
setup();
drawMap();
stage.addEventListener(KeyboardEvent.KEY_UP, movePlayer);
}
private function movePlayer(e:KeyboardEvent):void {
if (! gameOver) {
var keyPressed:Number=e.keyCode;
switch (keyPressed) {
case 37 :
addTurn(-1,0);
break;
case 38 :
addTurn(0,-1);
break;
case 39 :
addTurn(1,0);
break;
case 40 :
addTurn(0,1);
break;
}
}
else {
gameOver=false;
score=0;
energy=9;
level=1;
setup();
drawMap();
}
}
private function checkMove(i:Number,j:Number,d:Number):Boolean {
var deltaRow:Number=(d%2)*(2-d);
var deltaCol:Number=((d+1)%2)*(d-1);
if (i+deltaRow>=0 && i+deltaRow<rows && j+deltaCol>=0 && j+deltaCol<cols && map[i+deltaRow][j+deltaCol]==".") {
map[i][j]=".";
if (d==1||d==2) {
map[i+deltaRow][j+deltaCol]="F";
}
else {
map[i+deltaRow][j+deltaCol]="M";
}
return true;
}
return false;
}
private function addTurn(deltaCol,deltaRow):void {
if (heroRow+deltaRow>=0&&heroRow+deltaRow<rows&&heroCol+deltaCol>=0&&heroCol+deltaCol<cols&&map[heroRow+deltaRow][heroCol+deltaCol]!="#") {
if (map[heroRow+deltaRow][heroCol+deltaCol]==".") {
map[heroRow+deltaRow][heroCol+deltaCol]=energy.toString();
map[heroRow][heroCol]=".";
heroRow+=deltaRow;
heroCol+=deltaCol;
}
else {
if (map[heroRow+deltaRow][heroCol+deltaCol]=="M") {
map[heroRow+deltaRow][heroCol+deltaCol]=".";
score+=level;
}
}
var monsterCount:Number=0;
var enemyDir:Number=-1;
for (var i:Number=0; i<rows; i++) {
for (var j:Number=0; j<cols; j++) {
if (map[i][j]=="M") {
monsterCount++;
var manhattan:Number=Math.abs(i-heroRow)+Math.abs(j-heroCol);
if (manhattan==1) {
energy-=level;
if (energy<0) {
energy=0;
}
map[heroRow][heroCol]=energy.toString();
}
else {
if (manhattan<enemyRange) {
if (Math.abs(i-heroRow)>Math.abs(j-heroCol)) {
if (i>heroRow) {
enemyDir=3;
}
else {
enemyDir=1;
}
}
else {
if (j>heroCol) {
enemyDir=0;
}
else {
enemyDir=2;
}
}
}
if (enemyDir==-1 || !checkMove(i,j,enemyDir)) {
var secureCount=0;
do {
secureCount++;
enemyDir=Math.floor(Math.random()*4);
} while (!checkMove(i,j,enemyDir)&&secureCount<10);
}
}
}
else {
if (map[i][j]=="F") {
map[i][j]="M";
}
}
}
}
if (monsterCount==0) {
level++;
setup();
}
drawMap();
}
}
private function setup():void {
map=new Array();
for (var i:Number = 0; i < rows; i++) {
map[i]=new Array();
for (var j:Number = 0; j < cols; j++) {
if (Math.random()>emptyRatio) {
map[i].push('#');
}
else {
map[i].push('.');
}
}
}
for (i = 0; i <= monsters+5*level; i++) {
do {
var randomRow:Number=Math.floor(Math.random()*rows);
var randomCol:Number=Math.floor(Math.random()*cols);
} while (map[randomRow][randomCol]!=".");
if (i==0) {
heroRow=randomRow;
heroCol=randomCol;
map[randomRow][randomCol]=energy.toString();
}
else {
map[randomRow][randomCol]="M";
}
}
}
private function drawMap():void {
for (var k:Number=numChildren-1; k>=0; k--) {
removeChild(getChildAt(k));
}
for (var i:Number = 0; i < rows; i++) {
for (var j:Number = 0; j < cols; j++) {
var t:TextField=new TextField();
t.autoSize="center";
t.selectable=false;
addChild(t);
t.x=j*12+10;
t.y=i*12+8;
t.text=map[i][j];
switch (map[i][j]) {
case "M" :
t.textColor=0xff0000;
break;
case "." :
t.textColor=0x666666;
break;
case "#" :
t.textColor=0xFFFFFF;
break;
default :
t.textColor=0x00ff00;
}
}
}
if (energy==0) {
gameOver=true;
gameOverSprite=new Sprite();
addChild(gameOverSprite);
gameOverSprite.graphics.lineStyle(1,0x000000,0.6);
gameOverSprite.graphics.beginFill(0x000000,0.6);
gameOverSprite.graphics.drawRect(0,0,640,480);
gameOverSprite.graphics.endFill();
t=new TextField();
t.width=500;
t.text="YOUR SCORE: "+score.toString()+" - PRESS ANY KEY TO RESTART";
addChild(t);
t.textColor=0x00ff00;
t.selectable=false;
}
}
}
}
When looking at the source code, please consider it’s optimized for small compiled swf size.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.