I'm trying to build 4 in row game so when I try to make the players have the same choice it give this error
list index out of range
this is an example of what happedenter image description here
it seems the probleme with winning condition but I don't understand why this happend
it gives right answer when the input is correct like make the one player win but when make them have same choice it is break.
the code is
Width = 4
Height = 4
game_board = []
for x in range(Width): game_board.append(list(['a'] * Height))
def make_move(board, borad_row, board_col, piece):
board[borad_row][board_col] = piece
# check if the slot is empty
def is_slot_empty(board, board_col):
return board[Width - 1][board_col] == 'a'
# get the next available
def next_available_slot(board, board_col):
for i in range(Width):
if board[i][board_col] == 'a':
return i
# method for winning conditions
def winning(board, piece):
# check horizontally
for i in range(Height):
for j in range(Width):
if board[j][i] == piece and board[j][i + 1] == piece and board[j][i + 2] == piece and board[j][
i + 3] == piece:
return True
# check vertically
for i in range(Height):
for j in range(Width):
if board[j][i] == piece and board[j + 1][i] == piece and board[j + 2][i] == piece and board[j + 3][
i] == piece:
return True
# positive diagonal
for i in range(Height):
for j in range(Width):
if board[j][i] == piece and board[j + 1][i + 1] == piece and board[j + 2][i + 2] == piece and board[j + 3][
i + 3] == piece:
return True
# negative diagonal
for i in range(Height):
for j in range(Width):
if board[j][i] == piece and board[j - 1][i + 1] == piece and board[j - 2][i + 2] == piece and board[j - 3][
i + 3] == piece:
return True
game_end = False
turn_1 = 0
while not game_end:
if turn_1 == 0:
user_input = int(input("player_1:"))
if is_slot_empty(game_board, user_input):
user_input_row = next_available_slot(game_board, user_input)
make_move(game_board, user_input_row, user_input, 'X')
if winning(game_board, 'X'):
print("player 1 wins")
game_end = True
else:
user_input: int = int(input("player_2:"))
if is_slot_empty(game_board, user_input):
user_input_row = next_available_slot(game_board, user_input)
make_move(game_board, user_input_row, user_input, 'Z')
if winning(game_board, 'Z'):
print("player_2 wins")
game_end = True
for row in reversed(game_board):
print(row)
# alternating between 2 users
turn_1 += 1
turn_1 = turn_1 % 2
To fix winning(), try something like this:
def winning(board, piece):
# check horizontally
for i in range(Height):
if all(board[j][i] == piece for j in range(Width)):
return True
# check vertically
for j in range(Width):
if all(board[j][i] == piece for i in range(Height)):
return True
# positive diagonal
if all(board[i][i] == piece for i in range(Width)):
return True
# negative diagonal
if all(board[i][-(i+1)] == piece for i in range(Width)):
return True
This is only designed to handle a board with equal values for Height and Width.
When you do the checks, make sure to check only the indices that are within the board.
For example, for the horizontal check, both the left end (i) and the right end (i + 3) need to be within the 0..Width-1 range. So you should limit the value of the i index so that i + 3 is still less than Width:
...
# check horizontally
for i in range(Width - 3):
for j in range(Height):
if board[j][i] == piece and board[j][i + 1] == piece and board[j][i + 2] == piece and board[j][i + 3] == piece:
return True
...
Related
I am trying to create a tic tac toe game with an adjustable game size and a computer that uses the minimax algorithm. The game sizes can only be odd numbers, to make sure that diagonal wins are always possible. The game runs with no errors, but I know that the minimax algorithm isn't working 100% correct because I can still beat the computer. I've looked extensively over my code and cannot find where the algorithm is going wrong. Here is my code:
Main.py
import TicTacToe
import Minimax
if (__name__ == "__main__"):
t = TicTacToe.ttt(3)
m = Minimax.Minimax(3, t)
while (t.winner == None):
if (t.turn == 1):
playerInputI = int(input("Input row: "))
playerInputJ = int(input("Input column: "))
bestIndex = (playerInputI, playerInputJ)
else:
winner, bestIndex = m.minimax(t.grid, (-1, -1), 15, -1)
t.winner = None
t.findWinner(bestIndex, t.grid)
t.updateGameGrid(bestIndex)
print(t.grid)
print(t.grid)
Minimax.py
class Minimax:
def __init__(self, gs, t):
self.gridSize = gs
self.ttt = t
def minimax(self, state, currIndex, depth, turn):
if (currIndex[0] != -1 and currIndex[1] != -1):
winner = self.ttt.findWinner(currIndex, state)
if (winner == -1):
return winner - depth, currIndex
elif (winner == -1):
return winner + depth, currIndex
elif (winner == 0):
return 0, currIndex
if (depth==0 and winner==None):
return 0, currIndex
evalLimit = -turn * 1000
bestIndex = None
for i in range(self.gridSize):
for j in range(self.gridSize):
if (state[i][j] == 0):
state[i][j] = turn
eval, newIndex = self.minimax(state, (i, j), depth-1, -turn)
state[i][j] = 0
if (turn > 0 and eval > evalLimit):
bestIndex = newIndex
evalLimit = eval
elif (turn < 0 and eval < evalLimit):
bestIndex = newIndex
evalLimit = eval
return evalLimit, bestIndex
Tictactoe.py
from random import randint
class ttt:
def __init__(self, size):
self.gridSize = size
self.grid = self.createGrid()
# If using minimax algorithm, user is maximizer(1) and computer is minimizer(-1)
# If single player, then user is 1, computer is -1
# If multiplayer, user1 is 1, user2 = -1
self.turn = 1
self.winner = None
def createGrid(self):
grid = []
for i in range(self.gridSize):
grid.append([])
for j in range(self.gridSize):
grid[i].append(0)
# grid = [[-1, 1, 0], [0, -1, 0], [0, 0, 0]]
return grid
def updateGameGrid(self, index):
if (self.grid[index[0]][index[1]] != 0):
return
self.grid[index[0]][index[1]] = self.turn
winner = self.findWinner(index, self.grid)
self.turn = -self.turn
def randomIndex(self):
x = randint(0, self.gridSize-1)
y = randint(0, self.gridSize-1)
while (self.grid[x][y] != 0):
x = randint(0, self.gridSize-1)
y = randint(0, self.gridSize-1)
return (x, y)
def findWinner(self, index, grid):
# Row
found = True
for j in range(self.gridSize-1):
if (grid[index[0]][j] != grid[index[0]][j+1] or grid[index[0]][j] == 0):
found = False
break
if (found):
self.winner = self.turn
return self.turn
# Column
found = True
for i in range(self.gridSize-1):
if (grid[i][index[1]] != grid[i+1][index[1]] or grid[i][index[1]] == 0):
found = False
break
if (found):
self.winner = self.turn
return self.turn
# Top Left to Bottom Right Diagonal
if (index[0] == index[1]):
found = True
for i in range(self.gridSize-1):
if (grid[i][i] != grid[i+1][i+1] or grid[i][i] == 0):
found = False
break
if (found):
self.winner = self.turn
return self.turn
# Top Right to Bottom Left Diagonal
if (index[0] + index[1] == self.gridSize-1):
found = True
for i in range(self.gridSize-1):
if (grid[self.gridSize-i-1][i] != grid[self.gridSize-i-2][i+1] or grid[self.gridSize-i-1][i] == 0):
found = False
break
if (found):
self.winner = self.turn
return self.turn
tie = True
for i in range(self.gridSize):
for j in range(self.gridSize):
if (grid[i][j] == 0):
tie = False
if (tie):
self.winner = 0
return 0
return None
The grid is represented as a 2d array, with each element being either a -1 for O, 1 for X and 0 for nothing. The player is 1 and the computer is -1. Who's turn it is is represented as a -1 or 1, corresponding to O or X. If anyone is able to find where the error in my code is that would be a great.
I see two troubles :
a) the two following conditions are the same in minimax
if (winner == -1):
return winner - depth, currIndex
elif (winner == -1):
return winner + depth, currIndex
b) When you return bestIndex, you actually return the winning move, the one that completes a line or a row or a diagonal, at one of the leaves of the game tree. What you really want is the next move to play. Write instead bestIndex = (i, j) in the condition if eval > evalLimit
I didn't check for everything but that is a good start. Run your code at depth=1 or 2 with many printf inside the minimax function, and look at the different moves, with the corresponding score, if they look correct or not.
I'm making a scalable tic tac toe game in Tkinter (meaning the board size can be 2x2 up to whatever fits the screen). I'm using cget("image") to find what mark a button has. For some reason, the win check displays very random things. I've tried a lot of semi-random things to fix it, but had no success in fixing it. Here's the code:
from tkinter import *
class XOGame:
def main_game(self):
self.__game_window = Tk()
self.__grid_size = 3 # User inputted in a different part of the code
self.__game_window.title("Tic Tac Toe (" + str(self.__grid_size) + "x"
+ str(self.__grid_size) + ")")
# this is user inputted in a different part of the program.
self.__players = ["p1", "p2"]
self.__player1 = self.__players[0]
self.__player2 = self.__players[1]
self.build_board(self.__game_window)
self.__game_window.mainloop()
def build_board(self, window):
self.__size = self.__grid_size ** 2
self.__turn_nr = 1
self.__win = False
self.__empty_square = PhotoImage(master=window,
file="rsz_empty.gif")
self.__x = PhotoImage(master=window,
file="rsz_cross.gif")
self.__o = PhotoImage(master=window,
file="rsz_nought.gif")
self.__squares = [None] * self.__size
self.create_win_check_lists()
# Building the buttons and gridding them
for i in range(self.__size):
self.__squares[i] = (Button(window, image=self.__empty_square))
row = 0
column = 0
number = 1
for j in self.__squares:
j.grid(row=row, column=column)
j.config(command=lambda index=self.__squares.index(j):
self.change_mark(index))
column += 1
if number % 3 == 0:
row += 1
column = 0
number += 1
# This is the part where the picture changing happens.
def change_mark(self, i):
"""
Function changes mark of empty button to either X or O depending on the
player in turn. It also checks if the change in mark results in a win.
:param i: The button number, whose mark is being changed
:return: None
"""
if self.__turn_nr % 2 == 1:
self.__player_in_turn = self.__player1
else:
self.__player_in_turn = self.__player2
if self.__player_in_turn == self.__player1:
self.__mark = self.__x
else:
self.__mark = self.__o
self.__squares[i].configure(image=self.__mark, state=DISABLED)
self.__turn_nr += 1
self.check_win(i)
if self.__win is True:
print("this is thought to be a win")
else:
print("the game thinks this is not a win")
# Checking if the game tied.
if self.__turn_nr == self.__size + 1 and not self.__win:
print("the game thinks it tied.")
def check_win(self, i):
"""
Checks if mark placement leads to a win.
:param i: i is the button location.
:return: None
"""
# checks row
if self.__win == False:
for row in self.__rows:
if i + 1 in row:
self.__win = self.checksameimage(row)
if self.__win == True:
break
# checks column
if self.__win == False:
for column in self.__columns:
if i + 1 in column:
self.__win = self.checksameimage(column)
if self.__win == True:
break
# if i is in a diagonal, checks one/both diagonals
if self.__win == False:
for diag in self.__diagonals:
if i + 1 in diag:
self.__win = self.checksameimage(diag)
if self.__win == True:
break
return self.__win
# checking if all the images are same
# This is likely where the issue is. Either this part or checkEqual.
def checksameimage(self, lst):
images = []
for nr in lst:
try:
images.append(self.__squares[nr].cget("image"))
except IndexError:
pass
return self.checkEqual(images)
def checkEqual(self, lst):
"""
Function checks if all elements in a list are equal. Used for checking
if the dice throws are the same.
:param lst: The list the check is performed on
:return: True/False, True if all elements are equal.
"""
if all(x == lst[0] for x in lst):
return True
else:
return False
def create_win_check_lists(self):
"""
Creates lists whose elements are lists of the locations of each spot
of the game board that belongs to a row/column/diagonal
:return:
"""
self.__rows = [[] for _ in range(self.__grid_size)]
for i in range(self.__grid_size):
self.__rows[i].append(i + 1)
for k in range(1, self.__grid_size):
self.__rows[i].append(i + 1 + self.__grid_size * k)
self.__columns = [[] for _ in range(self.__grid_size)]
for i in range(self.__grid_size):
for j in range(1, self.__grid_size + 1):
self.__columns[i].append(i * self.__grid_size + j)
self.getDiagonals(self.__columns)
def getDiagonals(self, lst):
self.__diagonals = [[], []]
self.__diagonals[0] = [lst[i][i] for i in range(len(lst))]
self.__diagonals[1] = [lst[i][len(lst) - i - 1] for i in
range(len(lst))]
def start(self):
# Function starts the first window of the game.
self.main_game()
def main():
ui = XOGame()
ui.start()
main()
The images used in the code are 125x125. Here is a link that works for 24h: https://picresize.com/b5df006025f0d8
your problem is unquestionably with indices. We can see what index each square corresponds to by changing the image to be text corresponding to the index:
Try changing this loop in build_board:
for i in range(self.__size):
self.__squares[i] = (Button(window, image=self.__empty_square))
To instead show the index it corresponds to:
for i in range(self.__size):
self.__squares[i] = (Button(window, text=str(i)))
Then print out lst passed to checksameimage and you will see that the indices you are checking are all one higher than you intended.
I am making a tic tac toe game and trying to create a function that checks if 3 of the same spots in a row have the same input 'x' or '0'. I am having trouble with the three_in_row function I am trying to make to trigger game over. I am trying to figure out how to do this in a simple way so all rows or columns will be triggers if 3 X's or 0's are played... Here's what I have so far. This is in python 2.7.13
(this is only part of the code I think should be relevant)
def setup_board(size):
board = []
for row in range(size):
board.append([])
for col in range(size):
board[row].append(empty)
return board
def three_in_row(b):
b[0][0] and b[0][1] and b[0][2] == 'x'
def game_over(b):
if three_in_row(b) == True:
print "Congratulations You Win!"
else:
return False
def tic_tac_toe():
b = setup_board(3)
run_game(b)
In my opinion, it might make more sense to store X's as +1 and O's as -1, so that you can easily do arithmetic to check if the game is over.
For example:
def three_in_row(b):
xWin = check_winner(b,3)
oWin = check_winner(b,-3)
return xWin | oWin
def check_winner(b, valToCheck):
foundWin = any(sum(r) in {valToCheck} for r in b) # check rows
# now check columns
for i in range(3):
foundWin = foundWin | (sum([b[j][i] for j in range(3)]) == valToCheck)
# now check diagonals
foundWin = foundWin | (sum([b[i][i] for i in range(3)]) == valToCheck)
foundWin = foundWin | (sum([b[i][2-i] for i in range(3)]) == valToCheck)
return foundWin
Thanks to Blender for the following more succinct method:
def three_in_row(b):
return any(sum(r) in {3, -3} for r in b)
def line_match(game):
for i in range(3):
set_r = set(game[i])
if len(set_r) == 1 and game[i][0] != 0:
return game[i][0]
return 0
#transposed column function for future use
#def column(game):
# trans = numpy.transpose(game)
# for i in range(3):
# set_r = set(trans[i])
# if len(set_r) == 1 and trans[i][0] != 0:
# return list(set_r)[0]
def diagonal_match(game):
if game[1][1] != 0:
if game[1][1] == game[0][0] == game[2][2]:
return game[1][1]
elif game[1][1] == game[0][2] == game[2][0]:
return game[1][1]
return 0
The correct syntax for the checks is either:
b[0][0] == 'x' and b[0][1] == 'x' and b[0][2] == 'x'
or (more succinctly):
b[0][0] == b[0][1] == b[0][2] == 'x'
You are also missing a return just before your check, like:
return b[0][0] == b[0][1] == b[0][2] == 'x'
Anyways, your code does not iterate over all the rows. A possible correction would be:
def three_in_row(b):
for row in rage(0, 3):
if b[row][0] == b[row][1] == b[row][2] == 'x':
return True
return False
Doing a three_in_column(b) should be fairly easy (changing b[row][n] in b[n][column]), so is also manually checking the two diagonals.
I'm currently trying to implement an AI for my Python TicTacToe game.
Everything performs greatly, apart from one single situation.
My current code:
def testLine(line):
'''
' :param line: Liste containing 3 ints
' :return: 1, if all elements of the list == 1
' -1, if all elements of the list == -1
' 0, otherwise
'''
if line[0] == 1 and line[1] == 1 and line[2] == 1:
return 1
elif line[0] == -1 and line[1] == -1 and line[2] == -1:
return -1
return 0
def getWinner(board):
# test columns
for idx in range(3):
line = [board[0][idx], board[1][idx], board[2][idx]]
if not testLine(line) == 0:
return line[0]
# test rows
for idx in range(3):
line = board[idx]
if not testLine(line) == 0:
return line[0]
# test diagonals
line = [board[0][0], board[1][1], board[2][2]]
if not testLine(line) == 0:
return line[0]
line = [board[0][2], board[1][1], board[2][0]]
if not testLine(line) == 0:
return line[0]
# no winner
return 0
def count(board, obj):
c = 0
for r in range(len(board)):
for col in range(len(board[r])): # FIXED IT
if board[r][col] == obj:
c += 1
return c
def nextMove(board, player):
if len(board[0]) + len(board[1]) + len(board[2]) == 1: return 0, 4
nextPlayer = player * (-1)
if not getWinner(board) == 0:
if player is 1: return -1, (-1, -1)
else: return 1, (-1, -1)
listOfResults = [] # empty array
if count(board, 0) == 0: # there is no empty field
return 0, (-1, -1)
_list = []
for i in range(len(board)):
for j in range(len(board[i])):
if board[i][j] == 0:
_list.append((i, j))
for (i, j) in _list:
board[i][j] = player
ret, move = nextMove(board, nextPlayer)
listOfResults.append(ret)
board[i][j] = 0
if player is 1:
maxPossibleValue = max(listOfResults)
return maxPossibleValue, _list[listOfResults.index(maxPossibleValue)]
else:
minPossibleValue = min(listOfResults)
return minPossibleValue, _list[listOfResults.index(minPossibleValue)]
if __name__ == '__main__':
print(str(nextMove([[ 1, -1, 0],
[ -1, -1, 1],
[ 1, 1, 0]],
-1)))
Output: (0, (0, 2))
I can say for sure that count, getWinner and testLine work perfectly.
But the output of the scenario at the very bottom of the code is simply wrong, as it should be (0, 2, 2) because the computer has to "block" my chance to win in the bottom line.
Do you have suggestions on how to fix my minimax algorithm?
EDIT: I've fixed it. The error was in the count method. You shouldn't say
for col in board[r]
but
for col in range(len(board[r]))
Because otherwise it won't keep the elements in the right order and the whole method returned a false value.
I've fixed it. The error was in the count method. You shouldn't say
for col in board[r]
but
for col in range( len(board[r]) )
Because otherwise it won't keep the elements in the right order and the whole method returned a false value.
The first thing you need to know is that return a, b is similar to return (a,b), because defining a tuple does not need parenthesis (exept for an empty tuple).
So you can easily return (0, 0, 2) instead of (0, (0, 2)) :
return (maxPossibleValue,) + _list[listOfResults.index(maxPossibleValue)]
# use (a,) for a tuple of len 1
But I'm aware this solves only the half of your problem.
I'm working on an exercise where given a set of connections between two points (ie. 12 is a connection between 1 and 2 ect.). I decided to tackle the approach recursively in order to have it systematically check every path and return when it finds one that hits every node and starts and ends with one.
However upon debugging this it seems that as I pass down the adjMatrix further into the recursion it's also editing the upper levels and causing it not to search any further as it goes back up the tree. I think it has something to when I set newMatrix = adjMatrix, but I'm not exactly sure.
def checkio(teleports_string):
#return any route from 1 to 1 over all points
firstnode, secondnode, size = 0, 0, 8
#Makes the adjacency matrix
adjMatrix = [[0 for i in range(size)] for j in range(size)]
for x in teleports_string:
#Assigns Variables
if firstnode == 0 and x != ",":
#print("Node1:" + x)
firstnode = x
elif secondnode == 0 and x != ",":
#print("Node2:" + x)
secondnode = x
#Marks connections
if firstnode != 0 and secondnode != 0:
adjMatrix[int(firstnode) - 1][int(secondnode) - 1] = 1
adjMatrix[int(secondnode) - 1][int(firstnode) - 1] = 1
firstnode, secondnode = 0, 0
print(adjMatrix)
return findPath(adjMatrix, 1, "1")
def findPath(adjMatrix, currentnode, currentpath):
if isFinished(currentpath):
return currentpath
for x in range(0, 8):
if adjMatrix[currentnode - 1][x] == 1:
print(currentpath + "+" + str(x+1))
newMatrix = adjMatrix
newMatrix[currentnode - 1][x] = 0
newMatrix[x][currentnode - 1] = 0
temp = currentpath
temp += str(x+1)
newpath = findPath(newMatrix, x+1,temp)
print(newpath)
if isFinished(newpath):
print ("Returning: " + newpath)
return newpath
return ""
def isFinished(currentpath):
#Checks if node 1 is hit at least twice and each other node is hit at least once
if currentpath == "":
return False
for i in range(1, 9):
if i == 1 and currentpath.count(str(i)) < 2:
return False
elif currentpath.count(str(i)) < 1:
return False
#Checks if it starts and ends with 1
if not currentpath.startswith(str(1)) or not currentpath.endswith(str(1)):
return False
return True
#This part is using only for self-testing
if __name__ == "__main__":
def check_solution(func, teleports_str):
route = func(teleports_str)
teleports_map = [tuple(sorted([int(x), int(y)])) for x, y in teleports_str.split(",")]
if route[0] != '1' or route[-1] != '1':
print("The path must start and end at 1")
return False
ch_route = route[0]
for i in range(len(route) - 1):
teleport = tuple(sorted([int(route[i]), int(route[i + 1])]))
if not teleport in teleports_map:
print("No way from {0} to {1}".format(route[i], route[i + 1]))
return False
teleports_map.remove(teleport)
ch_route += route[i + 1]
for s in range(1, 9):
if not str(s) in ch_route:
print("You forgot about {0}".format(s))
return False
return True
assert check_solution(checkio, "13,14,23,25,34,35,47,56,58,76,68"), "Fourth"
The line
newMatrix = adjMatrix
merely creates another reference to your list. You'll need to actually create a new list object. As this is a matrix, do so for the contents:
newMatrix = [row[:] for row in adjMatrix]
This creates a new list of copies of your nested lists.