REVERSI EVOLUTION

by shohei909
遺伝的アルゴリズムによる学習プログラムをつみこんだリバーシゲームです。


EVOLUTIONを押すとAIが強化されます。
No.1あたりだとへっぽこですが、No.1000くらいまで進めるとまともなAIと戦えます。
♥34 | Line 1228 | Modified 2015-11-19 01:22:19 | MIT License
play

ActionScript3 source code

/**
 * Copyright shohei909 ( http://wonderfl.net/user/shohei909 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/1r0C
 */

/*
アルゴリズムの概要


評価方法

12個の要素をつかって盤面に評価値をつけてます。
[相手に取られない駒の数, 打てる手の数, 位置0にあるの駒の数, 1にある駒の数, …, 9にある駒の数 ]

盤面と位置の番号の対応表
0,4,5,6,6,5,4,0
4,1,7,8,8,7,1,4
5,7,2,9,9,2,7,5
6,8,9,3,3,9,8,6
6,8,9,3,3,9,8,6
5,7,2,9,9,2,7,5
4,1,7,8,8,7,1,4
0,4,5,6,6,5,4,0



遺伝的アルゴリズム

(1), 評価する要素のウェイトが異なるAIを30こ作る。
(2), AIどうしを対戦させて、負けたAIは破棄する。
(3), 残ったAIと似たAIを作って、AIを30こに戻す。
(4). 2,3を繰り返す。

学習システムはこれだけです。



先読みのアルゴリズム(モンテカルロ法)
人間との対戦の時には、AIにモンテカルロ法を使わせて手を読んでます。

(1), 1手だけ読んで評価値の高いものをいくつか取り出す。
(2), その手について、ランダム要素を持ったAIで7手先まで対戦シュミレートする。
(3), (2)を手一つについて25回繰り返す。
(4), シュミレーションで最も成績が良かった手を打つ。



*/
package {
    import flash.display.Sprite;
    import flash.display.Graphics;
    import flash.events.Event;
    import flash.filters.DropShadowFilter;
    import flash.utils.getTimer;
    import flash.geom.Matrix;
    import com.bit101.components.InputText;
    import com.bit101.components.Label;
    import com.bit101.components.PushButton;
    import com.bit101.components.RadioButton;
    import com.bit101.components.Style;
    
    [SWF(width="465", height="465", frameRate="60")]
    public class Main extends Sprite {
        public var reversi:Reversi = new Reversi();
        public var viewer:ReversiViewer = new ReversiViewer( reversi );
        public var pool:GenePool = new GenePool( reversi );
        public var input:InputText;
        public var list:List;
        public var message:Label;
        
        public var evolution:PushButton;
        public var blackRadio:RadioButton;
        public var whiteRadio:RadioButton;
        public var startBtn:PushButton;
        public var player:ReversiPlayer;
        
        public var evReversi:Reversi = new Reversi();
        public var gene1:Gene;
        public var gene2:Gene;
        public var evViewer:MiniViewer = new MiniViewer( evReversi );
        public var window:BattleWindow = new BattleWindow( viewer );
        
        private var count:int = 0;
        function Main() {
            //背景
            var g:Graphics = graphics;
            g.beginGradientFill("linear",[5,1578539,4209266,2170428],[1,1,1,1],[2,60,187,255],new Matrix(0.0000,0.2838,-0.2838,0.0000,232.5400,232.5400),"pad","rgb",0.6900000000000001);
            g.drawRect( 0, 0, 465, 465 )
            
            //スタイル
            Style.LABEL_TEXT = 0xAAAAAA;
            Style.BUTTON_FACE = 0xF33300;
            
            //タイトル
            var title:Label = new Label( this, 10, 0, "REVERSI EVOLUTION" );
            title.scaleX = title.scaleY = 2;
            
            new Label( this, 10, 40, "AI POOL" );
            list = new List( this, 10, 155, pool.genes );
            list.setSize( 100, 155 );
            evViewer.x = 10; evViewer.y = 55;
            addChild( evViewer );
            evolution = new PushButton( this, 10, 315, "EVOLUTION", onPush );
            
            new Label( this, 10, 335, "code" );
            input = new InputText( this, 10, 350 );
            input.setSize( 100, 20 );
            
            list.addEventListener( "select", onSelect );
            list.selectedIndex = 0;
            
            
            blackRadio = new RadioButton( this, 10, 400, "black", true  );
            whiteRadio = new RadioButton( this, 60, 400, "white", false );
            startBtn = new PushButton( this, 5, 420, "START", onPush );
            startBtn.setSize( 55, 20 );
            startBtn.scaleX = startBtn.scaleY = 2;
            
            viewer.y = 50;
            viewer.x = 455 - viewer.width;
            addChild( viewer );
            
            window.x = 127; window.y = 385;
            addChild( window );
            
            
            Style.LABEL_TEXT = 0xFF8800;
            message = new Label( this, 130, 330 );
            message.filters = [ new DropShadowFilter() ]
            message.scaleX = message.scaleY = 3;
            
            reset();
            evReversi.onGameOver = evNext;
            evNext();
            
            message.text = "SELECT AI"
        }
        
        private function onPush( e:Event ):void {
            switch( e.target.label ) {
                case "EVOLUTION": startEvolute(); break;
                case "STOP": stopEvolute(); break;
                case "START": start(); break;
                case "GIVE UP": reset(); break;
            }
        }
        private function onSelect( e:Event ):void {
            var index:int = list.selectedIndex;
            var l:int = pool.genes.length;
            if( index < l ){ input.text = "" + pool.genes[ list.selectedIndex ].getCode(); }
        }
        private function start():void {
            count = 0;
            addEventListener( "enterFrame", game );
            
            message.text = "";
            stopEvolute();
            evolution.enabled = false;
            list.enabled = false;
            blackRadio.enabled = false;
            whiteRadio.enabled = false;
            startBtn.label = "GIVE UP";
            
            var arr:Array = input.text.split(",");
            var l:int = reversi.getMaxWeight().length
            if ( arr.length != l ) {
                reset();
                message.text = "ERROR";
            }else{
                var turn:int = blackRadio.selected ? 1 : 2;
                reversi.players[ turn ] = player = new ReversiPlayer( reversi, viewer );
                reversi.players[ 3 - turn ] = new MonteAI( arr );
                window.label[ turn ].text = "YOU";
                window.label[ 3 - turn ].text = "CPU";
                reversi.init();
                reversi.onGameOver = reset;
            }
        }
        
        private function reset():void {
            viewer.draw( null );
            if( reversi.winner == 0 ){ "DRAW" }
            else if( reversi.players[ reversi.winner ] == player ){ message.text = "YOU WIN" }
            else{ message.text = "YOU LOSE" }
            evolution.enabled = true;
            list.enabled = true;
            blackRadio.enabled = true;
            whiteRadio.enabled = true;
            startBtn.label = "START";
            removeEventListener( "enterFrame", game ); 
        }
        
        private function game( e:Event ):void {
            if ( count != 0 ) { reversi.progress(); }
            else{ count++ }
        }
        
        private function stopEvolute():void { 
            if( evolution.label == "STOP" ){
                removeEventListener( "enterFrame", evolute ); 
                evolution.label = "EVOLUTION";
            }
        }
        private function startEvolute():void {
            if( evolution.label == "EVOLUTION" ){
                addEventListener( "enterFrame", evolute );
                evolution.label = "STOP";
            }
        }
        private function evolute( e:Event ):void { 
            var time:int = getTimer();
            do { evReversi.progress(); } while ( 60 > getTimer() - time )
            list.items = pool.genes;
        }
        private function evNext():void {
            if ( gene1 ) { gene1.point = evReversi.black; if(evReversi.winner==Reversi.BLACK){gene1.win++} }
            if ( gene2 ) { gene2.point = evReversi.white; if(evReversi.winner==Reversi.WHITE){gene2.win++} }
            list.selectedIndex = pool.index;
            
            gene1 = pool.getGene();
            pool.next();
            gene2 = pool.getGene();
            pool.next();
            evReversi.init();
            evReversi.players[1] = new NormalAI( gene1.code, 0 );
            evReversi.players[2] = new NormalAI( gene2.code, 0 );
        }
    }
}
import flash.display.*;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filters.BlurFilter;
import flash.geom.Rectangle;
import flash.utils.getTimer;
import com.bit101.components.Label;
import com.bit101.components.PushButton;

