In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Piet Interpreter

Piet Interpreter
* 
* @see http://www.dangermouse.net/esoteric/piet.html
* @see http://ja.wikipedia.org/wiki/Piet
* 
* [使い方]
*   1. [Open]ボタンを押してプログラム画像を選択します。
*  2. [Run]ボタンを押してプログラムを実行します。
*  3. [Input]ボタンが有効になっている時はプログラムが入力を求めている状態です。
*    左上のフィールドに数値または文字を入力してください。
*      なお、10文字まで入る場合は数値、1文字しか入らない場合は文字の入力を求められています。
* 
* サンプルはnPietに同梱されていた物を拝借中。
Get Adobe Flash player
by hycro 14 Feb 2016
/**
 * Copyright hycro ( http://wonderfl.net/user/hycro )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/xYU5
 */

/**
 * Piet Interpreter
 * 
 * @see http://www.dangermouse.net/esoteric/piet.html
 * @see http://ja.wikipedia.org/wiki/Piet
 * 
 * [使い方]
 *   1. [Open]ボタンを押してプログラム画像を選択します。
 *  2. [Run]ボタンを押してプログラムを実行します。
 *  3. [Input]ボタンが有効になっている時はプログラムが入力を求めている状態です。
 *    左上のフィールドに数値または文字を入力してください。
 *      なお、10文字まで入る場合は数値、1文字しか入らない場合は文字の入力を求められています。
 * 
 * サンプルはnPietに同梱されていた物を拝借中。
 */
package {
    import com.bit101.components.InputText;
    import com.bit101.components.PushButton;
    import com.bit101.components.TextArea;
    
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.net.FileFilter;
    import flash.net.FileReference;
    import flash.net.URLRequest;
    import flash.system.Security;
    import flash.text.TextField;
    
    import org.libspark.thread.EnterFrameThreadExecutor;
    import org.libspark.thread.Thread;
    import org.libspark.thread.ThreadState;
        
    public class Piet extends Sprite {
        private var _output:TextArea;
        private var _input:InputText;
        private var _commit:PushButton;
        private var _select:PushButton;
        private var _play:PushButton;
        
        private var _interpreter:PietInterpreter;
        private var _file:FileReference;
        private var _loader:Loader;
        
        public function Piet() {
            
            _play = new PushButton(this, 0, 0, "Run", onPlayClicked);
            _play.x = stage.stageWidth - _play.width;
            _play.enabled = false;
            
            _select = new PushButton(this, 0, 0, "Open", onSelectClicked);
            _select.x = _play.x - _select.width;
            
            _commit = new PushButton(this, 0, 0, "Input", onInputClicked);
            _commit.x = _select.x - _commit.width;
            _commit.enabled = false;
            
            _input = new InputText(this, 0, 0);
            _input.width = stage.stageWidth - _select.width - _commit.width - _play.width;
            _input.height = _input.height + 4;
            
            _output = new TextArea(this, 0, 0);
            _output.width = stage.stageWidth;
            _output.height = 150;
            _output.y = _commit.height;
            
            _loader = new Loader();
            _loader.y = _output.y + _output.height + 2;
            _loader.x = 1;
            addChild(_loader);
            
            stage.frameRate = 120;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            Thread.initialize(new EnterFrameThreadExecutor());
               
               _file = new FileReference();
            _file.addEventListener(Event.SELECT, onFileSelected);
            _file.addEventListener(Event.COMPLETE, onFileLoadCompleted);
            
            this.addEventListener(PietInterpreter.INPUT_NUM, onInputNum);
            this.addEventListener(PietInterpreter.INPUT_TEXT, onInputText);
            this.addEventListener(PietInterpreter.OUTPUT_NUM, onOutputNum);
            this.addEventListener(PietInterpreter.OUTPUT_TEXT, onOutputText);
            this.addEventListener(PietInterpreter.FINISH, onFinish);
            this.addEventListener(PietInterpreter.INFINATE_LOOP, onInfinateLoop);
            
            _loader.contentLoaderInfo.addEventListener(Event.INIT, onInit);
            
            _loader.load(new URLRequest("http://hycro.crz.jp/wonderfl/sample.gif"));
            Security.loadPolicyFile("http://hycro.crz.jp/crossdomain.xml");
        }
        
        private function onSelectClicked(evt:MouseEvent):void {
                _file.browse([new FileFilter("ImageFile", "*.jpg;*.gif;*.png")]);
        }
        
        private function onFileSelected(evt:Event):void {
                _file.load();
        }
        
        private function onFileLoadCompleted(evt:Event):void {
            _loader.loadBytes(_file.data);
        }

        public function onInit(evt:Event):void {
            _loader.scaleX = _loader.scaleY = 4;
            _play.enabled = true;
        }
        
        private function onPlayClicked(evt:MouseEvent):void {
            _loader.scaleX = _loader.scaleY = 1;
            var bmpd:BitmapData = new BitmapData(_loader.width, _loader.height);
            bmpd.draw(_loader);
            _loader.scaleX = _loader.scaleY = 4;
            
            _interpreter = new PietInterpreter(bmpd, this);
            _interpreter.start();
            
            _commit.enabled = false;
            _play.enabled = false;
            _output.text = "";
            _input.text = "";
        }
        
        private var _inputMode:int = 0; // 0=>text, 1=>number     
        private function onInputClicked(evt:MouseEvent):void {
            if (_inputMode == 0) {
                _interpreter.inputText(_input.text);
            } else {
                _interpreter.inputNum(parseInt(_input.text));
            }
            _input.text = "";
            _commit.enabled = false;
        }
            
        private function onInputNum(evt:Event):void {
            _commit.enabled = true;
            _inputMode = 1;
            _input.maxChars = 10;
        }
        
        private function onInputText(evt:Event):void {
            _commit.enabled = true;
            _inputMode = 0;
            _input.maxChars = 1;
        }
        
        private function onOutputNum(evt:Event):void {
            _output.text += _interpreter.outputNum.toString();
        }
        private function onOutputText(evt:Event):void {
            _output.text += _interpreter.outputText;
        }
        
        private function onFinish(evt:Event):void {
            _output.text += "\n[end]";
            _play.enabled = true;
        }
        
        private function onInfinateLoop(evt:Event):void {
            _output.text += "\n[infinate loop]";
            _play.enabled = true;
        }
    }
}

