PYTHON DFS Infinite while loop - python

Im trying to implement dfs on a matrix but inside my while loop in cases the goal is unreachable it never exists the loop.
Any ideas how to tackle that?
Some pointers for the code:
RowCol and RowNum have indices of reachable neighbours(i.e right, down,right diagonal down)I am adding that to the indices of the current node to get the neighbours indices. If it's not visited or an obstacle(equal to 1) add it to stack and repeat.
while stack:
curr = stack.get() # Dequeue the front cell
path.append(curr.pt)
# If we have reached the destination cell,
# we are done
pt = curr.pt
if pt == goal:
print("Sequence : ")
print(path)
print("Path : ")
print(curr.path+" to "+str(pt))
return curr.dist
# Otherwise enqueue its adjacent cells
for i in range(3):
if i == 0 or i == 1:
cost = 2
elif i == 2:
cost = 3
row = pt[0] + rowNum[i]
col = pt[1] + colNum[i]
# if adjacent cell is valid, has path
# and not visited yet, enqueue it.
if isValid(row, col):
if matrix[row][col] == "0" and not visited[row][col]:
visited[row][col] = True
Adjcell = queueNode([row, col], curr.dist + cost,curr.path+" to "+str(curr.pt))
stack.put(Adjcell)
# Return -1 if destination cannot be reached
print(matrix[start[0]][start[1]])
print(matrix[goal[0]][goal[1]])
print("Can't reach goal")
return -1

If you want to make loop use
while True :

Related

Optimization of a Needleman Wunsch algorithm

