考虑一个2 x 2的正方形网格。在以下情况下,玩家可以移动到广场上:
- 没有其他玩家想在下一回合进入广场
- 没有其他玩家等待,并且此回合仍在占据广场
我已包含上面的图像来描述我的问题。
玩家同时移动。
如果2个(或更多)玩家尝试移动到相同的正方形,则两个都不移动。
考虑一个2 x 2的正方形网格。在以下情况下,玩家可以移动到广场上:
- 没有其他玩家想在下一回合进入广场
- 没有其他玩家等待,并且此回合仍在占据广场
我已包含上面的图像来描述我的问题。
玩家同时移动。
如果2个(或更多)玩家尝试移动到相同的正方形,则两个都不移动。
Answers:
我认为应该可以。它确实适用于您发布的案例,以及我测试过的其他一些琐碎的案例。
解决冲突,而不是预防冲突。
只需移动对象,然后检查是否有任何碰撞。如果与另一个方块发生了碰撞,请移回上一个方块,或者根据游戏类型,选择另一个方块。
Move all players according to their request.
while there are still some squares multiply occupied:
For each square that is now multiply occupied:
For each player in that square that moved there this turn:
Return them to their previous square
Mark them as having not moved this turn
这要求每个玩家记住他们刚从何处移动,以便可以将其返回,并且还记得是否在本回合中移动。这第二次检查意味着每片只需返回一次,并应确保算法正确终止。它还可以确保仅退回移动的球员-原住民得以保留,因为他们不会被考虑撤职。
另一个解决方案是使用比您显示的地图大2倍的地图。每次您要移动玩家时,都将它们移动两次,因此玩家总是会在X和Y均值相等的地块上着陆。同样,有些罕见的情况需要引起更多注意,但大多数情况下都可以解决(例如您描述),无需三思。
使用数组或地图注册所有请求的移动。
如果存在冲突,请还原有问题的移动请求。如果那使该对象返回另一个对象正试图占据的正方形,请还原发出请求的对象的请求。
伪代码:
int[][] game; // game board
var doMoves() {
int[][] dest = [][]; // destinations; cleared each run
for (obj in gameObjects)
if (obj.moveRequest) {
var o = dest[obj.moveX][obj.moveY];
if (o) {
// collision!
o.doNotMove = true;
obj.doNotMove = true;
} else {
dest[obj.moveX][obj.moveY] = obj;
}
}
}
// check move validity
for (obj in gameObjects) {
if (obj.doNotMove) continue;
var o = game[obj.moveX][obj.moveY];
if (o and o.doNotMove)
revertRequest(obj, dest);
}
// process moves
//etc
}
// recursive function to back up chained moves
var revertRequest(obj, dest) {
if (!obj.doNotMove) {
obj.doNotMove = true;
var next = dest[obj.x][obj.y];
if (next)
revertRequest(next, dest);
}
}
基于SimonW的答案,这是一个显式算法:
设squares
一个按玩家位置索引的数组,并为每个可能的位置包含另一个位置的索引或特殊值NULL
。(您可能希望将其存储为稀疏数组。)此数组中条目的可能值可能解释如下:
squares[S]
为NULL
,则正方形S
可以自由移动。squares[S] == S
,则处的玩家S
无法移动或不会移动,或者两个(或更多)玩家试图同时移动到S
,并且都被拒绝。squares[S]
将包含玩家想要从其移动到平方的平方的索引S
。在每一回合中,初始化squares
to的所有条目,NULL
然后运行以下算法:
for each player:
current := the player's current location;
target := the location the player wants to move to (may equal current);
if squares[target] is NULL:
squares[target] := current; // target is free, mark planned move
else
// mark the target square as contested, and if necessary, follow
// the pointers to cancel any moves affected by this:
while not (target is NULL or squares[target] == target):
temp := squares[target];
squares[target] := target;
target := temp;
end while
// mark this player as stationary, and also cancel any moves that
// would require some else to move to this square
while not (current is NULL or squares[current] == current):
temp := squares[current];
squares[current] := current;
current := temp;
end while
end if
end for
之后,再次遍历玩家列表,并移动有能力的玩家:
for each player:
current := the player's current location;
if not squares[current] == current:
move player;
end if
end for
由于每个动作只能计划一次,最多只能取消一次,因此即使在最坏的情况下,该算法也会在O(n)时间内为n个玩家运行。
(las,这种算法不会阻止玩家切换位置或沿对角线穿越路径。也许有可能使Gajet的两步戏法适应它,但是完全天真的方法行不通,我太累了以找出一种更好的方法。)