你正试图决定谁将赢得一场假设最佳选择的比赛。这个想法是有多个空格,_ _ _ _ _ _ _。玩家一上去并标记两个连续点。然后玩家2去做同样的事情。第一个不能去胜利的球员。因此,考虑到之前的董事会,这是一个可能性:
P1:x x _ _ _ _ _
P2:x x _ x x _ _
P1:x x _ x x x x
所以玩家2获胜。您将获得一个数组,其中0表示可用空间,1表示标记点。阵列可能已经标记了一些斑点。
我能想到的唯一方法是检查每一个动作,并检查每次动作是否在获胜后检查每一个可能的动作。我甚至无法弄清楚我会怎么做,但我希望有更好的方法。有什么想法吗?
答案 0 :(得分:3)
你应该向后解决这样的问题。
考虑到所有可能的游戏状态,请通过并确定哪个是玩家1(W)的胜利,哪个是玩家2(L)的胜利。最初,你只知道没有人可以玩的州的答案。在这种情况下答案是
W
如果没有两个连续点和总点数为4k
L
如果没有两个连续点和总点数为4k+2
现在倒退了。如果玩家1可以移动到W
的任何棋盘位置,请将其标记为W
,如果有任何棋盘位置,玩家2可以移动到L
,将其标记为L
。同样,这不会立即标记所有位置,但迭代这将。迭代部分是这个
W
如果有4k+2
个点和两个连续点,当填充时,会给出标记为W
的位置L
如果有4k
个点和两个连续点,当填充时,会给出标记为L
的位置 示例:考虑董事会_ _ _ _ _ _
。从最后的状态进行评估
第二名球员:
X X X X X X (L - terminal)
玩家一动:
X X _ X X _ (W - terminal)
_ X X _ X X (W - terminal)
_ X X X X _ (W - terminal)
X X X X _ _ (L - must move to X X X X X X)
_ _ X X X X (L - must move to X X X X X X)
第二名球员:
X X _ _ _ _ (L - can move to X X X X _ _)
_ X X _ _ _ (W - must move to _ X X _ X X or _ X X X X _)
_ _ X X _ _ (L - can move to X X X X _ _)
_ _ _ X X _ (W - must move to X X _ X X _ or _ X X X X _)
_ _ _ _ X X (L - can move to X X _ _ X X)
玩家一动:
_ _ _ _ _ _ (W - can move to _ X X _ _ _)
您可以递归编程,以便将每个位置评估为W
或L
。让每个董事会职位P
由长度为n
的二进制向量表示,其中1
表示采用的点,0
表示开放点。这是doesPlayerOneWin
的伪代码:
// STORE NUMBER OF ONES
int m = 0;
for (int i=0; i<n; ++i)
m += P[i];
// LOOK FOR LEGAL MOVES
bool canMove = false;
for (int i=0; i<n-1; ++i)
int[] newP = P;
if (P[i]+P[i+1] == 0) {
canMove = true;
newP[i] = 1;
newP[i+1] = 1;
// PLAYER ONE CAN MOVE TO WIN
if (m % 4 == 0 && doesPlayerOneWin(newP))
return true;
// PLAYER TWO CAN MOVE TO WIN
if (m % 4 == 2 && !doesPlayerOneWin(newP))
return false;
}
}
// IF NO LEGAL MOVES, PLAYER TO MOVE WINS
if (!canMove && m % 4 == 0)
return true;
else if (!canMove && m % 4 == 2)
return false;
// OTHERWISE IF LOOP RUNS, PLAYER TO MOVE LOSES
if (m % 4 == 0)
return false;
else
return true;
答案 1 :(得分:2)
有一种理论允许解决此类游戏。
你的游戏是一个公正的游戏 - 两个玩家从每个位置都有相同的动作。国际象棋不公正,因为白色只能控制白人。当玩家没有移动时游戏结束,然后他输了。假设每场比赛都在有限的时间内结束。
你可以分析位置,并像PengOne建议的那样标记它们,L和W.失败的位置是所有可能的移动导致获胜位置,并且获胜位置是至少有一个移动位置的位置失败的位置。一种递归的,但定义明确的标签。当玩家没有移动时,所有连续的位置都会获胜(vacuous truth),所以这被标记为失败位置。
您可以计算更多信息,这对您有所帮助。将mex(A)称为不在A中的最小非负整数。例如,mex({0,1,5})= 2且mex({1,2,3})= 0。现在,您可以使用所有标签的mex标记每个位置。这也是一个递归的,定义明确的标签。如果一个位置的值为0,则该位置正在丢失。在此分类下,标记为0的位置正在丢失,但您对获胜位置的细粒度分类为1,2,....
这些数字可以让你计算两场比赛之和的价值。您可以通过独立播放来添加两个游戏。在移动过程中,您可以在第一场比赛或第二场比赛中进行比赛。游戏___X__X__
中的位置实际上是三个游戏___
,__
,__
的总和。
Sprague-Grundy定理。 N个游戏的总和值a_1,a_2,...,a_N的值为a_1 x或a_2 xor ... a_N。因此,如果他们的值xor为0,则N场比赛的总和正在失去。
您的初始位置是K个独立游戏的总和,以X分隔。你需要找到每个空条___...__
的Sprague-Grundy值,xor它们,如果结果是0,则返回。我想如果你试图计算前50个,你可能会得到一个如何计算值的提示它们。
由于我不喜欢用这个网站作为工作的替代品,所以我就此止步。希望你能够完成,如果你遇到困难,请提出问题。
答案 2 :(得分:1)
Assuming optimal choices
实际上是一个最佳的策略。
为玩家“选择”移动的常用方法是使用min-max algorithm。 [它实际上是deep blue用来赢得卡斯帕罗夫的算法]。 Min-Max为每个玩家“选择”最佳移动,以便选择它将产生的移动。
min-max算法通常是启发式算法,并对游戏的每个状态进行评估,但是 - 在您的情况下,由于您正在寻找最佳移动和暴力解决方案,因此您需要在每个状态中使用min-max迭代直到游戏以赢家/输家结束,这是对董事会的唯一评价。
使用此方法,您可以使用起始状态的值确定哪个玩家将获胜 - 它将指示您认为是“我”的玩家是赢家还是输家。
请注意,对于这种特殊情况,只根据最终状态对电路板进行评估,min-max会衰减成与@ PengOne解决方案非常相似的东西。
答案 3 :(得分:1)
对于它的价值,这是一个用Python编写的蛮力解算器。有趣!您甚至可以使用N&gt;运行它。 2用于每回合放下更大的块。请注意,失败的玩家只能进行最左边的有效移动。
首先是输出。我以不同的棋盘尺寸(这里从2到16)运行游戏。
Size = 2
..
11
Player 1 Wins!
Size = 3
...
11.
Player 1 Wins!
Size = 4
....
.11.
Player 1 Wins!
Size = 5
.....
11...
1122.
Player 2 Wins!
Size = 6
......
..11..
2211..
221111
Player 1 Wins!
Size = 7
.......
11.....
1122...
112211.
Player 1 Wins!
Size = 8
........
.11.....
.1122...
.112211.
Player 1 Wins!
Size = 9
.........
11.......
1122.....
112211...
11221122.
Player 2 Wins!
Size = 10
..........
....11....
22..11....
22..1111..
22221111..
2222111111
Player 1 Wins!
Size = 11
...........
11.........
1122.......
112211.....
11221122...
1122112211.
Player 1 Wins!
Size = 12
............
.11.........
.1122.......
.112211.....
.11221122...
.1122112211.
Player 1 Wins!
Size = 13
.............
...11........
22.11........
22.11.11.....
22.11.1122...
22.11.112211.
Player 1 Wins!
Size = 14
..............
......11......
22....11......
22....1111....
2222..1111....
2222..111111..
222222111111..
22222211111111
Player 1 Wins!
Size = 15
...............
11.............
11...22........
1111.22........
1111.22.22.....
1111.22.2211...
1111.22.221122.
Player 2 Wins!
Size = 16
................
.....11.........
22...11.........
2211.11.........
2211.1122.......
2211.112211.....
2211.11221122...
2211.1122112211.
Player 1 Wins!
以下是代码:
N = 2 # number of pieces placed per turn
CACHE = {}
def compute_moves(board):
gaps = [0] * len(board)
previous = 0
for i in range(len(board) - 1, -1, -1):
if board[i]:
previous = 0
gaps[i] = 0
else:
previous += 1
gaps[i] = previous
return [i for i, gap in enumerate(gaps) if gap >= N]
def do_move(board, index, player):
for i in range(N):
board[index + i] = player
def undo_move(board, index):
for i in range(N):
board[index + i] = 0
def search(board):
key = tuple(board)
if key in CACHE:
return CACHE[key]
moves = compute_moves(board)
for move in moves:
do_move(board, move, 1)
a, _ = search(board)
undo_move(board, move)
if not a:
result = (True, move)
break
else:
result = (False, moves[0] if moves else None)
CACHE[key] = result
return result
def play(board):
player = 0
while True:
print ''.join(str(x or '.') for x in board)
_, index = search(board)
if index is None:
break
do_move(board, index, player + 1)
player = int(not player)
print 'Player %d Wins!' % (int(not player) + 1)
for size in range(2, 17):
print 'Size = %d' % size
board = [0] * size
play(board)
print