2 x 2の正方形のグリッドを考えます。次の場合、プレイヤーは広場に移動できます。
- 次のターンに他のプレイヤーは広場に移動したくない
- 他のプレイヤーは誰も待っておらず、このターンはまだ広場を占領しています
私の問題を説明するために上の画像を含めました。
プレイヤーは同時に動きます。
2人(またはそれ以上)のプレイヤーが同じマスに移動しようとした場合、どちらも移動しません。
2 x 2の正方形のグリッドを考えます。次の場合、プレイヤーは広場に移動できます。
- 次のターンに他のプレイヤーは広場に移動したくない
- 他のプレイヤーは誰も待っておらず、このターンはまだ広場を占領しています
私の問題を説明するために上の画像を含めました。
プレイヤーは同時に動きます。
2人(またはそれ以上)のプレイヤーが同じマスに移動しようとした場合、どちらも移動しません。
回答:
うまくいくと思う。それは確かにあなたが投稿したケースと、私がテストした他のいくつかのささいなケースに有効です。
衝突防止の代わりの衝突解決。
オブジェクトを単に移動し、衝突があったかどうかを確認します。別のブロックとの衝突があった場合、前の正方形に戻るか、ゲームの種類に応じて、別の正方形に移動します。
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番目のチェックは、各ピースが1回だけ戻る必要があり、アルゴリズムが正しく終了することを保証する必要があることを意味します。また、移動したプレイヤーのみが返されるようにします-元の居住者は、削除の対象と見なされないため、そのまま残ります。
別の解決策は、表示しているものより2倍大きいマップを使用することです。プレーヤーを移動するたびに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
は移動できないか、移動しないか、2人(またはそれ以上)のプレイヤーS
が同時に移動しようとし、両方とも拒否されました。squares[S]
プレイヤーがsquareに移動したい正方形のインデックスが含まれます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
各動きは1回しか計画できず、1回しかキャンセルできないため、このアルゴリズムは最悪の場合でもn人のプレーヤーに対してO(n)時間で実行されます。
(残念ながら、このアルゴリズムはプレイヤーが場所を切り替えたり、パスを斜めに交差させたりするのを止めることはありません。Gajetの2ステップトリックをそれに適応させることは可能かもしれませんが、それを行う完全に素朴な方法は機能せず、疲れすぎます今より良い方法を見つけ出すために。)