Project Euler 144

by antalg
My solution to Project Euler 144,
regarding reflections of a laser
in an ellipse.

This simulates 2 bounces every frame,
my C# port of this (if used without
creating graphics) runs almost instantly.

Port stats:
Without compiler optimization:
6510ms elapsed for 100,000 runs = 0.065 ms/run
With compiler optimization:
4870ms elapsed for 100,000 runs = 0.049 ms/run

About 33 lines of C# code
♥0 | Line 96 | Modified 2011-10-07 06:25:18 | MIT License
play

ActionScript3 source code

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

package {
    import flash.display.BitmapData;
    import flash.display.Bitmap;
    import flash.events.Event;
    import flash.text.TextField;
    import flash.geom.Point;
    import flash.display.Sprite;
    
    [SWF(backgroundColor=0xDDDDDD, frameRate=30)]
    
    //////////
    /////////    My solution to Project Euler 144,
    ////////    regarding reflections of a laser
    ///////    in an ellipse.
    //////
    /////    This simulates 2 bounces every frame,
    ////    my C# port of this (if used without
    ///    creating graphics) runs almost instantly.
    //    
    ///    Port stats:
    ////    Without compiler optimization:
    /////    6510ms elapsed for 100,000 runs = 0.065 ms/run
    //////    With compiler optimization:
    ///////    4870ms elapsed for 100,000 runs = 0.049 ms/run
    ////////
    /////////    About 33 lines of C# code
    //////////
    
    public class Euler144 extends Sprite {
        public function Euler144() {
            var p:Point = new Point(1.4, -9.6);
            
            run(p);
        }
        
        private function run(impactPoint:Point):void {
            addDebug();
            
            stage.addChild(_canvas);
            _canvas.cacheAsBitmap = true;
            _canvas.x = stage.stageWidth/2;
            _canvas.y = stage.stageHeight/2;
            
            drawEllipse(0,0,5,10);
            
            A = new Point(0, 10.1);
            B = impactPoint;
            
            initLine(A);
            drawLine(B);
            
            slope = (A.y-B.y)/(A.x-B.x);
            
            var count:int = 0;
            
            this.addEventListener(Event.ENTER_FRAME, iter);
        }
        
        private function iter(e:Event):void {
            for(var i:int = 0; i<2; i++) {
                A = B;
                slope = reflect(slope, -4*B.x/B.y);
                
                var m:Number = B.y - B.x*slope;
                
                B = intersection(slope, m, B);
                
                drawLine(B);
                
                count++;
                
                clear();
                out("Working...");
                
                // Uncomment to stop when at hole
                if( (B.x>0?B.x:-B.x) < 0.01 && B.y>0 ) {
                    this.removeEventListener(Event.ENTER_FRAME, iter);
                    clear();
                    out("Done!");
                    break;
                }
            }
            
            //clear();
            //out(count);
        }
        
        private var A:Point;
        private var B:Point;
        private var slope:Number;
        private var count:int;
        
        private function reflect(slope:Number, tangent:Number):Number {
            return (slope*(tangent*tangent-1)+2*tangent)/(2*slope*tangent-tangent*tangent+1);
        }
        
        private function intersection(k:Number, m:Number, p:Point):Point {
            var x1:Number = (-k*m + 2*Math.sqrt(25*k*k-m*m+100))/(4+k*k);
            var x2:Number = (-k*m - 2*Math.sqrt(25*k*k-m*m+100))/(4+k*k);
            
            var dx1:Number = p.x-x1;
            var dx2:Number = p.x-x2;
            dx1=dx1>0?dx1:-dx1;
            dx2=dx2>0?dx2:-dx2;
            
            if(dx1>dx2)    // x1 correct
                return new Point( x1, p.y+k*(x1-p.x) );
            else
                return new Point( x2, p.y+k*(x2-p.x) );
        }
        
        
        /////////// Drawing /////////////
        private var _canvas:Sprite = new Sprite();
        private var _scale:Number = 20;
        
        private function drawEllipse(x:Number, y:Number, radX:int, radY:int):void {
            _canvas.graphics.lineStyle(1);
            
            x*=_scale;
            y*=_scale;
            radX*=_scale;
            radY*=_scale;
            
            _canvas.graphics.drawEllipse(x-radX, y-radY, radX*2, radY*2);
        }
        
        private function initLine(p:Point):void {
            _canvas.graphics.moveTo(p.x*_scale, p.y*-_scale);
        }
        
        private function drawLine(p:Point):void {
            _canvas.graphics.lineStyle(1, 0xDD0000, 0.4);
            _canvas.graphics.lineTo(p.x*_scale, p.y*-_scale);
        }
        /////////////////////////////////
        
        private var txt:TextField = new TextField();
        private function addDebug():void {
            stage.addChild(txt);
            txt.width = 300;
            txt.height = 600;
        }
        
        private function out(data:*):void {
            txt.appendText(data+"\n");
        }
        
        private function clear():void {
            txt.text = "";
        }
    }
}