class ReversiViewer extends Sprite{
    static public const CELL:int = 41;
    static public const THICKNESS:int = 3;
    static public const BACKGROUND:uint = 0x006600;
    public var reversi:Reversi;    
    private var shapeMap:Vector.<Vector.<Shape>> = new Vector.<Vector.<Shape>>;
    private var map:Vector.<Vector.<uint>> = new Vector.<Vector.<uint>>;
    private var effect:Array = []
    private var turn:uint = 0;
    
    public function ReversiViewer( reversi:Reversi ){
        this.reversi = reversi;
        drawGrid();
        
        map = new Vector.<Vector.<uint>>;
        shapeMap = new Vector.<Vector.<Shape>>;
        for ( var i:uint = 0; i < Reversi.W; i++ ) {
            map[i] = new Vector.<uint>
            shapeMap[i] = new Vector.<Shape>
            for ( var j:uint = 0; j < Reversi.H; j++ ) {
                map[i][j] = 0;
                shapeMap[i][j] = null;
            }
        }
        
        this.addEventListener( "exitFrame",draw )
    }
    
    public function draw(e:Event):void {
        var change:Boolean = false;
        if( turn != reversi.turn ){ turn = reversi.turn; change = true; }
        for ( var i:uint = 0; i < Reversi.W; i++ ) {
            for ( var j:uint = 0; j < Reversi.H; j++ ) {
                if ( reversi.map[i][j] != map[i][j] ) {
                    change = true;
                    var m:int = map[i][j]
                    var s:Shape;
                    if ( m != 0 ) {
                        s = shapeMap[i][j]
                        removeChild( s );
                        shapeMap[i][j] = null
                    }
                    m = map[i][j] = reversi.map[i][j];
                    if ( m != 0 ) {
                        s = new Circle(m)
                        s.x = i * CELL;
                        s.y = j * CELL;
                        addChild( s );
                        shapeMap[i][j] = s;
                    }
                }
            }
        }
        if ( change ) { dispatchEvent( new Event( "change" ) ); }
    }
    
