[Robotics Studio] 開始建造迷宮 -- Day25

  • 10538
  • 0
  • 2009-02-17

[Robotics Studio] 開始建造迷宮 -- Day25

話說教會機器人走迷宮是我學習 Robotics Studio 的小小夢想之一.

要學習走迷宮, 第一步需要有迷宮才行.

無奈, 之前的 Visual Simulation Enviroment Express version 的 64 個實體物件嚇到我了...

如何用 64 個實體來建造偉大的迷宮, 我一直是覺得很困難的一件事情...

直到, 我發現這個 64 個 Entities 的限制是 -- 直接放入 VSE World 的實體數量.

也就是說, 如果我把整個迷宮看成是一個 Entity 物件, 不就解決這樣的問題了嗎?!

所以, 可以透過實做 MultiShapeEntity 這個 class 來達成夢想的第一步了!

using System;
using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.Ccr.Core;
using Microsoft.Dss.Core.Attributes;
using Microsoft.Dss.ServiceModel.Dssp;
using Microsoft.Dss.ServiceModel.DsspServiceBase;
using W3C.Soap;
using submgr = Microsoft.Dss.Services.SubscriptionManager;
using engine = Microsoft.Robotics.Simulation.Engine.Proxy;
using Microsoft.Robotics.Simulation.Engine;
using Microsoft.Robotics.Simulation.Physics;
using Microsoft.Robotics.PhysicalModel;


namespace MazeGenerator
{
    public class MazeEntity : MultiShapeEntity
    {
        /// <summary>
        /// Default constructor
        /// </summary>
        public MazeEntity() { }

        /// <summary>
        /// Initialization constructor
        /// </summary>
        /// <param name="shape"></param>
        /// <param name="initialPos"></param>
        public MazeEntity(Vector3 initialPos, byte[,] blocks, float mazeheight, float blocksize)
        {
            for (int x = 0; x < blocks.GetLength(0); x++)
            {
                for (int y = 0; y < blocks.GetLength(1); y++)
                {
                    if (blocks[x,y] == 0)
                        continue;

                    var boxshape = new BoxShape(
                        new BoxShapeProperties(
                        1, // mass in kilograms.
                        new Pose(new Vector3(initialPos.X + x * blocksize, initialPos.Y + mazeheight / 2, initialPos.Z - y*blocksize)), // relative pose
                        new Vector3(blocksize, mazeheight, blocksize)));

                    this.BoxShapes.Add(boxshape); // dimensions
                }
            }

            this.State.MassDensity.Mass = blocks.GetLength(0)*blocks.GetLength(1);
        }
    }
}

 

上面這個迷宮需要傳入一個 2 維 byte 陣列,
主要的想法當然還是參考自古早的 RPG Game 都是利用 block 的方式來產生世界的.

然後寫一個 DSS Service (參考之前的 DSS with VSE 吧), 這次我們加入一個 MazeEntity :

void AddMaze()
{
    // create simple movable entity, with a single shape
    maze = new MazeEntity(
    new Vector3(3f, 0f, 3f), new byte[,] {
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    {0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,1},
    {1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,1},
    {1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,1,0,1},
    {1,0,1,0,1,1,1,1,1,1,1,0,1,0,0,0,0,1,0,1},
    {1,0,0,0,0,0,0,0,0,1,0,0,1,1,1,1,0,0,0,1},
    {1,1,1,1,0,1,1,1,0,1,1,0,1,0,0,0,0,1,0,1},
    {1,0,0,1,0,1,0,0,0,0,1,0,1,1,0,1,0,1,0,1},
    {1,0,0,1,0,1,1,1,1,0,1,0,1,0,0,1,0,1,0,1},
    {1,0,0,1,0,0,0,0,1,0,1,0,1,0,0,1,0,1,0,1},
    {1,0,0,1,1,1,1,0,1,0,1,0,1,1,1,1,0,1,0,1},
    {1,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,0,1},
    {1,0,1,1,0,1,1,0,1,0,1,0,1,0,0,1,1,1,0,1},
    {1,0,1,0,0,1,1,0,1,1,1,1,1,0,0,0,0,1,0,1},
    {1,0,1,0,0,1,1,0,0,0,0,0,0,0,1,1,0,1,0,1},
    {1,0,0,0,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,1},
    {1,0,1,1,1,0,0,0,0,0,0,1,0,1,0,0,0,1,0,1},
    {1,0,1,0,0,0,0,1,1,1,0,0,0,0,1,1,0,1,1,1},
    {1,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0},
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, }, 3.0f, 3.0f);

    // Name the entity. All entities must have unique names
    maze.State.Name = "maze";

    // Insert entity in simulation.
    SimulationEngine.GlobalInstancePort.Insert(maze);
}

 

