I have implemented a collision check algorithm that I have created for my game, and the algorithm uses recursion.
Now it all works fine, and I thought to myself, what if there was a way to solve this algorithm, without using recursion, and more something with promise callbacks, or something, but unfortunately after researching, I could not find any way that I can think of at this time, so I thought I should ask there.
Now by turn-based I mean that the game is basically a big board of rectangles where each rectangle is a tile position, and there are ships, each ship can be on a tile unless its a rock, or another ship on there.
Now the collision is handled server-sided, so the coordinates are always integers, so it's not some graphical collision check, don't get me wrong.
There's an Example of collision, you can see that the ship selected the moves Right, Left, Right, and the left move collided, because the other ship does not move at that turn of the move.
So how does my algorithm work
// Loop through all turns
for (int turn = 0; turn < 4; turn++) {
// Loop through phases in the turn (split turn into phases, e.g turn left is 2 phases, turn forward is one phase).
// So if stopped in phase 1, and its a turn left, it will basically stay in same position, if in phase 2, it will only
// move one step instead of 2 full steps.
for (int phase = 0; phase < 2; phase++) {
// Go through all players and check if their move causes a collision
for (Player p : players) {
// If a player is already collided in this step, we don't want him to move
// anywhere, so we skip this iteration
if (p.getCollisionStorage().isCollided(turn)) {
continue;
}
// Checks collision for the player, according to his current step #phase index and turn
collision.checkCollision(p, turn, phase, true);
}
}
}
So basically checkCollision(Player p, int turn, int phase, boolean setPos)
is where the collision is handled, and is a big method. This method basically goes through the collision rules and saves into players' collision storage if it collided or not.
But here comes the recursion part: When a tile is already claimed, in the method, we check if the claimed player has a move placed on that turn, if yes, we will run the checkCollision
method on that player that claimed it, if the player collided during his move, the method will return true, and then we know that this claimed player did not move, so the player we check for will collide with him. And now imagine a line of ships, and ship A wants to move forward, so we check ship B, ship B checks ship C and on and on and makes sure that every checkCollision
returned false, so ship A can move and rest can move as-well.
Now I am curious, if there is a better way on implementing this besides using a recursion?
/**
* Checks if a player has a collision according to his move, in the given turn and move-phase
* @param p The player to check
* @param turn The turn
* @param phase The move-phase step
* @param setPosition If to set the next position or not on non-collided result
* @return <code>TRUE</code> If the player was collided, <code>FALSE</code> if not.
*/
public boolean checkCollision(Player p, int turn, int phase, boolean setPosition) {
// The current selected move of the player
MoveType move = p.getMoves().getMove(turn);
// If this player was bumped, and a move was not selected, we want to process the bump animation
// But we have to check if the position to be bumped is available to be claimed
if (move == MoveType.NONE && p.getCollisionStorage().isBumped()) {
Position pos = p.getCollisionStorage().getBumpAnimation().getPositionForAnimation(p);
Player claimed = players.getPlayerByPosition(pos.getX(), pos.getY());
// Claiming checking for the new position for bump
return claimed != null && (claimed.getMoves().getMove(turn) == MoveType.NONE || checkCollision(claimed, turn, phase, false));
}
// Use the current position as default, imply we have already set it
Position position = p;
// If not set by default.txt on previous loops, gets the next position on the map for the given phase on the given move
if (!p.getCollisionStorage().isPositionChanged()) {
position = move.getNextPositionWithPhase(p, p.getFace(), phase);
}
// If the player has moved since his last position
if (!position.equals(p)) {
// Check for bounds collision with the border
if (checkBoundCollision(p, turn, phase) || checkRockCollision(p, turn, phase)) {
return true;
}
// Check if the next position is claimed by another player, null result if not
Player claimed = players.getPlayerByPosition(position.getX(), position.getY());
// If the result is not null, the position is claimed
if (claimed != null) {
Position claimedNextPos = claimed;
if (!claimed.getCollisionStorage().isPositionChanged()) {
claimedNextPos = claimed.getMoves().getMove(turn).getNextPositionWithPhase(claimed, claimed.getFace(), phase);
}
// Check if the claimed position doesn't move away
if (claimed.getMoves().getMove(turn) == MoveType.NONE || claimedNextPos.equals(claimed)) {
if (move != MoveType.FORWARD || claimed.getVessel().getSize() >= p.getVessel().getSize()) {
collide(p, claimed, turn, phase);
}
if (move == MoveType.FORWARD && canBumpPlayer(p, claimed, turn, phase)) {
bumpPlayer(claimed, p, turn, phase);
p.set(position);
p.getCollisionStorage().setPositionChanged(true);
}
claimed.getVessel().appendDamage(p.getVessel().getRamDamage());
return true;
}
else if (claimedNextPos.equals(p)) { // If they switched positions (e.g nose to nose, F, F move)
collide(p, claimed, turn, phase);
collide(claimed, p, turn, phase);
return true;
}
else {
// Make sure that the claimed position moves away successfully
if (!checkCollision(claimed, turn, phase, false)) {
if (setPosition) {
// Moved successfully, claim position
p.set(position);
p.getCollisionStorage().setPositionChanged(true);
}
}
else {
// did not move successfully, collide
collide(p, claimed, turn, phase);
collide(claimed, p, turn, phase);
return true;
}
}
} else {
// List of players that collided with this player, while performing this move, in this phase
List<Player> collisions = getPlayersTryingToClaim(p, position, turn, phase);
if (collisions.size() > 0) { // Collision has happened
collisions.add(p);
Player largest = getLargestSize(collisions);
if (countPlayersForSize(collisions, largest.getVessel().getSize()) > 1) {
// Stop players from movement
for (Player pl : collisions) {
pl.getCollisionStorage().setCollided(turn, phase);
collide(pl, pl, turn, phase);
}
}
else {
for (Player pl : collisions) {
if (pl == largest) {
continue;
}
pl.getCollisionStorage().setCollided(turn, phase);
collide(pl, largest, turn, phase);
}
if (!largest.getCollisionStorage().isPositionChanged()) {
largest.set(position);
largest.getCollisionStorage().setPositionChanged(true);
}
}
return true;
} else {
if (setPosition) {
p.set(position);
p.getCollisionStorage().setPositionChanged(true);
}
}
}
}
return false;
}