    private function drawGrid():void {
        var g:Graphics = this.graphics;
        g.beginFill( BACKGROUND, 1 );
        g.drawRect( 0, 0, Reversi.W * CELL, Reversi.H * CELL );
        g.lineStyle(THICKNESS, 0, 1);
        for ( var i:uint = 0; i < Reversi.W + 1; i++ ) {
            g.moveTo( i * CELL, 0 );
            g.lineTo( i * CELL, Reversi.H * CELL );    
        }
        for ( var j:uint = 0; j < Reversi.H + 1; j++ ) {
            g.moveTo( 0, j * CELL );
            g.lineTo( Reversi.W * CELL, j * CELL );    
        }
    }
}
class BattleWindow extends Sprite {
    public var counter1:Counter = new Counter( 0x000000 );
    public var counter2:Counter = new Counter( 0xFFFFFF );
    public var frame:Shape = new Shape();
    public var count1:int = 0;
    public var count2:int = 0;
    public var label:Array = [];
    public var viewer:ReversiViewer;
    function BattleWindow( viewer:ReversiViewer ):void {
        label[1] = new Label( this, 45, 22, "---" );
        label[2] = new Label( this, 255, 22, "---" );
        
        addChild( counter1 );
        counter1.x = 0; counter1.y = 50;
        addChild( counter2 );
        counter2.x = 320; counter2.y = 50;
        counter2.scaleX = -1;
        this.viewer = viewer;
        viewer.addEventListener( "change", onChange );
        
        var g:Graphics = graphics;
        g.lineStyle( 3, 0x000000 );
        g.beginFill( 0x006600 );
        g.drawRect( 0, 0, 40, 40 );
        g.drawRect( 280, 0, 40, 40 );
        g.beginFill( 0x000000 );
        g.drawCircle( 20, 20, 16 );
        g.beginFill( 0xFFFFFF );
        g.drawCircle( 300, 20, 16 );
        
        g.lineStyle( 0.5, 0x880000, 1 );
        g.moveTo( 32 * 5, 45 );
        g.lineTo( 32 * 5, 75 );
        
        addChild( frame )
        g = frame.graphics;
        g.lineStyle( 3, 0x880000 );
        g.drawRect( -1, -1, 42, 42 );
    }
    private function onChange( e:Event ):void {
        counter1.setCount( viewer.reversi.black );
        counter2.setCount( viewer.reversi.white );
        frame.x = viewer.reversi.turn == 2 ? 280 : 0; 
    }
}
class Counter extends Shape {
    public var color:uint;
    function Counter( color:uint ):void { this.color = color; }
    public function setCount( count:int ):void {
        var g:Graphics = graphics;
        g.clear();
        g.lineStyle( 3, color );
        for( var i:int = 0; i < count; i++ ) {
            g.moveTo( i * 5, 0 );
            g.lineTo( i * 5, 20 );
        }
    }
}
class Circle extends Shape{
    function Circle( turn:int ) {
        var g:Graphics = this.graphics;
        var color:uint = turn == 1 ? 0x000000 : 0xFFFFFF;
        g.lineStyle( ReversiViewer.THICKNESS, 0, 1);
        g.beginFill( color, 1 );
        g.drawCircle( ReversiViewer.CELL / 2, ReversiViewer.CELL / 2, ReversiViewer.CELL/2 - ReversiViewer.THICKNESS );
    }
}
class MiniViewer extends Bitmap {
    private var reversi:Reversi;
    private var map:Vector.<Vector.<int>> = new Vector.<Vector.<int>>();
    public static const CELL:int = 12;
    public static const THICKNESS:int = 1;
    public static const COLOR:Array = [0x006600, 0x000000, 0xFFFFFF];
    function MiniViewer( reversi:Reversi ):void {
        super( new BitmapData( CELL * Reversi.W + THICKNESS, CELL * Reversi.H + THICKNESS, false, 0x006600 ) );
        drawGrid()
        this.reversi = reversi;
        for ( var i:int = 0; i < Reversi.W; i++ ) {
            map[i] = new Vector.<int>;
            for ( var j:int = 0; j < Reversi.H; j++ ) {
                map[i][j] = 0;
            }
        }
        addEventListener( "exitFrame", onFrame )
    }
    private function drawGrid():void {
        var b:BitmapData = bitmapData;
        for ( var i:uint = 0; i < Reversi.W + 1; i++ ) { b.fillRect( new Rectangle( i * CELL, 0, THICKNESS, b.height ), 0 ); }
        for ( var j:uint = 0; j < Reversi.H + 1; j++ ) { b.fillRect( new Rectangle( 0, j * CELL, b.width,  THICKNESS ), 0 ); }
    }
    private function onFrame( e:Event ):void {
        if( reversi && visible ){
            var b:BitmapData = bitmapData;
            for ( var i:int = 0; i < Reversi.W; i++ ) {
                for ( var j:int = 0; j < Reversi.H; j++ ) {
                    if ( map[i][j] != reversi.map[i][j] ) { 
                        map[i][j] = reversi.map[i][j]; 
                        b.fillRect( new Rectangle( i * CELL + THICKNESS + 1 , j * CELL + THICKNESS + 1, CELL - THICKNESS - 2, CELL - THICKNESS - 2 ), COLOR[map[i][j]] )
                    }
                }
            }
        }
    }
}
class Reversi implements Game{        
    static public var BLACK:int = 1;
    static public var WHITE:int = 2;
    static public var W:int = 8;
    static public var H:int = 8;
    public var turn:int = 1;
    public var map:Vector.<Vector.<uint>>;
    public var gameOvered:Boolean = false;
    public var passed:Boolean = false;
    public var players:Array = [null,null,null];
    public var white:uint = 2;
    public var black:uint = 2;
    public var onGameOver:Function;
    public var winner:int = -1;
    function Reversi( initialize:Boolean = true ){ if(initialize){ init() } }
    public function init():void {
        white = 2; black = 2; turn = 1; gameOvered = false; passed = false; winner = -1;
        map = new Vector.<Vector.<uint>>(W);
        for ( var i:uint = 0; i < W; i++ ) {
            map[i] = new Vector.<uint>(H)
            for ( var j:uint = 0; j < H; j++ ) { map[i][j] = 0; }
        }
        var cx:int = W / 2; var cy:int = H / 2; map[cx-1][cy-1] = 2; map[cx-1][cy] = 1; map[cx][cy-1] = 1; map[cx][cy] = 2; 
    }
    public function progress():void{
        if (! gameOvered ) {
            var p:Player = players[turn];
            if ( p ){ p.action( this ) }
        }
    }
    
    //評価用の関数1 確定済みの駒の数を数える-------------------------------------------------------------------
    public function fixedNum( turn:uint ):int {
        var c:uint = 0;
        var en:uint = 3 - turn; 
        for ( var i:uint = 0; i < W; i++ ) {
            for ( var j:uint = 0; j < H; j++ ) {
                if( isFixed(i, j, turn) ) { c++ }
                else if( isFixed(i, j, en) ) { c-- }
            }
        }
        return c;
    }
    private function isFixed( tx:uint, ty:uint, turn:uint ):Boolean { 
        var map:Vector.<Vector.<uint>> = this.map; 
        if ( map[tx][ty] != turn ) { return false }
        var en:int = 3 - turn;    
        for ( var i:int = -1, j:int = -1; i < 0 || j < 0; i++) {
            if ( i > 1 ) { i = -1; j++; }
            var x:uint = tx, y:uint = ty, vx:int = i, vy:int = j, f1:Boolean = false, f2:Boolean = false;
            
            do{
                x += vx; y += vy;
                if ( x >= W || y >= H ) {
                    break;
                }else{
                    var m:uint = map[x][y];
                    if ( m == 0 ) { f1 = true; break; }
                    if ( m == en ) { f2 = true; }
                }
            }while(true)
            
            if( f1 || f2 ){
                x = tx; y = ty;
                vx = -vx; vy = -vy;
                
                while(true){
                    x += vx; y += vy;
                    if( x < 0 || x >= W || y < 0 || y >= H ){
                        break;
                    }else{
                        m = map[x][y];
                        if ( m == 0 ) { if (f1 || f2) { return false } }
                        if ( m == en ) { if ( f1 ) { return false; } }
                    }
                }
            }
        }
        return true;
    }
    //----------------------------------------------------------------------------------------
    
    
    
