Improve python code with many if statements - python

I've seen a code on a project and would like to know how to write a code with less statements.
Here is the code:
if not self.test_done:
phase = "Test"
else:
if self.need_video and not self.video_sent:
phase = 'Video'
else:
if not self.screening1_made and not self.screening2_made:
phase = 'Screening 1'
else:
if ( self.screening1_made and not self.screening2_made ) or not self.passed_screening:
phase = 'Screening 2'
else:
if not self.passed_phase1:
phase = 'Interview 1'
else:
if not self.passed_phase2:
phase = 'Interview 2'
else:
if not self.passed_phase3:
phase = 'Interview 3'
else:
if not self.got_offer:
phase = 'Waiting offer decision'
else:
if self.accepted_offer is None:
phase = 'Waiting decision from candidate'
elif self.accepted_offer == "Accepted":
phase = 'Accepted offer'
elif self.accepted_offer == "Declined":
phase = 'Declined offer'
What would be best practices to avoid nested if statements?

You can extract it to the method and early return, also you can use a dictionary lookup to get your output.
OFFER = {
None: "Waiting decision from candidate",
"Accepted": "Accepted offer",
"Declined": "Declined offer",
}
def some_function_name(self):
if not self.test_done:
return "Test"
if self.need_video and not self.video_sent:
return "Video"
if not self.screening1_made and not self.screening2_made:
return "Screening 1"
if (self.screening1_made and not self.screening2_made) or not self.passed_screening:
return "Screening 2"
if not self.passed_phase1:
return "Interview 1"
if not self.passed_phase2:
return "Interview 2"
if not self.passed_phase3:
return "Interview 3"
if not self.got_offer:
return "Waiting offer decision"
return OFFER.get(self.accepted_offer, None)

This smells to me like a state machine. A "switch - case" construct is often used when evaluating state machines, and while python doesn't currently have that exact feature yet (3.10 will introduce match: case), A list of elif's should do the trick just fine:
if not self.test_done:
phase = "Test"
elif self.need_video and not self.video_sent:
phase = 'Video'
elif self.need_video and not self.video_sent:
phase = 'Video'
elif not self.screening1_made and not self.screening2_made:
phase = 'Screening 1'
elif ( self.screening1_made and not self.screening2_made ) or not self.passed_screening:
phase = 'Screening 2'
elif not self.passed_phase1:
phase = 'Interview 1'
elif not self.passed_phase2:
phase = 'Interview 2'
elif not self.passed_phase3:
phase = 'Interview 3'
elif not self.got_offer:
phase = 'Waiting offer decision'
elif self.accepted_offer is None:
phase = 'Waiting decision from candidate'
elif self.accepted_offer == "Accepted":
phase = 'Accepted offer'
elif self.accepted_offer == "Declined":
phase = 'Declined offer
flattened it's not so bad to look at, and you probably won't find a significantly more elegant solution without changing how you track all the conditionals.

