Optimising movement on hex grid - c#

I am making a turn based hex-grid game. The player selects units and moves them across the hex grid. Each tile in the grid is of a particular terrain type (eg desert, hills, mountains, etc) and each unit type has different abilities when it comes to moving over the terrain (e.g. some can move over mountains easily, some with difficulty and some not at all).
Each unit has a movement value and each tile takes a certain amount of movement based on its terrain type and the unit type. E.g it costs a tank 1 to move over desert, 4 over swamp and cant move at all over mountains. Where as a flying unit moves over everything at a cost of 1.
The issue I have is that when a unit is selected, I want to highlight an area around it showing where it can move, this means working out all the possible paths through the surrounding hexes, how much movement each path will take and lighting up the tiles based on that information.
I got this working with a recursive function and found it took too long to calculate, I moved the function into a thread so that it didn't block the game but still it takes around 2 seconds for the thread to calculate the moveable area for a unit with a move of 8.
Its over a million recursions which obviously is problematic.
I'm wondering if anyone has an clever ideas on how I can optimize this problem.
Here's the recursive function I'm currently using (its C# btw):
private void CalcMoveGridRecursive(int nCenterIndex, int nMoveRemaining)
{
//List of the 6 tiles adjacent to the center tile
int[] anAdjacentTiles = m_ThreadData.m_aHexData[nCenterIndex].m_anAdjacentTiles;
foreach(int tileIndex in anAdjacentTiles)
{
//make sure this adjacent tile exists
if(tileIndex == -1)
continue;
//How much would it cost the unit to move onto this adjacent tile
int nMoveCost = m_ThreadData.m_anTerrainMoveCost[(int)m_ThreadData.m_aHexData[tileIndex].m_eTileType];
if(nMoveCost != -1 && nMoveCost <= nMoveRemaining)
{
//Make sure the adjacent tile isnt already in our list.
if(!m_ThreadData.m_lPassableTiles.Contains(tileIndex))
m_ThreadData.m_lPassableTiles.Add(tileIndex);
//Now check the 6 tiles surrounding the adjacent tile we just checked (it becomes the new center).
CalcMoveGridRecursive(tileIndex, nMoveRemaining - nMoveCost);
}
}
}
At the end of the recursion, m_lPassableTiles contains a list of the indexes of all the tiles that the unit can possibly reach and they are made to glow.
This all works, it just takes too long. Does anyone know a better approach to this?

As you know, with recursive functions you want to make the problem as simple as possible. This still looks like it's trying to bite off too much at once. A couple thoughts:
Try using a HashSet structure to store m_lPassableTiles? You could avoid that Contains condition this way, which is generally an expensive operation.
I haven't tested the logic of this in my head too thoroughly, but could you set a base case before the foreach loop? Namely, that nMoveRemaining == 0?
Without knowing how your program is designed internally, I would expect m_anAdjacentTiles to contain only existing tiles anyway, so you could eliminate that check (tileIndex == -1). Not a huge performance boost, but makes your code simpler.
By the way, I think games which do this, like Civilization V, only calculate movement costs as the user suggests intention to move the unit to a certain spot. In other words, you choose a tile, and it shows how many moves it will take. This is a much more efficient operation.
Of course, when you move a unit, surrounding land is revealed -- but I think it only reveals land as far as the unit can move in one "turn," then more is revealed as it moves. If you choose to move several turns into unknown territory, you better watch it carefully or take it one turn at a time. :)
(Later...)
... wait, a million recursions? Yeah, I suppose that's the right math: 6^8 (8 being the movements available) -- but is your grid really that large? 1000x1000? How many tiles away can that unit actually traverse? Maybe 4 or 5 on average in any given direction, assuming different terrain types?
Correct me if I'm wrong (as I don't know your underlying design), but I think there's some overlap going on... major overlap. It's checking adjacent tiles of adjacent tiles already checked. I think the only thing saving you from infinite recursion is checking the moves remaining.
When a tile is added to m_lPassableTiles, remove it from any list of adjacent tiles received into your function. You're kind of doing something similar in your line with Contains... what if you annexed that if statement to include your recursive call? That should cut your recursive calls down from a million+ to... thousands at most, I imagine.