這樣我們就有一個迷宮了耶...

image

但是這樣自己做迷宮會爽嗎? -- 不會!!

身為程式設計師, 怎麼可以自己畫迷宮呢, 當然這種事也是要交給電腦畫的, 不然我們會畫到搾乾...

所以採用簡單的迷宮劃分法:

image

於是在 MazeEntity 當中加入了亂數產生迷宮的程式:

public static byte[,] GenerateMaze(int width, int height)
{
    byte[,] result = new byte[width, height];
    Random r = new Random();
    for (int x = 0; x < width; x++)
        for (int y = 0; y < height; y++)
            result[x, y] = (byte)(((x == 0) || (y == 0) || (x == width - 1) || (y == height - 1)) ? 1 : 0);

    splitMazeChamber(r, result, 0, 0, width - 1, height - 1);

    // dig a hole of left-top, and right-down
    result[0, 1] = 0;
    result[width - 1, height - 2] = 0;

    return result;
}

private static void splitMazeChamber(Random r, byte[,] maze, int left, int top, int right, int bottom)
{
    int wallX = 0;
    if (right - left > 3)
        wallX = r.Next(left + 2, right - 1);

    int wallY = 0;
    if (bottom - top > 3)
        wallY = r.Next(top + 2, bottom - 1);

    if ((wallX > 0) && (wallY > 0))
    {
        for (int i = top + 1; i < bottom; i++)
            maze[wallX, i] = 1;
        for (int i = left + 1; i < right; i++)
            maze[i, wallY] = 1;

        List<KeyValuePair<int, int>> holes = new List<KeyValuePair<int, int>>();
        holes.Add(new KeyValuePair<int, int>(wallX, r.Next(top + 1, wallY - 1)));
        holes.Add(new KeyValuePair<int, int>(wallX, r.Next(wallY + 1, bottom - 1)));
        holes.Add(new KeyValuePair<int, int>(r.Next(left + 1, wallX - 1), wallY));
        holes.Add(new KeyValuePair<int, int>(r.Next(wallX + 1, right - 1), wallY));
        holes.RemoveAt(r.Next(0, 4));
        holes.ForEach(hole => maze[hole.Key, hole.Value] = 0);

        splitMazeChamber(r, maze, left, top, wallX, wallY);
        splitMazeChamber(r, maze, wallX, top, right, wallY);
        splitMazeChamber(r, maze, left, wallY, wallX, bottom);
        splitMazeChamber(r, maze, wallX, wallY, right, bottom);
    }
    else if (wallX > 0)
    {
        for (int i = top + 1; i < bottom; i++)
            maze[wallX, i] = 1;

        maze[wallX, r.Next(top + 1, bottom - 1)] = 0;

        splitMazeChamber(r, maze, left, top, wallX, bottom);
        splitMazeChamber(r, maze, wallX, top, right, bottom);
    }
    else if (wallY > 0)
    {
        for (int i = left + 1; i < right; i++)
            maze[i, wallY] = 1;

        maze[r.Next(left + 1, right - 1), wallY] = 0;

        splitMazeChamber(r, maze, left, top, right, wallY);
        splitMazeChamber(r, maze, left, wallY, right, bottom);
    }
}

 

先產生一個  10x10 的迷宮吧:

maze = new MazeEntity(new Vector3(3f, 0, 3f), MazeEntity.GenerateMaze(10,10), 3.0f, 1.0f);

image

50x50 的迷宮也只要改兩個數字:

image

換 100x100 的迷宮....
這下子虛擬環境變得龜龜的了...

image