import flash.display.BitmapData;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.utils.Dictionary;

import org.libspark.thread.Thread;
import org.libspark.thread.ThreadState;

class PietInterpreter extends Thread {
    static public const INPUT_TEXT:String = "jp.crz.hycro.PietInterpreter:INPUT_TEXT";
    static public const OUTPUT_TEXT:String = "jp.crz.hycro.PietInterpreter:OUTPUT_TEXT";
    static public const INPUT_NUM:String = "jp.crz.hycro.PietInterpreter:INPUT_NUM";
    static public const OUTPUT_NUM:String = "jp.crz.hycro.PietInterpreter:OUTPUT_NUM";
    static public const TIMEOUT:String = "jp.crz.hycro.PietInterpreter:TIMEOUT";
    static public const FINISH:String = "jp.crz.hycro.PietInterpreter:FINISH";
    static public const INFINATE_LOOP:String = "jp.crz.hycro.PietInterpreter:INFINATE_LOOP";
    
    static public const LIMIT:uint = 1000000;
    static public const STEP_PER_FRAME:uint = 100;
    
    static private const DP_RIGHT:uint = 0;
    static private const DP_DOWN:uint = 1;
    static private const DP_LEFT:uint = 2;
    static private const DP_UP:uint = 3;
    static private const CC_LEFT:Boolean = true;
    static private const CC_RIGHT:Boolean = false;
    
    private var _observer:EventDispatcher;
    private var _stack:Array;
    private var _source:BitmapData;
    private var _original:BitmapData;
    private var _curr:ColorBlock;
    private var _prev:ColorBlock;
    private var _whiteFlg:Boolean;
    private var _x:uint;
    private var _y:uint;
    private var _dp:uint;
    private var _cc:Boolean;
    private var _count:int;
    
    public var outputNum:int;
    public var outputText:String;
    
    public function PietInterpreter(source:BitmapData, observer:EventDispatcher) {
        _source = source;
        _observer = observer;
    }
    
    protected override function run():void {
        init();
        step();
    }
    
