从中间向外循环列表
Loop over a list from the middle outwards
我正在制作一个连接四个 AI,并希望 AI 从中间和向外循环遍历可用的动作,因为在连接四个中,中间的动作通常更好,然后发生 alpha beta 修剪的概率要高得多.
例如,如果我给它一个这样的数组:[1,2,3,4,5]
它应该像这样循环 3,4,2,5,1
或者像这样的数组 [1,2,3,4,5,6]
它应该像这样循环 4,3,5,2,6,1
提前致谢
这是我连接四的代码:
import os # os.system('cls') to clear screen
import random
import time
import sys
class ConnectFour:
def __init__(self):
self.board = [[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "]]
self.numberLine = [1,2,3,4,5,6,7]
self.rowCount = 6
self.columnCount = 7
def printBoard(self, column=None, row=None):
os.system('cls') # clears the screen
terminalWidth = os.get_terminal_size().columns
terminalHeight = os.get_terminal_size().lines
playerToHighlighted = {
'R': '3[91;44mR3[0m',
'Y': '3[93;44mY3[0m' }
print("\n"*round(terminalHeight/2 - (1 + self.columnCount))) #
highlightedPlayer = ''
try:
playerHold = self.board[row][column]
self.board[row][column] = 'H'
highlightedPlayer = playerToHighlighted[playerHold]
except:
pass
print(
((((
((" {} | {} | {} | {} | {} | {} | {} ".format(*self.board[5])).center(terminalWidth)) + '\n' +
'3[94m' + ("---------------------------------------").center(terminalWidth) + '3[0m' + '\n' +
((" {} | {} | {} | {} | {} | {} | {} ".format(*self.board[4])).center(terminalWidth)) + '\n' +
'3[94m' + ("---------------------------------------").center(terminalWidth) + '3[0m' + '\n' +
((" {} | {} | {} | {} | {} | {} | {} ".format(*self.board[3])).center(terminalWidth)) + '\n' +
'3[94m' + ("---------------------------------------").center(terminalWidth) + '3[0m' + '\n' +
((" {} | {} | {} | {} | {} | {} | {} ".format(*self.board[2])).center(terminalWidth)) + '\n' +
'3[94m' + ("---------------------------------------").center(terminalWidth) + '3[0m' + '\n' +
((" {} | {} | {} | {} | {} | {} | {} ".format(*self.board[1])).center(terminalWidth)) + '\n' +
'3[94m' + ("---------------------------------------").center(terminalWidth) + '3[0m' + '\n' +
((" {} | {} | {} | {} | {} | {} | {} ".format(*self.board[0])).center(terminalWidth)) + '\n' +
'3[94m' + ("---------------------------------------").center(terminalWidth) + '3[0m' + '\n' +
(((" {} | {} | {} | {} | {} | {} | {} ").format(*self.numberLine)).center(terminalWidth))).replace('Y', '3[93mY3[0m')).replace('R', "3[91mR3[0m")).replace('|', '3[94m|3[0m')).replace('H', highlightedPlayer) )
try:
self.board[row][column] = playerHold
except:
pass
def clearBoard(self):
self.board = [[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "]]
def makeMove(self, position, player):
for row in range(self.rowCount):
if self.board[row][position] == " ":
self.board[row][position] = player
return row
def availableMoves(self): # this might be broken
moves = []
for column in range(self.columnCount):
if self.board[self.rowCount - 1][column] == ' ':
moves.append(column)
return moves
def removeMove(self, row, column):
self.board[row][column] = " "
def checkWin(self):
for player in ('R', 'Y'):
for column in range(self.columnCount - 3): # Check vertical locations for win
for row in range(self.rowCount):
if self.board[row][column] == player and self.board[row][column+1] == player and self.board[row][column+2] == player and self.board[row][column+3] == player:
return player
for column in range(self.columnCount): # Check vertical locations for win
for row in range(self.rowCount - 3):
if self.board[row][column] == player and self.board[row+1][column] == player and self.board[row+2][column] == player and self.board[row+3][column] == player:
return player
for column in range(self.columnCount- 3): # Check positively sloped diaganols
for row in range(self.rowCount - 3):
if self.board[row][column] == player and self.board[row+1][column+1] == player and self.board[row+2][column+2] == player and self.board[row+3][column+3] == player:
return player
for column in range(self.columnCount-3): # Check negatively sloped diaganols
for row in range(3, self.rowCount):
if self.board[row][column] == player and self.board[row-1][column+1] == player and self.board[row-2][column+2] == player and self.board[row-3][column+3] == player:
return player
def whoWon(self):
if self.checkWin() == 'R':
return "Red"
elif self.checkWin() == 'Y':
return "Yellow"
elif self.checkGameOver() == True:
return "Nobody"
def checkGameOver(self):
if self.checkWin() != None:
return True
for column in range(self.columnCount):
if self.board[self.rowCount - 1][column] == " ":
return False
return True
def getMoveCount(self, player): # getStoneCount this can be better
moveCount = 0
for column in range(self.columnCount):
for row in range(self.rowCount):
if self.board[row][column] == player:
moveCount += 1
return moveCount
def minimax(self, node, depth, alpha, beta, player):
if depth == 0 or node.checkGameOver():
if node.checkWin() == 'R':
return -(22 - self.getMoveCount('R'))
elif node.checkWin() == 'Y':
return 22 - self.getMoveCount('Y')
else:
return 0
if player == 'Y':
maxValue = -(sys.maxsize)
for move in node.availableMoves():
moveRow = node.makeMove(move, player)
moveValue = self.minimax(node, depth-1, alpha, beta, changePlayer(player))
node.removeMove(moveRow, move)
maxValue = max(maxValue, moveValue)
alpha = max(alpha, moveValue)
if beta <= alpha:
break
return maxValue
if player == 'R':
minValue = sys.maxsize
for move in node.availableMoves():
moveRow = node.makeMove(move, player)
moveValue = self.minimax(node, depth-1, alpha, beta, changePlayer(player))
node.removeMove(moveRow, move)
minValue = min(minValue, moveValue)
beta = min(beta, moveValue)
if beta <= alpha:
break
return minValue
def changePlayer(player):
if player == 'R':
return 'Y'
else:
return 'R'
def makeBestMove(board, depth, player):
neutralValue = 0
choices = []
bestLossingMoveValue = -(sys.maxsize)
for move in board.availableMoves():
moveRow = board.makeMove(move, player)
moveValue = board.minimax(board, depth-1, -(sys.maxsize), sys.maxsize, changePlayer(player))
board.removeMove(moveRow, move)
print(moveValue)
if moveValue > neutralValue:
choices = [move]
#instaWin = True
break
elif moveValue == neutralValue:
choices.append(move)
elif moveValue > bestLossingMoveValue:
bestLossingMove = move
if len(choices) > 0:
return random.choice(choices), choices
elif bestLossingMove != None:
return bestLossingMove, [False]
else:
return random.choice(board.availableMoves()), [] # idk if i need this
def askPlayAgain():
terminalWidth = os.get_terminal_size().columns
print("Would you like to play again?".center(terminalWidth))
playAgain = input(' ' * round(terminalWidth/2 - 8) + "Enter y/n here: ")
if playAgain == 'y':
game.clearBoard()
game.printBoard()
setupGame()
elif playAgain == 'n':
exit()
else:
game.printBoard()
print("Error: Please try again!".center(terminalWidth))
askPlayAgain()
playerToColor = {
'R': '3[91mRed3[0m',
'Y': '3[93mYellow3[0m' }
def askPlayerMove(player):
terminalWidth = os.get_terminal_size().columns
print(("You are " + playerToColor[player] + ": Choose number from 1-7").center(terminalWidth + 8))
move = input(" " * round(terminalWidth/2 - 9) + "Enter Move Here: ")
try:
move = int(move) - 1
except:
game.printBoard()
print("Error: Please try again!".center(terminalWidth))
return askPlayerMove(player)
if move >= 0 and move <= game.columnCount - 1:
row = game.makeMove(move, player)
return [move, row]
else:
game.printBoard()
print("Error: Please try again!".center(terminalWidth))
return askPlayerMove(player)
def askDepth():
terminalWidth = os.get_terminal_size().columns
print(("What depth do you want the engine to use?").center(terminalWidth))
depth = input(" " * round(terminalWidth/2 - 17) + "Enter depth here: (Recommended: 9) ")
try:
depth = int(depth)
return depth
except:
game.printBoard()
print("Error: Please try again!".center(terminalWidth))
return askDepth()
def setupGame():
terminalWidth = os.get_terminal_size().columns
print("Would you like to play computer or player".center(terminalWidth))
gameMode = input(" " * round(terminalWidth/2 - 8) + "Enter c/p Here: ")
if gameMode == 'p':
startGame(gameMode)
elif gameMode == 'c':
game.printBoard()
depth = askDepth()
startGame(gameMode, depth)
else:
game.printBoard()
print("Error: Please try again!".center(terminalWidth))
setupGame()
def startGame(gameMode, depth=None):
game.printBoard()
while game.checkGameOver() == False:
movePosition = askPlayerMove('R')
game.printBoard(movePosition[0], movePosition[1])
if game.checkGameOver() == True:
break
if gameMode == 'c':
terminalWidth = os.get_terminal_size().columns
print("Computer choosing move...".center(terminalWidth))
computerMove = makeBestMove(game, depth, 'Y')
movePosition[1] = game.makeMove(computerMove[0], 'Y')
movePosition[0] = computerMove[0]
computerChoices = computerMove[1]
elif gameMode == 'p':
movePosition = askPlayerMove('Y')
game.printBoard(movePosition[0], movePosition[1])
if gameMode == 'c':
print(("Computer choices are " + str(computerChoices)).center(terminalWidth))
terminalWidth = os.get_terminal_size().columns
game.printBoard()
print(("Game Over. " + game.whoWon() + " Wins").center(terminalWidth))
askPlayAgain()
if __name__ == '__main__':
game = ConnectFour()
game.printBoard()
setupGame() #start game
如果想按到中间的距离排序,可以使用lambda表达式。
a = [1, 2, 3, 4, 5]
sortedByMiddle = sorted(a, key = lambda x: abs(x-a[len(a)//2]))
这将遍历数组并按数组元素与中间值之差的绝对值排序。
总是把中间的去掉:
def middle_out(a):
while a:
yield a.pop(len(a) // 2)
演示:
>>> print(*middle_out([1,2,3,4,5]))
3 4 2 5 1
>>> print(*middle_out([1,2,3,4,5,6]))
4 3 5 2 6 1
我正在制作一个连接四个 AI,并希望 AI 从中间和向外循环遍历可用的动作,因为在连接四个中,中间的动作通常更好,然后发生 alpha beta 修剪的概率要高得多.
例如,如果我给它一个这样的数组:[1,2,3,4,5]
它应该像这样循环 3,4,2,5,1
或者像这样的数组 [1,2,3,4,5,6]
它应该像这样循环 4,3,5,2,6,1
提前致谢
这是我连接四的代码:
import os # os.system('cls') to clear screen
import random
import time
import sys
class ConnectFour:
def __init__(self):
self.board = [[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "]]
self.numberLine = [1,2,3,4,5,6,7]
self.rowCount = 6
self.columnCount = 7
def printBoard(self, column=None, row=None):
os.system('cls') # clears the screen
terminalWidth = os.get_terminal_size().columns
terminalHeight = os.get_terminal_size().lines
playerToHighlighted = {
'R': '3[91;44mR3[0m',
'Y': '3[93;44mY3[0m' }
print("\n"*round(terminalHeight/2 - (1 + self.columnCount))) #
highlightedPlayer = ''
try:
playerHold = self.board[row][column]
self.board[row][column] = 'H'
highlightedPlayer = playerToHighlighted[playerHold]
except:
pass
print(
((((
((" {} | {} | {} | {} | {} | {} | {} ".format(*self.board[5])).center(terminalWidth)) + '\n' +
'3[94m' + ("---------------------------------------").center(terminalWidth) + '3[0m' + '\n' +
((" {} | {} | {} | {} | {} | {} | {} ".format(*self.board[4])).center(terminalWidth)) + '\n' +
'3[94m' + ("---------------------------------------").center(terminalWidth) + '3[0m' + '\n' +
((" {} | {} | {} | {} | {} | {} | {} ".format(*self.board[3])).center(terminalWidth)) + '\n' +
'3[94m' + ("---------------------------------------").center(terminalWidth) + '3[0m' + '\n' +
((" {} | {} | {} | {} | {} | {} | {} ".format(*self.board[2])).center(terminalWidth)) + '\n' +
'3[94m' + ("---------------------------------------").center(terminalWidth) + '3[0m' + '\n' +
((" {} | {} | {} | {} | {} | {} | {} ".format(*self.board[1])).center(terminalWidth)) + '\n' +
'3[94m' + ("---------------------------------------").center(terminalWidth) + '3[0m' + '\n' +
((" {} | {} | {} | {} | {} | {} | {} ".format(*self.board[0])).center(terminalWidth)) + '\n' +
'3[94m' + ("---------------------------------------").center(terminalWidth) + '3[0m' + '\n' +
(((" {} | {} | {} | {} | {} | {} | {} ").format(*self.numberLine)).center(terminalWidth))).replace('Y', '3[93mY3[0m')).replace('R', "3[91mR3[0m")).replace('|', '3[94m|3[0m')).replace('H', highlightedPlayer) )
try:
self.board[row][column] = playerHold
except:
pass
def clearBoard(self):
self.board = [[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "],
[" ", " ", " ", " ", " ", " "," "]]
def makeMove(self, position, player):
for row in range(self.rowCount):
if self.board[row][position] == " ":
self.board[row][position] = player
return row
def availableMoves(self): # this might be broken
moves = []
for column in range(self.columnCount):
if self.board[self.rowCount - 1][column] == ' ':
moves.append(column)
return moves
def removeMove(self, row, column):
self.board[row][column] = " "
def checkWin(self):
for player in ('R', 'Y'):
for column in range(self.columnCount - 3): # Check vertical locations for win
for row in range(self.rowCount):
if self.board[row][column] == player and self.board[row][column+1] == player and self.board[row][column+2] == player and self.board[row][column+3] == player:
return player
for column in range(self.columnCount): # Check vertical locations for win
for row in range(self.rowCount - 3):
if self.board[row][column] == player and self.board[row+1][column] == player and self.board[row+2][column] == player and self.board[row+3][column] == player:
return player
for column in range(self.columnCount- 3): # Check positively sloped diaganols
for row in range(self.rowCount - 3):
if self.board[row][column] == player and self.board[row+1][column+1] == player and self.board[row+2][column+2] == player and self.board[row+3][column+3] == player:
return player
for column in range(self.columnCount-3): # Check negatively sloped diaganols
for row in range(3, self.rowCount):
if self.board[row][column] == player and self.board[row-1][column+1] == player and self.board[row-2][column+2] == player and self.board[row-3][column+3] == player:
return player
def whoWon(self):
if self.checkWin() == 'R':
return "Red"
elif self.checkWin() == 'Y':
return "Yellow"
elif self.checkGameOver() == True:
return "Nobody"
def checkGameOver(self):
if self.checkWin() != None:
return True
for column in range(self.columnCount):
if self.board[self.rowCount - 1][column] == " ":
return False
return True
def getMoveCount(self, player): # getStoneCount this can be better
moveCount = 0
for column in range(self.columnCount):
for row in range(self.rowCount):
if self.board[row][column] == player:
moveCount += 1
return moveCount
def minimax(self, node, depth, alpha, beta, player):
if depth == 0 or node.checkGameOver():
if node.checkWin() == 'R':
return -(22 - self.getMoveCount('R'))
elif node.checkWin() == 'Y':
return 22 - self.getMoveCount('Y')
else:
return 0
if player == 'Y':
maxValue = -(sys.maxsize)
for move in node.availableMoves():
moveRow = node.makeMove(move, player)
moveValue = self.minimax(node, depth-1, alpha, beta, changePlayer(player))
node.removeMove(moveRow, move)
maxValue = max(maxValue, moveValue)
alpha = max(alpha, moveValue)
if beta <= alpha:
break
return maxValue
if player == 'R':
minValue = sys.maxsize
for move in node.availableMoves():
moveRow = node.makeMove(move, player)
moveValue = self.minimax(node, depth-1, alpha, beta, changePlayer(player))
node.removeMove(moveRow, move)
minValue = min(minValue, moveValue)
beta = min(beta, moveValue)
if beta <= alpha:
break
return minValue
def changePlayer(player):
if player == 'R':
return 'Y'
else:
return 'R'
def makeBestMove(board, depth, player):
neutralValue = 0
choices = []
bestLossingMoveValue = -(sys.maxsize)
for move in board.availableMoves():
moveRow = board.makeMove(move, player)
moveValue = board.minimax(board, depth-1, -(sys.maxsize), sys.maxsize, changePlayer(player))
board.removeMove(moveRow, move)
print(moveValue)
if moveValue > neutralValue:
choices = [move]
#instaWin = True
break
elif moveValue == neutralValue:
choices.append(move)
elif moveValue > bestLossingMoveValue:
bestLossingMove = move
if len(choices) > 0:
return random.choice(choices), choices
elif bestLossingMove != None:
return bestLossingMove, [False]
else:
return random.choice(board.availableMoves()), [] # idk if i need this
def askPlayAgain():
terminalWidth = os.get_terminal_size().columns
print("Would you like to play again?".center(terminalWidth))
playAgain = input(' ' * round(terminalWidth/2 - 8) + "Enter y/n here: ")
if playAgain == 'y':
game.clearBoard()
game.printBoard()
setupGame()
elif playAgain == 'n':
exit()
else:
game.printBoard()
print("Error: Please try again!".center(terminalWidth))
askPlayAgain()
playerToColor = {
'R': '3[91mRed3[0m',
'Y': '3[93mYellow3[0m' }
def askPlayerMove(player):
terminalWidth = os.get_terminal_size().columns
print(("You are " + playerToColor[player] + ": Choose number from 1-7").center(terminalWidth + 8))
move = input(" " * round(terminalWidth/2 - 9) + "Enter Move Here: ")
try:
move = int(move) - 1
except:
game.printBoard()
print("Error: Please try again!".center(terminalWidth))
return askPlayerMove(player)
if move >= 0 and move <= game.columnCount - 1:
row = game.makeMove(move, player)
return [move, row]
else:
game.printBoard()
print("Error: Please try again!".center(terminalWidth))
return askPlayerMove(player)
def askDepth():
terminalWidth = os.get_terminal_size().columns
print(("What depth do you want the engine to use?").center(terminalWidth))
depth = input(" " * round(terminalWidth/2 - 17) + "Enter depth here: (Recommended: 9) ")
try:
depth = int(depth)
return depth
except:
game.printBoard()
print("Error: Please try again!".center(terminalWidth))
return askDepth()
def setupGame():
terminalWidth = os.get_terminal_size().columns
print("Would you like to play computer or player".center(terminalWidth))
gameMode = input(" " * round(terminalWidth/2 - 8) + "Enter c/p Here: ")
if gameMode == 'p':
startGame(gameMode)
elif gameMode == 'c':
game.printBoard()
depth = askDepth()
startGame(gameMode, depth)
else:
game.printBoard()
print("Error: Please try again!".center(terminalWidth))
setupGame()
def startGame(gameMode, depth=None):
game.printBoard()
while game.checkGameOver() == False:
movePosition = askPlayerMove('R')
game.printBoard(movePosition[0], movePosition[1])
if game.checkGameOver() == True:
break
if gameMode == 'c':
terminalWidth = os.get_terminal_size().columns
print("Computer choosing move...".center(terminalWidth))
computerMove = makeBestMove(game, depth, 'Y')
movePosition[1] = game.makeMove(computerMove[0], 'Y')
movePosition[0] = computerMove[0]
computerChoices = computerMove[1]
elif gameMode == 'p':
movePosition = askPlayerMove('Y')
game.printBoard(movePosition[0], movePosition[1])
if gameMode == 'c':
print(("Computer choices are " + str(computerChoices)).center(terminalWidth))
terminalWidth = os.get_terminal_size().columns
game.printBoard()
print(("Game Over. " + game.whoWon() + " Wins").center(terminalWidth))
askPlayAgain()
if __name__ == '__main__':
game = ConnectFour()
game.printBoard()
setupGame() #start game
如果想按到中间的距离排序,可以使用lambda表达式。
a = [1, 2, 3, 4, 5]
sortedByMiddle = sorted(a, key = lambda x: abs(x-a[len(a)//2]))
这将遍历数组并按数组元素与中间值之差的绝对值排序。
总是把中间的去掉:
def middle_out(a):
while a:
yield a.pop(len(a) // 2)
演示:
>>> print(*middle_out([1,2,3,4,5]))
3 4 2 5 1
>>> print(*middle_out([1,2,3,4,5,6]))
4 3 5 2 6 1