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

make maze - 迷路生成

ローグライク風の迷路を生成する

通路の作成方法が違いますが
http://wise9.jp/archives/6964
こちらを参考にしました
Get Adobe Flash player
by cpu_t 30 Mar 2012
    Embed
/**
 * Copyright cpu_t ( http://wonderfl.net/user/cpu_t )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/rCfp
 */

package 
{
    import flash.display.Sprite;
    import flash.geom.Rectangle;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    /**
     * 迷路生成関数
     * @author cput
     */
    public class Main extends Sprite 
    {
        
        public function Main()
        {
            var tf:TextField = new TextField();
            tf.autoSize = "left";
            tf.defaultTextFormat = new TextFormat("_typewriter", 10);
            addChild(tf);
            
            var maze:Array = makeMaze();
            for (var i:int = 0; i < maze.length; i++)
            {
                tf.appendText(maze[i].join("") + "\n");
            }
            
            addEventListener("click", function(e:*):void
            {
                maze = makeMaze();
                tf.text = "";
                for (i = 0; i < maze.length; i++)
                {
                    tf.appendText(maze[i].join("") + "\n");
                }
            });
        }
        
        //迷路を作成します
        public function makeMaze():Array
        {
            var WALL:* = "#";                        //壁として扱う定数
            var FLOOR:* = ".";                       //床として扱う定数
            var MAP_WIDTH:int = 64;                  //全体の横幅
            var MAP_HEIGHT:int = 32;                 //全体の縦幅
            var ROOM_SEPARATE_COLS:int = 3;          //全体を縦方向に区切る回数
            var ROOM_SEPARATE_ROWS:int = 2;          //全体を横方向に区切る回数
            var ROOM_SEPARATE_WIDTH_MIN:int = 8;     //部屋区切りの最小値
            var ROOM_MIN_SCALE:Number = 1/4;         //区切られた領域に対する部屋の最小の割合
            var ROOM_DROPOUT_PERCENT:Number = 1/3;   //部屋が作成されない確率
            
            //全体のデータを作成
            var maze:Array = [];
            for (var yy:int = 0; yy < MAP_HEIGHT; yy++)
            {
                maze[yy] = [];
                for (var xx:int = 0; xx < MAP_WIDTH; xx++)
                {
                    //とりあえず壁で埋める
                    maze[yy][xx] = WALL;
                }
            }
            
            //部屋を区切る
            var colsSeparatePoints:Array = [MAP_WIDTH];
            var separateCount:int = ROOM_SEPARATE_COLS;
            while (separateCount--)
            {
                //最大値を探す
                var maxIndex:int = 0;
                var maxWidth:int = 0;
                for (var iCheck:int = 0; iCheck < colsSeparatePoints.length; iCheck++)
                {
                    if (colsSeparatePoints[iCheck] > maxWidth)
                    {
                        maxIndex = iCheck;
                        maxWidth = colsSeparatePoints[iCheck];
                    }
                }
                //これ以上区切れない場合break
                if (maxWidth < ROOM_SEPARATE_WIDTH_MIN * 2) break;
                //区切り値を決定
                var separatePoint:int = 
                    ROOM_SEPARATE_WIDTH_MIN + 
                    Math.floor(Math.random() * (maxWidth - ROOM_SEPARATE_WIDTH_MIN * 2));
                //配列に挿入
                colsSeparatePoints.splice(maxIndex, 1, separatePoint, (maxWidth - separatePoint));
            }
            //部屋を区切る
            var rowsSeparatePoints:Array = [MAP_HEIGHT];
            separateCount = ROOM_SEPARATE_ROWS;
            while (separateCount--)
            {
                //最大値を探す
                maxIndex = 0;
                maxWidth = 0;
                for (iCheck = 0; iCheck < rowsSeparatePoints.length; iCheck++)
                {
                    if (rowsSeparatePoints[iCheck] > maxWidth)
                    {
                        maxIndex = iCheck;
                        maxWidth = rowsSeparatePoints[iCheck];
                    }
                }
                //これ以上区切れない場合break
                if (maxWidth < ROOM_SEPARATE_WIDTH_MIN * 2) break;
                //区切り値を決定
                separatePoint = 
                    ROOM_SEPARATE_WIDTH_MIN + 
                    Math.floor(Math.random() * (maxWidth - ROOM_SEPARATE_WIDTH_MIN * 2));
                //配列に挿入
                rowsSeparatePoints.splice(maxIndex, 1, separatePoint, (maxWidth - separatePoint));
            }
            var colsLength:int = colsSeparatePoints.length;
            var rowsLength:int = rowsSeparatePoints.length;
            
            //部屋を作成する
            var separateBlocks:Array = [];           //区切りエリア
            var rooms:Array = [];                    //中に作成する部屋
            for (var iRows:int = 0; iRows < rowsLength; iRows++)
            {
                separateBlocks[iRows] = [];
                for (var iCols:int = 0; iCols < colsLength; iCols++)
                {
                    //区切りエリアを作成する
                    var areaRect:Rectangle = new Rectangle();
                    areaRect.x = iCols == 0 ? 0 : separateBlocks[iRows][iCols - 1].right;
                    areaRect.y = iRows == 0 ? 0 : separateBlocks[iRows - 1][iCols].bottom;
                    areaRect.width = colsSeparatePoints[iCols];
                    areaRect.height = rowsSeparatePoints[iRows];
                    separateBlocks[iRows][iCols] = areaRect;
                    
                    //部屋を作成する
                    var roomRect:Rectangle = new Rectangle();
                    var roomWidthMin:int = Math.floor(areaRect.width * ROOM_MIN_SCALE);
                    var roomWidthMax:int = areaRect.width - 3;
                    var roomHeightMin:int = Math.floor(areaRect.height * ROOM_MIN_SCALE);
                    var roomHeightMax:int = areaRect.height - 3;
                    //部屋が作成されない場合
                    if (Math.random() < ROOM_DROPOUT_PERCENT)
                    {
                        roomRect.width = 1;
                        roomRect.height = 1;
                    }
                    else
                    {
                        roomRect.width = roomWidthMin + Math.floor(Math.random() * (roomWidthMax - roomWidthMin + 1));
                        roomRect.height = roomHeightMin + Math.floor(Math.random() * (roomHeightMax - roomHeightMin + 1));
                    }
                    //部屋の大きさから座標を算出する
                    roomRect.x = areaRect.x + 1 + Math.floor(Math.random() * (roomWidthMax - roomRect.width));
                    roomRect.y = areaRect.y + 1 + Math.floor(Math.random() * (roomHeightMax - roomRect.height));
                    
                    rooms.push({
                        x:iCols,
                        y:iRows,
                        rect:roomRect,
                        connection:0
                    });
                }
            }
            
            //通路を設置する箇所を決定する
            var connections:Array = [];
            var disconnections:Array = rooms.concat();
            //基準となる部屋をランダムに選ぶ
            xx = Math.floor(Math.random() * colsLength);
            yy = Math.floor(Math.random() * rowsLength);
            var room:Object/*x,y,rect,connection*/ = getRoom(xx, yy);
            connections.push(room);
            disconnections.splice(disconnections.indexOf(room), 1);
            while (disconnections.length > 0)
            {
                //接続済みの部屋からランダムに一つ選ぶ
                room = connections[Math.floor(Math.random() * connections.length)];
                //新しい接続方向を選ぶ
                var newConnection:int = 1 << Math.floor(Math.random() * 4);
                //既に接続されていたらやり直し
                if ((room.connection & newConnection) != 0) continue;
                //接続先の部屋を取得
                var neighbor:Object/*x,y,rect,connection*/;
                if (newConnection == 1)
                    neighbor = getRoom(room.x + 1, room.y);
                else if (newConnection == 2)
                    neighbor = getRoom(room.x, room.y + 1);
                else if (newConnection == 4)
                    neighbor = getRoom(room.x - 1, room.y);
                else if (newConnection == 8)
                    neighbor = getRoom(room.x, room.y - 1);
                //隣に部屋が存在しなかったらやり直し
                if (neighbor == null) continue;
                //接続情報を更新
                room.connection |= newConnection;
                if (newConnection == 1) neighbor.connection |= 4;
                else if (newConnection == 2) neighbor.connection |= 8;
                else if (newConnection == 4) neighbor.connection |= 1;
                else if (newConnection == 8) neighbor.connection |= 2;
                //接続先の部屋が未接続のところだった場合未接続リストから接続済みリストに移動
                var disIndex:int = disconnections.indexOf(neighbor);
                if (disIndex != -1)
                {
                    disconnections.splice(disIndex, 1);
                    connections.push(neighbor);
                }
            }
            
            //部屋のくり抜き
            for each(room in rooms)
            {
                roomRect = room.rect;
                //部屋のくり抜き
                for (yy = roomRect.y; yy < roomRect.bottom; yy++)
                for (xx = roomRect.x; xx < roomRect.right; xx++)
                {
                    maze[yy][xx] = FLOOR;
                }
                //通路のくり抜き
                //右方向に接続
                if (room.connection & 1)
                {
                    neighbor = getRoom(room.x + 1, room.y);
                    //部屋から通路が出る位置を決定
                    var bridgePos:int = room.rect.top + Math.floor(Math.random() * room.rect.height);
                    //通路が隣の部屋に入る位置を決定
                    var goalPos:int = neighbor.rect.top + Math.floor(Math.random() * neighbor.rect.height);
                    //通路同士の接続位置を決定
                    var bridgeTurnPos:int = room.rect.right + 1 + Math.floor(Math.random() * (neighbor.rect.left - room.rect.right - 2));
                    //くり抜き作業
                    for (xx = room.rect.right; xx <= bridgeTurnPos; xx++)
                    {
                        maze[bridgePos][xx] = FLOOR;
                    }
                    for (xx = bridgeTurnPos; xx < neighbor.rect.left; xx++)
                    {
                        maze[goalPos][xx] = FLOOR;
                    }
                    var turnEndPos:int = bridgePos > goalPos ? bridgePos : goalPos;
                    for (yy = (bridgePos > goalPos ? goalPos : bridgePos) + 1; yy < turnEndPos; yy++)
                    {
                        maze[yy][bridgeTurnPos] = FLOOR;
                    }
                }
                //下方向に接続
                if (room.connection & 2)
                {
                    neighbor = getRoom(room.x, room.y + 1);
                    //部屋から通路が出る位置を決定
                    bridgePos = room.rect.left + Math.floor(Math.random() * room.rect.width);
                    //通路が隣の部屋に入る位置を決定
                    goalPos = neighbor.rect.left + Math.floor(Math.random() * neighbor.rect.width);
                    //通路同士の接続位置を決定
                    bridgeTurnPos = room.rect.bottom + 1 + Math.floor(Math.random() * (neighbor.rect.top - room.rect.bottom - 2));
                    //くり抜き作業
                    for (yy = room.rect.bottom; yy <= bridgeTurnPos; yy++)
                    {
                        maze[yy][bridgePos] = FLOOR;
                    }
                    for (yy = bridgeTurnPos; yy < neighbor.rect.top; yy++)
                    {
                        maze[yy][goalPos] = FLOOR;
                    }
                    turnEndPos = bridgePos > goalPos ? bridgePos : goalPos;
                    for (xx = (bridgePos > goalPos ? goalPos : bridgePos) + 1; xx < turnEndPos; xx++)
                    {
                        maze[bridgeTurnPos][xx] = FLOOR;
                    }
                }
            }
            
            return maze;
            
            function getRoom(xx:int, yy:int):Object/*x,y,rect,connection*/
            {
                for each(var room:Object/*x,y,rect,connection*/ in rooms)
                {
                    if (room.x == xx && room.y == yy) return room;
                }
                return null;
            }
        }
        
    }
    
}