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

forked from: simple ga demo

Демка для объяснения принципа работы генетических алгоритмов.
@author makc
Get Adobe Flash player
by mitien 22 Jul 2011
// forked from makc3d's simple ga demo
package {
    import com.bit101.components.Label;
    import flash.display.Sprite;
    import flash.events.MouseEvent;
    import flash.utils.setTimeout;
    
    /**
     * Демка для объяснения принципа работы генетических алгоритмов.
     * @author makc
     */
    public class GA extends Sprite {
        public var phase:Label;
        public var target:Target;
        public var delay:int = 4000;
        public var population:Vector.<Creature>;
        public function GA () {
            stage.scaleMode = "noScale";
            phase = new Label (this, 10, 10);
            addChild (target = new Target);
            target.x = 200; target.y = CoordinateTransform.screenY (0);
            stage.addEventListener (MouseEvent.CLICK, changeTarget);

            // создаём популяцию из 3-х случайных особей
            population = new <Creature> [new Creature, new Creature, new Creature];
            population [0].gene = 30 * (1 + Math.random ());
            population [1].gene = 30 * (1 + Math.random ());
            population [2].gene = 30 * (1 + Math.random ());

            // и запускаем алгоритм
            calculateFitness ();
        }

        private function calculateFitness ():void {
            startNewPhase ("FITNESS CALCULATION\n" +
                "CIRCLE SIZE IS PROPORTIONAL TO FITNESS");

            for each (var c:Creature in population) {
                // устанавливаем фитнес равным модулю высоты траектории над целью
                c.fitness = Math.abs (Physics.yx (Number (c.gene), CoordinateTransform.actualX (target.x)));

                drawCreature (c, 0x9F9F9F, true);
            }

            waitAndCall (makeOffspring);
        }
        
        private function makeOffspring ():void {
            startNewPhase ("SELECTION, CROSSOVER & MUTATION\n" +
                "RED IS DEAD, GREEN ARE PARENTS, BLUE IS NEW GENERATION");

            // сортируем популяцию по фитнесу
            population.sort (Creature.compare);

            drawCreature (population [0], 0xFF00);
            drawCreature (population [1], 0xFF00);
            drawCreature (population [2], 0xFF0000);

            // особь 2 - лузер, её место займт потомок особей 0 и 1
            population [2].gene = 0.5 * (
                Number (population [0].gene) +
                Number (population [1].gene)
            );

            // этот потомок может быть как лучше, так и хуже своих родителей;
            // чтобы увеличить его шансы во втором случае, подвергнем его гены мутации
            population [2].gene = Number (population [2].gene) + 10 * (Math.random () - Math.random ())

            drawCreature (population [2], 0x7FFF);

            waitAndCall (calculateFitness);
        }

        private function waitAndCall (f:Function):void {
            delay = 100 + (delay - 100) * 0.9; setTimeout (f, delay);
        }

        private function changeTarget (e:MouseEvent):void {
            target.x = Math.max (mouseX, CoordinateTransform.screenX (50));
        }

        private function startNewPhase (text:String):void {
            graphics.clear ();
            graphics.lineStyle (0, 0x7f7f7f);
            var y0:Number = CoordinateTransform.screenY (0);
            graphics.moveTo (0, y0); graphics.lineTo (465, y0);
            phase.text = text;
        }

        private function drawCreature (c:Creature, color:uint, showFitness:Boolean = false):void {
            graphics.lineStyle (0, color);
            var x0:Number = CoordinateTransform.screenX (0);
            var x1:Number = target.x;
            var y0:Number = CoordinateTransform.screenY (0);
            graphics.moveTo (x0, y0);
            for (var xi:int = x0 + 5; xi < 465; xi += 5) {
                var yi:Number = CoordinateTransform.screenY (
                    Physics.yx (Number (c.gene),
                        CoordinateTransform.actualX (xi)
                    )
                );
                graphics.lineTo (xi, yi);
                if (yi > 465) break;
            }
            if (showFitness) {
                graphics.beginFill (0xFFFFFF);
                var y1:Number = CoordinateTransform.screenY (
                    Physics.yx (Number (c.gene),
                        CoordinateTransform.actualX (x1)
                    )
                );
                graphics.drawCircle (x1, y1, 0.2 * c.fitness);
                graphics.endFill ();
            }
        }
    }
}

/** уравнения движения нашего тела под действием силы
    тяжести (не содержат решения задачи в явном виде) */
class Physics {
    static private var g:Number = 10;
    /** дальность полёта в момент t */
    static public function x (v:Number, t:Number):Number {
        var vx:Number = v / Math.SQRT2;
        return vx * t;
    }
    /** высота траектории в момент t */
    static public function y (v:Number, t:Number):Number {
        var vy:Number = v / Math.SQRT2;
        return vy * t - g * t * t / 2;
    }
    /** высота траектории над точкой x */
    static public function yx (v:Number, x:Number):Number {
        var vx:Number = v / Math.SQRT2;
        var t:Number = x / vx;
        return y (v, t);
    }
}

/** преобразования координат для рисования на экране */
class CoordinateTransform {
    static public function screenX (x:Number):Number { return x + 50; }
    static public function actualX (x:Number):Number { return x - 50; }
    static public function screenY (y:Number):Number { return 200 - y; }
    static public function actualY (y:Number):Number { return 200 - y; }
}

/** виртуальная "особь" */
class Creature {
    public var gene:Object;
    public var fitness:Number;

    static public function compare (c1:Creature, c2:Creature):Number {
        if (c1.fitness > c2.fitness) return +1;
        if (c1.fitness < c2.fitness) return -1;
        return 0;
    }
}

import flash.display.Shape;
class Target extends Shape {
    public function Target () {
        graphics.lineStyle (5, 0, 1, false, "normal", "none");
        graphics.moveTo (-5, -5);
        graphics.lineTo (+5, +5);
        graphics.moveTo (-5, +5);
        graphics.lineTo (+5, -5);
    }
}