You should refactor your program. Here's a rough Interview class and related objects, which includes a basic dictionary for event names and number of rounds.
class InterviewEvent:
def __init__(self, event_id, event_name, total_rounds) -> None:
self.event_id: int = event_id
self.event_name: str = event_name
self.total_rounds: int = total_rounds
self.competed_rounds: int = 0
self.completed: bool = False
class Interview:
"""Interview class to track interview progress.
"""
def __init__(self, expected_workflow: dict = None):
self.events = []
self.current_event: InterviewEvent = None
if expected_workflow:
self.__init_events(expected_workflow)
def __init_events(self, wf: dict) -> None:
"""Populate events list from workflow dictionary."""
self.events = [InterviewEvent(i, k, v) for i, (k, v) in enumerate(wf.items())]
# Set current event
self.current_event = self.events[0]
def completed_event_round(self, title, round_number):
"""Update round number for event. If complete, increment
to next round or next event in workflow.
"""
for event in self.events:
if event.event_name.lower() == title.lower():
if event.competed_rounds < round_number:
event.competed_rounds = round_number
# Mark event completed if completed rounds equals total rounds.
if event.competed_rounds == event.total_rounds:
event.completed = True
if event.completed:
# Increment current event by 1.
self.current_event = self.events[event.event_id + 1]
def __event_attrs(self, e: InterviewEvent) -> str:
return "\n".join([f"{k}: {v}" for k, v in e.__dict__.items()])
#property
def view_current(self):
print(self.__event_attrs(self.current_event))
#property
def remaining_events(self):
return [e for e in self.events if not e.completed]
#property
def view_remaining_events(self):
events_remaining = self.remaining_events
if events_remaining:
print("\n\n".join([self.__event_attrs(e) for e in events_remaining]))
#property
def event_ids(self):
"""Return all current event IDs."""
return [e.event_id for e in self.events]
#property
def event_names(self):
"""Return all current event names."""
return [e.event_name for e in self.events]
# Dictionary of event names and number of rounds.
workflow_rounds = dict(
Test = 1,
Video = 1,
Screening = 2,
Interview = 3,
)
View events that are not complete.
interview.view_remaining_events
Output:
event_id: 0
event_name: Test
total_rounds: 1
competed_rounds: 0
completed: False
event_id: 1
event_name: Video
total_rounds: 1
competed_rounds: 0
completed: False
event_id: 2
event_name: Screening
total_rounds: 2
competed_rounds: 0
completed: False
event_id: 3
event_name: Interview
total_rounds: 3
competed_rounds: 0
completed: False
View current event
interview.view_current
Output:
event_id: 0
event_name: Test
total_rounds: 1
competed_rounds: 0
completed: False
Mark first Test round as complete
interview.completed_event_round(title = "Test", round_number = 1)
Again, view events that are not complete.
interview.view_remaining_events
Output:
event_id: 1
event_name: Video
total_rounds: 1
competed_rounds: 0
completed: False
event_id: 2
event_name: Screening
total_rounds: 2
competed_rounds: 0
completed: False
event_id: 3
event_name: Interview
total_rounds: 3
competed_rounds: 0
completed: False
Finally, view current event
interview.view_current
Output:
event_id: 1
event_name: Video
total_rounds: 1
competed_rounds: 0
completed: False

You can use lambda to run expressions. In the example below, we itterate through the list of tuples, containing lambda expressions and strings. When we find a lambda expression that is true, we break out of the loop.
x = 1
expressions = [
(lambda: x == 0, 'X is equal to 0'),
(lambda: x == 1, 'X is equal to 1'),
(lambda: x == 2, 'X is equal to 2'),
]
phrase = 'No result found'
for item in expressions:
expression = item[0]
text = item[1]
if expression():
phrase = text
break
print(phrase)
This is the result.
X is equal to 1

Related

Cleaner way to do if else on multiple variables in Python

i have 4 variables here that needs to check whether its true or false. together will all the combinations.. 4 true, 3 true 1 false, 2 true 2 false, 1 true 3 false and all false.. in total this will make like 10++ condition (a bit lazy to count).
if game and owner and platform and is_new and for_sale:
game_copy = GameCopy.objects.all()
elif game:
game_copy = GameCopy.objects.filter(game=game)
elif platform:
game_copy = GameCopy.objects.filter(platform=platform)
elif owner:
game_copy = GameCopy.objects.filter(user=owner)
elif is_new:
game_copy = GameCopy.objects.filter(is_new=is_new)
elif for_sale:
game_copy = GameCopy.objects.filter(for_sale=for_sale)
else:
game_copy = GameCopy.objects.all()
return game_copy
im looking for a leaner approach for this condition.. hopefully ended with something with 5 to 10 lines
You could add everything that's truthy to a dictionary and then pass those to the function by unpacking keyword arguments.
args = { "game": game, "owner": owner, "platform": platform, "is_new": is_new, "for_sale": for_sale }
# check if all args are truthy
all_args = all(v for v in args.values())
# filter out only the truthy args
filtered_args = {k: v for k, v in args.items() if v}
if all_args or not filtered_args:
# if all_args is true or filtered_args is empty
game_copy = GameCopy.objects.all()
else:
# Unpack filtered_args into filter()
game_copy = GameCopy.objects.filter(**filtered_args)
You could change your variable to a set of flags (bits set in a unique integer)
GAME = 1 << 0 # 1
OWNER = 1 << 1 # 2
PLATFORM = 1 << 2 # 4
IS_NEW = 1 << 3 # 8
FOR_SALE = 1 << 4 # 16
# for instance flags = GAME
# or flags = OWNER | IS_NEW
# ensures that there is only one flag set and pass it to you filter
if (flags & (flags-1) == 0) and flags != 0:
game_copy = GameCopy.objects.filter(flags)
# Otherwise
else: game_copy = GameCopy.objects.all()
return game_copy

