连接4:检查获胜者
在Delphi中,我有一个数组形式的Connect 4棋盘表示(7列x 6行):
TBoard = Array[1..7, 1..6] of SmallInt;
Board: TBoard; // instance ob TBoard
每个元素可以有三种不同的状态:
- 1 = 玩家1的棋子
- 0 = 空
- -1 = 玩家2的棋子
现在我需要一个函数它检查是否有获胜者或平局:
function CheckForWinner(): SmallInt;
...其中 1 表示玩家 1 获胜,0 表示平局,-1 表示玩家 2 获胜,“nil”表示尚未结束的游戏。
我的草案如下 - 分为两个单一功能:
function CheckForWinner(): SmallInt;
var playerToCheck: ShortInt;
s, z: Byte;
draw: Boolean;
begin
draw := TRUE;
for s := 1 to 7 do begin
for z := 1 to 6 do begin
if Board[s, z] = 0 then draw := FALSE; // if there are empty fields then it is no draw
end;
end;
if draw then begin
result := 0;
end
else begin
playerToCheck := Board[lastPieceX, lastPieceY]; // only for last-moving player
if searchRow(playerToCheck, +1, 0, lastPieceX, lastPieceY) then // search right/left
result := playerToCheck
else if searchRow(playerToCheck, 0, +1, lastPieceX, lastPieceY) then // search up/down
result := playerToCheck
else if searchRow(playerToCheck, +1, +1, lastPieceX, lastPieceY) then // search right-down/left-up
result := playerToCheck
else if searchRow(playerToCheck, +1, -1, lastPieceX, lastPieceY) then // search right-up/left-down
result := playerToCheck;
else
result := nil;
end;
end;
end;
function searchRow(player: SmallInt; sChange, zChange: ShortInt; startS, startZ: Byte): Boolean;
var inRow, s, z: SmallInt;
begin
inRow := 0;
s := startS;
z := startZ;
while (Board[s, z] = player) AND (inRow < 4) AND (s >= 1) AND (s <= 7) AND (z >= 1) AND (z <= 6) do begin
s := s+sChange;
z := z+zChange;
inRow := inRow+1;
end;
s := startS-sChange;
z := startZ-zChange;
while (Board[s, z] = player) AND (inRow < 4) AND (s >= 1) AND (s <= 7) AND (z >= 1) AND (z <= 6) do begin
s := s-sChange;
z := z-zChange;
inRow := inRow+1;
end;
if inRow = 4 then
result := TRUE
else
result := FALSE;
end;
您认为这种方法怎么样?您有更好(更快/更短)的解决方案吗?
非常感谢!
In Delphi, I have a Connect 4 board representation (7 columns x 6 lines) in form of an array:
TBoard = Array[1..7, 1..6] of SmallInt;
Board: TBoard; // instance ob TBoard
Each element can have three different states:
- 1 = player 1's pieces
- 0 = empty
- -1 = player 2's pieces
Now I need a function which checks if there's a winner or a draw:
function CheckForWinner(): SmallInt;
... where 1 is player 1's win, 0 is a draw, -1 is player 2's win and "nil" is for a game which has not ended yet.
My draft is as follows - split into two single functions:
function CheckForWinner(): SmallInt;
var playerToCheck: ShortInt;
s, z: Byte;
draw: Boolean;
begin
draw := TRUE;
for s := 1 to 7 do begin
for z := 1 to 6 do begin
if Board[s, z] = 0 then draw := FALSE; // if there are empty fields then it is no draw
end;
end;
if draw then begin
result := 0;
end
else begin
playerToCheck := Board[lastPieceX, lastPieceY]; // only for last-moving player
if searchRow(playerToCheck, +1, 0, lastPieceX, lastPieceY) then // search right/left
result := playerToCheck
else if searchRow(playerToCheck, 0, +1, lastPieceX, lastPieceY) then // search up/down
result := playerToCheck
else if searchRow(playerToCheck, +1, +1, lastPieceX, lastPieceY) then // search right-down/left-up
result := playerToCheck
else if searchRow(playerToCheck, +1, -1, lastPieceX, lastPieceY) then // search right-up/left-down
result := playerToCheck;
else
result := nil;
end;
end;
end;
function searchRow(player: SmallInt; sChange, zChange: ShortInt; startS, startZ: Byte): Boolean;
var inRow, s, z: SmallInt;
begin
inRow := 0;
s := startS;
z := startZ;
while (Board[s, z] = player) AND (inRow < 4) AND (s >= 1) AND (s <= 7) AND (z >= 1) AND (z <= 6) do begin
s := s+sChange;
z := z+zChange;
inRow := inRow+1;
end;
s := startS-sChange;
z := startZ-zChange;
while (Board[s, z] = player) AND (inRow < 4) AND (s >= 1) AND (s <= 7) AND (z >= 1) AND (z <= 6) do begin
s := s-sChange;
z := z-zChange;
inRow := inRow+1;
end;
if inRow = 4 then
result := TRUE
else
result := FALSE;
end;
What do you think of this approach? Do you have a better (faster / shorter) solution?
Thank you very much!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我没有读你的代码。我只是选择用一张白纸自己写一些。
这是我的版本:
一大堆代码。使用暴力方法,对于 Connect 4 来说性能并不重要。不喜欢四个相同的
if Result<>stNone then exit;
行,但您肯定可以想到一种更简洁的方法。代码尚未运行。可能根本就行不通!!这正是我的大脑试图解决问题的方式。I didn't read your code. I just elected to write some myself with a blank slate.
Here's my version:
Big long pile of code. Uses brute force approach, not that performance matters for Connect 4. Don't like the four identical
if Result<>stNone then exit;
lines, but you can surely think of a cleaner way. Code has not been run. It might not even work!! Just the way my brain attempted to solve the problem.检查获胜者的方式与您的方式非常相似,只是代码少了一点。
我认为您不需要检查所有字段来确定游戏是否完成。当您在游戏中掉落棋子时,只需增加一个计数器即可。如果计数器达到 42 并且尚未分出胜负,则游戏为平局。
Checking for a winner in very much the same way as you do, only with a little less code.
I think you wouldn't need to check all fields to determine if the game is done. Just increase a counter when you drop a piece in the game. The game is a draw if the counter reaches 42 and there is no winner yet.
免责声明:我没有详细研究该算法。下面的评论只是我盯着代码不到十秒后的第一反应。
我有一些非常简短的评论。首先,我认为
更好。当然,您可以通过执行以下操作来保存与旧代码的兼容性
其次,
您不需要
begin
和end
部分:更重要的是,作为性能的提高,您一旦将
drawn
设置为 false,就应该中断循环:但是,这只会中断
z
循环。要打破这两个循环,最好的方法是将上面的整个块放在本地函数中。我们将其称为CheckDraw
:或者,您可以使用
label
和goto
一次性跳出两个循环。更新
我现在知道你可以这样做
,甚至不需要引入
draw
局部变量!结束更新
此外,
很糟糕。你应该做的只是
最后,按照我的口味
应该这样写
,
应该是
哦,并且
nil
是一个指针,而不是一个整数。Disclaimer: I haven't studied the algorithm in detail. The comments below are merely my first reactions after staring at the code for less than ten seconds.
I have some very quick remarks. First, I think
is nicer. Of course, you can save compatibility with your old code by doing
Second,
You don't need the
begin
andend
parts:More importantly, as a gain in performance, you should break the loops as soon as you have set
drawn
to false:This will, however, only break the
z
loop. To break both loops, the nicest way is to put the entire block above in a local function. Let's call itCheckDraw
:Alternatively, you can use
label
andgoto
to break out of both loops at once.Update
I see now that you can just do
and you don't even need to introduce the
draw
local variable!End update
Furthermore,
is bad. You should do just
Finally, In my taste
should be written
and
should be
Oh, and
nil
is a pointer, not an integer.John Tromp 的 Fhourstones Benchmark 源代码使用了一种有趣的算法来测试四连胜游戏的获胜情况。该算法使用以下游戏位板表示:
红色玩家有一个位板,黄色玩家有一个位板。 0 代表空单元格,1 代表已填充单元格。位板存储在无符号 64 位整数变量中。位 6, 13, 20, 27, 34, 41, >= 48 必须为 0。
算法是:
你必须调用最后一步棋的玩家的位板的函数
The source code from the Fhourstones Benchmark from John Tromp uses a fascinating algorithm for testing a connect four game for a win. The algorithm uses following bitboard representation of the game:
There is one bitboard for the red player and one for the yellow player. 0 represents a empty cell, 1 represents a filled cell. The bitboard is stored in an unsigned 64 bit integer variable. The bits 6, 13, 20, 27, 34, 41, >= 48 have to be 0.
The algorithm is:
You have to call the function for the bitboard of the player who did the last move