    private function init():void {
        _stack = [];
        _x = 0;
        _y = 0;
        _dp = DP_RIGHT;
        _cc = CC_LEFT;
        _curr = null;
        _prev = null;
        _original = new BitmapData(_source.width, _source.height);
        _original.copyPixels(_source, _source.rect, new Point(0, 0));
        _source.lock();
        _count = -1;
    }
    
    private function step():void {
        for (var i:uint = 0; (i < STEP_PER_FRAME) && (state != ThreadState.WAITING); i++) {
            if (getNextColorBlock()) {
                if (!_whiteFlg) {
                    execCommand();
                }
            } else {
                return;
            }
            _count++;
        }
        
        if (_count > LIMIT) {
            _observer.dispatchEvent(new Event(TIMEOUT));
            trace("limit");
        } else {
            next(step);
        }
    }
    
    private function getNextColorBlock():ColorBlock {
        var cb:ColorBlock;
        _whiteFlg = false;
        if (_curr == null) {
            cb = getColorBlock(_x, _y);
        } else {
            var breakFlg:Boolean = false;
            for (var i:uint=0; i<4 && !breakFlg; i++) {
                for (var j:uint=0; j<2 && !breakFlg; j++) {
                    moveToEdge();
                    if (checkEdge()) {
                        forward();
                        if (Color.isWhite(_source.getPixel(_x, _y))) {
                            skipWhite();
                            _whiteFlg = true;
                        }
                        cb = getColorBlock(_x, _y);
                        breakFlg = true;
                    } else if (j==0) {
                        toggleCc();
                    }
                }
                if (!breakFlg) {
                    rotateDp();
                }
            }
            if (!breakFlg) {
                return null;
            }
        }
        
        _prev = _curr;
        _curr = cb;
        return _curr;
    }
    
    private function skipWhite():void {
        var xx:int = _x;
        var yy:int = _y;
        var ddpp:uint = _dp;
        
        while (Color.isWhite(_source.getPixel(_x, _y))) {
            forward();
            if ((_x == xx) && (_y == yy) && (ddpp == _dp)) {
                _observer.dispatchEvent(new Event(INFINATE_LOOP));
                this.wait();
                return;
            }
            if (Color.isBlack(_source.getPixel(_x, _y))) {
                back();
                rotateDp();
            }
        }
    }
    
    private function moveToEdge():void {
        var p:Point;
        switch (_dp) {
            case DP_RIGHT:
                p = getRightEdge();
                break;
            case DP_DOWN:
                p = getBottomEdge();
                break;
            case DP_LEFT:
                p = getLeftEdge();
                break;
            case DP_UP:
                p = getUpperEdge();
                break;
        }
        if (p) {
            _x = p.x;
            _y = p.y;
        }
    }
    
    private function getRightEdge():Point {
        var right:Point;
        for each (var codel:Point in _curr.codels) {
            if (!right) {
                right = codel;
            } else if ((right.x < codel.x) || 
                       (right.x == codel.x) && (
                       (_cc==CC_LEFT) && (right.y > codel.y) ||
                       (_cc==CC_RIGHT) && (right.y < codel.y))) {
                right = codel;
            }
        }
        return right;
    }
    
    private function getLeftEdge():Point {
        var left:Point;
        for each (var codel:Point in _curr.codels) {
            if (!left) {
                left = codel;
            } else if ((left.x > codel.x) || 
                    (left.x == codel.x) && (
                    (_cc==CC_LEFT) && (left.y < codel.y) ||
                    (_cc==CC_RIGHT) && (left.y > codel.y))) {
                left = codel;
            }
        }
        return left;
    }
    
    private function getUpperEdge():Point {
        var upper:Point;
        for each (var codel:Point in _curr.codels) {
            if (!upper) {
                upper = codel;
            } else if ((upper.y > codel.y) || 
                       (upper.y == codel.y) && (
                       (_cc==CC_LEFT) && (upper.x > codel.x) ||
                       (_cc==CC_RIGHT) && (upper.x < codel.x))) {
                upper = codel;
            }
        }
        return upper;
    }
    