How to change "ON/OFF" string data into 0/1?

I am monitoring and controlling a smart light bulb from my dashboard(User Interface Dashboard) and every time I open or close the light bulb it sends back a data that shows the light bulb is on or off, what I want to do is to change this "ON/OFF" data string in to binary of 1/0, is there anyway I can get 1 or 0 instead of 'ON/OFF'.
this is some of the API code I have used to control the light bulb
def identifyDevice(self):
identifyDeviceResult = False
print(" {0}Agent for {1} is identifying itself by doing colorloop. Please observe your lights"
.format(self.variables.get('agent_id', None), self.variables.get('model', None)))
try:
devicewasoff = 0
if self.get_variable('status') == "OFF":
devicewasoff = 1
self.setDeviceStatus({"status": "ON"})
elif self.only_white_bulb:
self.setDeviceStatus({"status": "OFF"})
if self.only_white_bulb is False:
self.setDeviceStatus({"effect": "colorloop"})
if self.only_white_bulb:
time_iden = 3
else:
time_iden = 10 # time to do identification
t0 = time.time()
self.seconds = time_iden
while time.time() - t0 <= time_iden:
self.seconds = self.seconds - 1
print("wait: {} sec".format(self.seconds))
time.sleep(1)
self.setDeviceStatus({"effect": "none"})
if devicewasoff == 1:
self.setDeviceStatus({"status": "OFF"})
else:
self.setDeviceStatus({"status": "ON"})
identifyDeviceResult = True
except:
print("ERROR: classAPI_PhilipsHue connection failure! # identifyDevice")
return identifyDeviceResult
If you can guarantee that the returned value is always one of 'ON' or 'OFF', you can use the fact that True and 1 are interchangeable
result = state == 'ON'
If you need some error handling, something like this might work.
if state in {'ON', 'OFF'}:
result = state == 'ON'
else:
# handle error state
If you need an int to serialise, call int with the result
>>> state_on = 'ON'
>>> state_off = 'OFF'
>>> int(state_on == 'ON')
1
>>> int(state_off == 'ON')
0

can anyone solve this raw input error and configure settings error in python?

