I am working on a practice problem where we are to input a list into a function argument, that will represent a tic tac toe board, and return the outcome of the board. That is, X wins, O wins, Draw, or None (null string).
I have it solved, but I was wondering if there is a way I could manipulate my algorithm into a loop, as it was recommended to use a loop to compare each element of the main diagonal with all the
elements of its intersecting row and column, and then check the two diagonals. I'm new to python, so my solution might be a bit longer then it needs to be. How could a loop be implemented to check the outcome of the tic tac toe board?
def gameState (List):
xcounter=0
ocounter=0
if List[0][0]==List[0][1] and List[0][0]==List[0][2]:
return List[0][0]
elif List[0][0]==List[1][0] and List[0][0]==List[2][0]:
return List[0][0]
elif List[0][0]==List[1][1] and List[0][0]==List[2][2]:
return List[0][0]
elif List[1][1]==List[1][2] and List[1][1]==List[1][0] :
return List[1][1]
elif List[1][1]==List[0][1] and List[1][1]==List[2][1]:
return List[1][1]
elif List[1][1]==List[0][0] and List[1][1]==List[2][2]:
return List[1][1]
elif List[2][2]==List[2][0] and List[2][2]==List[2][1]:
return List[2][2]
elif List[2][2]==List[1][2] and List[2][2]==List[0][2]:
return List[2][2]
elif List[2][2]==List[1][1] and List[2][2]==List[0][0]:
return List[2][2]
for listt in List:
for elm in listt:
if elm=="X" or elm=="x":
xcounter+=1
elif elm=="O" or elm=="o":
ocounter+=1
if xcounter==5 or ocounter==5:
return "D"
else:
return ''
First up, there are only eight ways to win at TicTacToe. You have nine compare-and-return statements so one is superfluous. In fact, on further examination, you check 00, 11, 22 three times (cases 3, 6 and 9) and totally miss the 02, 11, 20 case.
In terms of checking with a loop, you can split out the row/column checks from the diagonals as follows:
# Check all three rows and columns.
for idx in range(3):
if List[0][idx] != ' ':
if List[0][idx] == List[1][idx] and List[0][idx] == List[2][idx]:
return List[0][idx]
if List[idx][0] != ' ':
if List[idx][0] == List[idx][1] and List[idx][0] == List[idx][2]:
return List[idx][0]
# Check two diagonals.
if List[1][1] != ' ':
if List[1][1] == List[0][0] and List[1][1] == List[2][2]:
return List[1][1]
if List[1][1] == List[0][2] and List[1][1] == List[2][0]:
return List[1][1]
# No winner yet.
return ' '
Note that this ensures a row of empty cells isn't immediately picked up as a win by nobody. You need to check only for wins by a "real" player. By that, I mean you don't want to detect three empty cells in the first row and return an indication based on that if the second row has an actual winner.
Of course, there are numerous ways to refactor such code to make it more easily read and understood. One way is to separate out the logic for checking a single line and then call that for each line:
# Detect a winning line. First cell must be filled in
# and other cells must be equal to first.
def isWinLine(brd, x1, y1, x2, y2, x3, y3):
if brd[x1][y1] == ' ': return False
return brd[x1][y1] == brd[x2][y2] and brd[x1][y1] == brd[x3][y3]
# Get winner of game by checking each possible line for a winner,
# return contents of one of the cells if so. Otherwise return
# empty value.
def getWinner(brd):
# Rows and columns first.
for idx in range(3):
if isWinLine(brd, idx, 0, idx, 1, idx, 2): return brd[idx][0]
if isWinLine(brd, 0, idx, 1, idx, 2, idx): return brd[0][idx]
# Then diagonals.
if isWinLine(brd, 0, 0, 1, 1, 2, 2): return brd[1][1]
if isWinLine(brd, 2, 0, 1, 1, 0, 2): return brd[1][1]
# No winner yet.
return ' '
Then you can just use:
winner = getWinner(List)
in your code and you'll either get back the winner or an empty indication if there isn't one.
Related
I'm having issue with this function question:
x = [[0,0,0,0,0],[0,0,0,0,1],[0,1,0,0,0]]
Function: Book(seat) #assuming the seat is A5
The function assumes the seat is valid in the format A, B and C. the function needs to transform the letter part of seat to an integer A = 0, B = 1 and C = 2. The string digit also needs to be changed to "1" → 0, "2" → 1, "3" → 2, "4" → 3 and "5" → 4. These can be used to check if the chair in the x list-of-lists is already booked 1 or not 0. If it is not booked, then it should be changed to booked and the function should return True else it should return False.
My solution is
a = {"A":[0,0,0,0,0], "B":[0,0,0,0,1], "C":[0,1,0,0,],}
rowIndex = ["A","B","C"]
columnIndex = [1,2,3,4,5]
def book(seat):
row = seat[0]
column = seat[1]
while row in rowIndex and column in columnIndex:
if x[row][column-1] == 0:
return True
else: return False
It output False (seat already booked) in respective of the seat I book. I think there is an issue with my code but can't seems to figure it out.
There are a number of problems with your code for the function:
There is no x variable defined — you called it a in the
a = {"A":[0,0,0,0,0], "B":[0,0,0,0,1], "C":[0,1,0,0,],}
After the
row = seat[0]
column = seat[1]
you then test the values in the following:
while row in rowIndex and column in columnIndex:
which will prevent any of the rest of the code from executing unless it's True.
What you need inside of the while to iterate through all the possibilities would require two for loops, one nested inside the other. However…
You don't need to loop at all as illustrated below.
BOOKED = 1
x = [[0,0,0,0,0], [0,0,0,0,1], [0,1,0,0,0]]
letter_to_index = {"A": 0, "B": 1, "C": 2}
digit_to_index = {"1": 0, "2": 1, "3": 2, "4": 3, "5": 4}
def book(seat):
# Convert each seat character to integer.
row = letter_to_index[seat[0]]
col = digit_to_index[seat[1]]
if x[row][col] == BOOKED:
return False
else:
# Book the seat and return True
x[row][col] = BOOKED
return True
if __name__ == '__main__':
print(book('A5')) # -> True
# Try doing it again.
print(book('A5')) # -> False
Here is a simpler implementation of your code. You don't need to use loops. You have a dictionary. You can lookup the dictionary in a much simpler way.
a = {"A":[0,0,0,0,0], "B":[0,0,0,0,1], "C":[0,1,0,0,],}
def book(seat):
r,c = seat #this allows A to be r and 5 to be C
#check if value of r in keys of a
#also check if seat # is within length of seats for key
if r in a and int(c) <= len(a[r]):
#if valid request, then check if seat already booked
#if not, set seat to booked by setting value to 1
#return True
#if already booked, return False
if a[r][int(c)-1] == 0:
a[r][int(c)-1] = 1
return True
else:
return False
# if not a value request, send appropriate message
else:
return 'invalid request'
print ('A5', book('A5'))
print ('C2', book('C2'))
print ('A7', book('A7'))
print (a)
Output of this will be:
A5 True
C2 False
A7 invalid request
{'A': [0, 0, 0, 0, 1], 'B': [0, 0, 0, 0, 1], 'C': [0, 1, 0, 0]}
I have about as simple of a negamax algorithm as possible, for evaluating positions in Tic Tac Toe. The state of the game is stored as an array in numpy, with X's pieces represented by 1, and O's pieces represented by four.
I was testing this just now, and found:
a = np.zeros(9).reshape(3,3)
negaMax(a, 6, 1) # Returned zero as it should
negaMax(a, 7, 1) # Returns 100
Meaning that my algorithm thinks it has found a way for X to win in seven plies in a game of Tic Tac Toe, which is obviously impossible against decent play. I can't work out how to have it print the best moves it has found, so am having real trouble debugging this. What am I doing wrong?
def winCheck(state):
"""Takes a position, and returns the outcome of that game"""
# Sums which correspond to a line across a column
winNums = list(state.sum(axis=0))
# Sums which correspond to a line across a row
winNums.extend(list(state.sum(axis=1)))
# Sums which correspond to a line across the main diagonal
winNums.append(state.trace())
# Sums which correspond to a line across the off diagonal
winNums.append(np.flipud(state).trace())
if Square.m in winNums:
return 'X'
elif (Square.m**2 + Square.m) in winNums:
return 'O'
elif np.count_nonzero(state) == Square.m**2:
return 'D'
else:
return None
def moveFind(state):
"""Takes a position as an nparray and determines the legal moves"""
moveChoices = []
# Iterate over state, to determine which squares are empty
it = np.nditer(state, flags=['multi_index'])
while not it.finished:
if it[0] == 0:
moveChoices.append(it.multi_index)
it.iternext()
return moveChoices
def moveSim(state, move, player):
"""Create the state of the player having moved without interfering with the board"""
simState = state.copy()
if player == 1:
simState[move] = 1
else:
simState[move] = gamecfg.n + 1
return simState
def positionScore(state):
"""The game is either won or lost"""
if winCheck(state) == 'X':
return 100
elif winCheck(state) == 'O':
return -100
else:
return 0
def negaMax(state, depth, colour):
"""Recursively find the best move via a negamax search"""
if depth == 0:
return positionScore(state) * colour
highScore = -100
moveList = moveFind(state)
for move in moveList:
score = -negaMax(moveSim(state, move, colour), depth -1, colour * -1)
highScore = max(score, highScore)
return highScore
Your code does not consider the game to stop when a line of 3 symbols is made.
This means that it is playing a variant of tic-tac-toe where X wins if he makes a line of 3 even after O has made a line of 3.
For this variant, the program has correctly found that it is possible for X to always win!
(I came across the same situation with a chess program I made where the computer was happy to sacrifice its king if it would reach checkmate a little later...)
I am trying to write a Python function which can return "True" or "False" in evaluating a magic square. A magic square is a matrix all of whose row sums, column sums and the sums of the two diagonals are the same. (One diagonal of a matrix goes from the top left to the bottom right, the other diagonal goes from top right to bottom left.)
Here is my code:
def isMagic(A3):
dim = A3.shape[0] * A3.shape[1]
construct = np.arange(1,dim+1)
if A3.shape[0] == A3.shape[1]:
exist = []
for r in range(len(A3)):
for c in range(len(A3)):
exist.append(A3[r,c] in construct)
if all(exist):
def all_same(items):
return all(x == items[0] for x in items)
dig_1 = sum(np.diag(A3))
dig_2 = sum(np.diag(np.fliplr(A3)))
dig = all_same(np.array([dig_1, dig_2]))
column = all_same(np.sum(A3, axis = 1))
row = all_same(np.sum(A3, axis = 0).transpose())
if all(dig, column, row):
return True
else:
return False
Yet, when I try to test my code on one of the magic square, the function doesn't return any value:
test2 = np.matrix([[8, 1, 6],
[3, 5, 7],
[4, 9, 2]])
isMagic(test2) # True
I wondered that if it was because the indentation?
For your first two if statements if A3.shape[0] == A3.shape[1] and if all(exist), notice if the condition is false, nothing (None) is returned. I guess you want to return False if all of your if conditions are not met. Then just put return False at the very end of the function so it is run if return True is not reached:
def isMagic(A3):
...
return False
I'm working on a 2-player board game (e.g. connect 4), with parametric board size h, w. I want to check for winning condition using hw-sized bitboards.
In game like chess, where board size is fixed, bitboards are usually represented with some sort of 64-bit integer. When h and w are not constant and maybe very big (let's suppose 30*30) are bitboards a good idea? If so, are the any data types in C/C++ to deal with big bitboards keeping their performances?
Since I'm currently working on python a solution in this language is appreciated too! :)
Thanks in advance
I wrote this code while ago just to play around with the game concept. There is no intelligence behaviour involve. just random moves to demonstrate the game. I guess this is not important for you since you are only looking for a fast check of winning conditions. This implementation is fast since I did my best to avoid for loops and use only built-in python/numpy functions (with some tricks).
import numpy as np
row_size = 6
col_size = 7
symbols = {1:'A', -1:'B', 0:' '}
def was_winning_move(S, P, current_row_idx,current_col_idx):
#****** Column Win ******
current_col = S[:,current_col_idx]
P_idx= np.where(current_col== P)[0]
#if the difference between indexes are one, that means they are consecutive.
#we need at least 4 consecutive index. So 3 Ture value
is_idx_consecutive = sum(np.diff(P_idx)==1)>=3
if is_idx_consecutive:
return True
#****** Column Win ******
current_row = S[current_row_idx,:]
P_idx= np.where(current_row== P)[0]
is_idx_consecutive = sum(np.diff(P_idx)==1)>=3
if is_idx_consecutive:
return True
#****** Diag Win ******
offeset_from_diag = current_col_idx - current_row_idx
current_diag = S.diagonal(offeset_from_diag)
P_idx= np.where(current_diag== P)[0]
is_idx_consecutive = sum(np.diff(P_idx)==1)>=3
if is_idx_consecutive:
return True
#****** off-Diag Win ******
#here 1) reverse rows, 2)find new index, 3)find offest and proceed as diag
reversed_rows = S[::-1,:] #1
new_row_idx = row_size - 1 - current_row_idx #2
offeset_from_diag = current_col_idx - new_row_idx #3
current_off_diag = reversed_rows.diagonal(offeset_from_diag)
P_idx= np.where(current_off_diag== P)[0]
is_idx_consecutive = sum(np.diff(P_idx)==1)>=3
if is_idx_consecutive:
return True
return False
def move_at_random(S,P):
selected_col_idx = np.random.permutation(range(col_size))[0]
#print selected_col_idx
#we should fill in matrix from bottom to top. So find the last filled row in col and fill the upper row
last_filled_row = np.where(S[:,selected_col_idx] != 0)[0]
#it is possible that there is no filled array. like the begining of the game
#in this case we start with last row e.g row : -1
if last_filled_row.size != 0:
current_row_idx = last_filled_row[0] - 1
else:
current_row_idx = -1
#print 'col[{0}], row[{1}]'.format(selected_col,current_row)
S[current_row_idx, selected_col_idx] = P
return (S,current_row_idx,selected_col_idx)
def move_still_possible(S):
return not (S[S==0].size == 0)
def print_game_state(S):
B = np.copy(S).astype(object)
for n in [-1, 0, 1]:
B[B==n] = symbols[n]
print B
def play_game():
#initiate game state
game_state = np.zeros((6,7),dtype=int)
player = 1
mvcntr = 1
no_winner_yet = True
while no_winner_yet and move_still_possible(game_state):
#get player symbol
name = symbols[player]
game_state, current_row, current_col = move_at_random(game_state, player)
#print '******',player,(current_row, current_col)
#print current game state
print_game_state(game_state)
#check if the move was a winning move
if was_winning_move(game_state,player,current_row, current_col):
print 'player %s wins after %d moves' % (name, mvcntr)
no_winner_yet = False
# switch player and increase move counter
player *= -1
mvcntr += 1
if no_winner_yet:
print 'game ended in a draw'
player = 0
return game_state,player,mvcntr
if __name__ == '__main__':
S, P, mvcntr = play_game()
let me know if you have any question
UPDATE: Explanation:
At each move, look at column, row, diagonal and secondary diagonal that goes through the current cell and find consecutive cells with the current symbol. avoid scanning the whole board.
extracting cells in each direction:
column:
current_col = S[:,current_col_idx]
row:
current_row = S[current_row_idx,:]
Diagonal:
Find the offset of the desired diagonal from the
main diagonal:
diag_offset = current_col_idx - current_row_idx
current_diag = S.diagonal(offset)
off-diagonal:
Reverse the rows of matrix:
S_reversed_rows = S[::-1,:]
Find the row index in the new matrix
new_row_idx = row_size - 1 - current_row_idx
current_offdiag = S.diagonal(offset)
I am working on implementing an iterative deepening depth first search to find solutions for the 8 puzzle problem. I am not interested in finding the actual search paths themselves, but rather just to time how long it takes for the program to run. (I have not yet implemented the timing function).
However, I am having some issues trying to implement the actual search function (scroll down to see). I pasted all the code I have so far, so if you copy and paste this, you can run it as well. That may be the best way to describe the problems I'm having...I'm just not understanding why I'm getting infinite loops during the recursion, e.g. in the test for puzzle 2 (p2), where the first expansion should yield a solution. I thought it may have something to do with not adding a "Return" in front of one of the lines of code (it's commented below). When I add the return, I can pass the test for puzzle 2, but something more complex like puzzle 3 fails, since it appears that the now the code is only expanding the left most branch...
Been at this for hours, and giving up hope. I would really appreciate another set of eyes on this, and if you could point out my error(s). Thank you!
#Classic 8 puzzle game
#Data Structure: [0,1,2,3,4,5,6,7,8], which is the goal state. 0 represents the blank
#We also want to ignore "backward" moves (reversing the previous action)
p1 = [0,1,2,3,4,5,6,7,8]
p2 = [3,1,2,0,4,5,6,7,8]
p3 = [3,1,2,4,5,8,6,0,7]
def z(p): #returns the location of the blank cell, which is represented by 0
return p.index(0)
def left(p):
zeroLoc = z(p)
p[zeroLoc] = p[zeroLoc-1]
p[zeroLoc-1] = 0
return p
def up(p):
zeroLoc = z(p)
p[zeroLoc] = p[zeroLoc-3]
p[zeroLoc-3] = 0
return p
def right(p):
zeroLoc = z(p)
p[zeroLoc] = p[zeroLoc+1]
p[zeroLoc+1] = 0
return p
def down(p):
zeroLoc = z(p)
p[zeroLoc] = p[zeroLoc+3]
p[zeroLoc+3] = 0
return p
def expand1(p): #version 1, which generates all successors at once by copying parent
x = z(p)
#p[:] will make a copy of parent puzzle
s = [] #set s of successors
if x == 0:
s.append(right(p[:]))
s.append(down(p[:]))
elif x == 1:
s.append(left(p[:]))
s.append(right(p[:]))
s.append(down(p[:]))
elif x == 2:
s.append(left(p[:]))
s.append(down(p[:]))
elif x == 3:
s.append(up(p[:]))
s.append(right(p[:]))
s.append(down(p[:]))
elif x == 4:
s.append(left(p[:]))
s.append(up(p[:]))
s.append(right(p[:]))
s.append(down(p[:]))
elif x == 5:
s.append(left(p[:]))
s.append(up(p[:]))
s.append(down(p[:]))
elif x == 6:
s.append(up(p[:]))
s.append(right(p[:]))
elif x == 7:
s.append(left(p[:]))
s.append(up(p[:]))
s.append(right(p[:]))
else: #x == 8
s.append(left(p[:]))
s.append(up(p[:]))
#returns set of all possible successors
return s
goal = [0,1,2,3,4,5,6,7,8]
def DFS(root, goal): #iterative deepening DFS
limit = 0
while True:
result = DLS(root, goal, limit)
if result == goal:
return result
limit = limit + 1
visited = []
def DLS(node, goal, limit): #limited DFS
if limit == 0 and node == goal:
print "hi"
return node
elif limit > 0:
visited.append(node)
children = [x for x in expand1(node) if x not in visited]
print "\n limit =", limit, "---",children #for testing purposes only
for child in children:
DLS(child, goal, limit - 1) #if I add "return" in front of this line, p2 passes the test below, but p3 will fail (only the leftmost branch of the tree is getting expanded...)
else:
return "No Solution"
#Below are tests
print "\ninput: ",p1
print "output: ",DFS(p1, goal)
print "\ninput: ",p2
print "output: ",DLS(p2, goal, 1)
#print "output: ",DFS(p2, goal)
print "\ninput: ",p3
print "output: ",DLS(p3, goal, 2)
#print "output: ",DFS(p2, goal)
The immediate issue you're having with your recursion is that you're not returning anything when you hit your recursive step. However, unconditionally returning the value from the first recursive call won't work either, since the first child isn't guaranteed to be the one that finds the solution. Instead, you need to test to see which (if any) of the recursive searches you're doing on your child states is successful. Here's how I'd change the end of your DLS function:
for child in children:
child_result = DLS(child, goal, limit - 1)
if child_result != "No Solution":
return child_result
# note, "else" removed here, so you can fall through to the return from above
return "No Solution"
A slightly more "pythonic" (and faster) way of doing this would be to use None as the sentinel value rather than the "No Solution" string. Then your test would simply be if child_result: return child_result and you could optionally leave off the return statement for the failed searches (since None is the default return value of a function).
There are some other issues going on with your code that you'll run into once this recursion issue is fixed. For instance, using a global visited variable is problematic, unless you reset it each time you restart another recursive search. But I'll leave those to you!
Use classes for your states! This should make things much easier. To get you started. Don't want to post the whole solution right now, but this makes things much easier.
#example usage
cur = initialPuzzle
for k in range(0,5): # for 5 iterations. this will cycle through, so there is some coding to do
allsucc = cur.succ() # get all successors as puzzle instances
cur = allsucc[0] # expand first
print 'expand ',cur
import copy
class puzzle:
'''
orientation
[0, 1, 2
3, 4, 5
6, 7, 8]
'''
def __init__(self,p):
self.p = p
def z(self):
''' returns the location of the blank cell, which is represented by 0 '''
return self.p.index(0)
def swap(self,a,b):
self.p[a] = self.p[b]
self.p[b] = 0
def left(self):
self.swap(self.z(),self.z()+1) #FIXME: raise exception if not allowed
def up(self):
self.swap(self.z(),self.z()+3)
def right(self):
self.swap(self.z(),self.z()-1)
def down(self):
self.swap(self.z(),self.z()-3)
def __str__(self):
return str(self.p)
def copyApply(self,func):
cpy = self.copy()
func(cpy)
return cpy
def makeCopies(self,s):
''' some bookkeeping '''
flist = list()
if 'U' in s:
flist.append(self.copyApply(puzzle.up))
if 'L' in s:
flist.append(self.copyApply(puzzle.left))
if 'R' in s:
flist.append(self.copyApply(puzzle.right))
if 'D' in s:
flist.append(self.copyApply(puzzle.down))
return flist
def succ(self):
# return all successor states for this puzzle state
# short hand of allowed success states
m = ['UL','ULR','UR','UDR','ULRD','UDL','DL','LRD','DR']
ss= self.makeCopies(m[self.z()]) # map them to copies of puzzles
return ss
def copy(self):
return copy.deepcopy(self)
# some initial state
p1 = [0,1,2,3,4,5,6,7,8]
print '*'*20
pz = puzzle(p1)
print pz
a,b = pz.succ()
print a,b