I apologize for all the code but I had a hard time describing what I was trying to do. I am creating a 2D grid for a tile map. The tiles (blocks) are broken up into say a 10x10 square of tiles called a chunk, the chunks form 10x10 squares to form regions, a 10x10 square of regions forms a world. The square side dimension is blockSize.
All coordinates are [X,Y], 0,0 is upper left, all scanning left to right and then top to down.
From Largest to Smallest: World -> Region -> Chunk -> Block.
This image shows how a World with a 2x2 Blocksize would be laid out:
The "Large" addresses show what each blocks address would be if it were in a single giant array instead of broken up into subunits.
At the end of this post I give a cliff notes version of the code I already have working. I can create all the structures. I have iterators set up (see code at end) so that each level can iterate only the level lower than it. My code can do the following:
// Create a world of 10x10 regions, each made up of 10x10 chunks, each made up of 10x10 tiles
World world = new World(blockSize);
// Address the upper left hand corner of the world. The first region's first chunk's first block.
Block block = World.Regions[0,0].Chunks[0,0].Blocks[0,0];
// Address a random chunk
Chunk chunk = World.Regions[1,2].Chunks[6,2];
// Iterate over the Block[,] grid of the given chunk from left to right, up to down
// This will give us every block in Region 1,2 Chunk 6,2
foreach (Block block in chunk) {}
// Address a random region
Region region = World.Regions[4,5];
// Iterate over the Chunk[,] grid of the given region from left to right, up to down
// This will give us every Chunk in Region 4,5
foreach (Chunk chunk in region) {}
// In World, iterate over the Region[,] grid from left to right, up to down
// This will give us every Region in the World
foreach (Region region in World.regions) {}
...
What I want is for my iterators to be able to iterate across two levels of my data. For example, given a Region, scan over all the chunks in that region and give me a list of all blocks in the whole thing. Or given a world, get all the chunks in the world. Or an enormous list of all the blocks in the entire World.
...
// Given a region, return all chunks in that region
foreach (Chunk chunk in region) {}
// Given a region, return all blocks in all chunks in that region
foreach (Block block in region.GetAllBlocks)
{
// Scan the chunks from left to right.
// In each chunk, scan the blocks from left to right.
// Cover every block in the region.
}
// Given a world, return all the regions
Region regionArray = World.region; // For clarity
foreach (Region region in regionArray ) {}
// Given a world, return all the chunks in all the regions
Region regionArray = World.region; // For clarity
foreach (Chunk chunk in regionArray.GetAllChunks)
{
// Scan the regions from left to right, up to down.
// In each region, scan the chunks from left to right, up to down.
}
// Given a world, return all the blocks in all the chunks in all the regions
Region regionArray = World.region; // For clarity
foreach (Block block in regionArray.GetAllBlocks)
{
// Scan the regions from left to right, up to down.
// In each region, scan the chunks from left to right, up to down.
// In each chunk, scan the blocks from left to right, up to down.
}
...
How do I write my iterator code so I generate the lists I want? This is the first time I've done anything complex with iterators and I'm having a lot of trouble with figuring out how to do it. Is trying this sort of thing a good idea? Is it efficient/inefficient?
Here is a trimmed down version of what my code looks like to show what I have working:
/* **************************************** */
class Block
{
int blockX, blockY; // Grid X/Y of this block in it's chunk
public Block(blockX, blockY)
{
this.blockX = blockX;
this.blockY = blockY;
}
} // Block Class
/* **************************************** */
class Chunk : IEnumerator, IENumerable
{
int chunkX, chunkY; // X/Y of the chunk in it's region
int blockSize;
int containsBlocks;
public Block[,] blocks;
int enumeratorIndex = -1;
public Chunk(int chunkX, int chunkY, int blockSize)
{
this.chunkX = chunkX;
this.chunkY = chunkY;
this.blockSize = blockSize;
this.containsBlocks = blockSize * blockSize;
blocks = new Block[blockSize, blockSize];
for (int x = 0; x < blockSize; ++x)
{
for (int y = 0; y < blockSize; ++y)
{
blocks[x, y] = new Block(x, y);
}
}
} // constructor
public IEnumerator GetEnumerator()
{
return (IEnumerator)this;
}
public bool MoveNext()
{
enumeratorIndex++;
return (enumeratorIndex < this.containsBlocks);
}
public void Reset() { enumeratorIndex = 0; }
public object Current
{
get
{
int y = enumeratorIndex / blockSize;
int x = enumeratorIndex % blockSize;
return blocks[x, y];
}
}
} // Chunk Class
/* **************************************** */
class Region : IEnumerator, IENumerable
{
int regionX, int regionY; // X/Y of region in the world
int blockSize;
int containsBlocks;
public Chunks[,] chunks;
int enumeratorIndex = -1;
...
Same kind of constructor to setup the Chunks[,] array, but this time the iterator is
...
public object Current
{
get
{
int y = enumeratorIndex / blockSize;
int x = enumeratorIndex % blockSize;
return Chunks[x, y];
}
}
} // Region Class
/* **************************************** */
class World : IEnumerator, IENumerable
{
public Region[,] regions; // There is only one world, here are it's regions
int blockSize;
...
etc
...
public object Current
{
get
{
int y = enumeratorIndex / blockSize;
int x = enumeratorIndex % blockSize;
return Region[x, y];
}
}
}
Related
I'm a fan of Minecraft's old terrain generation with amazing overhangs, mountains and generally interesting worlds. My problem is that right now I'm using perlin noise, which while good for smooth terrain doesn't really give sporadic jumps that would allow mountains in a mostly flat area.
On top of that with the method I'm using gets 2d perlin noise, puts it in an array and then gets every Y value under it and sets it to a block; This stops generation of overhangs like this: Old Minecraft Terrain Image
Right now I have this:
public class GenerateIdMap : MonoBehaviour {
[Serializable] public class IBSerDict : SerializableDictionaryBase<int, byte> {};
public int size = 60;
public int worldHeight = 3;
public float perlinScale = 15f;
public int seed;
public int heightScale = 10;
public int maxHeight = 256;
public IBSerDict defaultBlocks = new IBSerDict();
void Start()
{
if (seed != 0) seed = (int)Network.time * 10;
CreateMap();
}
byte[,,] CreateMap()
{
byte[,,] map = new byte[size, maxHeight, size];
for (int x = 0; x < size; x++)
{
for (int z = 0; z < size; z++)
{
int y = (int)(Mathf.PerlinNoise((x + seed) / perlinScale, (z + seed) / perlinScale) * heightScale) + worldHeight;
y = Mathf.Clamp(y, 0, maxHeight-1);
while (y > 0)
{
map[x, y, z] = GetBlockType(y);
y--;
}
}
}
return map;
}
byte GetBlockType(int y)
{
SortedDictionary<int, byte> s_defaultBlocks = new SortedDictionary<int, byte>(defaultBlocks);
foreach (var item in s_defaultBlocks.OrderBy(key => key.Key))
{
if (y <= item.Key)
{
print(item.Value);
return item.Value;
}
}
return 0;
} }
The GetBlockType function is new and for getting the default block at that height, I'll fix it up later but it works for now. If you instantiate a prefab at that vector3 you would see terrain. Can someone help me figure out how to make better terrain? Thanks in advance!
Both of your problems should be tackled individually.
The first issue regarding the lack of variation in the generated values can usually be fixed in one of two ways, the first way is to modify the input into the perlin noise, i.e. the octaves and persistance and the second is to mix the output of multiple functions and even use the output of one function as the input to another. By functions, I mean Perlin/Simplex/Voronoi etc.
With the former method, as you mentioned, it can be pretty difficult to get terrain with interesting features over a large area (the generated values are homogeneous), but by playing with the coordinate range and octaves/persistance, it can be possible. The second approach is probably recommended however, because by mixing the inputs and outputs of different functions you can get some really interesting shapes (Voronoi produces circular crator-like shapes).
In order to fix the problem you are having with the overhangs, you would need to change your approach to generating the world slightly. Currently, you are just generating the height values of the terrain and assigning each of those values to give you the terrain surface only. What you ideally would want to do is, generate a pseudo-random value to use as a pass flag for each of the blocks in the 3d space (also those underground). The flag would indicate whether a block should be placed or not in the 3d world.
This is slower, but would generate caves and overhangs as you need.
I'm doing a text based game for a school project and I see myself stuck with a quite stupid problem.
The concept is simple, there's a map, a player, some monsters and some items.
For the map data structure I decided to use a 2d array of char's that have a unicode for content.
On top of this, I have a camera, which has a radius. The player never moves on screen, it has a x and y, but what has motion on screen is the camera itself. This works quite fine except when I get to the corners or any outside wall.
I get my camera doing this
int size = cameraSize/2;
int top = player.GetY() - size, bottom = player.GetY() + size;
char[,] camera = new char[cameraSize, cameraSize];
Console.SetCursorPosition(0,0);
for (int i = top; i < bottom; i++)
{
for (int j = top; j < bottom; j++)
{
camera[i, j] = map.ReMapPosition(i, j);
Console.Write(camera[i,j]);
}
Console.Write("\n");
}
Console.SetCursorPosition(cameraSize,cameraSize);
Console.Write(player.GetPlayerChar());
My 'cameraSize' is declared on the beginning of the class and is filled when the constructor is called
private int cameraSize;
cameraSize = difficulty.GetCameraSize();
The class 'difficulty' is irrelevant for my problem.
My problem itself is that I can't make the player positioned on the center when I get to the border walls as there is nothing to get from the array, since these are negative positions.
There are two approaches to this sort of problem.
1) In your loop, check if a value if out of range and output the value by hand.
2) Wrap your array in a custom class which ignores out of range values.
Something like this:
class MyWrapper
{
private readonly char[,] data;
public MyWrapper(char[,] data)
{
this.data=data;
}
private bool InRange(int x, int y)
{
return x >= 0 && y >= 0 && x < data.GetLength(0) && y < data.GetLength(1);
}
public char this[int x, int y]
{
get
{
return InRange(x,y) ? data[x,y] : ' ';
}
set
{
if(InRange(x,y)) data[x,y] = value;
}
}
}
My recommendation is for set to throw an exception when called on out of range values, but my example swallows the failure instead.
C# can't "retrieve values that aren't there", but you do have a couple options
Check to see if you are trying to get a negative position, or a position that's too big, and return a space
Or
Increase the size of the array by cameraSize/2 on all sides, which would effectively increase both the width and the height by cameraSize, and then make it to where your player can only move around in the coordinates (cameraSize/2,cameraSize/2) and (mapWidth-cameraSize/2,mapHeight-cameraSize/2). (<-- The coordinates might by (y,x) because of how 2d arrays work and depending on how your code is written). That way, the camera always has a padding around it, so there shouldn't ever be negative indicies
I want to check if a Rectangle(A Player) , intersects with one of the rectangles in a list (List).
I am currently using a for loop , which makes it slow , and poor performance.
for (int i = 0; i < gameObjects.objectList.Count; i++)
{
if (gameObjects.objectList[i].IntersectsWith(gameObjects.player))
{
gameObjects.objectList.RemoveAt(i); // if the player collided with an object, remove that object
}
}
How can I make it more efficient / is there another way to do it faster?
You can try organize your rectangles in a structure called k-d-tree.
It gives you up to O(log N) complexity in a large rectangle array (> 100).
F.e. make binary tree with fixed length, say, 2. Divide your space into left and right halves, then each half divide into top and bottom quarters (and so on).
Inside leaf node create a list on rectangles. If a rectangles falls into left half and top quarter, locate it in the list of this quarter.
A rectangle may be locates in a few list at the same time (f.e. in case if it falls in left and right halves).
To check intersection you should test rectangle in responding halves and quarters.
Or, you removes too many rectangles, it's faster to copy remaining rectangles into the new list in your own code.
Small example.
public enum CheckBy
{
Horizontal,
Vertical
}
public class Node
{
public Node First { get; set; }
public Node Second { get; set; }
public int Coordinate { get; set; }
public CheckBy CheckBy { get; set; }
public List<Rectangle> Rectangles { get; set; }
}
public bool IsRectangleInFist(Node node, Rectangle rectangle)
{
if (node.CheckBy == CheckBy.Horizontal)
return rectangle.Left <= node.Coordinate;
return rectangle.Top <= node.Coordinate;
}
public bool IsRectangelInSecond(Node node, Rectangle rectangle)
{
if (node.CheckBy == CheckBy.Horizontal)
return rectangle.Right >= node.Coordinate;
return rectangle.Bottom >= node.Coordinate;
}
public void AddRectangleInSuitableNode(Node node, Rectangle rectangle)
{
if (InRectangleInFirst(node, rectangle))
AddRectangleInSuitableNode(node.First, rectangle);
if (InRectangleInSecond(node, rectangle))
AddRectangleInSuitableNode(node.Second, rectangle);
}
public void SearchIntersectedRectangles(Node node, Rectangle rectangle, List<Rectangles> result)
{
// If not-leaf node
if (node.Rectangles == null && node.First != null && node.Second != null)
{
if (IsRectangleInFirst(node, rectangle))
SearchIntersecatedRectangles(node.First, rectangle, result);
if (IsRectangleInSecond(node, rectangle))
SearchIntersecatedRectangles(node.Second, rectangle, result);
return;
}
result.AddRangle(Rectangles.Where(r => r.IsIntersect(rectangle)));
}
These all lines makes simple 2D-tree. First, make the tree:
// Say, all rectangles would be inside this "space"
const int leftest = -1000;
const int rightest = 1000;
const int bottomest = -1000;
const int toppest = 1000;
// Tree with depth == 2
var tree = new Node
{
CheckBy = CheckBy.Hozirontal,
Coordinate = (leftest + rightest)/2,
First = new Node
{
CheckBy = CheckBy.Vertical,
Coordintate = (toppest + bottomest)/2,
Rectangles = new List<Rectangle>(),
},
Second = new Node
{
CheckBy = CheckBy.Vertical,
Coordintate = (toppest + bottomest)/2,
Rectangles = new List<Rectangle>(),
},
}
Then, sort all rectangles in this tree:
foreach (var rectangle in rectangles)
AddRectangleInSuitableNode(tree, rectangle);
Now you can fast get intersecting rectangles:
var intersecting = new List<Rectangles>();
SearchIntersecatedRectangles(tree, targetRectangle, intersecting);
// Here you can remove intersecting rectangles...
Basically, you need to stop checking all of the rectangles every time. You need to somehow figure out which rectangles are located in the vicinity of the player.
You could use some kind of spatial grid to store your rectangles so you could quickly find adjacent rectangles to be checked for collision. See this tutorial for example: N Tutorial B - Broad-Phase Collision.
I doubt it will be faster but you can always do it with Ling in a one liner:
gameObjects.objectList = gameObjects.objectList
.Select(go => go)
.Where(go => !go.IntersectsWith(gameObjects.player))
.ToList();
This essentially sets the list to one where any gameObject that collides with player is removed.
Also note that it is usually faster to process a sorted list first, so doing this:
gameObjects.objectList = gameObjects.objectList
.OrderBy(go => go.X)
.ThenBy(go => go.Y)
.ToList();
may help speed things up a bit. Doing this ordering every frame will be slow though so it will be worth ordering the objects as they are added to the list.
I am working on a graph problem for months now and I am at a point where I am looking for new input from others.
I spend hours in the library and hit the books. I am pretty confident that I found a solution but maybe someone here can give me a new perspective.
Problem:
I have a 2d plane and rectangles on it. Rectangles can overlap each other and I have to find an order of these rectangles. To visualize imagine windows on your screen and you have to find an order so that it looks right.
To give you a picture of it this may be one output:
Given:
A function that decides whether two rectangles overlap each other
public bool overlap(Rect a, Rect b) {...}
A function that given 2 rectangles overlap, decides which is has to be drawn first
//returns [1 => a before b, -1 => b before a, 0 => a & b have no "before (<)" relation]
public int compare(Rect a, Rect b) {...}
Rectangle entities with
int x,y,width,height
Screen width and height
int screen.width, int screen.height
runtime complexity of these two functions can be neglected for the solution of this problem.
The problem can be abstracted to a dependency graph in which I want to find a correct evaluation order. The rectangles are nodes and the isBefore relation specifies arcs between the nodes. The graph can have multiple connected components as shown in the pictures. So just throwing Sort over all nodes will not do. Note: compare avoids circular dependencies, so the graph will remain acyclic. So the good news is : and order actually exists, yayy!
Now here comes the hard part:
How do I find the dependencies as fast as possible in order to build the graph and run a topological sorting algorithm on it.
The most naive and worst way to do it is to just execute compare for each object on each object thus ending up with O(n²) complexity. But this is just not acceptable here since I may have thousands of these rectangles on the screen.
So how do I minimize the number of nodes I have to compare a node with in oder to find all dependencies?
Now here is my solution. Maybe you should read this after finding something yourself in oder to avoid to be biased.
First of all the problem can be simplified by taking away 1 dimension. The problems will still be isomorphic but much easier to understand, at least for me.
So let's just take lines(rectangles) on a big line(screen). A line has a position and a length. Lines that overlap build a connected component.
Since we have a finite amount of lines we can find the smallest line
of our set of lines in O(n).
In order for 2 lines to overlap their maximum distance is just the length of our smallest line. Anything above can't overlap with us.
We divide the screen by the size of the smallest line and end up with discrete chunks. We create a HashMap and a bucket for each chunk. We can now sort a line into these buckets.
we run over the set again O(n) and can decide very easy in which buckets we have to put our line. position % smallest.length = i and (position + length) % smallest.length = j will give the indicies of our HashMap. We sort the line into our HashMap from bucket[i] to bucket[j].
We have now minimized the set of lines we have to compare a line with in order to find all its dependencies. After doing this for all lines we only have to compare a line with all other lines in bucket[i-1] to bucket[j+1]. Any other line would be fo far away from us to overlap anyways. The modulo operation is efficent. The additional memory for the buckets shouldn't be very much.
This is the best I came up with. Maybe someone here has a better solution.
Some observations:
dividing screen by size of smallest line would make the algorithm very unpredictable and is not even necessary. You can use any size of bucket and the algorithm would work.
inspecting bucket[i-1] to bucket[j+1] is not necessary, bucket[i] to bucket[j] is enough
Let A and B be rectangles, B is not wider than A. Then part of left or right edge of B lies on A or those rectangles do not overlap (will be used later).
So the algorithm I made:
For every rectangle calculate ranges of buckets it belongs to
(bucketXFrom, bucketXTo, bucketYFrom, bucketYTo). This is class
RectangleInBucket.
Sort them by (bucketXTo - bucketXFrom). As there are not so many buckets, it is basically one step radix sort.
For every rectangle, starting from those with smallest width, scan all buckets it belongs to. If there are rectangles, compare to them and save relations where exist. Save rectagle into buckets under left and right edge.
I made total number of buckets equal to number of rectangles, it seems to work best.
It is usually faster than naive algorithm, but not as much as one could expect. As rectagles can (and do) belong to many buckets, one relation is reexamined many times. This increases number of steps. Moreover it uses not so cheap structures for this deduplication. But number of compare calls is reduced easily by several folds. It pays off even when this call is dirt cheap and the difference increases when the compare fuction is not trivial. And finally the code:
public class Rectangle
{
public int x;
public int y;
public int width;
public int height;
}
/// <summary>
/// Creates array of objects
/// </summary>
protected T[] InitializeArray<T>(int length) where T : new()
{
T[] array = new T[length];
for (int i = 0; i < length; ++i)
{
array[i] = new T();
}
return array;
}
/// <summary>
/// Creates array of objects
/// </summary>
protected T[,] InitializeArray<T>(int length, int width) where T : new()
{
T[,] array = new T[length, width];
for (int i = 0; i < length; ++i)
{
for (int j = 0; j < width; ++j)
{
array[i, j] = new T();
}
}
return array;
}
protected class RectangleInBucket
{
public readonly Rectangle Rect;
public readonly int RecNo;
public readonly int bucketXFrom;
public readonly int bucketXTo;
public readonly int bucketYFrom;
public readonly int bucketYTo;
public RectangleInBucket(Rectangle rectangle, int recNo, int bucketSizeX, int bucketSizeY)
{
Rect = rectangle;
RecNo = recNo;// arbitrary number unique for this rectangle
bucketXFrom = Rect.x / bucketSizeX;
bucketXTo = (Rect.x + Rect.width) / bucketSizeX;
bucketYFrom = Rect.y / bucketSizeY;
bucketYTo = (Rect.y + Rect.height) / bucketSizeY;
}
}
/// <summary>
/// Evaluates rectagle wrapped in RectangleInBucket object against all rectangles in bucket.
/// Saves result into tmpResult.
/// </summary>
protected void processBucket(Dictionary<long, int> tmpResult, List<RectangleInBucket> bucket, RectangleInBucket rib)
{
foreach (RectangleInBucket bucketRect in bucket)
{
if (bucketRect.RecNo < rib.RecNo)
{
long actualCouple = bucketRect.RecNo + (((long)rib.RecNo) << 32);
if (tmpResult.ContainsKey(actualCouple)) { continue; }
tmpResult[actualCouple] = overlap(bucketRect.Rect, rib.Rect) ? compare(bucketRect.Rect, rib.Rect) : 0;
}
else
{
long actualCouple = rib.RecNo + (((long)bucketRect.RecNo) << 32);
if (tmpResult.ContainsKey(actualCouple)) { continue; }
tmpResult[actualCouple] = overlap(rib.Rect, bucketRect.Rect) ? compare(rib.Rect, bucketRect.Rect) : 0;
}
}
}
/// <summary>
/// Calculates all couples of rectangles where result of "compare" function is not zero
/// </summary>
/// <param name="ra">Array of all rectangles</param>
/// <param name="screenWidth"></param>
/// <param name="screenHeight"></param>
/// <returns>Couple of rectangles and value of "compare" function</returns>
public List<Tuple<Rectangle, Rectangle, int>> GetRelations(Rectangle[] ra, int screenWidth, int screenHeight)
{
Dictionary<long, int> tmpResult = new Dictionary<long, int>();
// the key represents couple of rectangles. As index of one rectangle is int,
// two indexes can be stored in long. First index must be smaller than second,
// this ensures couple can be inserted only once. Value of dictionary is result
// of "compare" function for this couple.
int bucketSizeX = Math.Max(1, (int)Math.Sqrt(screenWidth * screenHeight / ra.Length));
int bucketSizeY = bucketSizeX;
int bucketsNoX = (screenWidth + bucketSizeX - 1) / bucketSizeX;
int bucketsNoY = (screenHeight + bucketSizeY - 1) / bucketSizeY;
List<RectangleInBucket>[,] buckets = InitializeArray<List<RectangleInBucket>>(bucketsNoX, bucketsNoY);
List<RectangleInBucket>[] sortedRects = InitializeArray<List<RectangleInBucket>>(bucketsNoX);
for (int i = 0; i < ra.Length; ++i)
{
RectangleInBucket rib = new RectangleInBucket(ra[i], i, bucketSizeX, bucketSizeY);
sortedRects[rib.bucketXTo - rib.bucketXFrom].Add(rib);// basically radix sort
}
foreach (List<RectangleInBucket> sorted in sortedRects) // start with most narrow rectangles
{
foreach (RectangleInBucket rib in sorted) // all of one width (measured in buckets)
{
for (int x = rib.bucketXFrom; x <= rib.bucketXTo; ++x)
{
for (int y = rib.bucketYFrom; y <= rib.bucketYTo; ++y)
{
processBucket(tmpResult, buckets[x, y], rib);
}
}
for (int y = rib.bucketYFrom; y <= rib.bucketYTo; ++y)
{
buckets[rib.bucketXFrom, y].Add(rib); // left edge of rectangle
if (rib.bucketXFrom != rib.bucketXTo)
{
buckets[rib.bucketXTo, y].Add(rib); // right edge of rectangle
}
}
}
}
List<Tuple<Rectangle, Rectangle, int>> result = new List<Tuple<Rectangle, Rectangle, int>>(tmpResult.Count);
foreach (var t in tmpResult) // transform dictionary into final list
{
if (t.Value != 0)
{
result.Add(Tuple.Create(ra[(int)t.Key], ra[(int)(t.Key >> 32)], t.Value));
}
}
return result;
}
The most basic way of representing a quadrile plane (a bunch of squares) is to use a two-dimensional array.
In C# we declare this as int[,] and can make our plane as big as we want:
string[3,3] => tic-tac-toe board (or similar)
string[8,8] => chess or checkers board
To "move" an item on the plane, we would just asign it toa new "position"
//using our tic-tac-toe board:
string[0,0] = "x"; //top-left
string[1,1] = "o"; //middle-middle
//to move
string[0,1] = bN; //Black Knight's starting positon
string[2,2] = bN; //Black Knight moves
string[0,1] = String.Empty;
So, how would you represent a hexagonal plane (a bunch of hexagons) and how would movement from one position to the next be handled?
Note: This is not purely theoretical, as I have an idea for a little game in my head which would require this kind of movement, but I can't wrap my head around how it would be done. I've looked through some of the other questions here, but can't really find a good match...
I do not know if this is the optimal solution but what I would do is create a new class of board the board would be a collection of "cells" each cell would contain a pointer to each of it's neighboring cell (or null if the cell is on a edge). You could implement some iterators on the board class that would walk the cells.
You would have to treat it more like a List instead of a vector. But it is at least a start.
Another solution is set you board up like this
and still just use the [,] to access each cell but it will take a little more math to figure out if you are traversing cells (Up Right is [+1,-1], Right is [+1,0], Down Right is [0,+1], Down Left is [-1,+1], Left is [-1,0], Up Left is [0,-1])
EDIT
If you want vertical walls instead of a slant just take make your width(X) equal X + Y*2 then on each row make the current row number (y) and make the cells 0 to Y-y and X-y to X off limits.
Example:
const int X = 10;
const int Y = 10;
int grid[,] = new int[X+(2*Y), Y];
bool IsCellOffLimits(int x, int y)
{
return (x < Y-y || x > X-y || y < 0 || y > Y);
}
you waste a little memory space but it gives you a board like this
If you are Very Clever© you can just use the normal space but just have your code have anything in that Y-y or X-y range be on the opposite side of the board. But ill leave that code up to the reader.
Three directions, left/right, up/down, funny angle one way/other way.
public class Player
{
public int X { get; set; }
public int Y { get; set; }
public void MoveLeft() { X++; }
public void MoveRight() { X--; }
public void MoveUp() { Y++; }
public void MoveDown() { Y--; }
public void MoveFunny() { Y++; X++; }
public void MoveOtherFunny() { Y--; X--; }
}