    private function getBottomEdge():Point {
        var bottom:Point;
        for each (var codel:Point in _curr.codels) {
            if (!bottom) {
                bottom = codel;
            } else if ((bottom.y < codel.y) || 
                       (bottom.y == codel.y) && (
                       (_cc==CC_LEFT) && (bottom.x < codel.x) ||
                       (_cc==CC_RIGHT) && (bottom.x > codel.x))) {
                bottom = codel;
            }
        }
        return bottom;
    }
    
    private function checkEdge():Boolean {
        forward();
        var c:Color = new Color(_source.getPixel(_x, _y));
        back();
        return c.isNotBlack();
    }
    
    private function forward():void {
        switch (_dp) {
            case DP_RIGHT:
                _x++;
                break;
            case DP_DOWN:
                _y++;
                break;
            case DP_LEFT:
                _x--;
                break;
            case DP_UP:
                _y--;
                break;
        }
    }
    
    private function back():void {
        switch (_dp) {
            case DP_RIGHT:
                _x--;
                break;
            case DP_DOWN:
                _y--;
                break;
            case DP_LEFT:
                _x++;
                break;
            case DP_UP:
                _y++;
                break;
        }
    }
    
    private function rotateDp():void {
        _dp = (_dp + 1) % 4;
    }
    
    private function toggleCc():void {
        _cc = !_cc;
    }

    private function getColorBlock(x:int, y:int):ColorBlock {
        var color:uint = _source.getPixel(x, y);
        var cb:ColorBlock = new ColorBlock();
        cb.color = new Color(color);
        scan(x, y, cb);
        _source.copyPixels(_original, _source.rect, new Point(0, 0));
        return cb;
    }
    
    private function scan(x:int, y:int, cb:ColorBlock):void {
        var stack:Vector.<Point> = new Vector.<Point>();
        var p:Point;
        stack.push(new Point(x, y));
        
        while (p = stack.pop()) {
            var c:uint = _source.getPixel32(p.x, p.y);
            if (((c >> 24 & 0xff) == 0x0) || !cb.color.equals(c)) {
                continue;
            }
            
            _source.setPixel32(p.x, p.y, c & 0x00ffffff);
            cb.codels.push(new Point(p.x, p.y));
            
            for (var left:int = p.x - 1; cb.color.equals(_source.getPixel32(left, p.y)); left--) {
                _source.setPixel32(left, p.y, c & 0x00ffffff);
                cb.codels.push(new Point(left, p.y));
            }
            for (var right:int = p.x + 1; cb.color.equals(_source.getPixel32(right, p.y)); right++) {
                _source.setPixel32(right, p.y, c & 0x00ffffff);
                cb.codels.push(new Point(right, p.y));
            }
            left++;
            right--;
            var c1:uint;
            var c2:uint;
            if (p.y > 0) {
                for (var xx:int = left; xx <= right; xx++) {
                    c1 = _source.getPixel32(xx, p.y-1);
                    c2 = _source.getPixel32(xx+1, p.y-1);
                    if (((c1 >> 24 & 0xff) != 0x0) && cb.color.equals(c1)  &&
                       (((c2 >> 24 & 0xff) == 0x0) || !cb.color.equals(c2) || (xx == right))) {
                        stack.push(new Point(xx, p.y-1));
                    }
                }
            }
            if (p.y < _source.height - 1) {
                for (xx = left; xx <= right; xx++) {
                    c1 = _source.getPixel32(xx, p.y+1);
                    c2 = _source.getPixel32(xx+1, p.y+1);
                    if (((c1 >> 24 & 0xff) != 0x0) && cb.color.equals(c1)  &&
                        (((c2 >> 24 & 0xff) == 0x0) || !cb.color.equals(c2) || (xx == right))) {
                        stack.push(new Point(xx, p.y+1));
                    }
                }
            }
        }
    }
    
