I am making a connect four AI and would like the AI to loop through the available moves from the middle and outwards because in connect four the middle moves are usually better and then the probability of alpha beta pruning happening is much higher.
For example if I gave it a array like this: [1,2,3,4,5]
It should loop though them like this 3,4,2,5,1
Or with a array like this [1,2,3,4,5,6]
It should loop though them like this 4,3,5,2,6,1
Thanks in advance
This is my code for connect four:
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': '\033[91;44mR\033[0m',
'Y': '\033[93;44mY\033[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' +
'\033[94m' + ("---------------------------------------").center(terminalWidth) + '\033[0m' + '\n' +
((" {} | {} | {} | {} | {} | {} | {} ".format(*self.board[4])).center(terminalWidth)) + '\n' +
'\033[94m' + ("---------------------------------------").center(terminalWidth) + '\033[0m' + '\n' +
((" {} | {} | {} | {} | {} | {} | {} ".format(*self.board[3])).center(terminalWidth)) + '\n' +
'\033[94m' + ("---------------------------------------").center(terminalWidth) + '\033[0m' + '\n' +
((" {} | {} | {} | {} | {} | {} | {} ".format(*self.board[2])).center(terminalWidth)) + '\n' +
'\033[94m' + ("---------------------------------------").center(terminalWidth) + '\033[0m' + '\n' +
((" {} | {} | {} | {} | {} | {} | {} ".format(*self.board[1])).center(terminalWidth)) + '\n' +
'\033[94m' + ("---------------------------------------").center(terminalWidth) + '\033[0m' + '\n' +
((" {} | {} | {} | {} | {} | {} | {} ".format(*self.board[0])).center(terminalWidth)) + '\n' +
'\033[94m' + ("---------------------------------------").center(terminalWidth) + '\033[0m' + '\n' +
(((" {} | {} | {} | {} | {} | {} | {} ").format(*self.numberLine)).center(terminalWidth))).replace('Y', '\033[93mY\033[0m')).replace('R', "\033[91mR\033[0m")).replace('|', '\033[94m|\033[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': '\033[91mRed\033[0m',
'Y': '\033[93mYellow\033[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
Always taking the middle out:
def middle_out(a):
while a:
yield a.pop(len(a) // 2)
Demo:
>>> 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
If you want to sort by distance to middle, you can use a lambda expression.
a = [1, 2, 3, 4, 5]
sortedByMiddle = sorted(a, key = lambda x: abs(x-a[len(a)//2]))
This will loop through the array and sort by the absolute value of the difference between the element and middle value of the array.
Related
from datetime import datetime
from datetime import date
import datetime
import time
import math
seconds_Yearly = 3656060*24
seconds_Daily = 606024
seconds_Hourly = 60*60
minute = 60
eventName1 = input("What is the name of the first event?")
print(" ")
eventName2 = input("What is the name of the second event?")
print(" ")
event_Year1 = input("Which year is" + " " + str(eventName1) + " " + "in?")
print(" ")
event_Month1 = input("Which month [1-12] is" + " " + str(eventName1) + " " + "in?")
print(" ")
event_Day1 = input("Which day [1-31] is" + " " + str(eventName1) + " " + "in?")
print(" ")
event_Year2 = input("Which year is" + " " + str(eventName2) + " " + "in?")
print(" ")
event_Month2 = input("Which month [1-12] is" + " " + str(eventName2) + " " + "in?")
print(" ")
event_Day2 = input("Which day [1-31] is" + " " + str(eventName2) + " " + "in?")
print(" ")
event_Year1 = int(event_Year1)
event_Month1 = int(event_Month1)
event_Day1 = int(event_Day1)
event_Year2 = int(event_Year2)
event_Month2 = int(event_Month2)
event_Day2 = int(event_Day2)
event1_dates = date((event_Year1), (event_Month1), (event_Day1))
event1_date = event1_dates.strptime((event_Year1), (event_Month1), (event_Day1), "d/m/Y")
print(str(event1_date))
print(" ")
event2_date = date((event_Year2), (event_Month2), event_Day2)
print(event2_date)
print(" ")
seconds_event1 = time.mktime(event1_date.timetuple())
print(seconds_event1)
print(" ")
seconds_event2 = time.mktime(event2_date.timetuple())
print(seconds_event2)
print(" ")
seconds_difference_rough = seconds_event2 - seconds_event1
seconds_difference = abs(seconds_difference_rough)
print(seconds_difference)
print(" ")
minutes_difference = seconds_difference/60
print(minutes_difference)
print(" ")
hours_difference = minutes_difference/60
print(hours_difference)
print(" ")
days_difference_rough = hours_difference/24
days_difference = abs(days_difference_rough)
print(days_difference)
print(" ")
years_difference_roughs = seconds_difference/seconds_Yearly
years_difference_rough = seconds_difference//seconds_Yearly
years_difference = abs(years_difference_rough)
years_difference_remainer = seconds_difference%seconds_Yearly
print(years_difference)
print(years_difference_roughs)
weeks_difference_parta = (days_difference/7)/52
weeks_difference_partb = weeks_difference_parta/years_difference_roughs
weeks_difference = abs(weeks_difference_partb)
print(weeks_difference)
months_difference_part1a = hours_difference/24/30.435
months_difference_part1aa = months_difference_part1a//1
months_difference_part1b = abs(months_difference_part1aa)
print(months_difference_part1b)
months_difference_part2a = (years_difference * 12) - months_difference_part1b
month_difference_roughs = (years_difference * 12) - months_difference_part1a
months_difference_part2b = abs(months_difference_part2a)
days_decimal, whole = math.modf(months_difference_part1a)
print(" ")
print(months_difference_part2b)
print(" ")
print(months_difference_part1a)
print(" ")
days_difference1a = days_difference/28
days_difference1ab = days_difference/30.45
days_difference1b = days_difference1ab//1
days_difference1c = (days_difference1a - days_difference1ab)
days_difference_total = abs(days_difference1b)
print(days_difference_total)
print(" ")
print(days_difference1ab)
week_difference = days_difference_total/7
week_difference_total = abs(week_difference)
print(week_difference_total)
print(" ")
#hours_one = hours_difference
def days ():
global days, days_remaing_negative, days_remaing_positve, days_remaing
if event_Year2 > event_Year1:
months_difference_rough = days_difference/(hours_difference/24/30.435)
months_difference = abs(days_difference_rough)
months_difference_remainer = seconds_difference%seconds_Daily
print(months_difference)
print(days_decimal)
days_remaings = (days_decimal * 30.435) + 1
days_remaing = days_remaings//1
days_remaing = abs(days_remaing)
print(days_remaing)
else:
days_remaings = (days_decimal * 30.435) + 1
days_remaing = days_remaings//1
print(days_remaing)
def weeks():
global days_remaing, days, new_week, new_days2
if days_remaing >= 7:
new_weeks = days_remaing/7
abs_new_week = abs(new_weeks)
new_week = new_weeks//1
new_week_decimal, whole = math.modf(abs_new_week)
new_days1 = new_week_decimal * 7
new_days2 = new_days1//1
print(new_week)
print(new_days2)
else:
new_week = 0
new_days2 = days_remaing
print(new_days2)
def difference():
global new_days2, new_week, months_difference_part1b, years_difference, eventName1, eventName2, days_remaing, days, new_week, months_difference_part2b
if event_Year2 > event_Year1 or event_Year2 == event_Year1 and event_Month2 > event_Month1:
print("a")
print(str(eventName1) + " "+ "is" + " " + str(years_difference) + "years" + " "+ str(months_difference_part2b
) + "months" + " " + str(new_week) + "weeks" + " " + str(new_days2) + "days" + " " + "before" + " " + str(eventName2))
elif event_Year2 == event_Year1 and event_Month2 > event_Month1:
print("b")
print(str(eventName1) + " "+ "is" + " " + str(years_difference) + "years" + " "+ str(months_difference_part2b
) + "months" + " " + str(new_week) + "weeks" + " " + str(new_days2) + "days" + " " + "before" + " " + str(eventName2))
elif event_Year2 == event_Year1 and event_Month2 == event_Month1 and event_Day2 > event_Day1:
print("C")
print(str(eventName1) + " "+ "is" + " " + str(years_difference) + "years" + " "+ str(months_difference_part2b
) + "months" + " " + str(new_week) + "weeks" + " " + str(new_days2) + "days" + " " + "before" + " " + str(eventName2))
else:
print("D")
print(str(eventName1) + " " + "is" + " " + "on" + " " + str(event1_date) + " " + " " + "which is" + str(years_difference) + "years" + " "+ str(months_difference_part2b
) + "months" + " " + str(new_week) + "weeks" + " " + str(new_days2) + "days" + " " + "After" + " " + " " + str(eventName2))
days ()
weeks()
difference()
Are you trying to convert event1_dates?
from datetime import date
event_Year1=2020
event_Month1=1
event_Day1=30
event1_dates = date((event_Year1), (event_Month1), (event_Day1))
print(event1_dates)
event1_date = event1_dates.strftime("%d/%m/%Y")
print(event1_date)
Output:
2020-01-30
30/01/2020
Sorry to dump my code like this, but I've been pulling my hair out the last hours trying to figure out where my minimax algorithm in python is going wrong. Any help is greatly appreciated!
inf = 1000
bo = [["x", "o", "o"],
[" ", "o", " "],
[" ", "x", "x"]]
def bestMove(board):
bestScore = -inf
bestMove = None
for i in range(3):
for j in range(3):
if(board[i][j]==" "):
board[i][j]=getTurn(board)
score = minimax(board, searchdepth, True)
board[i][j]=" "
if score > bestScore:
bestScore = score
bestMove = [i, j]
print("\n\n\n")
return bestMove
searchdepth = 10
def minimax(node, depth, maxP):
resultat = win(node)
if resultat=="x": return 1
if resultat=="o": return -1
if resultat=="tie": return 0
if depth == 0: return 0
if maxP==True:
value = -inf
for i in range(3):
for j in range(3):
if node[i][j] == " ":
node[i][j] = getTurn(node)
newval = minimax(node, depth - 1, False)
node[i][j] = " "
value = max(newval, value)
return value
if maxP==False:
value = inf
for i in range(3):
for j in range(3):
if node[i][j] == " ":
node[i][j] = getTurn(node)
newval = minimax(node, depth - 1, True)
node[i][j] = " "
value = min(newval, value)
return value
print(bestMove(bo))
Output: [1, 0]
Expected output: [2, 0]
You are always sending a 1 in case 'X' wins, this is not correct. This means that if it is O:s turn it will think it is good if X wins. The easiest way would be to give a different value depending on who's turn it is, that is give a score of 1 if YOURSELF win, -1 if OPPONENT wins and 0 if draw.
I supplied versions of getTurn and win, since you didn't supply them, and I fixed the indentation problems below, and this prints out [2,0].
inf = 1000
bo = [[" ", " ", " "],
[" ", " ", " "],
[" ", " ", " "]]
def getTurn(board):
x = sum(n == 'x' for row in bo for n in row)
o = sum(n == 'o' for row in bo for n in row)
return 'x' if x == o else 'o'
def win(board):
for i in range(3):
if board[i][0]==board[i][1]==board[i][2] and board[i][0] != ' ':
return board[i][0]
if board[0][i]==board[1][i]==board[2][i] and board[0][i] != ' ':
return board[0][i]
if board[0][0]==board[1][1]==board[2][2] and board[0][0] != ' ':
return board[0][0]
if board[0][2]==board[1][1]==board[2][0] and board[0][2] != ' ':
return board[0][2]
if not any( n == ' ' for row in board for n in row ):
return 'tie'
return None
def bestMove(board):
bestScore = -inf
bestMove = None
for i in range(3):
for j in range(3):
if(board[i][j]==" "):
board[i][j]=getTurn(board)
score = minimax(board, searchdepth, True)
board[i][j]=" "
if score > bestScore:
bestScore = score
bestMove = [i, j]
return bestMove
searchdepth = 10
def minimax(node, depth, maxP):
resultat = win(node)
if resultat=="x": return 1
if resultat=="o": return -1
if resultat=="tie": return 0
if depth == 0: return 0
if maxP:
value = -inf
for i in range(3):
for j in range(3):
if node[i][j] == " ":
node[i][j] = getTurn(node)
newval = minimax(node, depth - 1, False)
node[i][j] = " "
value = max(newval, value)
else:
value = inf
for i in range(3):
for j in range(3):
if node[i][j] == " ":
node[i][j] = getTurn(node)
newval = minimax(node, depth - 1, True)
node[i][j] = " "
value = min(newval, value)
return value
while not win(bo):
t = getTurn(bo)
m = bestMove(bo)
bo[m[0]][m[1]] = t
print(bo)
print("Result:",win(bo))
I am a young programmer that is learning python and struggling to implement an AI (using minimax) to play TicTacToe. I started watching a tutorial online, but the tutorial was on JavaScript and thus couldn't solve my problem. I also had a look at this question ( Python minimax for tictactoe ), but it did not have any answers and the implementation was considerably different from mine.
EDIT: the code you will find below is an edit suggested by one of the answers (#water_ghosts).
EDIT #2: I deleted possiblePositions, as the AI should choose a free field and not a place from the possiblePositions (that wouldn't make it that smart while implementing minimax :) )
Now the code doesn't give out any errors at all and functions properly, but one small thing: the AI always chooses the next available field. For example in situations where i am i move away from winning, instead of blocking my win option, it chooses the next free spot.
If you're wondering what that elements dict is doing there: i just wanted to make sure the programm chose the best index...
Here is my code:
class TicTacToe:
def __init__(self):
self.board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]
self.playerSymbol = ""
self.playerPosition = []
self.aiSymbol = ""
self.aiPosition = []
self.score = 0
self.winner = None
self.scoreBoard = {
self.playerSymbol: -1,
self.aiSymbol: 1,
"tie": 0
}
self.turn = 0
self.optimalMove = int()
def drawBoard(self):
print(self.board[0] + " | " + self.board[1] + " | " + self.board[2])
print("___" + "___" + "___")
print(self.board[3] + " | " + self.board[4] + " | " + self.board[5])
print("___" + "___" + "___")
print(self.board[6] + " | " + self.board[7] + " | " + self.board[8])
def choice(self):
answer = input("What do you want to play as? (type x or o) ")
if answer.upper() == "X":
self.playerSymbol = "X"
self.aiSymbol = "O"
else:
self.playerSymbol = "O"
self.aiSymbol = "X"
def won(self):
winningPositions = [{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, {0, 4, 8}, {2, 4, 6}, {0, 3, 6}, {1, 4, 7}, {2, 5, 8}]
for position in winningPositions:
if position.issubset(self.playerPosition):
self.winner = self.playerSymbol
print("Player Wins :)")
return True
elif position.issubset(self.aiPosition):
self.winner = self.aiSymbol
print("AI wins :(")
return True
if self.board.count(" ") == 0:
self.winner = "tie"
print("Guess it's a draw")
return True
return False
def findOptimalPosition(self):
bestScore = float("-Infinity")
elements = {} # desperate times call for desperate measures
for i in range(9):
if self.board[i] == " ":
self.board[i] = self.aiSymbol # AI quasi made the move here
if self.minimax(True) > bestScore:
bestScore = self.score
elements[i] = bestScore
self.board[i] = " "
return max(elements, key=lambda k: elements[k])
def minimax(self, isMaximizing):
if self.winner is not None:
return self.scoreBoard[self.winner]
if isMaximizing:
bestScore = float("-Infinity")
for i in range(9):
if self.board[i] == " ":
self.board[i] = self.aiSymbol
bestScore = max(self.minimax(False), bestScore)
self.board[i] = " "
return bestScore
else:
bestScore = float("Infinity")
for i in range(9):
if self.board[i] == " ":
self.board[i] = self.playerSymbol
bestScore = min(self.minimax(True), bestScore)
self.board[i] = " "
return bestScore
def play(self):
self.choice()
while not self.won():
if self.turn % 2 == 0:
pos = int(input("Where would you like to play? (0-8) "))
self.playerPosition.append(pos)
self.board[pos] = self.playerSymbol
self.turn += 1
self.drawBoard()
else:
aiTurn = self.findOptimalPosition()
self.aiPosition.append(aiTurn)
self.board[aiTurn] = self.aiSymbol
self.turn += 1
print("\n")
print("\n")
self.drawBoard()
else:
print("Thanks for playing :)")
tictactoe = TicTacToe()
tictactoe.play()
I come from a java background and am not used to this :(
Any help would be highly appreciated
I am open to suggestions and ways to improve my code and fix this problem.
Thanks in advance and stay healthy,
Kristi
Change this part, your implementation will return optimalMove even if it doesn't go inside the if statement, and optimalMove will not be assigned at that point, so put the return inside.
if score > sampleScore:
sampleScore = score
optimalMove = i
return optimalMove
optimalMove = 0 in play() and optimalMove = i in findOptimalField() are declaring two distinct variables, each of which is local to the function declaring it.
If you want multiple functions to have access to the same variable, you can use the global keyword, but that's generally considered a bad practice. It can make it hard to reason about the code (e.g. is var = x creating a new local variable or overwriting the value of a global?) and it doesn't stop you from accidentally using a variable before it's declared.
Since you're coming from a Java background, you can turn this into a class to get behavior more like what you expect, eliminating the need for globals:
class TicTacToe:
def __init__(self):
self.board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]
self.playerSymbol = ""
self.playerPosition = []
self.aiSymbol = ""
self.aiPosition = []
self.score = 0
self.playerSymbol = None
self.aiSymbol = None
...
def drawBoard(self):
print(self.board[0] + " | " + self.board[1] + " | " + self.board[2])
...
def choice(self):
answer = input("What do you want to play as? (type x or o) ")
if answer.upper() == "X":
self.playerSymbol = "X"
self.aiSymbol = "O"
...
Each method now takes an explicit self argument that refers to the current instance, and you can use this to access any variables that belong to the class instance instead of a particular method. If you don't include self. before a variable, that variable will still be local to the method that declares it. In this case, the drawBoard() method won't be able to access the answer variable defined in choice().
You can create new self. variables in any of the class's methods, but the best practice is to initialize all of them in the __init__ constructor method, using None as a placeholder for variables that don't have a value yet.
I am posting this as an answer, just in case somebody in the future stumbles upon the same problem :)
the main issue i encountered (besides my bad programming style) is that i forgot to update the contents the lists playerPosition and aiPosition.
You can review the rest of the changes in the working code:
class TicTacToe:
def __init__(self):
self.board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]
self.playerSymbol = ""
self.playerPosition = []
self.aiSymbol = ""
self.aiPosition = []
self.winner = None
self.scoreBoard = None
self.turn = 0
self.optimalMove = int()
def drawBoard(self):
print(self.board[0] + " | " + self.board[1] + " | " + self.board[2])
print("___" + "___" + "___")
print(self.board[3] + " | " + self.board[4] + " | " + self.board[5])
print("___" + "___" + "___")
print(self.board[6] + " | " + self.board[7] + " | " + self.board[8])
def choice(self):
answer = input("What do you want to play as? (type x or o) ")
if answer.upper() == "X":
self.playerSymbol = "X"
self.aiSymbol = "O"
else:
self.playerSymbol = "O"
self.aiSymbol = "X"
self.scoreBoard = {
self.playerSymbol: -1,
self.aiSymbol: 1,
"tie": 0
}
def availableMoves(self):
moves = []
for i in range(0, len(self.board)):
if self.board[i] == " ":
moves.append(i)
return moves
def won_print(self):
self.won()
if self.winner == self.aiSymbol:
print("AI wins :(")
exit(0)
elif self.winner == self.playerSymbol:
print("Player Wins :)")
exit(0)
elif self.winner == "tie":
print("Guess it's a draw")
exit(0)
def won(self):
winningPositions = [{0, 1, 2}, {3, 4, 5}, {6, 7, 8},
{0, 4, 8}, {2, 4, 6}, {0, 3, 6},
{1, 4, 7}, {2, 5, 8}]
for position in winningPositions:
if position.issubset(self.playerPosition):
self.winner = self.playerSymbol
return True
elif position.issubset(self.aiPosition):
self.winner = self.aiSymbol
return True
if self.board.count(" ") == 0:
self.winner = "tie"
return True
self.winner = None
return False
def set_i_ai(self, i):
self.aiPosition.append(i)
self.board[i] = self.aiSymbol
def set_clear_for_ai(self, i):
self.aiPosition.remove(i)
self.board[i] = " "
def set_i_player(self, i):
self.playerPosition.append(i)
self.board[i] = self.playerSymbol
def set_clear_for_player(self, i):
self.playerPosition.remove(i)
self.board[i] = " "
def findOptimalPosition(self):
bestScore = float("-Infinity")
elements = {} # desperate times call for desperate measures
for i in self.availableMoves():
self.set_i_ai(i)
score = self.minimax(False)
if score > bestScore:
bestScore = score
elements[i] = bestScore
self.set_clear_for_ai(i)
if bestScore == 1:
print("you messed up larry")
elif bestScore == 0:
print("hm")
else:
print("whoops i made a prog. error")
return max(elements, key=lambda k: elements[k])
def minimax(self, isMaximizing):
if self.won():
return self.scoreBoard[self.winner]
if isMaximizing:
bestScore = float("-Infinity")
for i in self.availableMoves():
self.set_i_ai(i)
bestScore = max(self.minimax(False), bestScore)
self.set_clear_for_ai(i)
return bestScore
else:
bestScore = float("Infinity")
for i in self.availableMoves():
self.set_i_player(i)
bestScore = min(self.minimax(True), bestScore)
self.set_clear_for_player(i)
return bestScore
def play(self):
self.choice()
while not self.won_print():
if self.turn % 2 == 0:
pos = int(input("Where would you like to play? (0-8) "))
self.playerPosition.append(pos)
self.board[pos] = self.playerSymbol
self.turn += 1
self.drawBoard()
else:
aiTurn = self.findOptimalPosition()
self.aiPosition.append(aiTurn)
self.board[aiTurn] = self.aiSymbol
self.turn += 1
print("\n")
print("\n")
self.drawBoard()
else:
print("Thanks for playing :)")
if __name__ == '__main__':
tictactoe = TicTacToe()
tictactoe.play()
But as mentioned, the code may work, but there are MANY problems regarding the logic and structure, so do not straight-forward copy-paste it :))
I am trying to have my program load a tree of every possible tic-tac-toe board and then when given a random board input it can print out all the possible next moves based on the tree. However, when I try to do this all of my nodes seem to return None, except the root node.
My code right now:
import random
def main():
root = createNode(board, None, "X")
setBoard = (" ", " ", " ",
" ", "X", "X",
" ", " ", "O")
nextMoves = find(root, setBoard)
print(nextMoves)
for child in nextMoves.children:
printBoard(child.boardState)
boardCases = set()
board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]
def printBoard(board):
for i in range(3):
print(board[i*3], end="")
print("|", end="")
print(board[i*3+1], end="")
print("|", end="")
print(board[i*3+2], end="")
print("")
if i < 2:
print("-+-+-")
print("~~~~~~~~~~~~")
def checkForWin(board, c):
# all column cases
for i in range(3):
if board[i] == c and board[i+3] == c and board[i+6] == c:
#print(c + " wins!")
return True
#all row cases
for i in range(3):
if board[i*3] == c and board[i*3+1] == c and board[i*3+2] == c:
#print(c + " wins!")
return True
#all across cases
if board[0] == c and board[4] == c and board[8] == c:
#print(c + " wins!")
return True
if board[2] == c and board[4] == c and board[6] == c:
#print(c + " wins!")
return True
#no wins game tie
counter = 0
for i in range(9):
if board[i] == " ":
# no tie found ---> game countinues
return False
else:
counter += 1
if counter == 9:
#print("Tie Game!")
return True
class boardNode:
def __init__(self):
self.boardState = ()
self.children = []
self.winner = ""
def numChildren(self):
return len(self.availableMoves())
def availableMoves(self):
moves = set()
for i in range(9):
if self.boardState[i] == " ":
moves.add(i)
return moves
counter = 0
def createNode(currentBoard, posToFill, c):
global counter
newNode = boardNode()
board = list(currentBoard)
counter +=1
if posToFill != None:
board[int(str(posToFill))] = c
newNode.boardState = tuple(board)
if checkForWin(tuple(board), c) == False:
newNode.winner = None
if c == "X":
for pos in newNode.availableMoves():
newNode.children.append(createNode(newNode.boardState, pos, "O"))
else:
for pos in newNode.availableMoves():
newNode.children.append(createNode(newNode.boardState, pos, "X"))
else:
newNode.winner = c
if newNode.boardState not in boardCases:
boardCases.add(newNode.boardState)
return newNode
def find(node, boardToSearch):
if list(node.boardState) == list(boardToSearch):
return node
else:
for child in node.children:
return find(child, boardToSearch)
if __name__ == "__main__":
main()
Here is my output for my current case:
$ python ticTacToe.py
None
Traceback (most recent call last):
File "test.py", line 115, in <module>
main()
File "test.py", line 11, in main
for child in nextMoves.children:
AttributeError: 'NoneType' object has no attribute 'children'
I've wrote a code for the Tic-Tac-Toe game.
Here's what the code does:
Gets the name of Player 1.
Gets the name of Player 2.
Asks if the Player 1 wants X or O.
If he chooses X then Player 2 gets O and vice versa.
Prints the empty board.
Asks both players to enter the row and column one by one and prints the updated board.
If a player doesn't repeat previous player's spot, everything works fine until the end without any problem.
If a player repeats previous player's spot, a message is displayed saying that the previous player has already taken this spot and makes him guess again.
Now, after this point, if I guess any index other than the one that the previous player has already taken, it overwrites the previous players spot.
Screenshot of the terminal when this occurs:
I'm certain that the problem is in the function enterguess(turncount), but haven't been able to spot it yet. Any idea where the problem is?
<----------------------------------------------[ Demo Without the colors ]-------------------------------------------->
Code:
import termcolor
board = [[" ", " ", " "], [" ", " ", " "], [" ", " ", " "]]
vb = termcolor.colored("|", "yellow", None, ['bold'])
s = termcolor.colored("\t+---+---+---+", "yellow", None, ['bold'])
players = {
'player1' : '',
'player2' : ''
}
sym = {
'player1': '',
'player2': ''
}
def rules ():
print("\nWelcome to Tic Tac Toe...")
def printboard():
#boardlist = [[" "," "," "],[" "," "," "],[" "," "," "]]
for row in board:
print s
print "\t" + vb + " " + row[0] + " " + vb + " " + row[1] + " " + vb + " " + row[2] + " " + vb
print s
def playerNames():
p1 = termcolor.colored(raw_input("\nEnter name of Player 1: ").title(), 'red', None, ['underline'])
p2 = termcolor.colored(raw_input("Enter name of Player 2: ").title(), 'blue', None, ['underline'])
sym['player1'] = termcolor.colored(raw_input("\n" + p1 + ', you want X or O? - ').capitalize(), 'red', None, ['bold'])
if sym['player1'] == 'X':
sym['player2'] = termcolor.colored('O', 'blue', None, ['bold'])
else:
sym['player2'] = termcolor.colored('X', 'red', None, ['bold'])
players['player1'] = p1
players['player2'] = p2
return {p1:termcolor.colored('X', 'red', None, ['bold']), p2: termcolor.colored('O', 'blue', None, ['bold'])}
def enterguess(turncount):
def guess(name):
return (int(input("\n" + players[name] + ", Enter row number: ")),
int(input(players[name] + ", Enter column number: ")))
if turncount % 2 == 0:
(row, col) = guess('player1')
try:
if board[row - 1][col - 1] in [sym['player1'], sym['player2']]:
print "\n" + players['player2'] + " already took that spot! Please guess again."
enterguess(turncount)
except:
print "\nPlease enter the indexes between 1 and 3."
enterguess(turncount)
else:
(row, col) = guess('player2')
try:
if board[row - 1][col - 1] in [sym['player1'], sym['player2']]:
print "\n" + players['player1'] + " already took that spot! Please guess again."
enterguess(turncount)
except IndexError:
print "\nPlease enter a number between 1 and 3."
enterguess(turncount)
return (row - 1, col - 1)
def changeboard(row, col, xo, c):
if c % 2 == 0:
board[row][col] = xo[players['player1']]
else:
board[row][col] = xo[players['player2']]
def checkWinner():
for x in range(3):
if board[x][0] == board[x][1] == board[x][2] and board[x][0] != " ":
return [players[n] for n, s in sym.iteritems() if s == board[x][0]][0]
for y in range(3):
if board[0][y] == board[1][y] == board[2][y] and board[0][y] != " ":
return [players[n] for n, s in sym.iteritems() if s == board[0][y]][0]
xx = 0
yy = 2
while True:
if board[xx][xx] == board[1][1] == board[2][yy] and board[xx][xx] != " ":
return [players[n] for n, s in sym.iteritems() if s == board[xx][xx]][0]
xx += 2
yy -= 2
if xx == 2:
break
def main():
rules()
xo = playerNames()
printboard()
turncount = 0
for turn in range(9):
(r, c) = enterguess(turncount)
print (r, c)
changeboard(r, c, xo, turncount)
turncount += 1
winner = checkWinner()
printboard()
if winner:
print("\nCongratulations " + winner + "! You are the winner.\n")
break
if turn == 8:
print ("\n Well! Its a tie.\n")
main()
The issue you have is that although you recall enterguess, you don't return it. So it simply keeps recalling enterguess until a valid input is given, then it just ignores all this and goes to the final return statement which still has the malformed guess.
Add return statements:
def enterguess(turncount):
def guess(name):
return (int(input("\n" + players[name] + ", Enter row number: ")),
int(input(players[name] + ", Enter column number: ")))
if turncount % 2 == 0:
(row, col) = guess('player1')
try:
if board[row - 1][col - 1] in [sym['player1'], sym['player2']]:
print "\n" + players['player2'] + " already took that spot! Please guess again."
return enterguess(turncount)
except:
print "\nPlease enter the indexes between 1 and 3."
return enterguess(turncount)
else:
(row, col) = guess('player2')
try:
if board[row - 1][col - 1] in [sym['player1'], sym['player2']]:
print "\n" + players['player1'] + " already took that spot! Please guess again."
return enterguess(turncount)
except IndexError:
print "\nPlease enter a number between 1 and 3."
return nterguess(turncount)
return (row - 1, col - 1)