For some Time, I've written a Needleman Wunsch algorithm for listening to NCBI data streams, but as I've been done with it i knew it could be done better but i by far to stupid to do so. If some of you know what I've done stupid overcomplicated, I would really like to know how to do it better.
At first I want to show you the full code to let you know about the context.
#!/usr/bin/env python
import pandas as pd #to create data frames
from numpy import full #to build the arrays
import subprocess
import os
from itertools import groupby
def needlemanWunsch(lista, listb, rowLen, colLen): #o(n*m)
levensteinTable = full([rowLen, colLen],0)
for j in range(0,colLen): #Fill out the first row
levensteinTable[0, j] = j
for i in range(0,rowLen): #Fill out the first column
levensteinTable[i, 0] = i
for i in range(1,rowLen): #Fill out the rest of the Board in dependency of several cases
for j in range (1,colLen): #traversing with a nested loop
levensteinTable[i,j]=min( #every Levenstein Score has to be the Minimum of three cases
(levensteinTable[i-1, j-1]+(0 if listb[j-1] == lista[i-1] else 1)), #in this part, the Delta of the two strings will be detected
(levensteinTable[i, j-1] +1), #The score of the left cell
(levensteinTable[i-1, j] +1) #The Score of the upper cell
)
return levensteinTable #the finished levenstein Table will be returned
upArrow = "\u2191"
right_arrow = "\u2192"
down_arrow = "\u2193"
leftArrow = "\u2190"
down_right_arrow = "\u2198"
upLeftArrow = "\u2196"
def needlemanWunschTraceBack(listx,listy,rows,columns, gapPenalty = -1, matchBonus = 1, mismatchPenalty = -1): #o(n*m)
#This algorithm will fill out the Penalty Scoreboard and the Arrow Score board
penaltyArray = full([rows, columns],0) #build up the penalty score Array
tracerArray = full([rows, columns],"-") #build up the Array for the traceback arrows
for row in range(rows): #filling the two arrays
for col in range(columns):
if row==0 and col==0: #the first cell
score = 0 #the first Cell doesn't have any alignment
arrow = "-" #no Alignment, no arrow
elif row==0 and col!=0: #the first Row
score = penaltyArray[row, col -1]+gapPenalty #every Cell in this row need his score
arrow = leftArrow #every Arrow in this row has to look up to the start of the array
elif row!=0 and col == 0: #the first column
score = penaltyArray[row-1, col]+gapPenalty #every Cell in this column need his score
arrow = upArrow #every Arrow in this row has to look up to the start of the array
else: #in this case, every other cell will be processed
fromLeftScore = penaltyArray[row,col-1] + gapPenalty #The score of the former left cell + gapPenalty
fromAboveScore = penaltyArray[row-1,col] + gapPenalty #The score of the former Above cell
diagonalLeftCellScore = penaltyArray[row-1,col-1] +(
matchBonus if(listx[row -1]==listy[col-1])else mismatchPenalty
) #if the alighment matches with the former diagonal left cell, it gets a match bonus, else a mismatchPenalty
score = max([fromLeftScore, fromAboveScore, diagonalLeftCellScore]) #The score of our Cell has to be the maximum of the three given scores
arrow =(leftArrow if score==fromLeftScore else upArrow if score==fromAboveScore else\
upLeftArrow if score==diagonalLeftCellScore else 0)
#the right arrow of every cell will be seperately detected
tracerArray[row, col]=arrow #the tracerArray gets the arrows
penaltyArray[row, col]=score #the penaltyArray gets the scores
return tracerArray, penaltyArray
def traceback_alignment(traceback_array,listC,listD,up_arrow = upArrow ,\
left_arrow=leftArrow,up_left_arrow=upLeftArrow,stop="-"): #o(n)
row = len(listC) #The Traceback Algo needs the sequences anyway
col = len(listD)
arrow = traceback_array[row,col] #to get the right arrow for the current position
alignedSeq1 = "" #to initiate the produced alignment upper line
alignedSeq2 = "" #to initiate the produced alignment under line
alignmentIndicator = "" #to indicate the alighment
while arrow != "-": #No Arrow, no interes
arrow = traceback_array[row,col] #the current position in the array inside the loop
print(f"Currently on row: {row} and col: {col}; Arrow: {arrow}") #Because you could get bored without visual process indication
if arrow == up_arrow: #up_arrow shows a gap in under sequence
alignedSeq2 = "-"+alignedSeq2
alignedSeq1 = listC[row-1] + alignedSeq1
alignmentIndicator = " "+alignmentIndicator #to show that here is no alignment
row -=1
elif arrow == up_left_arrow: #up_left_arrow shows that here is accordance between the sequences
alignedSeq1 = listC[row-1] + alignedSeq1
alignedSeq2 = listD[col-1] + alignedSeq2
if listC[row-1] == listD[col-1]:
alignmentIndicator = "|"+alignmentIndicator #visual indicator for accordance
else:
alignmentIndicator = " "+alignmentIndicator #visual indicator for no accordance
row -=1
col -=1
elif arrow == left_arrow:
alignedSeq1 = "-"+alignedSeq1
alignedSeq2 = listD[col-1] + alignedSeq2
alignmentIndicator = " "+alignmentIndicator #visual indicator for no accordance
col -=1
elif arrow == stop:
break
else:
raise ValueError(
f"Traceback array entry at {row},{col}: {arrow}" \
f"is not recognized as an up arrow ({up_arrow}),left_arrow ({left_arrow}), "\
f"up_left_arrow ({up_left_arrow}), or a stop ({stop})."
)
return f"{alignedSeq1}\n{alignmentIndicator}\n{alignedSeq2}"
def seqHandle(seq1, seq2): #to handle a two given Sequences o(n*m)
columnLabels = [label for label in "-"+seq1] #for the later buildet Dataframes
rowLabels = [label for label in "-"+seq2] ##for the later buildet Dataframes
nRows = len("-"+seq1) #Count of all rows
nColumns = len("-"+seq2) #Count of all columns
levensteinBoard = needlemanWunsch(seq1, seq2,nRows,nColumns) #to build the Board with the levensteinDistances
arrowArray, zuchtArray, = needlemanWunschTraceBack(seq1, seq2,nRows, nColumns) #Important for the traceback
levensteinDistance = levensteinBoard[len(seq1)][len(seq2)] #I'll explain the Levenstein Distance in the Readme
return (
f"This is our ScoreBoard with all the important distances\n"\
f"{pd.DataFrame(levensteinBoard, index=columnLabels, columns= rowLabels)}\n"\
f"The Levenstein Distance of{seq1} and {seq2} is {levensteinDistance}.\n"\
f"The trace back arrow board:\n{pd.DataFrame(arrowArray, index=columnLabels, columns= rowLabels)}\n"\
f"The Penalty Score Board:\n{pd.DataFrame(zuchtArray, index=columnLabels, columns= rowLabels)}\n"\
f"{traceback_alignment(arrowArray,seq1,seq2)}"), levensteinDistance
def fileProcessGenerator(fileE): #to iterate over a SequenceSummaryFile o(n)
with open(f'{os.getcwd()}/{fileE}', 'r') as fh: #to savely work with the given file
faiter = (x[1] for x in groupby(fh, lambda line: line[0] ==">")) #to group the single sequences to their related header Line
for header in faiter:
headerStr = header.__next__()[1:].strip() #to fetch the header line from the group
yield (
headerStr.strip().replace('>', '').split()[0], #name of the sequence
''.join(s.strip() for s in faiter.__next__())) #sequence
def FileOfSequencesAnalisis(fileName, OutputPath): #to analyse a SequenceSummaryFile o(n*m*a*b)
print(f"\n\n Warning: I'll proceed really a lot of Sequence combinations Sequences.\n\n This will take Time! Please stay patient")
processnumber = 1 #We want to know, in which process we are
levensteinDistanceCounter= 0 #To count all Levenstein distances so far
LevensteinDistancesAverage = 0 #to calculate the average of all levenstein distances so far
try: #because someone could try shit
for ff in fileProcessGenerator(fileName): #the main loop devines the sequence in the line
for fo in fileProcessGenerator(fileName): #the main loop devines the sequence in the column
if ff != fo: #nobody wants to know the levenstein Distance of two identical sequences
name, seqOne, = ff #to get the important stuff from the generator
namel, seqTwo, = fo
print(
f"processing for {name} and {namel} -Process Nr.{processnumber}\n"\
f"current average Levenstein Distances is {LevensteinDistancesAverage}") #to show, that the process works well
seqHandling, currentLevensteinDistance = seqHandle(seqOne, seqTwo) #to get the struff from seqHandle
with open(f"{OutputPath}/For_{name}_and_{namel}.txt", 'w') as bitch: #to savely save our Output to a file
bitch.write(f"Analysis for {name} and {namel}: \n{seqHandling}")
levensteinDistanceCounter += currentLevensteinDistance
LevensteinDistancesAverage = levensteinDistanceCounter/processnumber
processnumber += 1
except RuntimeError:
print("invalid File. Use a File with inherited fasta Sequences with a Header Line and Sequences")
print(f"I'm done. Here is the average Levenstein Distance of the analysed File:\n{LevensteinDistancesAverage}")
Now I'll show you, what I've been unsatisfied with
def needlemanWunsch(lista, listb, rowLen, colLen): #o(n*m)
levensteinTable = full([rowLen, colLen],0)
for j in range(0,colLen): #Fill out the first row
levensteinTable[0, j] = j
for i in range(0,rowLen): #Fill out the first column
levensteinTable[i, 0] = i
for i in range(1,rowLen): #Fill out the rest of the Board in dependency of several cases
for j in range (1,colLen): #traversing with a nested loop
levensteinTable[i,j]=min( #every Levenstein Score has to be the Minimum of three cases
(levensteinTable[i-1, j-1]+(0 if listb[j-1] == lista[i-1] else 1)), #in this part, the Delta of the two strings will be detected
(levensteinTable[i, j-1] +1), #The score of the left cell
(levensteinTable[i-1, j] +1) #The Score of the upper cell
)
return levensteinTable #the finished levenstein Table will be returned
I've tried to turn every for loop into a comprehension, but somehow there are things I don't know and even don't know for what kind of comprehension I need to search for.
II've tried to turn every for loop into a comprehension, but somehow there are things I don't know and even don't know for what kind of comprehension I need to search for.
from numpy import full #to build the arrays
def needlemanWunsch(lista, listb, rowLen, colLen): #o(n*m)
levensteinTable = full([rowLen, colLen],0)
#for j in range(0,colLen): #Fill out the first row
# levensteinTable[0, j] = j
levensteinTable[0, [j for j in range(0, colLen)]]
#for i in range(0,rowLen): #Fill out the first column
# levensteinTable[i, 0] = i
levensteinTable[[i for i in range(0, rowLen)], 0]
for i in range(1,rowLen): #Fill out the rest of the Board in dependency of several cases
for j in range (1,colLen): #traversing with a nested loop
levensteinTable[i,j]=min( #every Levenstein Score has to be the Minimum of three cases
(levensteinTable[i-1, j-1]+(0 if listb[j-1] == lista[i-1] else 1)), #in this part, the Delta of the two strings will be detected
(levensteinTable[i, j-1] +1), #The score of the left cell
(levensteinTable[i-1, j] +1) #The Score of the upper cell
)
return levensteinTable #the finished levenstein Table will be returned
seq1, seq2= "ATTACA","ATGCT"
nRows, nColumns = len("-"+seq1), len("-"+seq2)
print(needlemanWunsch(seq1, seq2, nRows, nColumns))
but Output of this is like:
[[0 0 0 0 0 0]
[0 0 1 1 1 1]
[0 1 0 1 2 1]
[0 1 1 1 2 2]
[0 0 1 2 2 3]
[0 1 1 2 2 3]
[0 0 1 2 3 3]]