    private function execCommand():void {
        if (!_prev) return;
        var h:uint = _prev.color.subtructHue(_curr.color);
        var l:uint = _prev.color.subtructLightness(_curr.color);
        switch (l) {
            case 0:
                switch (h) {
                    case 0:
                        break;
                    case 1:
                        _add();
                        break;
                    case 2:
                        _divide();
                        break;
                    case 3:
                        _greater();
                        break;
                    case 4:
                        _duplicate();
                        break;
                    case 5:
                        _inText();
                        break;
                }
                break;
            case 1:
                switch (h) {
                    case 0:
                        _push(_prev.codels.length);
                        break;
                    case 1:
                        _subtract();
                        break;
                    case 2:
                        _mod();
                        break;
                    case 3:
                        _pointer();
                        break;
                    case 4:
                        _roll();
                        break;
                    case 5:
                        _outNum();
                        break;
                }
                break;
            case 2:
                switch (h) {
                    case 0:
                        _pop();
                        break;
                    case 1:
                        _multiply();
                        break;
                    case 2:
                        _not();
                        break;
                    case 3:
                        _switch();
                        break;
                    case 4:
                        _inNum();
                        break;
                    case 5:
                        _outText();
                        break;
                }
        }
    }
    
    private function _push(n:uint):void {
        _stack.push(n);
        trace("[action:" + _count.toString() + "] push: ", n);
        trace("[stack] ", _stack);
    }
    
    private function _pop():void {
        if (_stack.length == 0) {
            trace("[action:" + _count.toString() + "] pop: stack is empty.");
            return;
        }
        var a:int = _stack.pop();
        trace("[action:" + _count.toString() + "] pop: ", a);
        trace("[stack] ", _stack);
    }
    
    private function _add():void {
        if (_stack.length == 0) {
            trace("[action:" + _count.toString() + "] add: stack is empty.");
            return;
        }
        
        var a:int = _stack.pop();
        var b:int = _stack.pop();
        _stack.push(a + b);
        trace("[action:" + _count.toString() + "] add: ", a, b);
        trace("[stack] ", _stack);
    }
    
    private function _subtract():void {
        if (_stack.length == 0) {
            trace("[action:" + _count.toString() + "] subtract: stack is empty.");
            return;
        }
        var a:int = _stack.pop();
        var b:int = _stack.pop();
        _stack.push(b - a);
        trace("[action:" + _count.toString() + "] subtract: ", a, b);
        trace("[stack] ", _stack);
    }
    
    private function _multiply():void {
        if (_stack.length == 0) {
            trace("[action:" + _count.toString() + "] multiply: stack is empty.");
            return;
        }
        var a:int = _stack.pop();
        var b:int = _stack.pop();
        _stack.push(a * b);
        trace("[action:" + _count.toString() + "] multiply: ", a, b);
        trace("[stack] ", _stack);
    }
    
    private function _divide():void {
        if (_stack.length == 0) {
            trace("[action:" + _count.toString() + "] divide: stack is empty.");
            return;
        }
        var a:int = _stack.pop();
        var b:int = _stack.pop();
        _stack.push(int(b / a));
        trace("[action:" + _count.toString() + "] divide: ", a, b);
        trace("[stack] ", _stack);
    }
    
    private function _mod():void {
        if (_stack.length == 0) {
            trace("[action:" + _count.toString() + "] mod: stack is empty.");
            return;
        }
        var a:int = _stack.pop();
        var b:int = _stack.pop();
        _stack.push(b % a);
        trace("[action:" + _count.toString() + "] mod: ", a, b);
        trace("[stack] ", _stack);
    }
    
    private function _not():void {
        if (_stack.length == 0) {
            trace("[action:" + _count.toString() + "] not: stack is empty.");
            return;
        }
        var a:int = _stack.pop();
        _stack.push(a==0 ? 1 : 0);
        trace("[action:" + _count.toString() + "] not: ", a);
        trace("[stack] ", _stack);
    }
    
    private function _greater():void {
        if (_stack.length == 0) {
            trace("[action:" + _count.toString() + "] not: stack is empty.");
            return;
        }
        var a:int = _stack.pop();
        var b:int = _stack.pop();
        _stack.push(a < b ? 1 : 0);
        trace("[action:" + _count.toString() + "] greater: ", a, b);
        trace("[stack] ", _stack);
    }
    
    private function _pointer():void {
        if (_stack.length == 0) {
            trace("[action:" + _count.toString() + "] pointer: stack is empty.");
            return;
        }
        var a:int = _stack.pop();
        for (var i:int=0; i<a; i++) {
            rotateDp();
        }
        trace("[action:" + _count.toString() + "] pointer: ", a);
        trace("[stack] ", _stack);
    }
    