I am beginner in python.I want to generate genetic algorithm source code in python.To be honest I downloaded this code from internet.I compiled this code in pycharm. It shows an error in MAIN FUNCTION AND RAW_INPUT IN CONFIGURE FUNCTION CONFIGURE SETTINGS can anyone please check the error in main function and check the raw input .I enclosed the code.Thanks in advance
from operator import itemgetter, attrgetter
import random
import sys
import os
import math
import re
# GLOBAL VARIABLES
genetic_code = {
'0000':'0',
'0001':'1',
'0010':'2',
'0011':'3',
'0100':'4',
'0101':'5',
'0110':'6',
'0111':'7',
'1000':'8',
'1001':'9',
'1010':'+',
'1011':'-',
'1100':'*',
'1101':'/'
}
solution_found = False
popN = 100 # n number of chromos per population
genesPerCh = 75
max_iterations = 1000
target = 1111.0
crossover_rate = 0.7
mutation_rate = 0.05
"""Generates random population of chromos"""
def generatePop ():
chromos, chromo = [], []
for eachChromo in range(popN):
chromo = []
for bit in range(genesPerCh * 4):
chromo.append(random.randint(0,1))
chromos.append(chromo)
return chromos
"""Takes a binary list (chromo) and returns a protein (mathematical expression in string)"""
def translate (chromo):
protein, chromo_string = '',''
need_int = True
a, b = 0, 4 # ie from point a to point b (start to stop point in string)
for bit in chromo:
chromo_string += str(bit)
for gene in range(genesPerCh):
if chromo_string[a:b] == '1111' or chromo_string[a:b] == '1110':
continue
elif chromo_string[a:b] != '1010' and chromo_string[a:b] != '1011' and chromo_string[a:b] != '1100' and chromo_string[a:b] != '1101':
if need_int == True:
protein += genetic_code[chromo_string[a:b]]
need_int = False
a += 4
b += 4
continue
else:
a += 4
b += 4
continue
else:
if need_int == False:
protein += genetic_code[chromo_string[a:b]]
need_int = True
a += 4
b += 4
continue
else:
a += 4
b += 4
continue
if len(protein) %2 == 0:
protein = protein[:-1]
return protein
"""Evaluates the mathematical expressions in number + operator blocks of two"""
def evaluate(protein):
a = 3
b = 5
output = -1
lenprotein = len(protein) # i imagine this is quicker than calling len everytime?
if lenprotein == 0:
output = 0
if lenprotein == 1:
output = int(protein)
if lenprotein >= 3:
try :
output = eval(protein[0:3])
except ZeroDivisionError:
output = 0
if lenprotein > 4:
while b != lenprotein+2:
try :
output = eval(str(output)+protein[a:b])
except ZeroDivisionError:
output = 0
a+=2
b+=2
return output
"""Calulates fitness as a fraction of the total fitness"""
def calcFitness (errors):
fitnessScores = []
totalError = sum(errors)
i = 0
# fitness scores are a fraction of the total error
for error in errors:
fitnessScores.append (float(errors[i])/float(totalError))
i += 1
return fitnessScores
def displayFit (error):
bestFitDisplay = 100
dashesN = int(error * bestFitDisplay)
dashes = ''
for j in range(bestFitDisplay-dashesN):
dashes+=' '
for i in range(dashesN):
dashes+='+'
return dashes
"""Takes a population of chromosomes and returns a list of tuples where each chromo is paired to its fitness scores and ranked accroding to its fitness"""
def rankPop (chromos):
proteins, outputs, errors = [], [], []
i = 1
# translate each chromo into mathematical expression (protein), evaluate the output of the expression,
# calculate the inverse error of the output
print ('%s: %s\t=%s \t%s %s' %('n'.rjust(5), 'PROTEIN'.rjust(30), 'OUTPUT'.rjust(10), 'INVERSE ERROR'.rjust(17), 'GRAPHICAL INVERSE ERROR'.rjust(105)))
for chromo in chromos:
protein = translate(chromo)
proteins.append(protein)
output = evaluate(protein)
outputs.append(output)
try:
error = 1/math.fabs(target-output)
except ZeroDivisionError:
global solution_found
solution_found = True
error = 0
print ('\nSOLUTION FOUND' )
print ('%s: %s \t=%s %s' %(str(i).rjust(5), protein.rjust(30), str(output).rjust(10), displayFit(1.3).rjust(130)))
break
else:
#error = 1/math.fabs(target-output)
errors.append(error)
print ('%s: %s \t=%s \t%s %s' %(str(i).rjust(5), protein.rjust(30), str(output).rjust(10), str(error).rjust(17), displayFit(error).rjust(105)))
i+=1
fitnessScores = calcFitness (errors) # calc fitness scores from the erros calculated
pairedPop = zip ( chromos, proteins, outputs, fitnessScores) # pair each chromo with its protein, ouput and fitness score
rankedPop = sorted ( pairedPop,key = itemgetter(-1), reverse = True ) # sort the paired pop by ascending fitness score
return rankedPop
""" taking a ranked population selects two of the fittest members using roulette method"""
def selectFittest (fitnessScores, rankedChromos):
while 1 == 1: # ensure that the chromosomes selected for breeding are have different indexes in the population
index1 = roulette (fitnessScores)
index2 = roulette (fitnessScores)
if index1 == index2:
continue
else:
break
ch1 = rankedChromos[index1] # select and return chromosomes for breeding
ch2 = rankedChromos[index2]
return ch1, ch2
"""Fitness scores are fractions, their sum = 1. Fitter chromosomes have a larger fraction. """
def roulette (fitnessScores):
index = 0
cumalativeFitness = 0.0
r = random.random()
for i in range(len(fitnessScores)): # for each chromosome's fitness score
cumalativeFitness += fitnessScores[i] # add each chromosome's fitness score to cumalative fitness
if cumalativeFitness > r: # in the event of cumalative fitness becoming greater than r, return index of that chromo
return i
def crossover (ch1, ch2):
# at a random chiasma
r = random.randint(0,genesPerCh*4)
return ch1[:r]+ch2[r:], ch2[:r]+ch1[r:]
def mutate (ch):
mutatedCh = []
for i in ch:
if random.random() < mutation_rate:
if i == 1:
mutatedCh.append(0)
else:
mutatedCh.append(1)
else:
mutatedCh.append(i)
#assert mutatedCh != ch
return mutatedCh
"""Using breed and mutate it generates two new chromos from the selected pair"""
def breed (ch1, ch2):
newCh1, newCh2 = [], []
if random.random() < crossover_rate: # rate dependent crossover of selected chromosomes
newCh1, newCh2 = crossover(ch1, ch2)
else:
newCh1, newCh2 = ch1, ch2
newnewCh1 = mutate (newCh1) # mutate crossovered chromos
newnewCh2 = mutate (newCh2)
return newnewCh1, newnewCh2
""" Taking a ranked population return a new population by breeding the ranked one"""
def iteratePop (rankedPop):
fitnessScores = [ item[-1] for item in rankedPop ] # extract fitness scores from ranked population
rankedChromos = [ item[0] for item in rankedPop ] # extract chromosomes from ranked population
newpop = []
newpop.extend(rankedChromos[:popN/15]) # known as elitism, conserve the best solutions to new population
while len(newpop) != popN:
ch1, ch2 = [], []
ch1, ch2 = selectFittest (fitnessScores, rankedChromos) # select two of the fittest chromos
ch1, ch2 = breed (ch1, ch2) # breed them to create two new chromosomes
newpop.append(ch1) # and append to new population
newpop.append(ch2)
return newpop
def configureSettings ():
configure = raw_input ('T - Enter Target Number \tD - Default settings: ')
match1 = re.search( 't',configure, re.IGNORECASE )
if match1:
global target
target = input('Target int: ' )
def main():
configureSettings ()
chromos = generatePop() #generate new population of random chromosomes
iterations = 0
while iterations != max_iterations and solution_found != True:
# take the pop of random chromos and rank them based on their fitness score/proximity to target output
rankedPop = rankPop(chromos)
print ('\nCurrent iterations:', iterations)
if solution_found != True:
# if solution is not found iterate a new population from previous ranked population
chromos = []
chromos = iteratePop(rankedPop)
iterations += 1
else:
break
if __name__ == "__main__":
main()
Probably you are using Python3, rather than Python2.
The function raw_input is for Python2.
In Python3, raw_input() was renamed to input()
From http://docs.python.org/dev/py3k/whatsnew/3.0.html
So, replace raw_input for input and give it a try.