    //評価用関数2 着手可能な手の数を数える。----------------------------------------------------------
    public function enableNum( turn:uint ):int{
        var c:uint = 0;
        var en:uint = 3 - turn;
        for ( var i:uint = 0; i < W; i++ ) {
            for ( var j:uint = 0; j < H; j++ ) {
                if( isEnable(i, j, turn)  ) { c++ }
                if( isEnable(i, j, en)  ) { c-- }
            }
        }
        return c;
    }
    private function isEnable( tx:uint, ty:uint, turn:uint ):Boolean { 
        var map:Vector.<Vector.<uint>> = this.map; 
        if ( map[tx][ty] != 0 ){ return false; }
        for ( var vx:int = -1; vx < 2; vx++ ) {
            for ( var vy:int = -1; vy < 2; vy++ ) {
                if( vx != 0 || vy != 0 ){
                    var x:uint = tx, y:uint = ty, f1:Boolean = false, f2:Boolean = false;
                    do{
                        x += vx; y += vy;
                        if ( x >= W || y >= H ) {
                            break;
                        }else {
                            var m:uint = map[x][y];
                            if (! f1 ) {
                                if( m == 0 || turn == m  ){ break; }
                                f1 = true;
                            }else {
                                if( m == 0 ){ break; }
                                else if( turn == m ){ f2 = true; break; }
                            }
                        }
                    }while(true)
                    if ( f2 ) { return true; }
                }
            }
        }
        return false;
    }
    //-------------------------------------------------------------------------------------
    
    
    
    //評価用関数3 各位置(10通り)にある駒の数を数える------------------------------------------------
    private function positionNums( turn:uint ):Array {
        var arr:Array = [];
        var en:uint = 3 - turn;
        for ( var i:uint = 0; i < 10; i++ ) {
            var line:Array = POSITION[i];
            var l:uint = line.length;
            arr[i] = 0
            for ( var j:uint = 0; j < l; j++ ){
                var p:Array = line[j];
                var m:int = map[ p[0] ][ p[1] ];
                if ( turn == m ) { arr[i]++; }
                else if ( turn == en ) { arr[i]--; }
            }
        }
        return arr;
    }
    private const POSITION:Array = [
        [ [0, 0], [7, 0], [0, 7], [7, 7] ],
        [ [1, 1], [6, 1], [1, 6], [6, 6] ],
        [ [2, 2], [5, 2], [2, 5], [5, 5] ],
        [ [4, 4], [4, 3], [3, 4], [4, 4] ], 
        [ [0, 1], [1, 0], [7, 1], [1, 7], [0, 6], [6, 0], [6, 7], [7, 6] ],
        [ [0, 2], [2, 0], [7, 2], [2, 7], [0, 5], [5, 0], [5, 7], [7, 5] ],
        [ [0, 3], [3, 0], [7, 3], [3, 7], [0, 4], [4, 0], [4, 7], [7, 4] ],
        [ [1, 2], [2, 1], [6, 2], [2, 6], [1, 5], [5, 1], [5, 6], [6, 5] ],
        [ [1, 3], [3, 1], [6, 3], [3, 6], [1, 4], [4, 1], [4, 6], [6, 4] ],
        [ [2, 3], [3, 2], [5, 3], [3, 5], [2, 4], [4, 2], [4, 5], [5, 4] ]
    ];
    //----------------------------------------------------------------------------------------
    
    
    public function clone():Game {
        var cl:Reversi = new Reversi( false );
        if( map ){
            var cmap:Vector.<Vector.<uint>> = new Vector.<Vector.<uint>>(W);
            for ( var i:uint = 0; i < W; i++ ) {
                var line:Vector.<uint> = new Vector.<uint>(H);
                for ( var j:uint = 0; j < H; j++ ) {
                    line[j] = map[i][j];
                }
                cmap[i] = line
            }
            cl.map = cmap;
        }
        cl.turn = turn;
        for( var str:String in players ){
            cl.players[str] = players[str];
        }
        return cl;
    }
    
    
    //着手---------------------------------------------------------------------------------------
    //(x,y,パス)
    public function action( ...arg ):void {
        if ( arg[2] ) { pass(); return }
        else{ passed = false; }
        var x:int = arg[0]; var y:int = arg[1];
        select(x, y);
    }
    private function select( tx:uint, ty:uint ):void {
        if ( map[tx][ty] != 0 ){ return; }
        var f:Boolean = false;
        for ( var vx:int = -1; vx < 2; vx++ ) {
            for ( var vy:int = -1; vy < 2; vy++ ) {
                if( vx != 0 || vy != 0 ){
                    var x:uint = tx, y:uint = ty, f1:Boolean = false, f2:Boolean = false, count:int = 0;
                    do{
                        x += vx; y += vy;
                        if ( x >= W || y >= H ) {
                            break;
                        }else {
                            var m:uint = map[x][y];
                            if (! f1 ) {
                                if ( turn == m || m == 0 ) { break; }
                                else { f1 = true; } 
                            }else {
                                if ( m == 0 ) { break; }
                                if ( turn == m ) { f2 = true; break; }
                            }
                        }
                        count++;
                    }while(true)
                    
                    if ( f2 ){
                        x = tx; y = ty;
                        for ( var j:uint = 0; j < count; j++ ) {
                            x += vx; y += vy;
                            map[x][y] = turn;
                        }
                        f = true;
                    }
                }
            }
        }
        if ( f ) { map[tx][ty] = turn; setCount(); passed = false; turn = 3 - turn; }
    }
    private function pass():void {            
        if ( passed == true ){ gameOver(); return; }
        passed = true;
        turn = 3 - turn;
    }
    //----------------------------------------------------------------------------------------------
    
    
    //着手可能な手を返す--------------------------------------------------------------------------
    public function getAction():Array {
        if( gameOvered ){ return [] }
        var arr:Array = [];
        for ( var i:uint = 0; i < W; i++ ) {
            for ( var j:uint = 0; j < H; j++ ) {
                if( isEnable(i,j,turn)  ){ arr.push( [i,j] ) }
            }
        }
        if ( arr.length == 0 ){ arr.push( [0, 0, true] ); }
        return arr;
    }
    public function passable():Boolean {
        for ( var i:int = 0; i < W; i++ ) {
            for ( var j:int = 0; j < H; j++ ) {
                if ( isEnable( i, j, turn ) ) { return false; }
            }
        }
        return true;
    }
    //-----------------------------------------------------------------------------------------
    
    
    //評価用の配列に従って評価値を返す
    public function getValue( player:Player, weight:Array ):Number {
        var turn:int = players.indexOf( player );
        var arr:Array = [];
        arr.push( fixedNum( turn ) );
        arr.push( enableNum( turn ) );
        arr.push.apply( null, positionNums( turn ) );
        var l:uint = arr.length
        var p:Number = 0;
        for ( var i:uint = 0; i < l; i++ ) { p += weight[i] * arr[i] }
        if( turn == winner ){ p += 100 }
        return p;
    }
    