    private function _switch():void {
        if (_stack.length == 0) {
            trace("[action:" + _count.toString() + "] switch: stack is empty.");
            return;
        }
        var a:int = _stack.pop();
        for (var i:int=0; i<a; i++) {
            toggleCc();
        }
        trace("[action:" + _count.toString() + "] switch: ", a);
        trace("[stack] ", _stack);
    }
    
    private function _duplicate():void {
        if (_stack.length == 0) {
            trace("[action:" + _count.toString() + "] duplicate: stack is empty.");
            return;
        }
        var a:int = _stack.pop();
        _stack.push(a);
        _stack.push(a);
        trace("[action:" + _count.toString() + "] duplicate: ", a);
        trace("[stack] ", _stack);
    }
    
    private function _roll():void {
        if (_stack.length == 0) {
            trace("[action:" + _count.toString() + "] roll: stack is empty.");
            return;
        }
        var n:int = _stack.pop();
        var d:int = _stack.pop();
        
        if (d < 0) {
            trace("[action:" + _count.toString() + "] roll: depth is negative number.");
            return;
        }
        if (n > 0) {
            for (var i:uint=0; i<n; i++) {
                var a:int = _stack.pop();
                _stack.splice(_stack.length-d+1, 0, a);
            }
        } else {
            _stack.reverse();
            for (var j:uint=0; j<-n; j++) {
                var b:int = _stack.pop();
                _stack.splice(_stack.length-d+1, 0, b);
            }
            _stack.reverse();
        }
        trace("[action:" + _count.toString() + "] roll: num=", n, "depth=",  d);
        trace("[stack] ", _stack);
    }
    
    private function _inNum():void {
        wait();
        _observer.dispatchEvent(new Event(INPUT_NUM));
    }

    private function _outNum():void {
        if (_stack.length == 0) {
            trace("[action:" + _count.toString() + "] out(number): stack is empty.");
            return;
        }
        outputNum = _stack.pop();
        _observer.dispatchEvent(new Event(OUTPUT_NUM));
        trace("[action:" + _count.toString() + "] out(number): ", outputNum);
        trace("[stack] ", _stack);
    }
    
    private function _inText():void {
        wait();
        _observer.dispatchEvent(new Event(INPUT_TEXT));
    }
    
    private function _outText():void {
        if (_stack.length == 0) {
            trace("[action:" + _count.toString() + "] out(text): stack is empty.");
            return;
        }
        outputText = String.fromCharCode(_stack.pop());
        _observer.dispatchEvent(new Event(OUTPUT_TEXT));
        trace("[action:" + _count.toString() + "] out(text): ", outputText);
        trace("[stack] ", _stack);
    }
    
    public function inputText(str:String):void {
        _stack.push(str.substr(0, 1).charCodeAt());
        notify();
        trace("[action:" + _count.toString() + "] input(text): ", str.substr(0, 1));
        trace("[stack] ", _stack);
    }
    
    public function inputNum(n:int):void {
        _stack.push(n);
        notify();
        trace("[action:" + _count.toString() + "] input(number): ", n);
        trace("[stack] ", _stack);
    }
    
    protected override function finalize():void {
        _observer.dispatchEvent(new Event(FINISH));
        trace("end");
    }
}

class Color {
    // lightness
    public static const LIGHT:uint = 0;
    public static const NORMAL:uint = 1;
    public static const DARK:uint = 2;
    // hue
    public static const RED:uint = 0;
    public static const YELLOW:uint = 1;
    public static const GREEN:uint = 2;
    public static const CYAN:uint = 3;
    public static const BLUE:uint = 4;
    public static const MAGENTA:uint = 5;
    // recognised color
    public static const LIGHT_RED:uint = 0xffffc0c0;
    public static const LIGHT_YELLOW:uint = 0xffffffc0;
    public static const LIGHT_GREEN:uint = 0xffc0ffc0;
    public static const LIGHT_CYAN:uint = 0xffc0ffff;
    public static const LIGHT_BLUE:uint = 0xffc0c0ff;
    public static const LIGHT_MAGENTA:uint = 0xffffc0ff;
    public static const NORMAL_RED:uint = 0xffff0000;
    public static const NORMAL_YELLOW:uint = 0xffffff00;
    public static const NORMAL_GREEN:uint = 0xff00ff00;
    public static const NORMAL_CYAN:uint = 0xff00ffff;
    public static const NORMAL_BLUE:uint = 0xff0000ff;
    public static const NORMAL_MAGENTA:uint = 0xffff00ff;
    public static const DARK_RED:uint = 0xffc00000;
    public static const DARK_YELLOW:uint = 0xffc0c000;
    public static const DARK_GREEN:uint = 0xff00c000;
    public static const DARK_CYAN:uint = 0xff00c0c0;
    public static const DARK_BLUE:uint = 0xff0000c0;
    public static const DARK_MAGENTA:uint = 0xffc000c0;
    public static const BLACK:uint = 0xff000000;
    public static const WHITE:uint = 0xffffffff;
    
