JigsawPuzzle

by momasahiro321
サイズが指定されたジグソーパズルを自動生成します。
ピースをドラックして移動。
正解のピースの所でボタンを離すとピースがくっつきます。
サイズを大きくしすぎると生成の時に重くなります。
♥0 | Line 367 | Modified 2014-07-21 11:17:49 | MIT License
play

ActionScript3 source code

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

package 
{
    import com.bit101.components.Calendar;
    import com.bit101.components.CheckBox;
    import com.bit101.components.Label;
    import com.bit101.components.NumericStepper;
    import com.bit101.components.PushButton;
    import com.bit101.components.Window;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    
    [SWF(frameRate="60")]
    
    public class Main extends Sprite 
    {
        private var window:Window;
        private var w:int;
        private var h:int;
        private var container:Sprite = new Sprite();
        private var groups:Array = new Array();
        private var completed:Window;
        
        public function Main():void 
        {
            //サイズウィンドウ
            
            addChild(container);           
            window = new Window(this, 0, 0, "Size");
            window.width = 250;
            window.x = 108;
            window.y = 183;
            
            var widthStepper:NumericStepper = new NumericStepper(window);
            var heightStepper:NumericStepper = new NumericStepper(window);
            
            widthStepper.x = 10;
            heightStepper.x = 130;
            
            widthStepper.y = heightStepper.y = 30;
            widthStepper.minimum = heightStepper.minimum = 2;
            widthStepper.value = heightStepper.value =  5;
            
            var label:Label = new Label(window, 105, 30, "×");
            
            var start:PushButton = new PushButton(window, 140, 70, "Start", function(e:MouseEvent):void
            {
                window.visible = false;
                
                w = widthStepper.value;
                h = heightStepper.value;
                
                if (w < 1)
                {
                    w = 2;
                }
                
                if (h < 2)
                {
                    h = 2;
                }
                
                create();
            });
            
            Edge.init();
        }
        
        public function piecesHitTest(group:Group, checkFunction:Function):void
        {
            //パズル同士の当たり判定
            loop: for each(var piece:Piece in group.pieces)
            {
                for each(var otherGroup:Group in groups)
                {
                    if (group.id != otherGroup.id)
                    {
                        for each(var otherPiece:Piece in otherGroup.pieces)
                        {
                            if (piece.indexX != otherPiece.indexX || piece.indexY != otherPiece.indexY)
                            {
                                if (piece.hitTestObject(otherPiece))
                                {
                                
                                    if (checkFunction(piece, otherPiece, otherGroup))
                                    {
                                        removeGroup(group);
                                        
                                        //多重ループを抜ける
                                        break loop;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        
        //ピースのグループを削除
        public function removeGroup(groupA:Group):void
        {
            for (var i:int = 0; i < groups.length;i++)
            {
                if (groupA.id == groups[i].id)
                {
                    groups.splice(i, 1);
                    break;
                }
            }
            
            if (groups.length == 1)
            {
                completed.visible = true;
            }
        }
        
        //ピースを生成
        private function create():void
        {
            var edges:Array = new Array();
           
            for (var hi:int = 0; hi < 2 * h + 1; hi++)
            {
                var line:Array = new Array();
                var wi:int = 0;
                var angle:int = 0;
                
                if (hi % 2 == 0)
                {
                    for (wi = 0; wi < w; wi++)
                    {
                        if (hi == 0)
                        {
                            line.push(new Edge(1, true));
                        }
                        else
                        {
                            angle = hi % 4 + 1;
                          
                            if (wi % 2 == 0 || (int)(Math.random() * 3) == 0)
                            {
                                angle = 4 - angle;
                            }
                           
                            line.push(new Edge(angle , hi == 2 * h));
                        }
                    }
                }
                else
                {
                    for (wi = 0; wi < w + 1; wi++)
                    {
                        if (wi == 0)
                        {
                            line.push(new Edge(2, true));
                        }
                        else
                        {
                            angle = wi % 2 * 2;
                            
                            if (hi % 4 == 1)
                            {
                                angle = 2 - angle;
                            }
                            
                            line.push(new Edge(angle, wi == w));
                        }
                    }
                }
                
                edges.push(line);
                
                completed = new Window(this, 183, 223, "Completed!");
                completed.width = 100;
                completed.height = 20;
                completed.visible = false;

            }
            
            for (var hj:int = 0; hj < h; hj++)
            {
                for (var wj:int = 0; wj < w; wj++)
                {
                    var piece:Group = new Group(new Piece(
                        [edges[2 * hj + 1][wj + 1],
                        edges[2 * hj][wj],
                        edges[2 * hj + 1][wj],
                        edges[2 * hj + 2][wj]], wj, hj));
                        
                    piece.container = container;
                    piece.hitTestFunction = piecesHitTest;
                    
                    piece.x = Math.random() * 100 * w + 50;
                    piece.y = Math.random() * 100 * h + 50;
                    
                    container.addChild(piece);
                    groups.push(piece);
                    piece.id = groups.length - 1;
                }
            }
            
            container.scaleX = container.scaleY = 465 / 200 / Math.max(w, h);
            container.x = (465 - container.width) / 2;
            container.y = (465 - container.height) / 2;
        }
    }
    
}

import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.filters.DropShadowFilter;
import flash.geom.Point;
import flash.utils.ByteArray;
import flash.events.Event;

//ピースのグループ
class Group extends Sprite
{    
    public var id:int;
    public var container:Sprite;
    public var pieces:Array = new Array();
    public var hitTestFunction:Function;
    
    private var margin:Point = new Point();
    private var mousePressing:Boolean = false;
    
    public function Group(piece:Piece):void
    {
        addPiece(piece);
                
        addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
        addEventListener(MouseEvent.MOUSE_UP, mouseUp);
        addEventListener(Event.ENTER_FRAME, enterFrame);
    }
    
    //上下左右どこにつくか
    public function check(pieceA:Piece, pieceB:Piece, groupB:Group):Boolean
    {
        var pA:Point = pieceA.localToGlobal(new Point(0, 0));
        var pB:Point = pieceB.localToGlobal(new Point(0, 0));
        
        var deg:Number = Math.atan2(pA.y - pB.y, pA.x - pB.x) * 180 / Math.PI;
        
        if (45 <= deg && deg < 135)
        {
            if (pieceA.indexX == pieceB.indexX && pieceA.indexY - 1 == pieceB.indexY)
            {
                groupB.addGroup(pieceB, this);
                
                return true;
            }
        }
        else if (-45 <= deg && deg < 45)
        {
            if (pieceA.indexX - 1 == pieceB.indexX && pieceA.indexY == pieceB.indexY)
            {
                groupB.addGroup(pieceB, this);
                
                return true;
            }
        }
        else if (-135 <= deg && deg < -45)
        {
            if (pieceA.indexX == pieceB.indexX && pieceA.indexY + 1 == pieceB.indexY)
            {
                groupB.addGroup(pieceB, this);
                
                return true;
            }
        }
        else
        {
            if (pieceA.indexX + 1 == pieceB.indexX && pieceA.indexY == pieceB.indexY)
            {
                groupB.addGroup(pieceB, this);
                
                return true;
            }
        }
        
        return false;
    }
    
    public function addGroup(origon:Piece, group:Group):void
    {
        for each(var piece:Piece in group.pieces)
        {
            addPiece(piece);
            
            piece.x = origon.x + (piece.indexX - origon.indexX) * 100;
            piece.y = origon.y + (piece.indexY - origon.indexY) * 100;
        }
    }
    
    public function addPiece(piece:Piece):void
    {
        addChild(piece);
        pieces.push(piece);
    }
    
    
    private function mouseDown(e:Event):void
    {
        mousePressing = true;
        margin.x = mouseX;
        margin.y = mouseY;
        
        container.setChildIndex(this, container.numChildren - 1);
    }
    
    private function mouseUp(e:Event):void
    {
        mousePressing = false;
        hitTestFunction(this, check);
    }
    
    private function enterFrame(e:Event):void
    {
        if (mousePressing)
        {
            x = (stage.mouseX - container.x) / container.scaleX - margin.x;
            y = (stage.mouseY - container.y) / container.scaleY - margin.y;
        }
    }
   
}

//ピース
class Piece extends Sprite
{
    public var indexX:int = 0;
    public var indexY:int = 0;
    
    public function Piece(edges:Array, ix:int, iy:int):void
    {
        var points:Array = [];
        
        points = points.concat(edges[0].getData(0));
        points = points.concat(edges[1].getData(1));
        points = points.concat(edges[2].getData(2));
        points = points.concat(edges[3].getData(3));
        
        //拡大、縮小で線の太さを変えない
        graphics.lineStyle(1, 0x000000, 1, true, "none", "round", "round");
        graphics.beginFill(0xF5F5F5);
        graphics.moveTo(points[0].x, points[0].y);
        
        for (var i:int = 1; i < points.length; i++)
        {
            graphics.lineTo(points[i].x, points[i].y);
        }
        
        graphics.endFill();
       
        indexX = ix;
        indexY = iy;
    }
}

//ピースの端
class Edge
{
    private static const HEAD:Array //出っ張りの頂点データ(半分)
        = [[new Point(0, 50), new Point( -5, 30), new Point( -5, 20), new Point( -2, 9), 
            new Point(2, 6), new Point(11, 13), new Point(21, 10), new Point(25, 5)]];
    private static const FLAT:Array //出っ張ってない頂点データ
        =[[new Point(0, -50), new Point(0, 50)], [new Point(50, 0), new Point( -50, 0)],
          [new Point(0, 50), new Point(0, -50)], [new Point( -50, 0), new Point(50, 0)]];
           
    private var data:Array;
    
    public var angle:int;
    
    public static function init():void
    {
        //対象コピー、回転させた頂点データを追加
        
        for (var i:int = 0; i < 8; i++)
        {
            HEAD[0].push(new Point(HEAD[0][7 - i].x, -HEAD[0][7 - i].y));
        }
        
        for (var xi:int = 1; xi < 4; xi++)
        {
            HEAD.push([]);
            
            for (var yi:int = 0; yi < 16; yi++)
            {
                HEAD[xi].push(new Point(HEAD[xi - 1][yi].y, -HEAD[xi - 1][yi].x));
            }
        }
    }
    
    //頂点データから出っ張りの形をランダムに変形する
    public function Edge(angle:int, type:Boolean = false)
    {
        this.angle = angle;
        
        if (type)
        {
            data = clone(FLAT[angle]);
        }
        else
        {
            var randomX:int = Math.random() * 10 - 5;
            var randomY:int = Math.random() * 10 - 5;
            
            data = clone(HEAD[angle]);
            
            for (var i:int = 3; i < 13; i++)
            {
                data[i].x += int(randomX + (Math.random() * 2 -1));
                data[i].y += int(randomY + (Math.random() * 2 -1));
            }
        }
    }
    

   public function getData(position:int):Array
   {
        var re:Array = clone(data);
        
        for (var i:int = 0; i < data.length; i++)
        {
            switch(position)
            {
                case 0:
                    re[i].x += 50;
                    break;
                case 1:
                    re[i].y -= 50;
                    break;
                case 2:
                    re[i].x -= 50;
                    break;
                case 3:
                    re[i].y += 50;
                    break;
            }
        }
        
        if (angle != position)
        {
            re.reverse();
        }
        
        return re;
    }
    
    //配列のコピーを作成
    private static function clone(source:Object):*
    {
        var ba:ByteArray = new ByteArray();
        ba.writeObject(source);
        ba.position = 0;
        return(ba.readObject());
    }
}