Recursive pathfinding algorithm keeps returning None

To find a path in a 2D map, I call get_building_path() on a Person instance, but it keeps returning None. I have no idea what is going wrong as I am pretty new to Python.
Below I have provided code needed to reproduce the issue. I have tried many things but I still don't understand why it keeps returning None:
from random import randint
width = 1400
height = 700
gridSize = 20
people = []
buildings = []
map = [['' for c in range(int(width/gridSize))] for r in range(int(height/gridSize))]
class Person():
def __init__(self,row,col):
self.row = row
self.col = col
def getMin(self,paths): # find shortest path
if (len(paths) == 0): return [] # if nothing was found, return an empty list
min = paths[0]
for n in range(1,len(paths),1):
if ((paths[n] != None and len(paths[n]) < len(min)) or min==[None]): # make sure [None] is not returned since that would be the shortest
min = paths[n]
return min
def path_helper(self,visited,path,row,col,destR,destC): # destR and destC are the row and col of the target position
if ((row,col) in visited or map[row][col] != 'road' or row < 0 or row > len(map)-1 or col < 0 or col > len(map[0])-1):
return None # make sure the current position has not been visited, and is in bounds of the map
path.append([row,col]) # add current position to the path
visited.append((row,col)) # mark current position as visited
if (row == destR and col == destC): # return the path if we found the destination
return path
others=[] # look in all four directions from the current position
others.append(self.path_helper(visited,path[:],row-1,col,destR,destC))
others.append(self.path_helper(visited,path[:],row+1,col,destR,destC))
others.append(self.path_helper(visited,path[:],row,col-1,destR,destC))
others.append(self.path_helper(visited,path[:],row,col+1,destR,destC))
others.remove(None) # remove any path that did not find anything
return self.getMin(others)
def get_path(self,row,col):
return self.path_helper([],[],self.row,self.col,row,col) #call to recursive helper function
def get_building_path(self,type):
all = []
for b in buildings:
if (b.type == type):
all.append(b)
target = all[randint(0,len(all)-1)] #get a random buildings of type 'type'
return self.get_path(target.row,target.col)
class Building():
def __init__(self,type,row,col):
self.type = type
self.row = row
self.col = col
midrow = int(height/gridSize/2) #get midpoint of the map
midcol = int(width/gridSize/2)
people.append(Person(midrow+1,midcol+2))
for n in range(0,3,2): #add new buildings to buildings list, along with the map
buildings.append(Building('house',midrow+n,midcol-2))
map[midrow+n][midcol-2] = buildings[len(buildings)-1]
buildings.append(Building('school',midrow+n,midcol-1))
map[midrow+n][midcol-1] = buildings[len(buildings)-1]
buildings.append(Building('workplace',midrow+n,midcol))
map[midrow+n][midcol] = buildings[len(buildings)-1]
buildings.append(Building('store',midrow+n,midcol+1))
map[midrow+n][midcol+1] = buildings[len(buildings)-1]
buildings.append(Building('restaurant',midrow+n,midcol+2))
map[midrow+n][midcol+2] = buildings[len(buildings)-1]
for n in range(-2,3,1): #add roads to connect the buildings together
map[midrow+1][midcol+n] = 'road'
testPath = people[0].get_building_path('house')
print(testPath)
If you would like to visually see how the buildings and roads are positioned, here is a picture of it:
From left to right, the buildings are 'house', 'school', 'workplace', 'store', 'restaurant'.
The blue circle represents the person.
(The top left building has a position of (17,33) (row,col)
A few issues:
others.remove(None) only removes the first occurrence of None
map[row][col] != 'road' will be True when arriving at the target, so this test should be only done after you have verified that you have not yet arrived at the target.
map[row][col] != 'road' will potentially give an error when row or col are out of range, so you should first do that range check
So path_helper should be corrected to:
def path_helper(self,visited,path,row,col,destR,destC):
path.append([row,col])
# next IF should come before the other IF:
if row == destR and col == destC: # no parentheses needed
return path
# reorder conditions in next IF statement
if (row,col) in visited or row < 0 or row > len(map)-1 or col < 0 or col > len(map[0])-1 or map[row][col] != 'road':
return None
visited.append((row,col))
others=[]
others.append(self.path_helper(visited,path[:],row-1,col,destR,destC))
others.append(self.path_helper(visited,path[:],row+1,col,destR,destC))
others.append(self.path_helper(visited,path[:],row,col-1,destR,destC))
others.append(self.path_helper(visited,path[:],row,col+1,destR,destC))
others = list(filter(None, others)) # delete ALL occurrences of None
return self.getMin(others)
There are some other things that could be improved (like using a set for visited, and not shadowing the native map function with your own variable), but the above mentioned changes will make it work.

Dijkstra algorithm in python using dictionaries

Dear computer science enthusiasts,
I have stumbled upon an issue when trying to implement the Dijkstra-algorithm to determine the shortest path between a starting node and all other nodes in a graph.
To be precise I will provide you with as many code snippets and information as I consider useful to the case. However, should you miss anything, please let me know.
I implemented a PQueue class to handle Priority Queues of each individual node and it looks like this:
class PQueue:
def __init__(self):
self.items = []
def push(self, u, value):
self.items.append((u, value))
# insertion sort
j = len(self.items) - 1
while j > 0 and self.items[j - 1][1] > value:
self.items[j] = self.items[j - 1] # Move element 1 position backwards
j -= 1
# node u now belongs to position j
self.items[j] = (u, value)
def decrease_key(self, u, value):
for i in range(len(self.items)):
if self.items[i][0] == u:
self.items[i][1] = value
j = i
break
# insertion sort
while j > 0 and self.items[j - 1][1] > value:
self.items[j] = self.items[j - 1] # Move element 1 position backwards
j -= 1
# node u now belongs to position j
self.items[j] = (u, value)
def pop_min(self):
if len(self.items) == 0:
return None
self.items.__delitem__(0)
return self.items.index(min(self.items))
In case you're not too sure about what the Dijkstra-algorithm is, you can refresh your knowledge here.
Now to get to the actual problem, I declared a function dijkstra:
def dijkstra(self, start):
# init
totalCosts = {} # {"node"= cost,...}
prevNodes = {} # {"node"= prevNode,...}
minPQ = PQueue() # [[node, cost],...]
visited = set()
# start init
totalCosts[str(start)] = 0
prevNodes[str(start)] = start
minPQ.push(start, 0)
# set for all other nodes cost to inf
for node in range(self.graph.length): # #nodes
if node != start:
totalCosts[str(node)] = np.inf
while len(minPQ.items) != 0: # Main loop
# remove smallest item
curr_node = minPQ.items[0][0] # get index/number of curr_node
minPQ.pop_min()
visited.add(curr_node)
# check neighbors
for neighbor in self.graph.adj_list[curr_node]:
# check if visited
if neighbor not in visited:
# check cost and put it in totalCost and update prev node
cost = self.graph.val_edges[curr_node][neighbor] # update cost of curr_node -> neighbor
minPQ.push(neighbor, cost)
totalCosts[str(neighbor)] = cost # update cost
prevNodes[str(neighbor)] = curr_node # update prev
# calc alternate path
altpath = totalCosts[str(curr_node)] + self.graph.val_edges[curr_node][neighbor]
# val_edges is a adj_matrix with values for the connecting edges
if altpath < totalCosts[str(neighbor)]: # check if new path is better
totalCosts[str(neighbor)] = altpath
prevNodes[str(neighbor)] = curr_node
minPQ.decrease_key(neighbor, altpath)
Which in my eyes should solve the problem mentioned above (optimal path for a starting node to every other node). But it does not. Can someone help me clean up this mess that I have been trying to debug for a while now. Thank you in advance!
Assumption:
In fact I realized that my dictionaries used to store the previously visited nodes (prevNodes) and the one where I save the corresponding total cost of visiting a node (totalCosts) are unequally long. And I do not understand why.

How to add a stopping condition for Jacobian Matrix?

def jacobi(m,numiter=100):
#Number of rows determins the number of variables
numvars = m.shape[0]
#construct array for final iterations
history = np.zeros((numvars,numiter))
i = 1
while(i < numiter): #Loop for numiter
for v in range(numvars): # Loop over all variables
current = m[v,numvars] # Start with left hand side (augmented side of matrix)
for col in range(numvars): #Loop over columns
if v != col: # Don't count colume for current variable
current = current - (m[v,col]*history[col, i-1]) #subtract other guesses form previous timestep
current = current/m[v,v] #divide by current variable coefficent
history[v,i] = current #Add this answer to the rest
i = i + 1 #iterate
#plot each variable
for v in range(numvars):
plt.plot(history[v,: i]);
return history[:,i-1]
I have this code that calculates Jacobian method. How do I add a stopping condition for when the solutions converge? i.e. the values for the current iteration have changed less than some threshold e from the values for the previous iteration.
The threshold e will be an input to the function and the default value to 0.00001
You could add another condition to your while loop, so when it reaches your error threshold it stops.
def jacobi(m,numiter=100, error_threshold = 1e-4):
#Number of rows determins the number of variables
numvars = m.shape[0]
#construct array for final iterations
history = np.zeros((numvars,numiter))
i = 1
err = 10*error_threshold
while(i < numiter and err > error_threshold): #Loop for numiter and error threshold
for v in range(numvars): # Loop over all variables
current = m[v,numvars] # Start with left hand side (augmented side of matrix)
for col in range(numvars): #Loop over columns
if v != col: # Don't count colume for current variable
current = current - (m[v,col]*history[col, i-1]) #subtract other guesses form previous timestep
current = current/m[v,v] #divide by current variable coefficent
history[v,i] = current #Add this answer to the rest
#check error here. In this case the maximum error
if i > 1:
err = max((history[:,i] - history[:,i-1])/history[:,i-1])
i = i + 1 #iterate
#plot each variable
for v in range(numvars):
plt.plot(history[v,: i]);
return history[:,i-1]

How to deal with very big Bitboards

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)

Categories