Thanks for the input everyone. I solved this by replacing the Recursive function with Dijkstra's Algorithm and it works perfectly.

Related

Simulate depressurization in a discrete room

I am trying to build a top down view spaceship game which has destructible parts. I need to simulate the process of depressurization in case of hull breach.
I have a tiled map which has the room partitioning code setup:
What I am trying to do is build some kind of a vector field which would determine the ways the air leaves depressurized room. So in case you would break the tile connecting the vacuum and the room (adjacent to both purple and green rooms), you'd end up with a vector map like this:
My idea is to implement some kind of scalar field (kind of similar to a potential field) to help determine the airflow (basically fill the grid with euclidean distances (taking obstacles into account) to a known zero-potential point and then calculate the vectors by taking into account all of the adjacent tiles with lower potential value that the current tile has:
However this method has a flaw to where the amount of force applied to a body in a certain point doesn't really take airflow bottlenecks and distance into account, so the force whould be the same in the tile next to vacuum tile as well as on the opposite end of the room.
Is there a better way to simulate such behavior or maybe a change to the algorithm I though of that would more or less realistically take distance and bottlenecks into account?
Algorithm upgrade ideas collected from comments:
(...) you want a realistic feeling of the "force" in this context, then it should be not based just on the distance, but rather, like you said, the airflow. You'd need to estimate it to some degree and note that it behaves similar to Kirchoff rule in electronics. Let's say the hole is small - then amount-of-air-sucked-per-second is small. The first nearest tile(s) must cover it, they lose X air per second. Their surrounding tiles also must conver it - they lose X air per second in total. And their neighbours.. and so on. That it works like Dijkstra distance but counting down.
Example: Assuming no walls, start with 16/sec at point-zero directing to hole in the ground, surrounding 8 tiles will get 2/sec directed to the point-zero tile. next layer of surrounding 12 tiles will get something like 1.33/sec and so on. Now alter that to i.e. (1) account for various initial hole sizes (2) various large no-pass-through obstacles (3) limitations in air flow due to small passages - which behave like new start points.
Another example (from the map in question): The tile that has a value of zero would have a value of, say, 1000 units/s. the ones below it would be 500/s each, the next one would be a 1000/s as well, the three connected to it would have 333/s each.
After that, we could base the coefficient for the vector on the difference of this scalar value and since it takes obstacles and distance into account, it would work more or less realistically.
Regarding point (3) above, imagine that instead of having only sure-100%-pass and nope-0%-wall you also have intermediate options. Instead of just a corridor and a wall you can also have i.e. broken window with 30% air pass. For example, at place on the map with distance [0] you've got the initial hole that generates flux 1000/sec. However at distance [2] there is a small air vent or a broken window with 30% air flow modifier. It means that it will limit the amount from incoming (2x500=1000) to 0.3x(2x500)=300/sec that will now flow further to the next areas. That will allow you to depressurize compartments with different speeds so the first few tiles will lose all air quickly and the rest of the deck will take some more time (unless the 30%-modifier window at point [2] breaks completely, etc).

How to randomly place objects as player move around in an infinity map without overlap?

I trying to make a game where player only move forward in an infinity map, and the path (just thing of them like points, the path is only the visual) is procedurally generated. I want those path to have different length (something like the tree of life, but only branches of the selected path are generated).
This is how I generate branches without overlap:
List<Vector3> everyPos; //predetermined position
public void Spawn(int amount)
{
List<Vector3> possiblePos = new List<Vector3>(everyPos);
for (int i = 0; i < amount; i++)
{
int index = Random(0, possiblePos.Count); //Find a random position
SpawnObjectAt(currentPosition+possiblePos[index]));//Create a point there
possiblePos.RemoveAt(index); //Remove that position from the list
}
}
The problem is , look at this image(I can't embed image yet):
Red is where player start, green is possible spawn position in the first move.
If there are 2 point spawned at 1 and 2, player choose point1, then the possible position in the second time will be a point in the black zone, which include point2, so if I keep continue there will eventually overlap.
How can I avoid this? I'm making a mobile game so I don't want to cache every single point. Any help would be really appreciated! Thanks!
This is a small web game that have somewhat similar mechanic to what I trying to achieve: newgrounds.com/portal/view/592325/
This is an attempt here to answer, but honestly, you need to provide more information.
Depending on the language you are writing in, you can handle this differently. You may need dynamic allocation, but for now lets assume, since your idea is quite small, that you can just do one large array predefined before compile time.
I assume you know how to make an array, so create one with say, 500 length to start. If you want to 'generate' a link like they did in that game, you simply need a random function, (there is a built in library in pretty much every language I think) and you need to do a little math.
Whatever language you use will surely have a built in graphics library, or you can use a popular easy to use one. I'll just draw a picture to make this clear.
There are a number of ways you can do this mathematically as shown in the image, using angles for example, the simplest way, however, is just to follow the boxes.
If you have worked with graphics before, you know what a vector is, if not, you will need to learn. The 9 vectors presented in this image (0,1) (1,0) (1,1) etc. can be created as vector objects, or even stored as individual ints.
To make your nodes 'move' into another path, you can simply do a rand 1-9 and then correlated the result to one of 9 possible vectors, and then add them to your position vector. It is easiest to do this in array and just use the rand int as the index. In most c derived languages you do that like this:
positionVector += changeVectorArray[rand(1,9)];
You then increment your position vector by one of the 9 vectors as shown above.
The simplest way of making the 'path' is to copy the position before you add the change vector, and then store all of the changes sequentially in another 'path' array.
To show the path on screen, simply draw a line between the first and second, second and third, third and forth elements of your path array. This formula (of joining lines) is discrete mathematics if I'm not mistaken, and you can do much more complicated path shapes if you want, but you get the gist.
That should at least start you off. Without more info I can't really help you.
I could go off on a tangent describe a bunch of different ways you can make this happen differently but its probably easier if you just ask for specifics.
EDIT>>>
Continuing with this answer, yes, looking at it now, the nodes can definitely overlap. To solve this problem you could use collision detection, every time you generate a new 'position', before adding it and drawing the line you have to loop through your array like this:
boolean copy = true;
for(int i = 0; i < getLength(pathArray); i++){
if( newVector == pathArray[i]){
copy=false;
}
}
Then of course, if copy still is true, copy the new position int the pathArray. NOTE: this whole solution is sloppy as hell, and as your array gets larger, your program is going to take longer and longer to search through that loop. This may not also guarantee that the path goes in one direction, but it is likely. And note that the lines will still be able to overlap each other, even though the position vectors can't be on top of one another.
All this considered, I think it will work, the optimization is up to you. I would suggest that there is probably a much more efficient solution using a discrete formula. You can also use such a formula to make the path go in particular directions and do other more complicated things.
You could also quite easily apply constraints on your random rolls if you want to make the path go in a particular direction. But there are so many ways of doing this I can't begin to explain. You could google path-finding algorithms for that.
Good luck.

Traveling salesman prob on 2d map with walls (obstacles) so pathfinding needed

I need to find optimal path between a number of points on a 2d map.
The 2d map is of a building and will simply have where you can not go (through walls) and all the points on the map. So it's not really a map, rather lines you cannot go through with points to pass through.
I have a number of points, say between 20 and 500
I start with one that I select and then need the route calculated for most optimal path.
I would love hints for where to look for this travelling salesman problem with obstacles. Or even better, done library for doing it.
Bonuses
Things like doors can be weighted as they are less fun to pass through back and forth.
Possibility of prioritizing/Weighting the ability to end close to where you started.
Selecting areas as passable but annoying (weighting down)
.Net/C# code that I can use, I want to use this both on .NET MVC project and Xamarin mobile project so .net code would be great (if code exists)
Update example
In my example here we have an office. Now I have not thought every detail out so this is merely an example.
All the purple dots need to be checked
Yellow area could mean annoying to pass through but doable
Red could mean not active but can be passed if no other option exists.
Blue (walls) are impenetrable and can not be passed.
Green is doors, weighted down possibly as it's annoying to go trough closed doors (usually this would probably make sense anyway as the dots in a room would be easiest to check together.
The user would go to one dot, check it, then the software should tell him which one to do next until he is done.
Bonus could be given for ending close to start place. So for instance in this example, if the red area was normal and contained dots it would have been easy to make it a loop. (So the user comes back close to where he started)
Finally I suppose it would also be smart to differentiate outdoors areas as you would need to get dressed for outdoors, so you only want to go out once.
Also it could be smart to be able to prioritize ending on a point close to stairwell to next floor if they intend to check multiple floors at once.
Of course would have more more complex and larger plans the this exmaple.
Again sorry for just brainstorming out ideas but I have never done this kind of work and is happy for any pointers :-)
Let N be the set of nodes to visit (purple points). For each i and j in N, let c(i,j) be the distance (or travel time) to get from i to j. These can be pre-computed based on actual distances plus walls, doors, other barriers, etc.
Now, you could then add a penalty to c(i,j) if the path from i to j goes through a door, "annoying" area, etc. But a more flexible way might be as follows:
Let k = 1,...,K be the various types of undesirable route attributes (doors, annoying areas, etc.). Let a_k(i,j) be the amount of each of these attributes on the path from i to j. (For example, suppose k=1 represents door, k=2 represents yellow areas, k=3 represents outside. Then from an i in the break area to j in the bathroom might have a_1(i,j) = 1, and from an i to a j both in the yellow areas would have a_2(i,j) = 0.5 or 2.0 or however annoying that area is, etc.)
Then, let p_k be a penalty for each unit of undesirable attribute k -- maybe p_1 = 0.1 if you don't mind going through doors too much but p_2 = 3.0 if you really don't like yellow areas.
Then, let c'(i,j) = c(i,j) + sum{k=1,...,K} p_k * a_k(i,j). In other words, replace the actual distance with the distance plus penalties for all the annoyances. The user can set the p_k values before the optimization in order to express his/her preferences among these. The final penalties p_k * a_k(i,j) should be commensurate with the distance units used for c(i,j), though -- you don't want distances of 100m but penalties of 1,000,000.
Now solve a TSP with distances given by c'(i,j).
The TSP requires you to start and end at the same node, so that preference is really a constraint. If you're going to solve for multiple floors simultaneously, then the stairway times would be in the c(i,j) so there's no need to explicitly encourage routes that end near a stairway -- the solution would tend to do that anyway since stairs are slow. If you're going to solve each floor independently, then just set the start node for each floor equal to the stairway.
I wouldn't do anything about the red (allowable but unused) areas -- that would already be baked into the c(i,j) calculations.
Hope this helps.

