[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);
}
這樣我們就有一個迷宮了耶...
但是這樣自己做迷宮會爽嗎? -- 不會!!
身為程式設計師, 怎麼可以自己畫迷宮呢, 當然這種事也是要交給電腦畫的, 不然我們會畫到搾乾...
所以採用簡單的迷宮劃分法:
於是在 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);
50x50 的迷宮也只要改兩個數字:
換 100x100 的迷宮....
這下子虛擬環境變得龜龜的了...