I have a tile-based RPG system where a specific tile type is represented by a string (i.e. Grass = "g", Dirt = "d"). The problem is that I do not know how to represent a map (a group of tiles gathered in a specific order) in a way where each tile can be accessed by their x/y coordinates efficiently. Should the maps be represented in array format :
map[0].coords[x][y] = "g";
Or perhaps in some other way?
It depends on what language you are using, but a 2-dimensional array is usually an efficient way to do this.
Accessing elements in an array is usually quick because the position of a given element in memory can be calculated based on the array indexes provided, without having to iterate over other elements. Other data structures, (eg linked lists) are much slower for this type of retrieval.
A few things, dependant on the language:
1: If possible, set constant integers for terrain type. Constants use less memory and are quicker to referance/retrieve, same with integers over strings
2: A two dimensional would probably be the most efficant way of doing it.
An example
CONST(INT) GRASS = 1;
CONST(INT) DIRT = 2;
CONST(INT) SNOW = 3;
// assuming map is an array containing objects, and coords is a 2d
// array of said object:
map[0].coords[x,y] = GRASS;
A two dimensional array is fine.
You can also use a one-dimensional array. Here's a snippet of Java code I have lying around:
char [] cells = new char[WORLD_WIDTH * WORLD_HEIGHT];
public char get(int x, int y) {
return cells[x + y * WORLD_WIDTH];
}
public void set(int x, int y, char c) {
cells[x + y * WORLD_WIDTH] = c;
}
Suppose your world is 10 by 10 tiles, then the first row is in cells[0] to cells[9], the last row in cells[90] to cells[99], and so on.
Of course, you may want to add additional checks to ensure that the x and y parameters are valid.
Related
Let me cut to the main issue, I have a grid which is 50 by 50. And I need a way of having a true or false variable for each cell in the grid and the default value would be false, every time the method is called.
I need the array so I can see which cells I have visited so I don't need to revisited them for my path finding system that I'm working on .
But currently I have a double array[] [] which I need to loop every time I use this method. (up to 3 times a second but mostly never) which means looping 2500 value. Which doesn't sound very good. What would be the best solution, is this the best solution or am I missing something stupid.
Hope you can help and point me into the right direction.
Another possible improvement is using a single-dimensional vector, maybe wrapped into a class, that will contain 2500 elements and its indexes would mean (width*50+height). Like this:
private var _visited:Vector.<Boolean>;
function checkVisited(x:int,y:int):Boolean {
return _visited(x*50+y); // x and y are in 0-49 range
}
Vectors can be two-dimensional should you need them, you declare vector of vectors like this:
var _visited:Vector.<Vector.<Boolean>>;
Initialize with pushing the filled Vector.<Boolean> once, then just change the elements as you do with a normal array.
The main advantage of vectors is that they are solid, that is, if there are 50 elements in a vector, you are sure that there exists a value at any index from 0 to 49 (even if null, or NaN in case of Numbers), this also makes internal processing of vectors easier, as the Flash engine can calculate proper memory location by just using the index and the link to the vector, which is faster than first referencing the array about whether there is a value at that index, if yes, get its memory location, then reference.
From my experience of making tile based games with different grids I usually have a Tile class, that will contain all your necessary values, most typical would be:
posX:int
posY:int
isChecked:Boolean
You can add as many as you need for your app.
Then I have a Grid class that will create you grid and have some useful methods like giving neighbor tiles.
In the Grid class I make the grid this way:
public var tileLineArray:Vector.<Tile>;
public var tile2dArray:Vector.<Vector.<Tile>>;
public function makeGrid(w:int, h:int):void
{
tileLineArray = new Vector.<Tile>();
tile2dArray = new Vector.<Vector.<Tile>>();
for (var i:int = 0; i < gridWidth; i++)
{
var rowArray:Vector.<Tile> = new Vector.<Tile>();
for (var j:int = 0; j < gridHeight; j++)
{
var t:Tile = new Tile();
t.posX = i;
t.posY = j;
tileLineArray.push(t);
rowArray.push(t);
}
tile2dArray.push(rowArray);
}
}
What it will give you is that you can access tiles in a single line to by coordinates x,y;
var myTile:Tile = tileLineArray[lineID];
var myTile:Tile = tile2dArray[targetX][targetY];
I use Vectors in this example as they outperform Arrays and you keep the type of the stored object intact.
It is not a problem for Flash to loop through the array; if you want improve performance, break the loop if you've done all what you wanted with it, continue the loop if the tile does not meet the requirements and you don't need to process it.
Also, having a 2d array can improve performance, since you can process only the area of the array that you need.
One more advice is not to be afraid to make X more smaller arrays to store some data from the bigger array and loop trough the small ones. As the data of the arrays is not a primitive (int, uint etc.) but a Class, it will hold a pointer/reference to the object, so you're not actually making copies of the objects every time.
Excerpt from the O'Reilly book :
From the above excerpt the author explain in performance terms why there should be a performance difference in big oh or other terms and the basis for the formula to find any element in n by c dimensional array.
Additional: Why are different data types used in the three dimensional example? Why would you even bother to represent this in different ways ?
The article seems to point out different ways to represent matrix data structures and the performance gains of a single array representation, although it doesn't really explain why you get the performance gains.
For example, to represent a NxNxN matrix:
In object form:
Cell {
int x,y,z;
}
Matrix {
int size = 10;
Cell[] cells = new Cell[size];
}
In three-arrays form:
Matrix {
int size = 10;
int[][][] data = new int[size][size][size];
}
In a single array:
Matrx {
int size = 10;
int[] data = new int[size*size*size];
}
To your question, there is a performance gain by representing a NxN matrix as a single array of N*N length, you gain performance because of caching (assuming you cannot fit the entire matrix in one chunk); a single array representation guarantees the entire matrix will be in a contiguous chunk of memory. When data is moved from memory into cache (or disk into memory), it is moved in chunks, you sometimes grabs more data than you need. The extra data you grab contains the area surrounding the data you need.
Say, you are processing the matrix row by row. When getting new data, the OS can grab N+10 items per chunk. In the NxN case, the extra data (+10) may be unrelated data. In the case of a N*N length array, the extra data (+10) is most likely from the matrix.
This article from SGI seems to give a bit more detail, specifically the Principles of Good Cache Use:
http://techpubs.sgi.com/library/dynaweb_docs/0640/SGI_Developer/books/OrOn2_PfTune/sgi_html/ch06.html
The size of the grid will be known at the start (but will be different each time the program starts). However, the DEPTH of each cell is not a mere value, but rather a population of objects that will vary constantly during runtime.
Q: What is the most recommended (efficient and easy to maintain; less prone to user error) way of implementing this ?
Is this some kind of a standard 2D array of vector pointers ?
Is it a 3D Vector array ?
Is it a 2D array of linked lists, or binary trees (I am thinking binary trees will add complexity overhead because of continuous deletion and insertion node-gymnastics)
Is it some other custom data structure ?
Use a 1D array for best cache locality. A vector would be fine for this.
std::vector<int> histdata( width * height );
If you need to index the rows quickly, then make something to point into it:
std::vector<int*> histogram( height );
histogram[0] = &histdata[0];
for( int i = 1; i < height; i++ ) {
histogram[i] = histogram[i-1] + width;
}
Now you have a 2D histogram stored in a 1D vector. You can access it like this:
histogram[row][col]++;
If you wrap all this up in a simple class, you're less likely to do something silly with the pointers. You can also make a clear() function to set the histogram data to zero (which just rips through the histdata vector and zeros it).
I want to learn how can i copy a 3 dimensional array from host memory to device memory.
Lets say i have a 3d array which contains data. For example
int host_data[256][256][256];
I want to copy that data to dev_data (a device array) in such a way so
host_data[x][y][z]=dev_data[x][y][z];
How can i do it? and how am i supposed to access the dev_data array in the device?
A simple example would be very helpfull.
The common way is to flatten an array (make it one-dimensional). Then you'll have to make some calculations to map from (x,y,z) triple to one number - a position in a flattened one-dimensional array.
Example 2D:
int data[256][256];
int *flattened = data;
data[x][y] == fattened[x * 256 + y];
Example 3D:
int data[256][256][256];
int *flattened = data;
data[x][y][z] == flattened[x * 256 * 256 + y * 256 + z];
or use a wrapper:
__host__ __device___ inline int index(const int x, const int y, const int z) {
return x * 256 * 256 + y * 256 + z;
}
Knowing that, you can allocate a linear array with cudaMalloc, as usual, then use an index function to access corresponding element in device code.
Update:
The author of this question claims to have found a better solution (at least for 2D), you might want to have a look.
For fixed dimensions (e.g. [256][256][256]) let the compiler do the work for you and follow this example. This is attractive because we need only do a single cudaMalloc/cudaMemcpy to tranfer the data, using a single pointer. If you must have variable dimensions, it's better to think about alternate ways to handle this due to the complexity, but you may wish to look at this example (referring to the second example code that I posted). Please be advised that this method is considerably more complicated and hard to follow. I recommend not using it if you can avoid it.
Edit: If you're willing to flatten your array, the answer provided by #Ixanezis is recommended, and is commonly used. My answer is based on the assumption that you really want to access the array using 3 subscripts both on the host and device. As pointed out in the other answer, however, you can simulate 3 subscript access using a macro or function to calculate offsets into a 1-D array.
Ok I want to pick a random point in the 2d array so it can be filled. Ive seen how to do this for a 1d array, and am wondering how this would be possible in a 2d array. All I have seen about this method is that the same position comes up again, which is a slight problem, but I don't know how to do it in the first place. The 2d array is essentially a grid, with the dimensions being the x and y coordinates. And the random element selecting a point within the boundaries (which is user selected but for the purposes of this problem can be 30x50.
EDIT:
import java.util.Random;
class pickRand{
public static String get (int x, int y){
int rndx = generator.nextInt(x) + 2;
int rndy = generator.nextInt(y) + 2;
}
}
So would this work, the x and y will correspond to the user generated number and have a raised boundary of 2 either side to prevent any objects going (partially outside or of the grid. Nothing needs to be returned right?
If you grid is of size M by N
Generate a random number between 0 and M-1 say i
Generate another random between 0 and N-1 say j
(i,j) will be a random element of the 2d array
What role does the array play here?
Essentially, the task is to pick... random integer 2D coordinates.
So if you want two coordinates, say i in 0...W-1 and j in 0...H-1, just draw two random integers. If you need more for higher dimensionality, draw more randoms.
Obviously, you can then access array[i][j].
In most languages, arrays can however be ragged, i.e. the rows/columns may have different lengths. This is however just as trivial to handle...