Why is my count variable not incrementing for each new instance of my class?

My code:
class Lobby(Definition):
Lcount = 0
def __init__(self):
if Lobby.Lcount == 0:
self.description = "test1"
elif Lobby.Lcount > 0:
self.description = "test2"
else:
print("\nHmmm something went wrong...\n")
self.contents = ["Briefcase"]
self.doors = {"n": "terminal", "w": "hallway"}
Lobby.Lcount += 1
I want it to be where after an instance of the room has been created (i.e. you have visited it before), it will display a different description than it the original one. However, it keeps printing the same description. So what precisely am I doing wrong here?
edit: Here is what is in my definition class:
class Definition:
def __init__(self):
self.description = ""
self.contents = []
self.doors = {}
def get_desc(self):
print("{}".format(self.description))
def get_direction(self):
direction = input("Enter a direction or search the room: ").lower()
search = True
if direction == "q":
return direction
elif direction in self.doors:
location = self.doors[direction]
return location
elif direction == "s":
while search:
action = input("\nYou search for some items... Press 1 to continue or 2 to quit.\n")
if action == "1":
if len(location.contents) == 0:
print("\nYou find nothing of value in the room.\n")
else:
find = random.randrange(1, 3)
if find == 1:
found = random.randrange(len(location.contents))
item = location.contents.pop(found)
print("\nYou found a {}\n".format(item))
self.items.append(item)
self.check_items()
else:
print("\nNothing found yet\n")
elif action == "2":
search = False
break
else:
print("\nLocation reminder: ")
location.get_description()
else:
return "\nNot a valid entry\n"
Something in Definition is preventing this from working appropriately.
class FooClass(object):
foo = 0
def __init__(self):
print("Foo {} before".format(self.__class__.foo))
self.__class__.foo += 1
print("Foo {} after".format(self.__class__.foo))
# Works appropriately
>>> x = Foo()
Foo 0 before
Foo 1 after
>>> x = Foo()
Foo 1 before
Foo 2 after
However I'd recommend a different way to track this if it's just a binary "seen" variable. Restricting your ability to create multiple Lobby objects may mess you up in the future. Strongly consider creating a dictionary that houses all your objects, with a visit method that runs the logic your __init__ is doing now, and sets a flag that makes a following visit do something different.
class SomeRoom(Room):
# where Room is some superclass like Definition is now
def __init__(self, *args):
super().__init__(args) # or however
self.__visited = False
#property
def visited(self):
return self.__visited
def visit(self):
if self.visited:
print("Welcome back")
else:
print("Welcome!")
self.__visited = True