    private function gameOver():void {
        gameOvered = true;    
        setCount();
        if ( black == white ) { winner = 0; }
        else if ( black > white ){ winner = BLACK }
        else if ( black < white ){ winner = WHITE }
        if ( onGameOver != null ) { onGameOver(); }
    }
    private function setCount():void {
        black = 0; white = 0;
        for ( var i:int = 0; i < W; i++ ) {
            for ( var j:int = 0; j < H; j++ ) {
                if ( map[i][j] == BLACK ){ black++; }
                if ( map[i][j] == WHITE ){ white++; }
            }
        }
    }
    public function getMaxWeight():Array { return [ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100] }
    public function getMinWeight():Array { return [-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100]}
    public function getPoint( player:Player ):Number { 
        var p:int = players.indexOf( player )
        if( p == winner ){ return p == 1 ? (64 - white) : (64 - black); }
        return p == 1 ? white : black;
    }
    public function setCurrentPlayer( player:Player ):void { players[turn] = player; }
    public function getCurrentPlayer():Player { return players[turn]; }
}
interface Game{
    function clone():Game;
    function action( ...arg ):void;
    function getAction():Array;
    function getValue( player:Player, weight:Array ):Number;
    function getPoint( player:Player ):Number;
    function getMaxWeight():Array;
    function getMinWeight():Array;
    function setCurrentPlayer( player:Player ):void
}
class GenePool {
    public const SIZE:int = 30;
    public const VARIATION:int = 30;
    public var game:Game;
    public var genes:Array = [];
    public var generation:int = 0;
    public var index:int = 0;
    public var num:int = 0;
    private var maxWeight:Array;
    private var minWeight:Array;
    private var weightLength:int;
    
    public function GenePool( game:Game ){
        this.game = game;
        maxWeight = game.getMaxWeight(); minWeight = game.getMinWeight();
        weightLength = maxWeight.length;
        
        for ( var i:uint = 0; i < SIZE;  i++ ) {
            var weight:Array = [];
            for ( var j:uint = 0; j < weightLength; j++ ) {
                var max:Number = maxWeight[j], min:Number = minWeight[j];
                weight[j] = min + ( max - min ) * Math.random();
            }
            
            var gene:Gene = new Gene();
            gene.code = weight;
            gene.num = ++num;
            genes.push( gene );
        }
    }
    public function getGene():Gene { return genes[index]; }
    public function next():void { index++; if ( index == SIZE ) { _nextGeneration(); index = 0; } }
    private function _nextGeneration():void{ 
        if ( (!genes) || genes.length == 0 ) { return }
        generation++;
        genes.sort( function _( x:Gene, y:Gene ):Number { return (y.point - x.point); } )
        genes.splice( SIZE / 2, SIZE / 2 );
        var max:Number = genes[0].point;
        while( genes.length < SIZE ){
            var codes:Array = [];
            for ( var i:uint = 0; i < 2; i++  ) {
                do{
                    var rand:int = ( genes.length * Math.random() )>>> 0; 
                    codes[i] = genes[ rand ].code;
                }while( codes[i].point < max * Math.random() )
            }
            var code:Array = [];
            var l:int = codes[0].length;
            for ( var n:uint = 0; n < l; n++ ) {
                var r:Number = Math.random();
                code[n] = codes[ (2*Math.random())>>>0 ][ n ] + (VARIATION * r*r*r*r) * (1 - 2 * Math.random());
            }
            var gene:Gene = new Gene;
            gene.code = code;
            gene.num = ++num;
            genes.push( gene )
        }
        shuffle();
    }
    public function shuffle():void {
        var a:Array = []
        for ( var i:uint = 0; i < SIZE;  i++ ) { genes[i].rand = Math.random(); }
        genes.sortOn( "rand" );

        }
    public function getArray():Array {
        var arr:Array = []
        for ( var i:uint = 0; i < SIZE;  i++ ) { arr[i] = genes[i].toString(); }
        return arr;
    }
}
class Gene {
    public var point:Number = -Infinity;
    public var code:Array;
    public var rand:Number;
    public var num:int = 0;
    public var win:int = 0;
    public function toString():String {
        return "No." +  num + ": " + (point!=-Infinity ?point.toString():"--")+"pt "+ win + "win";
    }
    public function getCode():String {
        var l:int = code.length;
        var abs:Number = Math.abs( code[0] )
        var str:String = "" + (code[0] / abs).toFixed(4);
        for ( var i:int = 1; i < l; i++ ) {
            str +="," + ( code[i] / abs ).toFixed(4);
        }
        return str
    }
}
class Player{
    public function action( game:Game ):void{}
    public function init():void{}
    public function getPoint(target:*):Object { return null; }
}
class ReversiPlayer extends Player {        
    private var arg:Array;
    private var viewer:ReversiViewer;
    private var game:Reversi;
    private var running:Boolean = false;
    
    function ReversiPlayer( game:Reversi, viewer:ReversiViewer ):void {
        this.viewer = viewer;
        this.game = game;
        viewer.stage.addEventListener( "mouseDown", onDown );
    }
    
    override public function init():void {
        arg = null;
    }
    
