I need some help with this problem I'm having of how to do Neighbor find of multiple quadtrees.
I have a cube. Each side of the cube is made of N individually colored quads (as you can see from the image below). Each one of these individually colored quads has the ability to perform a quadtree (recursively sub-divide itself into 4 smaller parts). In addition, every quad has a location attribute that can tell you its local and global {x,y,z} position. Also, all the quads are stored in a 1D array.
Now let's say for example I select a random quad Q1, and that quad splits into 4 children:
Q1 → (Q1)(1), (Q1)(2), (Q1)(3), (Q1)(4)
And then I select (Q1)(1) child to split into 4.
(Q1)(1) → ((Q1)(1))(1), ((Q1)(1))(2), ((Q1)(1))(3), ((Q1)(1))(4),
And now I would like to find all ((Q1)(1))(2) neighboring quads
In general, I'm asking How do I perform neighbor finding on this data structure so that I can find any neighboring quad of a child or parent quad at any level?
The way I'd go is by first renaming how you visualize your child nodes inside your quadtree. instead of using 1, 2, 3, 4 which can be confusing, we'll use NorthWest, NorthEast, SouthWest, SouthEast.
Then, you have to consider a list of neighbors for your node. Trivially, each node in the tree has 4 neighbors, that we will name South, North, East, West.
then you can use the following algorithm:
Entry: A node or a leaf 'N'
Entry: An empty list 'L' : [South = null, North = null, East = null, West = null]
=========
ParentNode = empty
childNode = N
while L not full && ChildNode.ParentNode != null:
ParentNode = ChildNode.ParentNode
if (childNode == ParentNode.SouthWest)
L.North = (L.North == null) ? ParentNode.North : L.North
L.East = (L.East == null) ? ParentNode.East : L.East
if (childNode == ParentNode.SouthEast)
L.North = (L.North == null) ? ParentNode.North : L.North
L.West = (L.West == null) ? ParentNode.West : L.West
if (childNode == ParentNode.NorthWest)
L.South = (L.South == null) ? ParentNode.South : L.South
L.East = (L.East == null) ? ParentNode.East : L.East
if (childNode == ParentNode.NorthEast)
L.South = (L.South == null) ? ParentNode.South : L.South
L.West = (L.West == null) ? ParentNode.West : L.West
childNode = ParentNode
that's just pseudo code on how i'd implement the search inside the quadtree.
In case there are some values inside the list that are still null (mainly because it was in the border of your tree), you will need to look at your data stored in the 1D array that we'll name 'A'.
To do that, you need to know the number of quads in your radius.
also, your quads need to have a function to search values provided a coordinate. named 'Search'.
then, you have to implement the following algorithm:
N = A.index(L) // should be known beforehand
C = sqrt(length(A))
if (L.East == null)
if (N + 1 < length(A))
L.East = A[N + 1].Search(L.position)
if (L.West == null)
if (N - 1 >= 0)
L.West = A[N - 1].Search(L.position)
if (L.North == null)
if (N + C < length(A))
L.North = A[N + C].Search(L.position)
if (L.South == null)
if (N - C >= 0)
L.South = A[N - C].Search(L.position)
there are a subtlety to consider though:
The search function should be able to return a quad despite the fact that the position provided is outside of it, if it is not possible, just change the value of the position that is outside to the value of the border of the quad.
And that's as far as I can go as I don't know how you implemented the different faces of your dice (or if there are any) or if you'd ever want to get those as neighbor as well.
Related
I'm working on some Physics stuff in Javascript and basically want to handle collided bodies as one.
I've got an array of all the collisions that occur, a collision consists of two bodies, body A and body B.
Let's say six collision occur, collisions between bodies:
X and Y
Y and Z
C and D
E and F
F and H
G and H
Now I want to merge all the bodies that are in some way connected into a single body. I want those merged bodies in a list. For example in this case I'd want a list that looks like this:
X, Y and Z (Because X collided with Y and Y collided with Z)
C and D (Because C collided with D)
E, F, G and H (Because E collided with F, F with H, and G with H)
Now I'm pretty sure there's some algorithm out there that I need, I just don't know where to look and I'm out of ideas to solve this myself.
How would you do this in real life?
I suppose I would read each rule. For each rule, I'd connect the two pieces. What I'd end up with is a collection of blobs. I could then walk each of the graphs to get the list of nodes in each one. Each "connected component" would be a "blob". Formalizing this algorithm a bit might give this:
// make the graph of connected components
nodes = map<symbol, pair<symbol, list<symbol>>>
for each (a, b) in rules do
if nodes[a] is null then nodes[a] = node(a, [b])
else nodes[a].connections.append(b)
if nodes[b] is null then nodes[b] = node(b, [a])
else nodes[b].connections.append(a)
loop
blobs = map<symbol, list<symbol>>
for each (a, b) in rules do
firstNode = nodes[a]
// do a DFS/BFS search starting from firstNode to find
// all nodes in the connected component. whenever you
// follow a link from a node, remove it from the node's
// list of links. this prevents ever searching from that
// node again since we know what component it's in already
// add each node to the list of symbols in blobs[a]
loop
In the first loop, we read each rule once, then do a constant amount of work, so it is O(n) time in the number of rules. It will store two connections for each rule and so is O(n) storage in terms of the number of rules.
In the second loop, we look at each rule and do a DFS or BFS for each rule's LHS symbol. However, note that the searches will only traverse any edge once, and so this is O(n) time in the number of rules. We will end up with some set of blobs the union of whose lists will be the set of symbols which is no more than the number of rules, so it's O(n) storage as well.
So we have an O(n) time, O(n) space complexity algorithm for determining the blobs. Can we do better, asymptotically speaking? Clearly we need to look at all n rules, so the time complexity is optimal. Also note that any solution to this problem must say for each symbol which blob that symbol ends up belonging to, so simply writing the answer down on the output tape takes O(n) space. So this should be optimal as well.
If you have an ADT (in this case a map) that contains all objects and you keep parent id to track object collisions you can handle each collision+merge in constant time.
// setup
var X = {id: 1, name:'X'};
var Y = {id: 2, name:'Y'};
var Z = {id: 3, name:'Z'};
var C = {id: 4, name:'C'};
var D = {id: 5, name:'D'};
var E = {id: 6, name:'E'};
var F = {id: 7, name:'F'};
var G = {id: 8, name:'G'};
var H = {id: 9, name:'H'};
var all = { 1:X, 2:Y, 3:Z, 4:C, 5:D, 6:E, 7:F, 8:G, 9:H };
// method to merge collided objects together
function collision(obj1, obj2) {
var p1 = obj1.parent;
var p2 = obj2.parent;
if(p1 === undefined && p2 === undefined) {
obj1.parent = obj1.id;
obj2.parent = obj1.id;
obj1.name += obj2.name;
delete all[obj2.id];
} else if(p1 !== undefined && p2 === undefined) {
obj2.parent = obj1.parent;
all[obj1.parent].name += obj2.name;
delete all[obj2.id];
} else if(p1 === undefined && p2 !== undefined) {
obj1.parent = obj2.parent;
all[obj2.parent].name += obj1.name;
delete all[obj1.id];
} else if(p1 !== undefined && p2 !== undefined && obj1.parent !== obj2.parent) {
if(all[obj1.parent] !== undefined) {
all[obj1.parent].name += all[obj2.parent].name;
delete all[obj2.parent];
} else if(all[obj2.parent] !== undefined) {
all[obj2.parent].name += all[obj1.parent].name;
delete all[obj1.parent];
}
}
}
// test
console.log(JSON.stringify(all));
collision(X, Y);
collision(Y, Z);
collision(C, D);
collision(E, F);
collision(F, H);
collision(G, H);
console.log(JSON.stringify(all));
collision(X, E);
console.log(JSON.stringify(all));
{"1":{"id":1,"name":"X"},"2":{"id":2,"name":"Y"},"3":{"id":3,"name":"Z"},"4":{"id":4,"name":"C"},"5":{"id":5,"name":"D"},"6":{"id":6,"name":"E"},"7":{"id":7,"name":"F"},"8":{"id":8,"name":"G"},"9":{"id":9,"name":"H"}}
{"1":{"id":1,"name":"XYZ","parent":1},"4":{"id":4,"name":"CD","parent":4},"6":{"id":6,"name":"EFHG","parent":6}}
{"1":{"id":1,"name":"XYZEFHG","parent":1},"4":{"id":4,"name":"CD","parent":4}}
I have a matrix (0 means nothing, 1 means terrain) that represents a level in my game. The matrix corresponds to a grid that my screen is broken up into, and indicates where my terrain goes.
My terrain is actually composed of 4 points in the corners of each block within the grid. When you have multiple blocks that are connected, I use a merge-cell algorithm that removes the duplicate points and any interior points. The result is that I end up with a list of points representing only the outer edges of the polygon.
To draw this polygon, I need the points to be in some sort of order (either clockwise or counter-clockwise) such that each point is followed by it's neighboring point. Obviously the first and last points need to be neighbors. Since this is all in a grid, I know the exact distance between neighboring points.
The problem is that I am having trouble coming up with an algorithm that allows me to "walk" around the edge of the polygon while putting the points in order. I believe there should be a way to utilize the fact that I have the matrix representing the geometry, meaning there is only 1 possible way to draw the polygon (even if it is concave).
I have tried several approaches using greedy-type algorithms, but can't seem to find a way to know, in every case, which direction I want to travel in. Given that any particular point can have up to 3 neighbors (the fourth isn't included because it is the "starting" point, meaning that I have already sorted it) I need a way of knowing which way to move.
Update
Another approach that I have been trying is to sort the points by their X (with tiebreaker of Y) which gives me the topmost/leftmost edge. It also guarantees that I am starting on an outer edge. However, I'm still struggling to find an algorithm that guarantees that I stay on the outside without crossing over.
Here is an example matrix:
0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 1 1 1 0 0
Which corresponds to this (black dots represent my points):
First of all please consider that for a general matrix the output can be composed of more than one closed loop; for example boundaries of the matrix
form three distinct loops, one of them placed inside another.
To extract these loops the first step is to build a map of all "walls": you have a vertical wall each time the content of one cell is different from the next cell on the same row; you have instead an horizontal wall when the content is different from the same cell in the next row.
data = [[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 1, 1, 1, 1, 0, 0, 0, 0, 0 ],
[ 0, 1, 0, 0, 1, 0, 1, 1, 0, 0 ],
[ 0, 1, 0, 0, 1, 0, 1, 1, 1, 0 ],
[ 0, 1, 1, 1, 1, 0, 0, 1, 1, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]]
rows = len(data)
cols = len(data[0])
walls = [[2*(data[r][c] != data[r][c+1]) + (data[r][c] != data[r+1][c])
for c in range(cols-1)]
for r in range(rows-1)]
In the example above I'm using two bits: 0x01 to mark horizontal walls and 0x02 to mark vertical walls. For a given (r, c) cell the walls are the right and bottom wall of the cell.
For simplicity I'm also assuming that the interesting areas are not touching the limits of the matrix; this can be solved by either adding extra rows and cols of zeros or by wrapping matrix access in a function that returns 0 for out-of-matrix virtual elements.
To build the list of boundaries you need to simply start from any point on a wall and move following walls, removing the walls from the map as you process them. When you cannot move any more a cycle has been completed (you're guaranteed to complete cycles because in a graph built in this way from a matrix of inside/outside flags the degree is guaranteed to be even in all vertices).
Filling all those cycles simultaneously using odd-even filling rules is also guaranteed to reproduce the original matrix.
In the code following I'm using r and c as row/col index and i and j instead to represent points on the boundary... for example for cell (r=3, c=2) the schema is:
where the red wall corresponds to bit 0x02 and the green wall to bit 0x01. The walls matrix has one row and one column less than the original data matrix because it's assumed that no walls can be present on last row or column.
result = []
for r in range(rows-1):
for c in range(cols-1):
if walls[r][c] & 1:
i, j = r+1, c
cycle = [(i, j)]
while True:
if i < rows-1 and walls[i][j-1] & 2:
ii, jj = i+1, j
walls[i][j-1] -= 2
elif i > 0 and walls[i-1][j-1] & 2:
ii, jj = i-1, j
walls[i-1][j-1] -= 2
elif j < cols-1 and walls[i-1][j] & 1:
ii, jj = i, j+1
walls[i-1][j] -= 1
elif j > 0 and walls[i-1][j-1] & 1:
ii, jj = i, j-1
walls[i-1][j-1] -= 1
else:
break
i, j = ii, jj
cycle.append((ii, jj))
result.append(cycle)
Basically the code starts from a point on a boundary and the checks if it can move on a wall going up, down, left or right. When it cannot move any more a cycle has been completed and can be added to the final result.
The complexity of the algorithm is O(rows*cols), i.e. it's proportional to the input size and it's optimal (in big-O sense) because you cannot compute the result without at least reading the input. This is easy to see because the body of the while cannot be entered more times than the total number of walls in the map (at each iteration a wall is removed).
Edit
The algorithm can be modified to generate as output only simple cycles (i.e. paths in which each vertex is visited only once).
result = []
index = [[-1] * cols for x in range(rows)]
for r in range(rows-1):
for c in range(cols-1):
if walls[r][c] & 1:
i, j = r+1, c
cycle = [(i, j)]
index[i][j] = 0
while True:
if i > 0 and walls[i-1][j-1] & 2:
ii, jj = i-1, j
walls[i-1][j-1] -= 2
elif j > 0 and walls[i-1][j-1] & 1:
ii, jj = i, j-1
walls[i-1][j-1] -= 1
elif i < rows-1 and walls[i][j-1] & 2:
ii, jj = i+1, j
walls[i][j-1] -= 2
elif j < cols-1 and walls[i-1][j] & 1:
ii, jj = i, j+1
walls[i-1][j] -= 1
else:
break
i, j = ii, jj
cycle.append((ii, jj))
ix = index[i][j]
if ix >= 0:
# closed a loop
result.append(cycle[ix:])
for i_, j_ in cycle[ix:]:
index[i_][j_] = -1
cycle = cycle[:ix+1]
index[i][j] = len(cycle)-1
This is implemented by adding to the output a separate cycle once the same vertex is met twice in the processing (the index table stores for a given i,j point the 0-based index in the current cycle being built).
This seems like it would work to me:
For every filled square, check which of its neighbours are filled. For those that aren't, add the appropriate edges to a list of edges. Generate those edges as directed, either clockwise or anticlockwise as you prefer.
To construct a full path, start by pulling any edge from the set and add it to the path. It has an order so look at the second vertex. Find the edge in the set with the first vertex that is equal to that second vertex. Pull that edge from the set and add it to the path. Continue until the path is closed.
Repeat to generate a list of paths. A simple polygon should end up as one path. A complex polygon — one with holes in the middle in this case — will be several.
I guess there are different ways to do this, I suppose there is quite simple one for case when diagonal connected cells counted as different contours:
You just need too keep cell and corner direction. For example you started from upper right corner of some earth cell (it supposed that either upper or right cell, or both are nothing if it is bourder) and want to go clockwise.
If cell to the right is earth, than you change current cell to it and change corner to upper left (it is the same point). Then you go to next iteration.
In other case, if you started from upper right corner of some earth cell and want to go clockwise. If cell to the right is NOT earth than you don't change current cell and change corner to bottom right, (it's next point)
So you also have symmetrical situation for other three possible corners, and you can go to next iteration until returning to start point.
So here is pseudo-code I wrote, it uses the same indexing as picture uses, and supposes that all cells along borders are free, otherwise you will need to check if index id not out of range.
I will also need additional array with almost the same dimensions as matrix to mark processed contours, it need to be 1 cell wider than matrix cause I'm going to mark vertical lines, and each vertical line is supposed to have coordinates of cell to the right of it. Note that there are only 2 cases midst 8 dwscribed above when you need to mark vertical line.
int mark[,] = new int[height,width+1]
start_i = i = 0;
start_j = j = 0;
direction = start_direction = top_left;
index = 0;
//outer cycle through different contours
while(true)
{
++index;
//scanning for contours through all the matrix
//continue from the same place, we stopped last time
for(/*i = i*/; i < n; i++)
{
for(/*j = j*/; j < n; j++)
{
//if we found earth
if(m[i,j] == 1)
{
//check if previous cell is nothing
//check if line between this and previous contour doesn't already added
if(m[i,j - 1] == 0 && mark[i,j] == 0)
{
direction = bottom_left;
break;
}
//the same for next cell
if(m[i,j + 1] == 0 && mark[i,j+1] == 0)
{
direction = top_right;
break;
}
}
}
//break if we found contour
if(i != start_i || j != start_j)
break;
}
//stop if we didn't find any contour
if(i == start_i && j == start_j)
{
break;
}
polygon = new polygon;
start_i = i;
start_j = j;
start_direction = direction;
//now the main part of algorithm described above
do
{
if(direction == top_left)
{
if(n(i-1,j) == 1)
{
direction = bottom_left;
position = (i-1,j)
}
else
{
direction = top_right;
polygon.Add(i,j+1);
}
}
if(direction == top_right;)
{
if(n[i,j + 1] == 1)
{
direction = top_left;
position = (i,j + 1)
}
else
{
direction = bottom_right;
mark[i, j + 1] = index;//don't forget to mark edges!
polygon.Add(i+1,j+1);
}
}
if(direction == bottom_right;
{
if(n[i+1,j] == 1)
{
direction = top_right;
position = (i+1,j)
}
else
{
direction = bottom_left;
polygon.Add(i+1,j);
}
}
if(direction == bottom_left)
{
if(n[i,j - 1] == 1)
{
direction = bottom_right;
position = [i,j - 1]
}
else
{
direction = top_left;
mark[i, j] = index;//don't forget to mark edges!
polygon.Add(i,j);
}
}
//and we can stop as we reached the starting state
}while(i != start_i || j != start_j || direction != start_direction);
//stop if it was last cell
if(i == n-1 && j == n- 1)
{
break;
}
}
Also you may need to know which contour is inside which, and you mat need a stack to keep what contours you are inside while you are scanning, so every time you are crossing the existing contour you need to add it to the stack or remove if it is already at the top of the stack.
It will cause the next changes in code:
...
//continue from the same place, we stopped last time
for(/*i = i*/; i < n; i++)
{
for(/*j = j*/; j < n; j++)
{
if(mark[i,j] != 0)
{
if(stack.top() == mark [i,j])
{
stack.pop();
}
else
{
stack.push(mark [i,j]);
}
}
//if we found earth
if(m[i,j] == 1)
{
...
If your matrix can contain random patterns, the answer is far more complicated than it seems.
For one thing they may be an arbitrary number of distinct polygons, and each of them might be hollow.
Besides, finding the contour of a region (even with no holes) is of little help for drawing the surface. Your GPU will eventually need triangles, which means you will need to decompose your polygons into rectangles.
Finding an optimal decomposition of a hollow bunch of squares (i.e. the smallest set of rectangles that will cover them all) is a well studied NP-complete problem with no known solution.
There exist algorithms to find an optimal decomposition of such shapes with no holes, but they are very complex.
A greedy algorithm is much easier to implement and usually yields acceptable results.
So I would do a greedy search on your matrix, collecting rectangles until all "1" values have been visited. Turning these rectangles into coordinates should be easy enough, since you know exactly where the top left and bottom right corners are.
The greedy scan will look like:
while your matrix is not empty
move to first "1" value. This is the rectangle top left corner
from this corner, extend the rectangle in x and y to maximize its surface
store the rectangle in a list and clear all corresponding "1" values
As the title implies, if we have node x in a Binary Search Tree (BST) and we know the info of x.successor instead of x.parent, also we know x.left and x.right. How to calculate x.parent based on the above information.
I decide to analyze it on two cases: (root have height 0)
if x does not have right child, x.successor's height must less than x. In other words, x.successor is in the "upper level" of x.
if x have right child, x.successor's height must greater than x. It means x.successor is in the "lower level" of x.
For the first case, we could have the following pseudo-code.
y = x.succ
if x.right == NIL
z = y.left
while x != z
y = z;
z = z.right
return z
How to handle the second case? what happened if x.right != NIL ?
15
6 18
3 7 17 19
2 4 13 20
9
How to retrieve the parent of node 18 and 19, since there rightmost node 20 does not have successor, so it will return NIL.
we can not get the parent always based on your info.
for instance, 2 nodes, nodeOne.right = x
we only know x left = null, right = null, successor = null
we are not able to retrieve nodeOne
we can get the parent when x is not at the right-most branch(which means it has some ancestor whose left branch includes the x).
the algorithm could be:
continue find the right son, right grandson, ..., till there is none
then get the successor, and go into your code(your code is a little bit err?)
function getParent(Node node){
Node right = node;
for(;right.right != null; right = right.right){
}
Node successor = right.successor;
if (successor == null)
return null;
if (successor.left == node)
return successor;
for(Node p = successor.left; p!= null; p=p.right){
if (p.right == node){
return p;
}
}
return null;
}
Saw this question recently:
Given 2 arrays, the 2nd array containing some of the elements of the 1st array, return the minimum window in the 1st array which contains all the elements of the 2nd array.
Eg :
Given A={1,3,5,2,3,1} and B={1,3,2}
Output : 3 , 5 (where 3 and 5 are indices in the array A)
Even though the range 1 to 4 also contains the elements of A, the range 3 to 5 is returned Since it contains since its length is lesser than the previous range ( ( 5 - 3 ) < ( 4 - 1 ) )
I had devised a solution but I am not sure if it works correctly and also not efficient.
Give an Efficient Solution for the problem. Thanks in Advance
A simple solution of iterating through the list.
Have a left and right pointer, initially both at zero
Move the right pointer forwards until [L..R] contains all the elements (or quit if right reaches the end).
Move the left pointer forwards until [L..R] doesn't contain all the elements. See if [L-1..R] is shorter than the current best.
This is obviously linear time. You'll simply need to keep track of how many of each element of B is in the subarray for checking whether the subarray is a potential solution.
Pseudocode of this algorithm.
size = bestL = A.length;
needed = B.length-1;
found = 0; left=0; right=0;
counts = {}; //counts is a map of (number, count)
for(i in B) counts.put(i, 0);
//Increase right bound
while(right < size) {
if(!counts.contains(right)) continue;
amt = count.get(right);
count.set(right, amt+1);
if(amt == 0) found++;
if(found == needed) {
while(found == needed) {
//Increase left bound
if(counts.contains(left)) {
amt = count.get(left);
count.set(left, amt-1);
if(amt == 1) found--;
}
left++;
}
if(right - left + 2 >= bestL) continue;
bestL = right - left + 2;
bestRange = [left-1, right] //inclusive
}
}
Say I'm building a board game with a hextile grid, like Settlers of Catan:
Note that each vertex and edge may have an attribute (a road and settlement above).
How would I make a data structure which represents this board? What are the patterns for accessing each tile's neighbors, edges and vertices?
Amit Patel has posted an amazing page on this topic. It's so comprehensive and wonderful that it needs to be the definitive answer to this question: Hexagonal Grids
Such a grid can be represented in a two-dimensional array:
If
2
7 3
1
6 4
5
is the number one with its neighbors in the hex grid, then you can put this into a 2D array like so:
2 3
7 1 4
6 5
Obviously neighbor-ness is determined in this grid not only by being horizontally or vertically adjacent but also using one diagonal.
You can use a graph too, if you like to, though.
This article goes through how to set up a Isomeric/Hexagonal grid game. I recommend you have a look at the Forcing Isometric and Hexagonal Maps onto a Rectangular Grid section and the the movement section. Although it is different from what you are looking for it may help you formulate how to do what you want.
I've dealt a lot with hexes. In cases like this, you track each of the 6 points for the borders of the hex. This lets you draw it quite easily.
You would have a single array of objects that represent hexes. Each of these hex objects also has 6 "pointers" (or an index to another array) pointing to another array of "sides". Same thing for "vertices". Of course the vertices would have 3 pointers to the adjoining hexes, and the sides would have 2.
So, a hex may be something like:
X, Y, Point(6), Vertices(6), Sides(6)
Then you have a Hex array, vertice array, and side array.
Then it is pretty simple to find the vertices/sides for a hex, or whatever.
When I say pointer it could just as easily be an integer pointing to the element in the vertice or side array or whatever. And of course arrays could be lists or whatever.
You could create a 2D array and then consider the valid positions as:
On even-numbered rows (0,2,4,...): the odd numbered cells.
On odd-numbered rows (1,3,5,...): the even numbered cells.
For each cell, its neighbors would be:
Same column, 2 rows up
Same column, 2 rows down
1 left + 1 up
1 left + 1 down
1 right + 1 up
1 right + 1 down
Illustration:
The x marks are hexes. x that are diagonal to each other are neighbors. | connects vertical neighbors.
2
7 3
1
6 4
5
You can also try to 'flat' rows of your map. For this example it would be:
2
7 1 3
6 5 4
Its sometimes more useful to have rows in one row:P
I would suggest something like the following (I'll use Delphi-style declarations):
type
THexEdge = record
Hexes: array[1..2] of Integer; // Index of adjoining hexes.
// Other edge stuff goes here.
end;
THexVertex = record
Hexes: array[1..3] of Integer; // Index of adjoining hexes.
// Other vertex stuff goes here.
end;
THex = record
Edges: array[1..6] of Integer; // Index of edge.
Vertices: array[1..6] of Integer; // Index of vertex.
// Other hex stuff goes here.
end;
var
Edges: array of THexEdge;
Vertices: array of THexVertex;
HexMap: array of THex;
Each hex has six edges and six vertices. Each edge keeps track of its two adjoining hexes, and each vertex keeps track of its three adjoining hexes (hexes on the edges of the map will be a special case).
There are many things that you could do a different way of course. You could use pointers rather than arrays, you could use objects rather than records, and you could store your hexes in a two-dimensional array as other answerers have suggested.
Hopefully, that might give you some ideas about one way to approach it though.
We implemented a Settlers of Catan AI for a class project, and modified code from this answer (which was buggy) to create a Board with constant time random access to vertices and edges. It was a fun problem, but the board took a lot of time, so in case anyone is still looking for a simple implementation here is our Python code:
class Board:
# Layout is just a double list of Tiles, some will be None
def __init__(self, layout=None):
self.numRows = len(layout)
self.numCols = len(layout[0])
self.hexagons = [[None for x in xrange(self.numCols)] for x in xrange(self.numRows)]
self.edges = [[None for x in xrange(self.numCols*2+2)] for x in xrange(self.numRows*2+2)]
self.vertices = [[None for x in xrange(self.numCols*2+2)] for x in xrange(self.numRows*2+2)]
for row in self.hexagons:
for hexagon in row:
if hexagon == None: continue
edgeLocations = self.getEdgeLocations(hexagon)
vertexLocations = self.getVertexLocations(hexagon)
for xLoc,yLoc in edgeLocations:
if self.edges[xLoc][yLoc] == None:
self.edges[xLoc][yLoc] = Edge(xLoc,yLoc)
for xLoc,yLoc in vertexLocations:
if self.vertices[xLoc][yLoc] == None:
self.vertices[xLoc][yLoc] = Vertex(xLoc,yLoc)
def getNeighborHexes(self, hex):
neighbors = []
x = hex.X
y = hex.Y
offset = 1
if x % 2 != 0:
offset = -1
if (y+1) < len(self.hexagons[x]):
hexOne = self.hexagons[x][y+1]
if hexOne != None: neighbors.append(hexOne)
if y > 0:
hexTwo = self.hexagons[x][y-1]
if hexTwo != None: neighbors.append(hexTwo)
if (x+1) < len(self.hexagons):
hexThree = self.hexagons[x+1][y]
if hexThree != None: neighbors.append(hexThree)
if x > 0:
hexFour = self.hexagons[x-1][y]
if hexFour != None: neighbors.append(hexFour)
if (y+offset) >= 0 and (y+offset) < len(self.hexagons[x]):
if (x+1) < len(self.hexagons):
hexFive = self.hexagons[x+1][y+offset]
if hexFive != None: neighbors.append(hexFive)
if x > 0:
hexSix = self.hexagons[x-1][y+offset]
if hexSix != None: neighbors.append(hexSix)
return neighbors
def getNeighborVertices(self, vertex):
neighbors = []
x = vertex.X
y = vertex.Y
offset = -1
if x % 2 == y % 2: offset = 1
# Logic from thinking that this is saying getEdgesOfVertex
# and then for each edge getVertexEnds, taking out the three that are ==vertex
if (y+1) < len(self.vertices[0]):
vertexOne = self.vertices[x][y+1]
if vertexOne != None: neighbors.append(vertexOne)
if y > 0:
vertexTwo = self.vertices[x][y-1]
if vertexTwo != None: neighbors.append(vertexTwo)
if (x+offset) >= 0 and (x+offset) < len(self.vertices):
vertexThree = self.vertices[x+offset][y]
if vertexThree != None: neighbors.append(vertexThree)
return neighbors
# used to initially create vertices
def getVertexLocations(self, hex):
vertexLocations = []
x = hex.X
y = hex.Y
offset = x % 2
offset = 0-offset
vertexLocations.append((x, 2*y+offset))
vertexLocations.append((x, 2*y+1+offset))
vertexLocations.append((x, 2*y+2+offset))
vertexLocations.append((x+1, 2*y+offset))
vertexLocations.append((x+1, 2*y+1+offset))
vertexLocations.append((x+1, 2*y+2+offset))
return vertexLocations
# used to initially create edges
def getEdgeLocations(self, hex):
edgeLocations = []
x = hex.X
y = hex.Y
offset = x % 2
offset = 0-offset
edgeLocations.append((2*x,2*y+offset))
edgeLocations.append((2*x,2*y+1+offset))
edgeLocations.append((2*x+1,2*y+offset))
edgeLocations.append((2*x+1,2*y+2+offset))
edgeLocations.append((2*x+2,2*y+offset))
edgeLocations.append((2*x+2,2*y+1+offset))
return edgeLocations
def getVertices(self, hex):
hexVertices = []
x = hex.X
y = hex.Y
offset = x % 2
offset = 0-offset
hexVertices.append(self.vertices[x][2*y+offset]) # top vertex
hexVertices.append(self.vertices[x][2*y+1+offset]) # left top vertex
hexVertices.append(self.vertices[x][2*y+2+offset]) # left bottom vertex
hexVertices.append(self.vertices[x+1][2*y+offset]) # right top vertex
hexVertices.append(self.vertices[x+1][2*y+1+offset]) # right bottom vertex
hexVertices.append(self.vertices[x+1][2*y+2+offset]) # bottom vertex
return hexVertices
def getEdges(self, hex):
hexEdges = []
x = hex.X
y = hex.Y
offset = x % 2
offset = 0-offset
hexEdges.append(self.edges[2*x][2*y+offset])
hexEdges.append(self.edges[2*x][2*y+1+offset])
hexEdges.append(self.edges[2*x+1][2*y+offset])
hexEdges.append(self.edges[2*x+1][2*y+2+offset])
hexEdges.append(self.edges[2*x+2][2*y+offset])
hexEdges.append(self.edges[2*x+2][2*y+1+offset])
return hexEdges
# returns (start, end) tuple
def getVertexEnds(self, edge):
x = edge.X
y = edge.Y
vertexOne = self.vertices[(x-1)/2][y]
vertexTwo = self.vertices[(x+1)/2][y]
if x%2 == 0:
vertexOne = self.vertices[x/2][y]
vertexTwo = self.vertices[x/2][y+1]
return (vertexOne, vertexTwo)
def getEdgesOfVertex(self, vertex):
vertexEdges = []
x = vertex.X
y = vertex.Y
offset = -1
if x % 2 == y % 2: offset = 1
edgeOne = self.edges[x*2][y-1]
edgeTwo = self.edges[x*2][y]
edgeThree = self.edges[x*2+offset][y]
if edgeOne != None: vertexEdges.append(edgeOne)
if edgeTwo != None: vertexEdges.append(edgeTwo)
if edgeThree != None: vertexEdges.append(edgeThree)
return vertexEdges
def getHexes(self, vertex):
vertexHexes = []
x = vertex.X
y = vertex.Y
xOffset = x % 2
yOffset = y % 2
if x < len(self.hexagons) and y/2 < len(self.hexagons[x]):
hexOne = self.hexagons[x][y/2]
if hexOne != None: vertexHexes.append(hexOne)
weirdX = x
if (xOffset+yOffset) == 1: weirdX = x-1
weirdY = y/2
if yOffset == 1: weirdY += 1
else: weirdY -= 1
if weirdX >= 0 and weirdX < len(self.hexagons) and weirdY >= 0 and weirdY < len(self.hexagons):
hexTwo = self.hexagons[weirdX][weirdY]
if hexTwo != None: vertexHexes.append(hexTwo)
if x > 0 and x < len(self.hexagons) and y/2 < len(self.hexagons[x]):
hexThree = self.hexagons[x-1][y/2]
if hexThree != None: vertexHexes.append(hexThree)
return vertexHexes
I am sitting here "in my free time coding for fun" with hexes. And it goes like this... I will tell you what it looks like in words.
Hexagon: it has six neighbour hexagons. It can deliver the reference for each neighbouring hex tile. It can tell you what it consists of(water ,rock, dust). It can connect itself to others and vice versa. It can even automatically connect the others surrounding him to create a greater field and or making sure all fields can be adressed by its neighbours.
A building references up to three roads and three Hex Tiles. They can tell you which they are.
A road references two hexes and other roads when they are adressed by neighbouring tiles. They can tell which tiles that are and which roads or buildings they connect to.
This is just an idea how I would work on it.