Python A* implementation

I am currently working on my Python game, in ika, which uses python 2.5
I decided to use A* pathfinding for the AI. However, I find it too slow for my needs (3-4 enemies can lag the game, but I would like to supply up to 4-5 without problems). I know, that such complex search like A* is not mean to be scripted in python, but I am pretty sure, that my pathfinder is also implemented in the wrong way.
My question is: How can I speed up this algorithm?
I wrote my own binary heap, and there are some try: except: lines inside some functions. Those lines can create large overhead? Are there better methods maintaining the open list?
I supplied the algorithm with graphics interface, for testing purposes (when the pathfinder finishes searching, it will write the number of iterations and seconds it takes to find the path, inside the ika.txt file. Also, Pressing A will do a complete search, and S does that step by step.)
Graphical version:
http://data.hu/get/6084681/A_star.rar
Also, here is a pastebin version:
http://pastebin.com/9N8ybX5F
Here is the main code I use for pathfinding:
import ika
import time
class Node:
def __init__(self,x,y,parent=None,g=0,h=0):
self.x = x
self.y = y
self.parent = parent
self.g = g
self.h = h
def cost(self):
return self.g + self.h
def equal(self,node):
if self.x == node.x and self.y == node.y:
return True
else:
return False
class Emerald_Pathfinder:
def __init__(self):
pass
def setup(self,start,goal):
self.start = start
self.goal = goal
self.openlist = [None,start] # Implemented as binary heap
self.closedlist = {} # Implemented as hash
self.onopenlist = {} # Hash, for searching the openlist
self.found = False
self.current = None
self.iterations = 0
def lowest_cost(self):
pass
def add_nodes(self,current):
nodes = []
x = current.x
y = current.y
self.add_node(x+1,y,current,10,nodes)
self.add_node(x-1,y,current,10,nodes)
self.add_node(x,y+1,current,10,nodes)
self.add_node(x,y-1,current,10,nodes)
# Dont cut across corners
up = map.is_obstacle((x,y-1),x,y-1)
down = map.is_obstacle((x,y+1),x,y+1)
left = map.is_obstacle((x-1,y),x-1,y)
right = map.is_obstacle((x+1,y),x+1,y)
if right == False and down == False:
self.add_node(x+1,y+1,current,14,nodes)
if left == False and up == False:
self.add_node(x-1,y-1,current,14,nodes)
if right == False and up == False:
self.add_node(x+1,y-1,current,14,nodes)
if left == False and down == False:
self.add_node(x-1,y+1,current,14,nodes)
return nodes
def heuristic(self,x1,y1,x2,y2):
return (abs(x1-x2)+abs(y1-y2))*10
def add_node(self,x,y,parent,cost,list):
# If not obstructed
if map.is_obstacle((x,y),x,y) == False:
g = parent.g + cost
h = self.heuristic(x,y,self.goal.x,self.goal.y)
node = Node(x,y,parent,g,h)
list.append(node)
def ignore(self,node,current):
# If its on the closed list, or open list, ignore
try:
if self.closedlist[(node.x,node.y)] == True:
return True
except:
pass
# If the node is on the openlist, do the following
try:
# If its on the open list
if self.onopenlist[(node.x,node.y)] != None:
# Get the id number of the item on the real open list
index = self.openlist.index(self.onopenlist[(node.x,node.y)])
# If one of the coordinates equal, its not diagonal.
if node.x == current.x or node.y == current.y:
cost = 10
else:
cost = 14
# Check, is this items G cost is higher, than the current G + cost
if self.openlist[index].g > (current.g + cost):
# If so, then, make the list items parent, the current node.
self.openlist[index].g = current.g + cost
self.openlist[index].parent = current
# Now resort the binary heap, in the right order.
self.resort_binary_heap(index)
# And ignore the node
return True
except:
pass
return False
def resort_binary_heap(self,index):
m = index
while m > 1:
if self.openlist[m/2].cost() > self.openlist[m].cost():
temp = self.openlist[m/2]
self.openlist[m/2] = self.openlist[m]
self.openlist[m] = temp
m = m / 2
else:
break
def heap_add(self,node):
self.openlist.append(node)
# Add item to the onopenlist.
self.onopenlist[(node.x,node.y)] = node
m = len(self.openlist)-1
while m > 1:
if self.openlist[m/2].cost() > self.openlist[m].cost():
temp = self.openlist[m/2]
self.openlist[m/2] = self.openlist[m]
self.openlist[m] = temp
m = m / 2
else:
break
def heap_remove(self):
if len(self.openlist) == 1:
return
first = self.openlist[1]
# Remove the first item from the onopenlist
self.onopenlist[(self.openlist[1].x,self.openlist[1].y)] = None
last = self.openlist.pop(len(self.openlist)-1)
if len(self.openlist) == 1:
return last
else:
self.openlist[1] = last
v = 1
while True:
u = v
# If there is two children
if (2*u)+1 < len(self.openlist):
if self.openlist[2*u].cost() <= self.openlist[u].cost():
v = 2*u
if self.openlist[(2*u)+1].cost() <= self.openlist[v].cost():
v = (2*u)+1
# If there is only one children
elif 2*u < len(self.openlist):
if self.openlist[2*u].cost() <= self.openlist[u].cost():
v = 2*u
# If at least one child is smaller, than parent, swap them
if u != v:
temp = self.openlist[u]
self.openlist[u] = self.openlist[v]
self.openlist[v] = temp
else:
break
return first
def iterate(self):
# If the open list is empty, exit the game
if len(self.openlist) == 1:
ika.Exit("no path found")
# Expand iteration by one
self.iterations += 1
# Make the current node the lowest cost
self.current = self.heap_remove()
# Add it to the closed list
self.closedlist[(self.current.x,self.current.y)] = True
# Are we there yet?
if self.current.equal(self.goal) == True:
# Target reached
self.goal = self.current
self.found = True
print self.iterations
else:
# Add the adjacent nodes, and check them
nodes_around = self.add_nodes(self.current)
for na in nodes_around:
if self.ignore(na,self.current) == False:
self.heap_add(na)
def iterateloop(self):
time1 = time.clock()
while 1:
# If the open list is empty, exit the game
if len(self.openlist) == 1:
ika.Exit("no path found")
# Expand iteration by one
self.iterations += 1
# Make the current node the lowest cost
self.current = self.heap_remove()
# Add it to the closed list
self.closedlist[(self.current.x,self.current.y)] = True
# Are we there yet?
if self.current.equal(self.goal) == True:
# Target reached
self.goal = self.current
self.found = True
print "Number of iterations"
print self.iterations
break
else:
# Add the adjacent nodes, and check them
nodes_around = self.add_nodes(self.current)
for na in nodes_around:
if self.ignore(na,self.current) == False:
self.heap_add(na)
time2 = time.clock()
time3 = time2-time1
print "Seconds to find path:"
print time3
class Map:
def __init__(self):
self.map_size_x = 20
self.map_size_y = 15
self.obstructed = {} # Library, containing x,y couples
self.start = [2*40,3*40]
self.unit = [16*40,8*40]
def is_obstacle(self,couple,x,y):
if (x >= self.map_size_x or x < 0) or (y >= self.map_size_y or y < 0):
return True
try:
if self.obstructed[(couple)] != None:
return True
except:
return False
def render_screen():
# Draw the Character
ika.Video.DrawRect(map.start[0],map.start[1],map.start[0]+40,map.start[1]+40,ika.RGB(40,200,10),1)
# Draw walls
for x in range(0,map.map_size_x):
for y in range(0,map.map_size_y):
if map.is_obstacle((x,y),x,y) == True:
ika.Video.DrawRect(x*40,y*40,(x*40)+40,(y*40)+40,ika.RGB(168,44,0),1)
# Draw openlist items
for node in path.openlist:
if node == None:
continue
x = node.x
y = node.y
ika.Video.DrawRect(x*40,y*40,(x*40)+40,(y*40)+40,ika.RGB(100,100,100,50),1)
# Draw closedlist items
for x in range(0,map.map_size_x):
for y in range(0,map.map_size_y):
try:
if path.closedlist[(x,y)] == True:
ika.Video.DrawRect(x*40,y*40,(x*40)+20,(y*40)+20,ika.RGB(0,0,255))
except:
pass
# Draw the current square
try:
ika.Video.DrawRect(path.current.x*40,path.current.y*40,(path.current.x*40)+40,(path.current.y*40)+40,ika.RGB(128,128,128), 1)
except:
pass
ika.Video.DrawRect(mouse_x.Position(),mouse_y.Position(),mouse_x.Position()+8,mouse_y.Position()+8,ika.RGB(128,128,128), 1)
# Draw the path, if reached
if path.found == True:
node = path.goal
while node.parent:
ika.Video.DrawRect(node.x*40,node.y*40,(node.x*40)+40,(node.y*40)+40,ika.RGB(40,200,200),1)
node = node.parent
# Draw the Target
ika.Video.DrawRect(map.unit[0],map.unit[1],map.unit[0]+40,map.unit[1]+40,ika.RGB(128,40,200),1)
def mainloop():
while 1:
render_screen()
if mouse_middle.Pressed():
# Iterate pathfinder
if path.found == False:
path.iterateloop()
elif mouse_right.Pressed():
# Iterate pathfinder by one
if path.found == False:
path.iterate()
elif ika.Input.keyboard["A"].Pressed():
# Iterate pathfinder
if path.found == False:
path.iterateloop()
elif ika.Input.keyboard["S"].Pressed():
# Iterate pathfinder by one
if path.found == False:
path.iterate()
elif mouse_left.Position():
# Add a square to the map, to be obstructed
if path.iterations == 0:
x = mouse_x.Position()
y = mouse_y.Position()
map.obstructed[(int(x/40),int(y/40))] = True
# Mouse preview
x = mouse_x.Position()
y = mouse_y.Position()
mx = int(x/40)*40
my = int(y/40)*40
ika.Video.DrawRect(mx,my,mx+40,my+40,ika.RGB(150,150,150,70),1)
ika.Video.ShowPage()
ika.Input.Update()
map = Map()
path = Emerald_Pathfinder()
path.setup(Node(map.start[0]/40,map.start[1]/40),Node(map.unit[0]/40,map.unit[1]/40))
mouse_middle = ika.Input.mouse.middle
mouse_right = ika.Input.mouse.right
mouse_left = ika.Input.mouse.left
mouse_x = ika.Input.mouse.x
mouse_y = ika.Input.mouse.y
# Initialize loop
mainloop()
I appreciate any help!
(sorry for any spelling mistakes, English is not my native language)
I think a proper implementation in python will be fast enough for your purposes. But the boost library has an astar implementation and python bindings. https://github.com/erwinvaneijk/bgl-python

Categories

Resources