Designing a flexible and extensible bonus system for a Scrabble's game implementation

Let's say I'm implementing my own version of Scrabble.
I currently have a Board class that contains lots of Squares. A Square in turn is composed of a IBonus and a Piece. The bonus implementations are actually the usual bonus for Scrabble, but it is possible that I might try to add some new and twisted bonus to spice the game -- flexibility here is paramount!
After thinking for a while I came to the conclusion that for IBonus implementations to work, they'll need to know the whole Board and also its current position(on the Board, so it knows where it is and it can check for the piece that's in the same square as the bonus is). This strikes me as bad as basically it needs to know a whole lot of information.
So, my naive implementation would be to pass the Board as argument to IBonus.calculate() method, IBonus.calculate(Board board, Point position), that is.
Also, it seems to create a circular reference. Or am I wrong?
I don't particulary like this approach, so I am looking for other possible approaches. I know I can make calculate accept an interface instead of a concrete class, i.e., calculate(IBoard board) but I IMO that isn't all that better than the first case.
I fear being too focused on my current implementation to be able to think of whole different designs that could fit at least as well as solutions to this problem.
Maybe I could re-architect the whole game and have the bonuses in other place, so it would facilitate this calculation? Maybe I am too focused on having them on the Board? I certainly hope there are other approaches to this problem out there!
Thanks
I assume Board has the visible state of the game, and there would be other objects such as Rack (one per Player,) and a DrawPile.
"Double Score if word contains a real (non-blank) Z" - would require you pass in the Word, or the Board and the position of the word.
"Double Score if the word is the longest on the board" requires the entire Board.
"Double Score if the first letter of the word matches a randomly selected letter from the DrawPile" requires the DrawPile of course.
So to me it just depends on the rules you implement. I'd be comfortable with passing Board to the IBonus score() implementation.
edit - more thoughts.
So a board has 17x17 squares, or whatever. I'd assign an IBonus implementation to each square of the board (there would be an implementation called PlainEmptySquare that was inert.) You'd only need to instantiate each implementation of IBonus once - it could be referenced many times. I'd probably take the low road and instantiate each one explicitly, passing in the arguments needed. If one type needs the Board, pass it in. If another needs the DrawPile, pass it in. In your implementation, you'd have perhaps 12 lines of ugliness. /shrug
Something like the following might work:
CurrentGame has a Board, which has a collection of Squares. A Square could have an IBonus, however there is no Calculate() method on a Square. A Square may have a Piece, and a Piece may have a Square (ie a square may or may not be empty, and a piece may or may not have been placed on the board).
Board also has a calculateScoreForTurn() method which would accept a collection of Pieces representing the pieces that have just been placed on the board for that turn. Board knows all the information about the pieces and squares that have just been placed, as well as the surrounding or intersecting pieces and squares (if applicable) and thus has all the information required to calculate the score.
This strikes me as bad as basically it
needs to know a whole lot of
information
I think it's necessary. You're just passing a reference to the board, not actually causing large quantities of data to be moved around.
The Board itself will probably have to drive the scoring for a given round. As each Tile is placed, the Board makes note of it. When the last Tile (for a turn) has been placed, the Board must get all of the Squares that have a newly added tile (the Bonus for these Squares will be calculated) AND all of the previously placed Tiles that the current turn is "reusing".
For example, play CAT
C A T
C falls on Double Letter Score. So, the score for the turn is C.Value*2 + A.Value + T.Value.
Next player places an S to make CATS. S falls on Triple Word Score. So, the score for the turn is (C.Value + A.Value + T.Value + S.Value)*3. When a Tile's Bonus has been applied, it must be "Deactivated" so that future "reuses" of that Tile do not also get the Bonus.
The implication is that some Bonuses apply the Tile placed in the Square while others apply to the collection of Tiles that make up the new Word AFTER the Bonuses for the individual letters have been calculated.
Given one or more Squares that have been filled with Tile(s) during a turn, the Board can find the Start of the Word(s) that have been created by traversing left until the edge of the board (or until an empty Square) and traversing up until the same condition. The Board can find the End of the Word(s) that have been created by similarly traversing right and down. You also have to traverse to the Start and End of words each time a newly placed Tile is Adjacent to an existing Tile (you could create many words during a turn).
Given a collection of Words (each composed of a Square containing a possible LetterBonus and a Tile with a Value), the Board (or each Word itself) computes the BaseValue (Sum of Values of Tiles - applying any LetterBonuses) and then applies the WordBonus (if any) to get the ultimate value of the Word.

