Thursday, 14 October 2010

Link List in JavaScript


I'm making tetris version 2, and it have a link list in it. Figure it a good idea to share :D.

Using the list list,
The following use inheritance in JavaScript and make a sub class Candy.
Candy = function(flavor){
  this.super = LinkListItem;
  this.super();
  this.flavor = flavor;
  this.toString = function(){
    return this.flavor + ' Candy' ;
  }
}

box = new LinkList();
box.append(new Candy('Apple'));
box.append(new Candy('Banana'));
box.append(new Candy('Orange'));
alert('I have '+box+' in my box');


Source Code
/**
 * A simple like list
 */
 
LinkList = function(){
  startItem = null;
  endItem = null;
  /**
   * Add an item to the end of the list
   */
  this.append = function (item){
    item.backward = this.endItem;
    item.forward = null;
    item.parent = this;
    if(this.endItem){
      this.endItem.forward = item
    }
    if(!this.startItem){
      this.startItem = item;
    }
    this.endItem = item;
  }
  /**
   * Add the item to the front of the list
   */
  this.prepend = function (item){
    item.forward = this.startItem;
    item.backward = null;
    item.parent = this;
    if(this.startItem){
      this.startItem.backward = item
    }
    this.startItem = item;
  }
  
  /**
   * Remove the item from the list
   */
  this.remove = function(item){
    if(item.backward){
      item.backward.forward = item.forward;
    }
    
    if(item.forward){
      item.forward.backward = item.backward;
    }
    
    if(this.startItem == item){
      this.startItem = item.forward;
    }

    if(this.endItem == item){
      this.endItem = item.backward;
    }
    item.parent = null;
  }
  
  this.toString = function(){
    var item = this.startItem;
    var items = new Array();
    while(item != null){
      items.push(item);
      item = item.forward;
    }
    if(!items.length){
      return "Empty";
    }
    return items.join(', ');
  }
}

/**
 * A link list item
 */
LinkListItem = function(){
  this.parent = null;
  this.forward = null;
  this.backward = null;

  this.remove = function(){
    if(this.parent){
      this.parent.remove(this);
    }
  }
  
  this.moveToFront = function(){
    if(this.parent){
      var parent = this.parent;
      parent.remove(this);
      parent.prepend(this);
    }
  }
  
  this.moveToBack = function(){
    if(this.parent){
      var parent = this.parent;
      parent.remove(this);
      parent.append(this);
    }
  }
}

/**
 * Testing Script
 *
Candy = function(flavor){
  this.inherit = LinkListItem;
  this.inherit();
  this.flavor = flavor;
  this.toString = function(){
    return this.flavor + ' Candy' ;
  }
}

var box = new LinkList();
box.append(new Candy('Apple'));
box.append(new Candy('Banana'));
box.append(new Candy('Orange'));
alert("What Do I have in my box? " + box);
/***/

Wednesday, 13 October 2010

UGotFile Stabilizer HOW TO

This is for the future people at UGotFile. I'm going to be talking about the stabilizer, what it do, and how to improve or fix error.

UGotFile stabilizer is a tool to automatically evenly distribute files between hard drive with in the server and on the network. The stabilizer achieve it task via http, a not very secure channel,but in our case it ideal, http don't cost CPU power or extra ram and it aren't cap at 100 Mbps per connection unlike SSH.

Stabilizer are split into three part

  1. Gather accurate information about the server and auto index the server into the database.
    This part is pretty well done, it unlikely to cause any problem.
  2. Volume that are 5% bigger then the average volume are selected to mark some of their files for export.
  3. Volume that are smaller then the average volume are selected to download the files mark to be exported. Volume that are next to the exporting volume have higher priority.

Problems in paradise.
After the take over of UGotFile, all the hardware change, the special equipment we require cost a lot of money. We came up with some new ideas that increase the speed from each server from 200Mbps to beyond 1000Mbps. This is one step of the plan, to use each volume separates. This might shock some people but removing raid increase performance with concurrent connection. Some will ask how about the data integrity, well we can always check for a hard dive that is about to die and move their data away.