    public static const LIGHT_COLOR_LIST:Array = [LIGHT_RED, LIGHT_YELLOW, LIGHT_GREEN, LIGHT_CYAN, LIGHT_BLUE, LIGHT_MAGENTA];
    public static const NORMAL_COLOR_LIST:Array = [NORMAL_RED, NORMAL_YELLOW, NORMAL_GREEN, NORMAL_CYAN, NORMAL_BLUE, NORMAL_MAGENTA];
    public static const DARK_COLOR_LIST:Array = [DARK_RED, DARK_YELLOW, DARK_GREEN, DARK_CYAN, DARK_BLUE, DARK_MAGENTA];
    public static const ALL_COLOR_LIST:Array = [[BLACK, WHITE], LIGHT_COLOR_LIST, NORMAL_COLOR_LIST, DARK_COLOR_LIST];
    
    public var hue:uint;
    public var lightness:uint;
    
    public function Color(c:uint) {
        
        var r:uint = (c >> 16) & 0xff;
        var g:uint = (c >> 8) & 0xff
        var b:uint = c & 0xff;
        
        var nearlest:Number = Number.MAX_VALUE;
        for (var i:uint = 0; i < 4; i++) {
            var length:uint = ALL_COLOR_LIST[i].length;
            for (var j:uint = 0; j < length; j++) {
                var cc:uint = ALL_COLOR_LIST[i][j];
                var rr:uint = (cc >> 16) & 0xff;
                var gg:uint = (cc >> 8) & 0xff
                var bb:uint = cc & 0xff;
                var d:Number = (r-rr)*(r-rr) + (g-gg)*(g-gg) + (b-bb)*(b-bb);
                if (d < nearlest) {
                    nearlest = d;
                    lightness = i;
                    hue = j;
                    if (d == 0) {
                        return;
                    }
                }
            }
        }
    }
    
    public function equals(c:uint):Boolean {
        if (ALL_COLOR_LIST[lightness][hue] == c) {
            return true;
        }
        return _equals(new Color(c));
    }
    
    public function _equals(c:Color):Boolean {
        return (hue == c.hue) && (lightness == c.lightness)
    }
    
    public function isBlack():Boolean {
        return (hue == 0) && (lightness == 0);
    }
    public function isNotBlack():Boolean {
        return (hue != 0) || (lightness != 0);
    }
    public function isWhite():Boolean {
        return (hue == 1) && (lightness == 0);
    }
    public function isNotWhite():Boolean {
        return (hue != 1) || (lightness != 0);
    }
    
    public function subtructHue(c:Color):uint {
        if ((c.hue - hue) >= 0) {
            return c.hue - hue;
        }
        return c.hue - hue + 6;
    }
    
    public function subtructLightness(c:Color):uint {
        if (c.lightness - lightness >= 0) {
            return c.lightness - lightness;
        }
        return c.lightness - lightness + 3;
    }
    
    // for debug
    public function toString():String {
        return uint(ALL_COLOR_LIST[lightness][hue]).toString(16);
    }
    
    public static function isWhite(c:uint):Boolean {
        if (c == 0xffffffff) {
            return true;
        }
        return new Color(c).isWhite();
    }
    
    public static function isBlack(c:uint):Boolean {
        if (c == 0xff000000) {
            return true;
        }
        return new Color(c).isBlack();
    }
}

class ColorBlock {
    public var color:Color;
    public var codels:Vector.<Point> = new Vector.<Point>();
    // for debug
    public function toString():String {
        return "color: " + color.toString() + ", count: " + codels.length.toString();
    }
}