To draw a set of objects in a breadth first search manner
To get this out of the way in the beginning, I'm quite a noob at programming in general so please pardon the obviously noobish things I would've done in the following project.
The goal of this project is to get a graph structure if you give it a table of nodes, their descriptions and the nodes that it is connected to. The hurdle though is that I cannot use any external libraries to do this apart from matplotlib.
The input would look something like this:
inputs as a table
The expected output should be something along the lines of this: (note this diagram ignores the dimensions)
Expected output
The logic which I developed is:
Convert the raw list data into an class object which has all the attributes from the list.
2)Proceed to searching the nodes in a breadth first manner
3)Put the sequence of spaces that the BFS searched into a list
4)Then draw out the spaces as they appeared in the list (note the distance between them is necessary for what I need it for)
How far I got:
I figured out everything till step no 3. The problem I have is that I have to set a x and y position for each space now.
To break down what I have in mind is that :
If the first node is an entry type node then it draws it out in the middle of the maximum breadth of the graph
Count the number of external connections it has and then draw them out as (maximum breadth /number of connections , incrementing through them. So, the living room has none so it will draw it out in the middle. The living room has 5 connections ( out of which one has already been drawn i.e. the entry so we iterate through the next set of four spaces only moving in the x axis) Then we step up the y and draw again.
To execute this I intended to have a loop which goes though the ordered list of spaces and have a counter. The process:
1)if the node encountered is one of the type of entry node then we draw out that node and set the counter to number of connections.
2)if the counter is 1 then we move the Y axis up. and draw out that space
3)if the counter is > 1 then we divide the the counter value by the maximum depth value
The problem I have:
The example shown here only takes into account that we have one entry.
2)The ordering of spaces is wrong when using the counter method.
3) The counter method doesnt draw out the termination nodes properly.
And when I actually write it down in python I get this, which means I've clearly failed:
The output which I got
The script as it stands now:
Pastebin link
And at this point I'm out of ideas as how I should actually fix this problem. Any help is much appreciated :)
#imports
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from math import pi , sin
#raw data
connections = ['1', '0,2,5,7,8', '1,3', '2,4', '3', '1,6', '5', '1', '1']
indices = ['0', '1', '2', '3', '4', '5', '6', '7', '8']
spaceTags = ['entry ', 'living area', 'dining area', 'Kitchen', 'Utility',
'Bedroom1', 'Toilet attached', 'Toilet common', 'Bedroom2']
area = ['1', '40', '20', '15', '6', '20', '6', '6', '20']
minimumDimension = ['1', '5', '4', '3', '2', '4', '2', '2', '4']
#define the space class
class Space(object):
def __init__(self,index,spaceName,connections,area,minimumDimension):
self.index = index
self.spaceName = spaceName
self.connections = connections
self.area = area
self.ydim = minimumDimension
self.xdim = (self.area / self.ydim)
self.x = 0
self.y = 0
self.isExplored = False
self.polarAngle = 0
self.spaceType = 0
# 0= not assigned ; 1 = entry; 2 = intermediate; 3 = termination
def ObjectAttributes(self):
return (self.index,
self.spaceName,
self.connections,
self.area,
self.y,self.x,
self.xdim,self.ydim,
self.isExplored,
self.spaceType)
#definations
#process integers
def convert_to_int_vals (input_list):
output_list = []
i = 0
while i < len(input_list):
output_list.append(int(input_list[i]))
i += 1
return output_list
#process floats
def convert_to_float_vals (input_list):
output_list = []
i = 0
while i < len(input_list):
output_list.append(float(input_list[i]))
i += 1
return output_list
#process 2D lists for connections
def process_2d_connections (input_list):
output_list = []
for item in input_list:
if len(item) <= 1:
lst = []
lst.append(int(item))
output_list.append(lst)
else:
var = item
var = (var.split(','))
var = convert_to_int_vals(var)
output_list.append(var)
return output_list
#make data into objects i.e. spaces
def convertDataToSpaces(index,spaceTag,connections,area,minimumDimension):
print('Processing data...')
if (len(index)==len(spaceTag)==len(connections)==len(area)==len(minimumDimension)):
i = 0
output_list = []
while i < len(spaceTag):
space = Space(index[i],spaceTag[i],connections[i],area[i],minimumDimension[i])
output_list.append(space)
i += 1
print('Done.')
return output_list
else:
print('Number of lists dont match')
#find first node
def FindFirstNode(spaceList):
output = 'null'
for item in spaceList:
if item.spaceName == 'entry ' or item.spaceName =='entry':
item.spaceType = 1
output = item
if output == 'null':
print('No entry defined. Please define entry!')
return output
#Calculate hypotenuse
def calculate_hypotenuse(arg1,arg2):
val = ((arg1**2)+(arg2**2))**(0.5)
return val
#Calculate max hypotenuse
def calculate_max_hypotenuse (spaceList):
outputval = 0
for item in spaceList:
var = calculate_hypotenuse(item.xdim,item.ydim)
if var > outputval:
outputval = var
else:
outputval
return outputval
# Note this is a FIFO list
def FindAndQueueNextNode(spaceList,searchNode,queue):
searchNode.isExplored = True
if len(searchNode.connections) == 1:
if searchNode.spaceName == 'entry ' or searchNode.spaceName =='entry':
searchNode.spaceType = 1
else:
searchNode.spaceType = 3
elif len(searchNode.connections) > 1:
searchNode.spaceType = 2
else:
searchNode.spaceType = 0
for item in spaceList:
if ( item.index in searchNode.connections) and (item.isExplored == False) :
queue.append(item)
# Calculate the position based on the dimension (inputs are the object dimensions and current floating dim)
def CalculatePosition(currentx, currenty, space):
spaceXdimadjusted = (space.xdim / 2)* -1
spaceYdimadjusted = (space.ydim / 2)* -1
adjustedx = currentx + spaceXdimadjusted
adjustedy = currenty + spaceYdimadjusted
return (adjustedx,adjustedy)
#core algorithm
def coreAlgorithm(spacesList):
## variable holding max hypotenuse distance
grid_dimension = int((calculate_max_hypotenuse(spacesList))*(1.5))
print('Grid dimensions are : ' + str(grid_dimension) + (' units'))
## create empty processing variables
processingQueue = []
orderedListOfSpacesInBFS = []
maxTreeWidth = 0
## find the first space
firstSpace = FindFirstNode(spacesList)
orderedListOfSpacesInBFS.append(firstSpace)
print('The first node is : ' + str(firstSpace.spaceName) +
'; Index being : ' + str(firstSpace.index))
## queue the next space
FindAndQueueNextNode(spacesList,firstSpace,processingQueue)
##start while loop (while queue length loop > 0)
while len(processingQueue) > 0 :
if len(processingQueue) > maxTreeWidth:
maxTreeWidth = len(processingQueue)
else:
maxTreeWidth = maxTreeWidth
item = processingQueue.pop(0)
orderedListOfSpacesInBFS.append(item)
FindAndQueueNextNode(spacesList,item,processingQueue)
## second loop to loop through spaces and draw them
maxXDepthDimension = grid_dimension * maxTreeWidth
ypos = grid_dimension
counter = 0
while len(orderedListOfSpacesInBFS) > 0:
item = orderedListOfSpacesInBFS.pop(0)
if item.spaceType == 1:
xpos = maxXDepthDimension / 2
(item.x , item.y) = CalculatePosition(xpos,ypos, item)
ypos += grid_dimension
counter = len(item.connections)
elif counter == 1:
xpos = maxXDepthDimension / 2
(item.x , item.y) = CalculatePosition(xpos,ypos, item)
ypos += grid_dimension
counter = len(item.connections) - 1
elif counter > 1:
xpos = (maxXDepthDimension / counter)
(item.x, item.y) = CalculatePosition(xpos, ypos, item)
counter -= 1
#draw lines as a separete method
#core algorithm preprocess
def coreAlgorithmLoop (spaces_list):
#check object feasibility and if the algorithm can run.
print('Starting algorithm...')
startrun = False
floatingvartoggle = 1
for item in spaces_list:
if type(item) == Space:
floatingvartoggle = floatingvartoggle * 1
else:
floatingvartoggle = floatingvartoggle * 0
if floatingvartoggle == 1:
startrun = True
else:
print('Objects should be spaces.')
#start of core-algorithm.
if startrun == True:
coreAlgorithm(spaces_list)
#implementation
#pre-process data
indices = convert_to_int_vals(indices)
spaceTags = spaceTags
connections = process_2d_connections(connections)
area = convert_to_float_vals(area)
minimumDimension = convert_to_float_vals(minimumDimension)
#initialize processing
listOfSpaces = convertDataToSpaces(indices,
spaceTags,
connections,
area,
minimumDimension)
coreAlgorithmLoop(listOfSpaces)
#matplotlibtester - start
fig, ax = plt.subplots()
ax.set_xlim((0, 100))
ax.set_ylim((0, 70))
for space in listOfSpaces:
var = space.area
print(space.ObjectAttributes())
rect = patches.Rectangle((space.x,space.y),
space.xdim,space.ydim,0,
linewidth=1,
edgecolor='r',
facecolor='none')
ax.add_patch(rect)
plt.show()
#matplotlibtester - end
So this is awkward but just in-case it helps someone, The method to solve it is:
Create the attributes explored from and depth
Then in the queue keep track of how the node was explored and what it's depth is (the current searchnode is assigned to the items getting queued, the searchnode's depth is equal to the depth of the node that it was explored from +1
Using this create a layered chart to understand where each object goes, (depth is the row/ tier/ level number) and the instance that the object is in that level is the column number
This is easier to do with another class.Here is the modified version of the code:
(note it draws an asymmetric chart which is okay for my purposes and I've still not solved the issue of more than one starting points which is okay for now)
#imports
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from math import pi , sin
#raw data
connections = ['1', '0,2,5,7,8', '1,3', '2,4', '3', '1,6', '5', '1', '1']
indices = ['0', '1', '2', '3', '4', '5', '6', '7', '8']
spaceTags = ['entry ', 'living area', 'dining area', 'Kitchen', 'Utility',
'Bedroom1', 'Toilet attached', 'Toilet common', 'Bedroom2']
area = ['1', '40', '20', '15', '6', '20', '6', '6', '20']
minimumDimension = ['1', '5', '4', '3', '2', '4', '2', '2', '4']
#define the space class
class Space(object):
def __init__(self,index,spaceName,connections,area,minimumDimension):
self.index = index
self.spaceName = spaceName
self.connections = connections
self.area = area
self.ydim = minimumDimension
self.xdim = (self.area / self.ydim)
self.x = 0
self.y = 0
self.isExplored = False
self.isExploredFrom = 0
self.Depth = 0
self.spaceType = 0
# 0= not assigned ; 1 = entry; 2 = intermediate; 3 = termination
self.CentroidX = 0
self.CentroidY = 0
def ObjectAttributes(self):
return (self.index,
self.spaceName,
self.y,self.x,
self.Depth)
class DrawingStructure(object):
def __init__(self,Depth):
self.Depth = Depth
self.numberOfInstances = 0
self.count = 0
#definations
#process integers
def convert_to_int_vals (input_list):
output_list = []
i = 0
while i < len(input_list):
output_list.append(int(input_list[i]))
i += 1
return output_list
#process floats
def convert_to_float_vals (input_list):
output_list = []
i = 0
while i < len(input_list):
output_list.append(float(input_list[i]))
i += 1
return output_list
#process 2D lists for connections
def process_2d_connections (input_list):
output_list = []
for item in input_list:
if len(item) <= 1:
lst = []
lst.append(int(item))
output_list.append(lst)
else:
var = item
var = (var.split(','))
var = convert_to_int_vals(var)
output_list.append(var)
return output_list
#make data into objects i.e. spaces
def convertDataToSpaces(index,spaceTag,connections,area,minimumDimension):
print('Processing data...')
if (len(index)==len(spaceTag)==len(connections)==len(area)==len(minimumDimension)):
i = 0
output_list = []
while i < len(spaceTag):
space = Space(index[i],spaceTag[i],connections[i],area[i],minimumDimension[i])
output_list.append(space)
i += 1
print('Done.')
return output_list
else:
print('Number of lists dont match')
#find first node
def FindFirstNode(spaceList):
output = 'null'
for item in spaceList:
if item.spaceName == 'entry ' or item.spaceName =='entry':
item.spaceType = 1
output = item
if output == 'null':
print('No entry defined. Please define entry!')
return output
#Calculate hypotenuse
def calculate_hypotenuse(arg1,arg2):
val = ((arg1**2)+(arg2**2))**(0.5)
return val
#Calculate max hypotenuse
def calculate_max_hypotenuse (spaceList):
outputval = 0
for item in spaceList:
var = calculate_hypotenuse(item.xdim,item.ydim)
if var > outputval:
outputval = var
else:
outputval
return outputval
# Note this is a FIFO list
def FindAndQueueNextNode(spaceList,searchNode,queue):
searchNode.isExplored = True
if len(searchNode.connections) == 1:
if searchNode.spaceName == 'entry ' or searchNode.spaceName =='entry':
searchNode.spaceType = 1
else:
searchNode.spaceType = 3
elif len(searchNode.connections) > 1:
searchNode.spaceType = 2
else:
searchNode.spaceType = 0
for item in spaceList:
if ( item.index in searchNode.connections) and (item.isExplored == False) :
item.isExploredFrom = searchNode.index
item.Depth = searchNode.Depth + 1
queue.append(item)
# Calculate the position based on the dimension (inputs are the object dimensions and current floating dim)
def CalculatePosition(currentx, currenty, space):
spaceXdimadjusted = (space.xdim / 2)* -1
spaceYdimadjusted = (space.ydim / 2)* -1
adjustedx = currentx + spaceXdimadjusted
adjustedy = currenty + spaceYdimadjusted
return (adjustedx,adjustedy)
#def return only unique values in a list
def ReturnUniqueValues(input_list):
output_list = []
for i in input_list:
if i not in output_list:
output_list.append(i)
return output_list
#core algorithm
def coreAlgorithm(spacesList):
## variable holding max hypotenuse distance
grid_dimension = int((calculate_max_hypotenuse(spacesList))*(1.5))
print('Grid dimensions are : ' + str(grid_dimension) + (' units'))
## create empty processing variables
processingQueue = []
orderedListOfSpacesInBFS = []
maxTreeWidth = 0
## find the first space
firstSpace = FindFirstNode(spacesList)
orderedListOfSpacesInBFS.append(firstSpace)
print('The first node is : ' + str(firstSpace.spaceName) +
'; Index being : ' + str(firstSpace.index))
##initialize first space
firstSpace.Depth = 0
firstSpace.isExploredFrom = 0
FindAndQueueNextNode(spacesList,firstSpace,processingQueue)
##start while loop (while queue length loop > 0)
while len(processingQueue) > 0 :
if len(processingQueue) > maxTreeWidth:
maxTreeWidth = len(processingQueue)
else:
maxTreeWidth = maxTreeWidth
item = processingQueue.pop(0)
orderedListOfSpacesInBFS.append(item)
FindAndQueueNextNode(spacesList,item,processingQueue)
#second loop for figuring out the depth and column number to draw
DepthList = []
uniquelist = []
DrawingList = []
for item in orderedListOfSpacesInBFS:
DepthList.append(item.Depth)
uniquelist = ReturnUniqueValues(DepthList)
for item in uniquelist:
var = DrawingStructure(item)
DrawingList.append(var)
copyList = orderedListOfSpacesInBFS
while len(copyList) > 0:
space = copyList.pop(0)
for thing in DrawingList:
if int(thing.Depth) == int(space.Depth):
thing.numberOfInstances += 1
##actually setting the values to later draw
for item in spacesList:
rowNum = item.Depth
rowData = 'null'
for entry in DrawingList:
if rowNum == entry.Depth:
rowData = entry
colNum = rowData.count
rowData.count += 1
xpos = rowNum * grid_dimension
ypos = colNum * grid_dimension
item.CentroidY = xpos
item.CentroidX = ypos
(item.x, item.y) = CalculatePosition(ypos,xpos,item)
#draw lines as a separete method
def DrawConnectionLines(spacesList):
for item in spacesList:
centroid = [item]
#core algorithm preprocess
def coreAlgorithmLoop (spaces_list):
#check object feasibility and if the algorithm can run.
print('Starting algorithm...')
startrun = False
floatingvartoggle = 1
for item in spaces_list:
if type(item) == Space:
floatingvartoggle = floatingvartoggle * 1
else:
floatingvartoggle = floatingvartoggle * 0
if floatingvartoggle == 1:
startrun = True
else:
print('Objects should be spaces.')
#start of core-algorithm.
if startrun == True:
coreAlgorithm(spaces_list)
DrawConnectionLines(spaces_list)
#implementation
#pre-process data
indices = convert_to_int_vals(indices)
spaceTags = spaceTags
connections = process_2d_connections(connections)
area = convert_to_float_vals(area)
minimumDimension = convert_to_float_vals(minimumDimension)
#initialize processing
listOfSpaces = convertDataToSpaces(indices,
spaceTags,
connections,
area,
minimumDimension)
coreAlgorithmLoop(listOfSpaces)
#matplotlibtester - start
fig, ax = plt.subplots()
ax.set_xlim((-10, 60))
ax.set_ylim((0, 70))
xvals = []
yvals = []
for space in listOfSpaces:
print(space.ObjectAttributes())
rect = patches.Rectangle((space.x,space.y),
space.xdim,space.ydim,0,
linewidth=1,
edgecolor='r',
facecolor='none')
ax.add_patch(rect)
xvals.append(space.CentroidX)
yvals.append(space.CentroidY)
plt.scatter(xvals,yvals)
plt.show()
#matplotlibtester - end
Related
Whenever k = 2, the code runs in a loop
if k > 2 it sets all, but one of the centroids location to 0,0
I've reviewed it a couple of times , and it doesn't seem like there are any errors probably some sort of logic flaw. The code starts by having a class and its methods which initiate the centroids, calculate the Euclidean distance, and reassign centroids to the average positions of the points that are in the cluster. It then runs a loop that consists of reassigning and calculating distance until a list of the assignments are equal and then plots it.
class Kmeans:
def __init__(self, K, dataset, centroids, sorting):
self.K = K
self.dataset = dataset
self.centroids = centroids
self.sorting = sorting
#sets starting position of centroids
def initializeCentroids(self):
bigX = 0
bigY = 0
self.centroids = []
for i in self.dataset:
if i[0] > bigX:
bigX = i[0]
if i[1] > bigY:
bigY = i[1]
for q in range(self.K):
self.centroids.append([random.randint(0, bigX), random.randint(0, bigY)])
plt.scatter((self.centroids[0][0], self.centroids[1][0]), (self.centroids[0][1], self.centroids[1][1]))
return self.centroids
#calculates euclidean distance
def calcDistance(self):
self.sorting = []
for w in self.dataset:
print(w)
distances = []
counter = 0
for centr in self.centroids:
distances.append(math.sqrt(abs((centr[0] - w[0] * centr[0] - w[0]) + (centr[1] - w[1] * centr[1] - w[1]))))
counter += 1
if counter > 0:
try:
if distances[0] > distances[1]:
distances.pop(0)
if distances[1] > distances[0]:
distances.pop(1)
counter -= 1
except IndexError:
pass
self.sorting.append([w, counter, distances[0]])
return self.sorting
def reassignCentroids(self):
counter3 = 1
for r in range(len(self.centroids)):
positionsX = []
positionsY = []
for t in self.sorting:
if t[1] == counter3:
positionsX.append(t[0][0])
positionsY.append(t[0][1])
population = len(positionsY)
if population == 0:
population = 1
self.centroids.append([sum(positionsX) / population, sum(positionsY) / population])
counter3 += 1
self.centroids.pop(0)
return
k = 4
dataSetSize = input("Enter the amount of tuples you want generated: ")
data_set = []
for o in range(int(dataSetSize)):
data_set.append((random.randint(0, 1000), random.randint(0, 1000)))
attempt = Kmeans(k, data_set, 0, 0)
attempt.initializeCentroids()
xvals = []
yvals = []
sortCompare = []
# plots
for p in data_set:
xvals.append(p[0])
yvals.append(p[1])
running = True
while running:
if len(sortCompare) > 1:
centroidChoice0 = []
centroidChoice1 = []
for p in sortCompare[0]:
centroidChoice0.append(p[1])
for d in sortCompare[1]:
centroidChoice1.append(d[1])
print(centroidChoice1)
print(attempt.centroids)
if centroidChoice1 == centroidChoice0:
running = False
for m in attempt.centroids:
plt.scatter((attempt.centroids[0][0], attempt.centroids[1][0]), (attempt.centroids[0][1], attempt.centroids[1][1]))
running = False
sortCompare.pop(0)
attempt.calcDistance()
sortCompare.append(attempt.sorting)
attempt.reassignCentroids()
I have converted the code given at this link into a python version. The code is supposed to calculate the correct value of maximum value to be filled in knapsack of weight W. I have attached the code below:
#http://www.geeksforgeeks.org/branch-and-bound-set-2-implementation-of-01-knapsack/
from queue import Queue
class Node:
def __init__(self):
self.level = None
self.profit = None
self.bound = None
self.weight = None
def __str__(self):
return "Level: %s Profit: %s Bound: %s Weight: %s" % (self.level, self.profit, self.bound, self.weight)
def bound(node, n, W, items):
if(node.weight >= W):
return 0
profit_bound = int(node.profit)
j = node.level + 1
totweight = int(node.weight)
while ((j < n) and (totweight + items[j].weight) <= W):
totweight += items[j].weight
profit_bound += items[j].value
j += 1
if(j < n):
profit_bound += (W - totweight) * items[j].value / float(items[j].weight)
return profit_bound
Q = Queue()
def KnapSackBranchNBound(weight, items, total_items):
items = sorted(items, key=lambda x: x.value/float(x.weight), reverse=True)
u = Node()
v = Node()
u.level = -1
u.profit = 0
u.weight = 0
Q.put(u)
maxProfit = 0;
while not Q.empty():
u = Q.get()
if u.level == -1:
v.level = 0
if u.level == total_items - 1:
continue
v.level = u.level + 1
v.weight = u.weight + items[v.level].weight
v.profit = u.profit + items[v.level].value
if (v.weight <= weight and v.profit > maxProfit):
maxProfit = v.profit;
v.bound = bound(v, total_items, weight, items)
if (v.bound > maxProfit):
Q.put(v)
v.weight = u.weight
v.profit = u.profit
v.bound = bound(v, total_items, weight, items)
if (v.bound > maxProfit):
# print items[v.level]
Q.put(v)
return maxProfit
if __name__ == "__main__":
from collections import namedtuple
Item = namedtuple("Item", ['index', 'value', 'weight'])
input_data = open("test.data").read()
lines = input_data.split('\n')
firstLine = lines[0].split()
item_count = int(firstLine[0])
capacity = int(firstLine[1])
print "running from main"
items = []
for i in range(1, item_count+1):
line = lines[i]
parts = line.split()
items.append(Item(i-1, int(parts[0]), float(parts[1])))
kbb = KnapSackBranchNBound(capacity, items, item_count)
print kbb
The program is supposed to calculate value of 235 for following items inside file test.data:
5 10
40 2
50 3.14
100 1.98
95 5
30 3
The first line shows number of items and knapsack weight. Lines below first line shows the value and weight of those items. Items are made using a namedtuple and sorted according to value/weight. For this problem I am getting 135 instead of 235. What am I doing wrong here?
EDIT:
I have solved the problem of finding correct items based on branch and bound. If needed, one can check it here
The problem is that you're inserting multiple references to the same Node() object into your queue. The fix is to initialize two new v objects in each iteration of the while-loop as follows:
while not Q.empty():
u = Q.get()
v = Node() # Added line
if u.level == -1:
v.level = 0
if u.level == total_items - 1:
continue
v.level = u.level + 1
v.weight = u.weight + items[v.level].weight
v.profit = u.profit + items[v.level].value
if (v.weight <= weight and v.profit > maxProfit):
maxProfit = v.profit;
v.bound = bound(v, total_items, weight, items)
if (v.bound > maxProfit):
Q.put(v)
v = Node() # Added line
v.level = u.level + 1 # Added line
v.weight = u.weight
v.profit = u.profit
v.bound = bound(v, total_items, weight, items)
if (v.bound > maxProfit):
# print(items[v.level])
Q.put(v)
Without these reinitializations, you're modifying the v object that you already inserted into the queue.
This is different from C++ where the Node objects are values that are implicitly copied into the queue to avoid aliasing problems such as these.
Faced an interview question in Python that was as follow?
ex:
input = ('192.168.15.1', '.', -1) ===> output = (192, 168, 15, 1)
input = ('192.168.15.1', '.', 2) ===> output = ( 192, 168, 15.1 )
Solve such that input(string,char,integer) will give the output such that the string is splited by the character and if integer is 0 or less than that else string is splited the number of by character where character split only occur for the value of integer.
I have written the code although it works fine within boundary and no error condition. Is there a better code out there plese share.
Thanks
def split_string(string,ch,inte):
q = string
c = ch
i = inte
s =list(q)
#Check if integer value is negative or positive
if i <= 0:
# split the string one time
r1 = split_once(s,c)
print r1
else:
#split according to the integear value
r2 = split_acc_intger(s,c,i)
print r2
def split_once(s,c):
y = 0
d = []
for x in range(len(s)):
if s[x] == c:
p=s[y:x]
d.append(''.join(p))
y = x + 1
elif x == len(s)-1:
p=s[y:]
d.append(''.join(p))
return d
def split_acc_intger(s,c,i):
y = 0
d =[]
count = 1
for x in range(len(s)):
# the leat number will 1
if s[x] == c:
p=s[y:x]
d.append(''.join(p))
y = x + 1
count += 1
elif count == i :
p=s[y:]
d.append(''.join(p))
break
return d
Simple and recursive.
def split_str(s,c,i):
if i == 0:
return [s]
else:
head, _, rest = s.partition(c)
if rest:
return [head] + split_str(rest, c, i - 1)
return [head]
def split(text, sep, maxsplit=-1):
parts = []
end = -1
while True:
start = end + 1
end = text.find(sep, start)
if (end == -1) or (maxsplit == 0):
parts.append(text[start:])
break
else:
parts.append(text[start:end])
if maxsplit != 0: maxsplit -= 1
return parts
print(split('192.168.15.1', '.', -1)) # ['192', '168', '15', '1']
print(split('192.168.15.1', '.', 2)) # ['192', '168', '15.1']
Something like this would work:
def splitter(string_, splitchar, maxsplits):
# if maxsplits is positive, split string into maxsplits of parts, on splitchar.
# Otherwise perform as many splits as the string allows.
out = []
sub = []
for ch in string_:
if ch != splitchar:
sub.append(ch)
else:
if maxsplits < 1 or (maxsplits > 0 and (len(out) < maxsplits)):
out.append(''.join(sub))
sub = []
else:
sub.append(ch)
out.append(''.join(sub))
return tuple(out)
>>> splitter.splitter('192.168.15.1', '.', -1)
('192', '168', '15', '1')
>>> splitter.splitter('192.168.15.1', '.', 2)
('192', '168', '15.1')
>>> splitter.splitter('192.168.15.1', '.', 0)
('192', '168', '15', '1')
>>> splitter.splitter('192.168.15.1', '.', 1)
('192', '168.15.1')
another simpler and short approach is you can use regular expressions like this:
import re
def split_without_using_split_fn(s, c, i):
if i<=0:
print(re.findall("[^"+c+"]+(?=\\"+c+"|$)", s))
else:
l=re.findall("[^"+c+"]+(?=\\"+c+"|$)", s)
print(l[0:-i]+[c.join(l[i:])])
split_without_using_split_fn(*('192.168.15.1', '.', -1))
split_without_using_split_fn(*('192.168.15.1', '.', 2))
Output:
$ python3 p.py
['192', '168', '15', '1']
['192', '168', '15.1']
$
You could solve this task with following function
def split_string(istr,ich,inte):
res = []
prev = 0
for i,ch in enumerate(istr):
if ch == ich:
res.append(istr[prev:i])
prev = i+1
inte = inte-1
if inte == 0:
break
if prev < len(istr):
res.append(istr[prev:])
return res
or another solution
def split_string(istr,ich,inte):
res = []
h,_,r = istr.partition(ich)
while r:
res.append(h)
inte = inte-1
if inte == 0:
h = r
break
h,_,r = r.partition(ich)
if h:
res.append(h)
return res
For following code
print split_string('192.168.15.1', '.', -1)
print split_string('192.168.15.1', '.', 2)
output will be
['192', '168', '15', '1']
['192', '168', '15.1']
I've programmed Conways Game of Life in Python and now I'm trying to display the simple data that it gives me as an output in a heat map.
This is my current code:
from Tkinter import *
import matplotlib.pyplot as plt
import time
import numpy as np
import random
size_x = 100
size_y = 10
# create the matrices
cell = [[0 for row in range(0, size_y)] for col in range(0, size_x)]
live = [[0 for row in range(0, size_y)] for col in range(0, size_x)]
temp = [[0 for row in range(0, size_y)] for col in range(0, size_x)]
# process and draw the next frame
def frame():
process()
draw()
root.after(100, frame)
# load the initial data
def load(initial=0.5):
for y in range(0, size_y):
for x in range(0, size_x):
if random.random()<initial: live[x][y] = 1
temp[x][y] = 0
# Applying rules
def process():
for y in range(0, size_y):
for x in range(0, size_x):
lives = live_neighbors(x,y)
if live[x][y] == 1:
if lives < 2 or lives > 3:
temp[x][y] = 0
else:
temp[x][y] = 1
if live[x][y] == 0:
if lives == 3:
temp[x][y] = 1
else:
temp[x][y] = 0
for y in range(0, size_y):
for x in range(0, size_x):
live[x][y] = temp[x][y]
# live = temp
# Count live neighbors
def live_neighbors(a,b):
lives = 0
if live[a][(b+1)%size_y] == 1: lives += 1
if live[a][(b-1)%size_y] == 1: lives += 1
if live[(a+1)%size_x][b] == 1: lives += 1
if live[(a+1)%size_x][(b+1)%size_y] == 1: lives += 1
if live[(a+1)%size_x][(b-1)%size_y] == 1: lives += 1
if live[(a-1)%size_x][b] == 1: lives += 1
if live[(a-1)%size_x][(b+1)%size_y] == 1: lives += 1
if live[(a-1)%size_x][(b-1)%size_y] == 1: lives += 1
return lives
# Draw all cells
def draw():
nLiving = 0
nDead = 0
for y in range(size_y):
for x in range(size_x):
if live[x][y]==0:
canvas.itemconfig(cell[x][y], fill="black")
nDead+=1
if live[x][y]==1:
canvas.itemconfig(cell[x][y], fill="white")
nLiving+=1
print nLiving,nDead
# count cells
def count():
nLiving = 0
nDead = 0
for y in range(size_y):
for x in range(size_x):
if live[x][y]==0:
nDead+=1
if live[x][y]==1:
nLiving+=1
z = nLiving / 10.0
print z,
print "%"
def one_game(initial):
load(initial)
for gen in range(1, 101):
print str(gen) + ":",
count()
process()
def many_games():
numbers = range(1,51)
for initial in numbers:
print initial/100.0
one_game(initial/100.0)
many_games()
#one_game(0.5)
The code for making a normal heat map with given input would be:
fig, ax = plt.subplots(1)
x = np.array( [[11,12,13], [21,22,23], [31,32,33]] )
p = ax.pcolormesh(x)
fig.colorbar(p)
plt.show()
How do I get my data (which in this case would be, the generations, the value which initializes the one_game() function, and nLiving) into an array?
I'm not 100% sure this is what you're intending, but it produced a pretty output heat map :)
def count():
nLiving = 0
nDead = 0
for y in range(size_y):
for x in range(size_x):
if live[x][y]==0:
nDead+=1
if live[x][y]==1:
nLiving+=1
z = nLiving / 10.0
print("nLiving over ten is: ", z,)
print("%")
return nLiving
def one_game(initial):
load(initial)
gen_array = []
for gen in range(1, 101):
print("Gen: ", str(gen) + ":",)
nLiving = count()
process()
gen_array.append(nLiving)
return gen_array
def many_games():
gen_output = []
numbers = range(1,51)
for initial in numbers:
print(initial/100.0)
gen_array = one_game(initial/100.0)
gen_output.append(gen_array)
return gen_output
gen_output = many_games()
#one_game(0.5)
fig, ax = plt.subplots(1)
x = np.array( gen_output )
p = ax.pcolormesh(x)
fig.colorbar(p)
plt.show()
That is just code modified from your count function to the end of the file. Basically you just need to return the output from the functions that you're calling into the right kind of data structures, I think...
so my code looks like this:
def parse(info):
"""
'info' is a list made out of data received (info = list(data)). Examples are as follows:
['NEWGAME', '1', 'testbot', 'testbot', '100', '200', '2', '1', '10000.00000']
['NEWHAND', '1', '0', '4c', '5d', '0', '0', '0', '10000.00000']
['GETACTION', '3', '0', '2', 'POST:testbot:1,POST:testbot:2', '3', 'FOLD,CALL,RAISE:4', '10000.0']
['HANDOVER', '0', '0', '0', '3', 'FOLD:bot3001,FOLD:testbot,WIN:testbot:3', '9999.9506']
"""
phase = info[0] # setting board cards to 0
boardCard1 = 0 # 0 means no card
boardCard2 = 0
boardCard3 = 0
boardCard4 = 0
boardCard5 = 0
if phase == "NEWGAME":
gameID = int(info[1]) # Game ID
leftOpp = info[2] # Left Opponent
rightOpp = info[3] # Right Opponent
maxHands = int(info[4]) #Initializing number of hands that will be played
maxStackSize = int(info[5]) # Stack size, also the maximum bet size
bigBlind = int(info[6])
smBlind = int(info[7])
maxTime = float(info[8]) # Initiallizing milliseconds of time allocated for the game
return (phase, gameID, leftOpp, rightOpp, maxHands, maxStackSize, bigBlind, smBlind, maxTime)
if phase == "NEWHAND":
handID = int(info[1]) # The hand number which we're currently at
position = int(info[2]) # Possition, either 0, 1, or 2. 0 = dealer, 1 = smalll blind, 2 = big blind
card1 = info[3] # Card 1
card1Rank = (list(info[3]))[0] # Rank of Card 1
card1Suit = (list(info[3]))[1] # Suit of Card 1
card2 = info[4] # Card 2
card2Rank = (list(info[4]))[0] # Rank of Card 2
card2Suit = (list(info[4]))[1] # Suit of Card 2
hand = [card1, card2] # A list containing the 2 cards in your hand
bankRoll = int(info[5]) # Your bank roll, total amount made (can be negative)
leftOppBank = info[6] # Left Opponent's bankroll
rightOppBank = info[7] # Right Opponent's bankroll
timeLeft = float(info[8]) # Milliseconds of time allocated for the game
boardPhase = "PREFLOP" # Initializing boardPhase
return(boardPhase, phase, handID, position, card1, card1Rank, card1Suit, card2, card2Rank, card2Suit, hand, bankRoll, leftOppBank, rightOppBank, timeLeft)
elif phase == "GETACTION":
potSize = int(info[1])
numBoardCards = int(info[2]) # can be 0,3,4 or 5
boardCardsPacket = 0 # 0 for none, 1 for present
boardCards =[]
if numBoardCards != 0:
boardCardsPacket = 1
boardCards = (info[4]).split(",") # info[4] looks like 'Th,Ad,Js,5h' and split(",") turns it into [Th, Ad, Js, 5h]
boardCard1 = boardCards[0]
boardCard2 = boardCards[1]
boardCard3 = boardCards[2]
try:
boardCard4 = boardCards[3]
try:
boardCard5 = boardCards[4]
except:
pass
except:
pass
numLastActions = int(info[3+boardCardsPacket])
lastActions = []
if numLastActions != 0:
actionsString = info[4+boardCardsPacket] # a string of actions ex: POST:testbot:1,POST:testbot:2
lastActions = actionsString.split(",") # A list of the actions ex: ['POST:testbot:1','FOLD:testbot','WIN:testbot:3']
for i, item in enumerate(actions):
if "DEAL" in item.split(":"):
boardPhase = item.split(":")[1]
numLegalActions = int(info[5+boardCardsPacket])
legalActions = (info[6+boardCardsPacket]).split(",") # List of Legal Actions, ex: ['FOLD', 'CALL', 'RAISE:4']
minBet = "NULL" # it will be NULL if BET is not a legal action.
for i, item in enumerate(legalActions):
if "BET" in item.split(":"):
minBet = item.split(":")[1]
minRaise = "NULL"
for i, item in enumerate(legalActions):
if "RAISE" in item.split(":"):
minRaise = item.split(":")[1]
realLegalActions = []
for item1 in legalActions:
for item2 in item1.split(":"): # this creates realLegalActions which will not include numbers, will look like ['FOLD','CALL', 'RAISE'], makes it easier to check if an action is in legalActions
try:
int(item2)
except:
realLegalActions.append(item2)
timeLeft = float(info[7+boardCardsPacket])
try: # If the boardPhase is still PREFLOP then there is no variable boardPhase to return, so we have an exception for that.
return (realLegalActions, minBet, timeLeft, boardPhase, legalActions, numLegalActions, lastActions, numLastActions, boardCardsPacket,
boardCards, boardCard1, boardCard2, boardCard3, boardCard4, boardCard5, potSize, numBoardCards, minRaise)
except:
return (realLegalActions, minBet, timeLeft, legalActions, numLegalActions, lastActions, numLastActions, boardCardsPacket,
boardCards, boardCard1, boardCard2, boardCard3, boardCard4, boardCard5, potSize, numBoardCards, minRaise)
elif info[0] == "HANDOVER":
bankRoll = int(info[1])
leftOppBank = int(info[2])
rightOppBank = int(info[3])
numLastAction = int(info[4])
LastActions =[]
if numLastAction!=0:
LastActions = (info[5]).split(",")
timeLeft = float(info[6])
return(numLastAction, timeLeft, bankRoll, leftOppBank, rightOppBank, LastActions)
How can I use all the variables that I am returning? What would be the easiest way? Should I have created a class (I'm not too good at using classes but I will revisit my code if it's possible with classes)?
edit----
I just want to parse the strings and then return all the variables so I can use them in other classes and functions. This is the main function that is importing it:
import socket
import sys
import pokerParser
from time import time
import betMaster
import historian
if __name__ == "__main__":
port = int(sys.argv[1])
s = socket.create_connection(('127.0.0.1', int(sys.argv[1])))
f_in = s.makefile()
while 1:
start = time() # converting from seconds to miliseconds
data = f_in.readline()
if not data:
print "Gameover, engine disconnected"
break
info = data.split()
pokerParser.parse(info)
if phase == "HANDOVER":
historian.lastActionInitiator(lastActions)
if phase == "GETACTION":
if (timeLeft/handsRemaining) < (maxTime*.90)/maxHands:
output = "NULL" #Null is not a legalAction so it will be changed to CHECK or FOLD later.
while (maxTime-(time()-startTimeelapsedTime)*1000)*.90/(handsRemaining-1): #This will set the cutoff time for a response at a time that will set the bot back on track time-wise.
#output = jonsModule.attribute()
else:
while (time()-startTime)*1000 < (maxTime*.90)/maxHands:
#output = jonsModule.attribute()
#if output.split(":")[0] not in realLegalActions and "CHECK" in realLegalActions:
#output = "CHECK"
#if output.split(":")[0] == "BET":
#if output.split(":")[1] < minBet:
#output = "CHECK"
#if output.split(":")[0] == "RAISE":
#if output.split(":")[1] < minRaise:
#output = "CHECK"
#elif output not in legalActions and "CHECK" not in legalActions:
#output = "FOLD"
#s.send(output)
s.close()
# commented = dummy code for now
def test():
return 1, 2, 3
a, b, c = test() # a=1, b=2, c=3
Btw, you don't need to return tuples.
The easiest is to return locals()
>>> def myfunction():
... a = 1
... b = 2
... c = 3
... return locals()
...
>>> print myfunction()
{'a': 1, 'c': 3, 'b': 2}
Short Answer
You may return variables from Python methods in any manner that makes sense to you, or your intended purpose. This may be a singular value, a tuple, a dictionary, a new class, or nothing at all - depending, of course, on your intention.
Long Answer (which may be a CodeReview issue)
In a context such as this, you will run into an issue in which there are too many variables defined keeping track of different things. It may be an old habit to use local-scope variables to keep track of stuff, but in Python, since you have the list (and you document what each position in the list represents somewhere), you could just use that explicitly instead.
Example:
if phase == "NEWGAME":
return tuple([i for i in info[1:]]) # Returns everything in info except "NEWGAME"
It's a matter of what the full intention and scope of your code is. I would strongly encourage that you sit down and refactor your code, so it is both clear in intent (not just to us, but to yourself), and each function is specific and well-defined in purpose.
If you want to return multiple values and a tuple won't work, you could instead return a dictionary:
{name1: value1, name2: value2, ...}
It then becomes easy to check if some variable was returned or not by using:
if name in returnvalues:
(This all is assuming you don't just reorganize your script completely, which looks to be a good idea)