The problem is before the take over of ugotfile, we where doing some upgrade and it's half done. Then we made another upgrade. The volume have three different file structural and the database also have three tables. All three set still works nicely.

  1. All files are store in one volumes
  2. file are split using mod by the number of volumes.
  3. Each volumes have it own file structure, the volumes can be transfer between server and automatically work.
TODO: Remove initial, and second upgrade, be warned there are some system that is still using them.
if some one where to delete the old tables, one of the problem that will occurred is the server will run out of space with in a few month and crash.
TODO: Make some tool to clean up files that aren't register in the database for third upgrade. 

How to run the stabilizer:
/etc/init.d/admin.uGotFile.com.Stabilizers

Please do not run it from any where else, that script prevent some conflict and automatically launch the program if it crash. There is an inbuild timer for the stabilizer to stop every so often, just use /etc/init.d/admin.uGotFile.com.Stabilizers it better.

Problem that you may encounter 
The stabilizers are running and nothing is happening. There are multiple system running ugotfile.com and one of them is the automatic deletion of old files. The problem is that the system don't notify the stabilizers.
Quick Fix: Delete everything from the stabilizer database.
Solution 1: Fix the store procedure to clean up the stabilizer database.
Solution 2: Make a foreign key with cascade delete from table "file__volume" to table "stabilizer"

After doing the quick fix or applying the solution and it still do nothing.
Quick Fix: Run "find * -size 0c -exec rm -f '{}' \;" in each volume to delete files that are zero size in length, and replicate "/home/web/admin.ugotfile.com/AdminUGotFile/models/Stabilizers.php" from server 13 code over to the other server. Just add some checking to the code. 
Solution 1: Check http upload and ftp upload. For error they should both delete zero size files. FTP upload should be the problem.
Solution 2: Modified the stabilizer to ignore zero size files.


Again after all your hard work and it look like it's doing nothing. Then you have been fool by your on own negligence. You have let the volume fill up with out running the stabilizer and there are now corrupted files with zero size length, but registered XXX byte in the database. The problem will go away after 2 month.
Quick Fix: NONE
Solution 1: Use "find * -size 0c -exec rm -f '{}' \;" on the volume, mark the volume as "dying", run the stabilizer. Mart the volume as "enable" and truncate the stabilizer table. Before all of this you need to replicate /home/web/admin.ugotfile.com/AdminUGotFile/models/Stabilizers.php from server 13.
Solution 2: Change the display for the admin control to use the stabilizer statistics, it way more accurate.


If the solution are apply all the above will not happen again.

Good Luck Bob,
Cheers.

Friday, 8 October 2010

Captcha - TPG - Part 1

Today, I was planning to change my internet to unlimited with TPG. To my surprise it will take 3 weeks to completed, it was disappointing. The cool thing is that TPG captcha look really cool, so we are going to crack it.
For a live demo go here.
https://cyberstore.tpg.com.au/register/homephone/images/generateimage.php


Feature of this captcha:
  • Random rotation
  • Random character and numbers
  • Random Font
  • Fix background distortion
  • Random circle background distortion
This is a good captcha, but it not a great captcha, to be great it need a bit more work
  • Random font size
  • Tilting of image vertical and horisontal
  • Random width for circle background distortion
Luckly for us they didn't have any of the three missing feature.
The first step is to remove the background like below.
 The image was clean using python with python image library. What happen is that each pixcel are compare with it surrounding, the ones with not so much surrounding are remove.

The next step is to identify the character, it a bit tricky, the approach I'm using is assist learning for the programs. The program is going to get a set of captcha and link the same character together. Since the captch don't know what the character are, we have to let it know watch each picture mean. This will involve assigning 26 character and 10 digit.