    override public function action( game:Game ):void {
        if( running ){
            if ( arg ) { 
                game.action.apply( null, arg )
                arg = null;
                running = false;
            }
        }else {
            running = true;
            var act:Array = game.getAction()
            if ( act.length == 1 && act[0][2] == true ) { pass(); }
        }
    }
    public function pass():void {
        if( running ){ arg = [0, 0, true]; }
    }
    private function onDown(e:MouseEvent):void {
        if( running ){
            var x:int = viewer.mouseX / ReversiViewer.CELL;
            var y:int = viewer.mouseY / ReversiViewer.CELL;
            if ( x >= 0 && x < Reversi.W && y >= 0 && y < Reversi.H ) {
                arg = [x, y];
            }
        }
    }    
}
class NormalAI extends Player{
    public var tolerance:int = 0;
    public var weight:Array;    
    public function NormalAI( weight:Array, tolerance:int = 0 ) {
        this.tolerance = tolerance;
        this.weight = weight;
    }
    override public function action( game:Game ):void{
        var act:Array = game.getAction();
        var act2:Array = [];
        var act3:Array = [];
        var arg:Array = weight;
        var l2:int = 0;
        var l3:int = 0;
        
        var points:Vector.<Number> = new Vector.<Number>();
        var l:uint = act.length;
        var max:Number = -Infinity;
        
        for( var i:uint = 0; i<l; i++ ){
            var clone:Game = game.clone();
            clone.action.apply( null,act[i] )
            var p:int = clone.getValue( this, arg );
            if( p >= max ){ max = p; points.push( p ); act2.push( act[i] ); l2++; }
        }
        
        for( var j:uint = 0; j<l2; j++ ){
            if( points[j]+tolerance >= max ){
                act3.push( act2[j] ); l3++;
            }
        }
        game.action.apply( null, act3[ (l3 * Math.random()) >>> 0 ] );
        
    }
}

class MonteAI extends Player {
    public var depth:int; //読みの深さ
    public var tolerance:Number; //寛容さ
    public var repeat:int; //シュミレーションの回数
    public var weight:Array;
    public var vartialPlayers:Array;
    
    function MonteAI( weight:Array, tolerance:Number = 2, depth:Number = 9, repeat:Number = 50 ) {
        this.vartialPlayers = [ new NormalAI( weight, 3 ), new NormalAI( weight, 2 ) ];
        this.weight = weight;
        this.tolerance = tolerance;
        this.depth = depth;
        this.repeat = repeat;
    }
    
    override public function action( game:Game ):void{
        var act:Vector.<Array> = Vector.<Array>( game.getAction() );
        
        var points:Vector.<Number> = new Vector.<Number>;
        var clones:Vector.<Game> = new Vector.<Game>;     
        var l:uint = act.length;
        var max:Number = -Infinity;
        var arg:Array = weight;    
        
        //1手読む
        function _1():void{
            var g:Game;
            var player:Player = vartialPlayers[0];
            for ( var i:uint = 0; i < l; i++ ) {
                g = game.clone();
                g.setCurrentPlayer( player );
                g.action.apply( null, act[i] );
                var p:Number = g.getValue( player, arg );        
                
                if ( p > max ) { max = p }
                
                points.push( p );
                clones.push( g );
            }
        }
        
        //点の高いものを選び出す。
        function _2():void{   
            var act2:Vector.<Array> = new Vector.<Array>;
            var points2:Vector.<Number> = new Vector.<Number>;
            var clones2:Vector.<Game> = new Vector.<Game>;  
            var l2:int = 0;
            
            for( var i:uint = 0; i<l; i++ ){
                var pt:Number = points[i];
                if( pt + tolerance >= max ){
                    points2.push( pt );
                    clones2.push( clones[i] );
                    act2.push( act[i] );
                    l2++;
                }
            }
            act=act2; points=points2; clones=clones2;
            l = l2;
        }
        
        //各手を深読み
        function _3():void {
            var g:Game;
            for( var i:uint = 0; i < l; i++ ){
                for( var j:uint = 0; j < repeat; j++ ){
                    g = clones[i].clone();
                    var index:int = 0;
                    for( var k:uint = 0; k<depth; k++ ){
                        index++;
                        if ( index == vartialPlayers.length ) { index = 0 }
                        var player:Player = vartialPlayers[index];
                        g.setCurrentPlayer( player );
                        player.action( g );
                        var p:Number = g.getValue( vartialPlayers[0], arg ); 
                    }
                    points[i] += g.getValue( vartialPlayers[0], arg ); 
                }
            }
        }
        
        //もっとも点の高い手を実行する。
        function _4():void {
            max = -Infinity;
            
            for( var i:uint = 0; i<l; i++ ){
                var p:Number = points[i];
                if( p > max ){ max = p }
                points.push( p )
            }
            
            for( var j:uint = 0; j < l; j++ ){
                if( points[j] < max ){
                    points.splice( j, 1 );
                    act.splice( j, 1 );
                    j--; l--;
                }
            }
            game.action.apply( null, act[ (l*Math.random()) >>> 0 ] );
        }
        _1(); _2(); _3(); _4();
    }    
}

/**
 * Style.as
 * Keith Peters
 * version 0.9.10
 * 
 * A collection of style variables used by the components.
 * If you want to customize the colors of your components, change these values BEFORE instantiating any components.
 * 
 * Copyright (c) 2011 Keith Peters
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
 
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
        import com.bit101.components.*;
	class Style
	{
		public static var TEXT_BACKGROUND:uint = 0xFFFFFF;
		public static var BACKGROUND:uint = 0xCCCCCC;
		public static var BUTTON_FACE:uint = 0xFFFFFF;
		public static var BUTTON_DOWN:uint = 0xEEEEEE;
		public static var INPUT_TEXT:uint = 0x333333;
		public static var LABEL_TEXT:uint = 0x666666;
		public static var DROPSHADOW:uint = 0x000000;
		public static var PANEL:uint = 0xF3F3F3;
		public static var PROGRESS_BAR:uint = 0xFFFFFF;
		public static var LIST_DEFAULT:uint = 0xFFFFFF;
		public static var LIST_ALTERNATE:uint = 0xF3F3F3;
		public static var LIST_SELECTED:uint = 0xCCCCCC;
		public static var LIST_ROLLOVER:uint = 0XDDDDDD;
		
		public static var embedFonts:Boolean = true;
		public static var fontName:String = "PF Ronda Seven";
		public static var fontSize:Number = 8;
		
		public static const DARK:String = "dark";
		public static const LIGHT:String = "light";
		
		/**
		 * Applies a preset style as a list of color values. Should be called before creating any components.
		 */
		public static function setStyle(style:String):void
		{
			switch(style)
			{
				case DARK:
					Style.BACKGROUND = 0x444444;
					Style.BUTTON_FACE = 0x666666;
					Style.BUTTON_DOWN = 0x222222;
					Style.INPUT_TEXT = 0xBBBBBB;
					Style.LABEL_TEXT = 0xCCCCCC;
					Style.PANEL = 0x666666;
					Style.PROGRESS_BAR = 0x666666;
					Style.TEXT_BACKGROUND = 0x555555;
					Style.LIST_DEFAULT = 0x444444;
					Style.LIST_ALTERNATE = 0x393939;
					Style.LIST_SELECTED = 0x666666;
					Style.LIST_ROLLOVER = 0x777777;
					break;
				case LIGHT:
				default:
					Style.BACKGROUND = 0xCCCCCC;
					Style.BUTTON_FACE = 0xFFFFFF;
					Style.BUTTON_DOWN = 0xEEEEEE;
					Style.INPUT_TEXT = 0x333333;
					Style.LABEL_TEXT = 0x666666;
					Style.PANEL = 0xF3F3F3;
					Style.PROGRESS_BAR = 0xFFFFFF;
					Style.TEXT_BACKGROUND = 0xFFFFFF;
					Style.LIST_DEFAULT = 0xFFFFFF;
					Style.LIST_ALTERNATE = 0xF3F3F3;
					Style.LIST_SELECTED = 0xCCCCCC;
					Style.LIST_ROLLOVER = 0xDDDDDD;
					break;
			}
		}
	}

