对于CodinGame,我正在建立一个名为War的纸牌游戏的裁判。规则描述here)。 TL; DR:具有最高抽牌的人将两张牌添加到他/她的牌组底部。我在Pascal中构建了一个链表来保存卡片堆栈。但帕斯卡指针并不像我期望的那样:
例如,我给程序提供以下输入(稍加修改):
9
8C
KD
AH
QH
3D
KD
AH
QH
6D
9
8D
2D
3H
4D
4S
2D
3H
4D
7H
示例输出是:
nrCards: 5
Player1: 13 14 12 6
Player2: 2 3 4 7
Player1: 14 12 6
Player2: 3 4 7
Player1: 12 6
Player2: 4 7
Player1: 6
Player2: 7
Player1:
Player2: 6 7
2 5
即,最后一个元素的nextNode指针大多没有正确更新...
码:
program Answer;
{$H+}
uses sysutils, math, strutils;
type
TNode = record
val : Int32;
nextNode : ^TNode;
end;
TNodePtr = ^TNode;
TNodePtrPtr = ^TNodePtr;
var
size1 : Int32;
size2 : Int32;
cards : Array of TNode;
player1first : TNodePtr = nil;
player1last : TNodePtr = nil;
player2first : TNodePtr = nil;
player2last : TNodePtr = nil;
winnerLast : TNodePtrPtr = nil;
i : Int32;
Line: String;
turns : Int32 = 0;
nrCards : Int32 = 1;
cardIt : TNodePtr = nil;
function ParseIn(i : Int32) : String;
begin
ParseIn := ExtractWord(i, Line, [' ']);
end;
function War(pl1it, pl2it : TNodePtr) : Int32;
var
nrCards : Int32 = 5;
i :Int32; // not reuse?
begin
for i := 0 to 3 do begin
pl1it := pl1it^.nextNode;
pl2it := pl2it^.nextNode;
if (pl1it = nil) or (pl2it = nil) then
exit(0);
end;
while (pl1it^.val = pl2it^.val) do begin
nrCards := nrCards + 4;
for i := 0 to 3 do begin
pl1it := pl1it^.nextNode;
pl2it := pl2it^.nextNode;
if (pl1it = nil) or (pl2it = nil) then
exit(0);
end;
end;
if pl1it^.val > pl2it^.val then
// player 1 wins
War := nrCards
else
// player 2 wins
War := -nrCards;
end;
begin
readln(Line);
size1 := StrToInt(ParseIn(1));
Setlength(cards, size1);
for i := 0 to size1-1 do begin
readln(Line);
//writeln(StdErr, Line);
case Line[1] of
'1' : cards[i].val := 10;
'J' : cards[i].val := 11;
'Q' : cards[i].val := 12;
'K' : cards[i].val := 13;
'A' : cards[i].val := 14;
else cards[i].val := Integer(Line[1])-48;
end;
if i = size1-1 then
cards[i].nextNode := nil
else
cards[i].nextNode := @cards[i+1];
end;
readln(Line);
size2 := StrToInt(ParseIn(1));
Setlength(cards, size1+size2);
for i := size1 to size1+size2-1 do begin
readln(Line);
//writeln(StdErr, Line);
case Line[1] of
'1' : cards[i].val := 10;
'J' : cards[i].val := 11;
'Q' : cards[i].val := 12;
'K' : cards[i].val := 13;
'A' : cards[i].val := 14;
else cards[i].val := Integer(Line[1])-48;
end;
if i = size1+size2-1 then
cards[i].nextNode := nil
else
cards[i].nextNode := @cards[i+1];
end;
player1first := @cards[0];
player1last := @cards[size1-1];
player2first := @cards[size1];
player2last := @cards[size1+size2-1];
// now for the game
while (player1first <> nil) and (player2first <> nil) do begin
if player1first^.val <> player2first^.val then begin
if player1first^.val > player2first^.val then begin
// player 1 wins
writeln(StdErr, 'Player1 wins');
winnerLast := @player1last;
end else begin
// player 2 wins
writeln(StdErr, 'Player2 wins');
winnerLast := @player2last;
end;
winnerLast^^.nextNode := player1first;
winnerLast^ := player1first;
player1first := player1first^.nextNode;
winnerLast^^.nextNode := player2first;
winnerLast^ := player2first;
player2first := player2first^.nextNode;
winnerLast^^.nextNode := nil;
end else begin
// war
nrCards := War(player1first, player2first);
writeln(StdErr, 'nrCards: ', nrCards);
if nrCards = 0 then
break;
if nrCards > 0 then begin
writeln(StdErr, 'Player1 wins');
winnerLast := @player1last;
end else begin
writeln(StdErr, 'Player2 wins');
winnerLast := @player2last;
nrCards := -nrCards;
end;
for i := 0 to nrCards-1 do begin
winnerLast^^.nextNode := player1first;
winnerLast^ := player1first;
player1first := player1first^.nextNode;
end;
for i := 0 to nrCards-1 do begin
winnerLast^^.nextNode := player2first;
winnerLast^ := player2first;
player2first := player2first^.nextNode;
end;
winnerLast^^.nextNode := nil;
end;
turns := turns + 1;
write(StdErr, 'Player1: ');
cardIt := player1first;
while cardIt <> nil do begin
write(StdErr, cardIt^.val, ' ');
cardIt := cardIt^.nextNode;
end;
writeln(StdErr, ' ');
write(StdErr, 'Player2: ');
cardIt := player2first;
while cardIt <> nil do begin
write(StdErr, cardIt^.val, ' ');
cardIt := cardIt^.nextNode;
end;
writeln(StdErr, ' ');
end;
// end game
if nrCards = 0 then
// equal
writeln('PAT')
else if player2first = nil then
// player 2 won
writeln('1 ',turns)
else
// player 2 won
writeln('2 ',turns);
flush(StdErr); flush(output); // DO NOT REMOVE
end.
我有C / C ++ / C#的背景,可以解释我的编码风格。 C中的相同程序正常工作。恕我直言我真的把它翻译成了......
C代码:
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct TNode {
int val;
struct TNode* nextNode;
};
int War(struct TNode* pl1It, struct TNode* pl2It) {
int nrCards = 5;
for (int i = 0; i < 4; ++i) {
pl1It = pl1It->nextNode;
pl2It = pl2It->nextNode;
if (pl1It == NULL || pl2It == NULL) {
return 0;
}
}
while (pl1It->val == pl2It->val) {
nrCards += 4;
for (int i = 0; i < 4; ++i) {
pl1It = pl1It->nextNode;
pl2It = pl2It->nextNode;
if (pl1It == NULL || pl2It == NULL) {
return 0;
}
}
}
if (pl1It->val > pl2It->val) {
// player 1 wins
return nrCards;
} else {
// player 2 wins
return -nrCards;
}
}
int main()
{
struct TNode cards[52];
int size1;
scanf("%d", &size1);
for (int i = 0; i < size1; ++i) {
char Line[4];
scanf("%s", Line);
if (Line[0] == '1') cards[i].val = 10;
else if (Line[0] == 'J') cards[i].val = 11;
else if (Line[0] == 'Q') cards[i].val = 12;
else if (Line[0] == 'K') cards[i].val = 13;
else if (Line[0] == 'A') cards[i].val = 14;
else cards[i].val = (int)Line[0] - 48;
if (i == size1 - 1) cards[i].nextNode = NULL;
else cards[i].nextNode = &cards[i + 1];
}
int size2;
scanf("%d", &size2);
for (int i = size1; i < size1 + size2; ++i) {
char Line[4];
scanf("%s", Line);
if (Line[0] == '1') cards[i].val = 10;
else if (Line[0] == 'J') cards[i].val = 11;
else if (Line[0] == 'Q') cards[i].val = 12;
else if (Line[0] == 'K') cards[i].val = 13;
else if (Line[0] == 'A') cards[i].val = 14;
else cards[i].val = (int)Line[0] - 48;
if (i == size1 + size2 - 1) cards[i].nextNode = NULL;
else cards[i].nextNode = &cards[i + 1];
}
struct TNode* player1first = &cards[0];
struct TNode* player1last = &cards[size1 - 1];
struct TNode* player2first = &cards[size1];
struct TNode* player2last = &cards[size1 + size2 - 1];
int nrOfCards = 1; // has to do with check
int turns = 0;
// now for the game
while (player1first != NULL && player2first != NULL) {
if (player1first->val != player2first->val) {
struct TNode** winnerLast = NULL;
if (player1first->val > player2first->val) {
// player 1 wins
fprintf(stderr, "Player 1 wins.\n");
winnerLast = &player1last;
} else {
// player 2 wins
fprintf(stderr, "Player 2 wins.\n");
winnerLast = &player2last;
}
(*winnerLast)->nextNode = player1first;
(*winnerLast) = player1first;
player1first = player1first->nextNode;
(*winnerLast)->nextNode = player2first;
(*winnerLast) = player2first;
player2first = player2first->nextNode;
(*winnerLast)->nextNode = NULL;
} else {
// war
fprintf(stderr, "War: ");
nrOfCards = War(player1first, player2first);
if (nrOfCards == 0) break;
struct TNode** winnerLast;
if (nrOfCards > 0) {
// Player 1 wins
fprintf(stderr, "Player 1 wins.\n");
winnerLast = &player1last;
} else {
// Player 2 wins
fprintf(stderr, "Player 2 wins.\n");
nrOfCards = -nrOfCards;
winnerLast = &player2last;
}
for (int i = 0; i < nrOfCards; ++i) {
(*winnerLast)->nextNode = player1first;
(*winnerLast) = player1first;
player1first = player1first->nextNode;
}
for (int i = 0; i < nrOfCards; ++i) {
(*winnerLast)->nextNode = player2first;
(*winnerLast) = player2first;
player2first = player2first->nextNode;
}
(*winnerLast)->nextNode = NULL;
}
turns = turns + 1;
}
// end game
if (nrOfCards == 0) {
// equal
printf("PAT\n");
} else if (player2first == NULL) {
// player 2 won
printf("1 %d\n", turns);
} else {
// player 2 won
printf("2 %d\n", turns);
}
// Write an action using printf(). DON'T FORGET THE TRAILING \n
// To debug: fprintf(stderr, "Debug messages...\n");
return 0;
}
有人可以解释为什么pascal指针表现得如此不同,或者我做错了什么?
我发现了这个问题,我还没有完全理解。
问题是由于使用动态数组cards
引起的。这也是C代码和pascal代码之间的巨大差异。我在做
readln(Line);
size1 := StrToInt(ParseIn(1));
Setlength(cards, size1);
for i := 0 to size1-1 do begin
readln(Line);
[...]
end;
readln(Line);
size2 := StrToInt(ParseIn(1));
Setlength(cards, size1+size2);
for i := size1 to size1+size2-1 do begin
第二个Setlength似乎已经使我的初始数组的最后一个元素无效..似乎player1last^.nextNode
和cards[size-1].nextNode
不再连接。我不完全理解水下发生的事情......我只能猜测Setlength会重新分配内存并复制内容,从而使所有现有指针无效!如果是这种情况,我在发现的教程中没有正确解释这一点。
我可以幸运地解决这个问题,因为最多有52张卡,所以通过选择一个固定长度的数组,我可以解决这个问题。
cards : Array[0..51] of TNode;
好的我找到了一个来源给我答案Dynamic array - Free Pascal wiki
虽然写入动态数组的元素不会在这样的数组上使用SetLength创建数组的新实例(没有Ansistrings存在的copy-on-write),但会创建一个副本!因此,如果2个动态数组变量指向同一个数组(一个已分配给另一个),则在其中一个(或两个)上使用SetLength后,它们不会这样做。在SetLength()调用之后,两个变量是不同的数组,其元素彼此独立。