Stay tune for part 2, I'll go into more detail for identify the characters, and some cool method to hack chaptcha that are distorted via tilting.

Sunday, 26 September 2010

Tetris - JavaScript - PART 3 - Glueing the parts together

Here is the game, glue together.

TODO:
  • Add the final two L shape
  • End Game Condition
  • Scoring
  • Put in some pretty graphics
I'm going to stop here, I figure out how to make the game lighting faster in any browser.

Ways to make it faster
  • Instead of creating new object and moving div around, we could initialize the game with all the object it need and use class to activate the object.
  • Some browser are hyperactive and don't delegate their display process and it will slow down the game when there are many object manipulation. This can be over come with grouping of object. Instead of moving the block one by one, we could group them into a row and move the rows.

hope this little game inspire or give some one a cool idea.

Cheers,

Below are the actual game, and it should be running on your browser right now.








/**
 * A little game of falling block, the goal are to score point by
 * controlling the falling block in a way to place them neatly on the bottom
 * of the screen. When the block are filled in contiguously from left to right
 * on either row, score will be earn.
 *
 * @author Do Quoc Cong
 */
Tetris = function(p_holder, p_blockSize, p_xSize, p_ySize) {
    //We don't need multiple instance
    if (window._Tetris != undefined) {
        return window._Tetris;
    }

    window._Tetris = this;
    this.m_keyCode = {
        40 : 'down',
        38 : 'up',
        37 : 'left',
        39 : 'right'
    };
    this.m_holder = p_holder;
    this.m_action = null;
    this.m_blockSize = p_blockSize ? p_blockSize: 32;
    this.m_xSize = p_xSize ? p_xSize: 20;
    this.m_ySize = p_ySize ? p_ySize: 10;
    this.m_grid = Array(p_xSize);
    this.m_falling = Array();
    this.m_timeOut = null;
    this.m_fallingStatue = 0;
    this.m_fallingShape = 0,
    this.m_shape = [
            [[0,0],[0,1],[1,0],[1,1]], // Square
            [[0,0],[1,0],[2,0],[3,0]], // Long Stick
            [[0,0],[1,0],[1,1],[2,1]], // \
            [[0,1],[1,1],[1,0],[2,0]], // /
            [[0,1],[1,0],[1,1],[1,2]]  // Spike shape
        ];
    
    this.m_shapeTranspormation = [
        [],
        [
            [[1,-1],[0,0],[-1,1],[-2,2]],
            [[-1,1],[0,0],[1,-1],[2,-2]]
        ],
        [
            [[2,0],[0,2],[0,0],[0,0]],
            [[-2,0],[0,-2],[0,0],[0,0]]
        ],
        [
            [[2,0],[0,-2],[0,0],[0,0]],
            [[-2,0],[0,2],[0,0],[0,0]]
        ],
        [
            [[1,-1],[1,1],[0,0],[-1,-1]],
            [[1,1],[-1,1],[0,0],[1,-1]],
            [[-1,1],[-1,-1],[0,0],[1,1]],
            [[-1,-1],[1,-1],[0,0],[-1,1]]
        ]
    ];
    $(p_holder).css('width', this.m_blockSize * this.m_ySize + 1 );
    $(p_holder).css('height', this.m_blockSize * this.m_xSize + 1 );

    for (var i = 0; i < p_xSize; i++) {
        this.m_grid[i] = Array(p_ySize);
    }

    // Bind window event to tetris
    $(window).keydown(function(event) {
        return Tetris().keyDown(event);
    });

    // Bind window event to tetris
    $(window).keyup(function(event) {
        return Tetris().keyUp(event);
    });

    //Event handler for KeyDown
    this.keyDown = function(event) {
        if (this.m_keyCode[event.keyCode] != undefined) {
//            console.log(this.m_keyCode[event.keyCode]);
            //Converting keycode into, up, down, left,
            //right action and set tetris action.
            this.m_action = this.m_keyCode[event.keyCode];
            return false;
        } else if(event.keyCode == 27){
            clearTimeout(this.m_timeOut);
        }
        return true;
    };

    //Event handler for KeyUp
    this.keyUp = function(event) {
        this.m_action = null;
        return true;
    }
    /**
     * Add a block to the tetris grid
     *
     * @param p_x the x cordinate to create the block
     * @param p_y the y cordinate to create the block
     * @param p_falling indicated wether this block is falling
     */
    this.addBlock = function(p_x, p_y, p_falling) {
        /**
         * This code is expensive, there are better ways
         * of implementing this section of the code
         * but for simplicity we leaving it
         */
        if (this.validCordinate(p_x, p_y) && !this.blockExist(p_x, p_y)) {
            var block = document.createElement('div');
            this.m_holder.append(block);
            $(block).addClass('block');
            if(this.moveBlock(block,p_x, p_y)){
                block.m_falling = p_falling?p_falling:false;
                if(block.m_falling){
                    $(block).addClass('falling');
                    this.m_falling.push(block);
                }
                return block;
            }
        }
        // There are some error
        return false;
    };
    
    /**
     * Check if the block existed at cordinate x, y
     *
     * @param p_x x cordinate to be check
     * @param p_x y cordinate to be check
     */
    this.blockExist = function(p_x, p_y) {
        if(this.m_grid[p_x]){
            return this.m_grid[p_x][p_y] != undefined && !this.m_grid[p_x][p_y].m_falling;
        } else {
            return false;
        }
    };
    
    /**
     * Check if the cordinate is with in the tetris region.
     * 
     * @param p_x x cordinate to be check
     * @param p_x y cordinate to be check
     */
    this.validCordinate = function(p_x, p_y) {
        return this.m_xSize > p_x && p_x >= 0 && this.m_ySize > p_y && p_y >= 0;
    }

    /**
     * Attemp to move the block to cordinate x and y, if the cordinate are
     * not occupied.
     *
     * @param p_block the block to be move
     * @param p_x the x cordinate to be move too
     * @param p_y the y cordinate to be move too
     */
    this.moveBlock = function(p_block, p_x, p_y) {
        if(!this.blockExist(p_x, p_y) && this.validCordinate(p_x, p_y)){
            if(p_block.m_x != undefined || p_block.m_y != undefined){
                if(this.m_grid[p_block.m_x][p_block.m_y] == p_block){
                    this.m_grid[p_block.m_x][p_block.m_y] = undefined;
                }
            }
            $(p_block).css("top", this.m_blockSize * p_x + 1);
            $(p_block).css("left", this.m_blockSize * p_y + 1);
            p_block.m_x = p_x;
            p_block.m_y = p_y;
            this.m_grid[p_x][p_y] = p_block;
            return true;
        }
        return false;
    };
    
    /**
     * Remove the block pass via parameter
     *
     * @param p_block the block to be remove.
     */
    this.removeBlock = function(p_block) {
        return this.removeCordinate(p_block.m_x, p_block.m_y);
    };
    
    /**
     * Remove the block from x, y cordinate if it existed.
     * 
     * @param p_x x cordinate of block
     * @param p_y y cordinate of block
     */
    this.removeCordinate = function( p_x, p_y){
        if(this.blockExist(p_x, p_y)){
            var block = this.m_grid[p_block.m_x][p_block.m_y];
            this.m_grid[p_block.m_x][p_block.m_y] = undefined;
            block.remove();
            return true;
        } else {
            return false;
        }
    };
    
    /**
     * Make the block that are mark as falling fall.
     */
    this.fall = function() {
        return this.moveFalling(1,0);
    };
    
    /**
     * Convert the falling block to normal blocks
     * And clear out and block when the rows are filled
     */
    this.fallingLanded = function() {
        for(var index in this.m_falling){
            block = this.m_falling[index];
            block.m_falling = false;
            $(block).removeClass('falling');
            
            var emptySlot = false;
            for(var i = 0; i < this.m_ySize ; i++){
                if(this.m_grid[block.m_x][i] == undefined){
                    emptySlot = true;
                }
            }
            
            if(!emptySlot){
                for(var i = 0; i < this.m_ySize ; i++){
                    $(this.m_grid[block.m_x][i]).remove();
                    this.m_grid[block.m_x][i] = undefined;
                }
                
                for(var x = block.m_x; x > 0; x--){
                    for(var y = 0; y < this.m_ySize; y++){
                        if(this.m_grid[x][y] != undefined){
                            this.moveBlock(this.m_grid[x][y],this.m_grid[x][y].m_x+1,this.m_grid[x][y].m_y);
                        };
                    }
                }
            }
        };
        
        this.m_falling = Array();
    };
    
    /**
     * Interperted the user, input and regulate how fast the games response
     * to the user input.
     */
    this.userControl = function(){
        switch(this.m_action){
            case 'left':
                this.moveFalling(0,-1);
                break;
            case 'right':
                this.moveFalling(0,1);
                break;
            case 'down':
                this.fall();
                break;
            case 'up':
                this.rotateFalling();
                break;
            default:
                break;
        }
        this.m_timeOutUserControl = setTimeout(function (){Tetris().userControl()}, 100);
    };
    
    /**
     * Rotate the shape using pre define transformations.
     */
    this.rotateFalling = function(){
        var shapeTransformation = this.m_shapeTranspormation[this.m_fallingShape];
        if(shapeTransformation.length > 1){
            var transformation = shapeTransformation[this.m_fallingStatue];
            
            for(var index in transformation){
                var x = transformation[index][0];
                var y = transformation[index][1];
                var block = this.m_falling[index];
                if(this.blockExist(block.m_x + x, block.m_y + y) || !this.validCordinate(block.m_x + x, block.m_y + y)){
                    return false;
                }
            }
            
            for(var index in transformation){
                var x = transformation[index][0];
                var y = transformation[index][1];
                var block = this.m_falling[index];
                this.moveBlock(block,block.m_x + x, block.m_y + y);
            }
            
            this.m_fallingStatue = (this.m_fallingStatue+1) % shapeTransformation.length;
        } else {
            console.log('NO TRANSFORMATION EXISTED');
        }
    }
    
    /**
     * Add a pre define block to the game.
     *
     * @param p_index the shape to add
     */
    this.addFallingShape = function(p_index) {
        if(p_index == undefined){
            p_index = Math.floor(Math.random()*5);
        }
        
        this.m_fallingShape = p_index;
        this.m_fallingStatue = 0;
        var shape = this.m_shape[p_index];
        
        for(var index in shape){
            this.addBlock(shape[index][0],shape[index][1], true);
        }
    }
    
    /**
     * Move all the falling block by p_x and p_y
     *
     * @param p_x cordinate for the falling blocks to move to
     * @param p_y cordinate for the falling blocks to move to 
     */
    this.moveFalling = function(p_x, p_y){
        for(var index in this.m_falling){
            block = this.m_falling[index];
            if(this.blockExist(block.m_x+p_x,block.m_y+p_y)
                    || !this.validCordinate(block.m_x+p_x, block.m_y+p_y)){
                return false;
            }
        };
        
        for(var index in this.m_falling){
            block = this.m_falling[index];
            this.moveBlock(block,block.m_x+p_x,block.m_y+p_y);
        };
        return true;
    }
    
    /**
     * Game Timer
     */
    this.run = function() {
        if(!this.fall()){
            this.fallingLanded();
            this.addFallingShape();
        };
        this.m_timeOut = setTimeout(function (){Tetris().run()}, 500);
    };
    
    /**
     * Initalize the game.
     */
    this.start = function() {
        this.addFallingShape();
        this.userControl();
        this.run();
    }
};