/**
 * List.as
 * Keith Peters
 * version 0.9.10
 * 
 * A scrolling list of selectable items. 
 * 
 * Copyright (c) 2011 Keith Peters
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */



	[Event(name="select", type="flash.events.Event")]
	class List extends Component
	{
		protected var _items:Array;
		protected var _itemHolder:Sprite;
		protected var _panel:Panel;
		protected var _listItemHeight:Number = 20;
		protected var _listItemClass:Class =ListItem;
		protected var _scrollbar:VScrollBar;
		protected var _selectedIndex:int = -1;
		protected var _defaultColor:uint = Style.LIST_DEFAULT;
		protected var _alternateColor:uint = Style.LIST_ALTERNATE;
		protected var _selectedColor:uint = Style.LIST_SELECTED;
		protected var _rolloverColor:uint = Style.LIST_ROLLOVER;
		protected var _alternateRows:Boolean = false;
		
		/**
		 * Constructor
		 * @param parent The parent DisplayObjectContainer on which to add this List.
		 * @param xpos The x position to place this component.
		 * @param ypos The y position to place this component.
		 * @param items An array of items to display in the list. Either strings or objects with label property.
		 */
		public function List(parent:DisplayObjectContainer=null, xpos:Number=0, ypos:Number=0, items:Array=null)
		{
			if(items != null)
			{
				_items = items;
			}
			else
			{
				_items = new Array();
			}
			super(parent, xpos, ypos);
		}
		
		/**
		 * Initilizes the component.
		 */
		protected override function init() : void
		{
			super.init();
			setSize(100, 100);
			addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
            addEventListener(Event.RESIZE, onResize);
            makeListItems();
            fillItems();
		}
		
		/**
		 * Creates and adds the child display objects of this component.
		 */
		protected override function addChildren() : void
		{
			super.addChildren();
			_panel = new Panel(this, 0, 0);
			_panel.color = _defaultColor;
			_itemHolder = new Sprite();
			_panel.content.addChild(_itemHolder);
			_scrollbar = new VScrollBar(this, 0, 0, onScroll);
            _scrollbar.setSliderParams(0, 0, 0);
		}
		
		/**
		 * Creates all the list items based on data.
		 */
		protected function makeListItems():void
		{
			var item:ListItem;
			while(_itemHolder.numChildren > 0)
			{
				item = ListItem(_itemHolder.getChildAt(0));
				item.removeEventListener(MouseEvent.CLICK, onSelect);
				_itemHolder.removeChildAt(0);
			}

            var numItems:int = Math.ceil(_height / _listItemHeight);
			numItems = Math.min(numItems, _items.length);
            numItems = Math.max(numItems, 1);
			for(var i:int = 0; i < numItems; i++)
			{

				item = new _listItemClass(_itemHolder, 0, i * _listItemHeight);
				item.setSize(width, _listItemHeight);
				item.defaultColor = _defaultColor;

				item.selectedColor = _selectedColor;
				item.rolloverColor = _rolloverColor;
				item.addEventListener(MouseEvent.CLICK, onSelect);
			}
		}

        protected function fillItems():void
        {
            var offset:int = _scrollbar.value;
            var numItems:int = Math.ceil(_height / _listItemHeight);
			numItems = Math.min(numItems, _items.length);
            for(var i:int = 0; i < numItems; i++)
            {
                var item:ListItem = _itemHolder.getChildAt(i) as ListItem;
				if(offset + i < _items.length)
				{
	                item.data = _items[offset + i];
				}
				else
				{
					item.data = "";
				}
				if(_alternateRows)
				{
					item.defaultColor = ((offset + i) % 2 == 0) ? _defaultColor : _alternateColor;
				}
				else
				{
					item.defaultColor = _defaultColor;
				}
                if(offset + i == _selectedIndex)
                {
                    item.selected = true;
                }
                else
                {
                    item.selected = false;
                }
            }
        }
		
		/**
		 * If the selected item is not in view, scrolls the list to make the selected item appear in the view.
		 */
		protected function scrollToSelection():void
		{
            var numItems:int = Math.ceil(_height / _listItemHeight);
			if(_selectedIndex != -1)
			{
				if(_scrollbar.value > _selectedIndex)
				{
//                    _scrollbar.value = _selectedIndex;
				}
				else if(_scrollbar.value + numItems < _selectedIndex)
				{
                    _scrollbar.value = _selectedIndex - numItems + 1;
				}
			}
			else
			{
				_scrollbar.value = 0;
			}
            fillItems();
		}
		
		
		
		///////////////////////////////////
		// public methods
		///////////////////////////////////
		
		/**
		 * Draws the visual ui of the component.
		 */
		public override function draw() : void
		{
			super.draw();
			
			_selectedIndex = Math.min(_selectedIndex, _items.length - 1);


			// panel
			_panel.setSize(_width, _height);
			_panel.color = _defaultColor;
			_panel.draw();
			
			// scrollbar
			_scrollbar.x = _width - 10;
			var contentHeight:Number = _items.length * _listItemHeight;
			_scrollbar.setThumbPercent(_height / contentHeight); 
			var pageSize:Number = Math.floor(_height / _listItemHeight);
            _scrollbar.maximum = Math.max(0, _items.length - pageSize);
			_scrollbar.pageSize = pageSize;
			_scrollbar.height = _height;
			_scrollbar.draw();
            scrollToSelection();
		}
		
		/**
		 * Adds an item to the list.
		 * @param item The item to add. Can be a string or an object containing a string property named label.
		 */
		public function addItem(item:Object):void
		{
			_items.push(item);
			invalidate();
			makeListItems();
      fillItems();
		}
		
		/**
		 * Adds an item to the list at the specified index.
		 * @param item The item to add. Can be a string or an object containing a string property named label.
		 * @param index The index at which to add the item.
		 */
		public function addItemAt(item:Object, index:int):void
		{
			index = Math.max(0, index);
			index = Math.min(_items.length, index);
			_items.splice(index, 0, item);
			invalidate();
      makeListItems();
      fillItems();
		}
		
		/**
		 * Removes the referenced item from the list.
		 * @param item The item to remove. If a string, must match the item containing that string. If an object, must be a reference to the exact same object.
		 */
		public function removeItem(item:Object):void
		{
			var index:int = _items.indexOf(item);
			removeItemAt(index);
		}
		
		/**
		 * Removes the item from the list at the specified index
		 * @param index The index of the item to remove.
		 */
		public function removeItemAt(index:int):void
		{
			if(index < 0 || index >= _items.length) return;
			_items.splice(index, 1);
			invalidate();
      makeListItems();
      fillItems();
		}
		
		/**
		 * Removes all items from the list.
		 */
		public function removeAll():void
		{
			_items.length = 0;
			invalidate();
      makeListItems();
      fillItems();
		}
		
		
		
		
		
		///////////////////////////////////
		// event handlers
		///////////////////////////////////
		
		/**
		 * Called when a user selects an item in the list.
		 */
		protected function onSelect(event:Event):void
		{
			if(! (event.target is ListItem)) return;
			
			var offset:int = _scrollbar.value;
			
			for(var i:int = 0; i < _itemHolder.numChildren; i++)
			{
				if(_itemHolder.getChildAt(i) == event.target) _selectedIndex = i + offset;
				ListItem(_itemHolder.getChildAt(i)).selected = false;
			}
			ListItem(event.target).selected = true;
			dispatchEvent(new Event(Event.SELECT));
		}
		
		/**
		 * Called when the user scrolls the scroll bar.
		 */
		protected function onScroll(event:Event):void
		{
            fillItems();
		}
		
		/**
		 * Called when the mouse wheel is scrolled over the component.
		 */
		protected function onMouseWheel(event:MouseEvent):void
		{
			_scrollbar.value -= event.delta;
            fillItems();
		}

        protected function onResize(event:Event):void
        {
            makeListItems();
            fillItems();
        }
		///////////////////////////////////
		// getter/setters
		///////////////////////////////////
		
		/**
		 * Sets / gets the index of the selected list item.
		 */
		public function set selectedIndex(value:int):void
		{
			if(value >= 0 && value < _items.length)
			{
				_selectedIndex = value;
//				_scrollbar.value = _selectedIndex;
			}
			else
			{
				_selectedIndex = -1;
			}
			invalidate();
			dispatchEvent(new Event(Event.SELECT));
		}
		public function get selectedIndex():int
		{
			return _selectedIndex;
		}
		
		/**
		 * Sets / gets the item in the list, if it exists.
		 */
		public function set selectedItem(item:Object):void
		{
			var index:int = _items.indexOf(item);
//			if(index != -1)
//			{
				selectedIndex = index;
				invalidate();
				dispatchEvent(new Event(Event.SELECT));
//			}
		}
		public function get selectedItem():Object
		{
			if(_selectedIndex >= 0 && _selectedIndex < _items.length)
			{
				return _items[_selectedIndex];
			}
			return null;
		}

		/**
		 * Sets/gets the default background color of list items.
		 */
		public function set defaultColor(value:uint):void
		{
			_defaultColor = value;
			invalidate();
		}
		public function get defaultColor():uint
		{
			return _defaultColor;
		}

		/**
		 * Sets/gets the selected background color of list items.
		 */
		public function set selectedColor(value:uint):void
		{
			_selectedColor = value;
			invalidate();
		}
		public function get selectedColor():uint
		{
			return _selectedColor;
		}

		/**
		 * Sets/gets the rollover background color of list items.
		 */
		public function set rolloverColor(value:uint):void
		{
			_rolloverColor = value;
			invalidate();
		}
		public function get rolloverColor():uint
		{
			return _rolloverColor;
		}

		/**
		 * Sets the height of each list item.
		 */
		public function set listItemHeight(value:Number):void
		{
			_listItemHeight = value;
            makeListItems();
			invalidate();
		}
		public function get listItemHeight():Number
		{
			return _listItemHeight;
		}

		/**
		 * Sets / gets the list of items to be shown.
		 */
		public function set items(value:Array):void
		{
			_items = value;
			invalidate();
		}
		public function get items():Array
		{
			return _items;
		}

		/**
		 * Sets / gets the class used to render list items. Must extend ListItem.
		 */
		public function set listItemClass(value:Class):void
		{
			_listItemClass = value;
			makeListItems();
			invalidate();
		}
		public function get listItemClass():Class
		{
			return _listItemClass;
		}

		/**
		 * Sets / gets the color for alternate rows if alternateRows is set to true.
		 */
		public function set alternateColor(value:uint):void
		{
			_alternateColor = value;
			invalidate();
		}
		public function get alternateColor():uint
		{
			return _alternateColor;
		}
		
		/**
		 * Sets / gets whether or not every other row will be colored with the alternate color.
		 */
		public function set alternateRows(value:Boolean):void
		{
			_alternateRows = value;
			invalidate();
		}
		public function get alternateRows():Boolean
		{
			return _alternateRows;
		}

        /**
         * Sets / gets whether the scrollbar will auto hide when there is nothing to scroll.
         */
        public function set autoHideScrollBar(value:Boolean):void
        {
            _scrollbar.autoHide = value;
        }
        public function get autoHideScrollBar():Boolean
        {
            return _scrollbar.autoHide;
        }

	}