Finding a Position on a 2D Map that meets several criteria

as my personal project i develop a game to which users can join at any time.
I have a tiled worldmap that is created from a simple Bitmap which has resources at random positions all over the map except for oceans.
When a player joins i want to create his starting position at a place that has at least 1 tile of each of the 4 resources in range (circle with a still to decide diameter, i think about 3-4 tiles) but no ocean tiles (Tile.Type != "ocean") and not conflicting with a field belonging to another player (Tile.Owner == null).
The map size can vary, currently it's 600x450 and it's implemented as a simple Array: Tile[][] with Tile.Resource being either null or having Tile.Resource.Type as a string of the resource name (as it's configurable by plaintext files to fit any scenery i want to put it in, so no built-in enums possible).
I currently have a loop that simple goes through every possible position, checks every field in range and counts the number of each resource field and discards it if there are none for one of them or if one of them belongs to a player or is an ocean field.
I would prefer if it finds a random position but thats not a requirement, mono-compatibility however is a requirement.
What would be the best way to implement an algorithm for that in C#?
Edit
The Area of players can and will increase/change and resources can be used up and may even appear randomly (=> "Your prospectors found a new goldmine") so pre-calculated positions will propably not work.
Instead of looping through all your positions, why don't you loop through all your resources? Your resources are likely to be more scant. Then pick one of the sets of resources that meet your clustering criterion.
You might consider simulated annealing ... it's not very complex to implement. You have a set of criteria with certain weight, and randomly "shake" the position at a certain "temperature" (the higher the temp, the greater the radius the position may randomly move within, from it's previous position), then when it "cools" you measure the value of the position based on the total weights and subtract negative things, like spawning too close to where they died, or next to other players, etc..., if the value is not within a certain range, you decrease the temperature, but "shake" the positions again, cool down, check weights and overall value, repeat until you get an acceptable solution.
Simulated annealing is used in map making, to label cities and features with maximum clarity, while staying within range and minimizing overlap. Since it's a heuristic approach there is no guarantee that there will be an optimal solution, so you keep "lowering the temp" and eventually just choose the best result.
Let's suppose that once your map is created you don't have to create a new one often.
Just add the following to each Tile and calculate them once after your map was generated:
-int NrOceanTiles
-int NrResourceA
-int ...
Now when you want to get a tile you can do it quite a bit faster:
IEnumerable<Tiles> goodTiles = tiles.Where(tile => tile.NrResourceA >= 1 && tile.NrResourceB >= 2);
Tile goodTile = goodTiles.ElementAt(randomI);
Predefined data would still be the best way forward.
As modifying the map size, and adding/losing resources would not happen as often, just update this data table when they do happen. Perhaps you could do the map/resource changes once per day, and have everything done in a daily database update.
In this way, finding a valid location would be far faster than any algorithm you implement to search all the tiles around it.
If the game isn't going to be designed for a huge number of players, most games implement "start spots" on the map. You'd hand-pick them and record the positions in your map somehow, probably similar to how you're implementing the map resources (i.e., on that spot, there exists an item you can pick up, but on top of the tile map).
Since the resources spawn at random, you could either not spawn resources on the start spots (which could be visible or not), or simply not spawn a player at a start spot on which there is a resource (or look within a 9-cell box to find a close alternate location).
Certainly you would want to hold the set of possible starting locations and update it as resources are created and consumed.
It seems like your best bet is to calculate open locations at map generation. Have your start location calculation function optionally take grid location and size or rectangle corners.
Have a list for Free locations and Occupied locations. Player occupies territory? Move resources in range to the Occupied list. Player gets crushed mercilessly? Move resources in range to the Free list. Resource eliminated? Delete any locations that used it in your Open/Occupied lists. Resource added? Recalculate using your effect radius to determine effected area. When your map area expands, just run the initial calculations on the new section of your grid + effect radius and add the new locations.
Then you just have to set the events up and pick a random Free value when someone joins.

Categories

Resources