Related
I'm simply working on a Minimax algorithm that can play TicTacToe, For some reason the max_value and min_value functions occasionally return None.
def player(board):
if Terminal(board) != False:
return None
else:
if turn(board) == "X":
value,move = max_value(board)
return move
else:
value,move = min_value(board)
return move
def max_value(board):
if Terminal(board) != False:
return Utility(board),None
else:
v = -1000
move = None
for action in Actions(board):
aux,act = min_value(Result(board,action))
print(aux)
if aux > v:
v = aux
move = action
if v == 1:
return v,move
return v,move
def min_value(board):
if Terminal(board) != False:
return Utility(board),None
else:
v = 1000
move = None
for action in Actions(board):
aux,act = max_value(Result(board,action))
print(aux)
if aux < v:
v = aux
move = action
if v == -1:
return v,move
return v,move
Terminal returns state of the game,Action returns possible moves and result creates a board given an action.
The error I get is '>' not supported between instances of 'NoneType' and 'int' pops up for if aux < v: and if aux > v:
When I print aux, it occasionally appears as None.
Thanks.
The code you have shared is fine.
The error message indicates that Utility returns None when you call it. This should not happen. Utility should always return an integer when the game is over, and looking at your code it should return -1, 0 or 1.
There is also the possibility that Utility returns None when the game is not over, but that Terminal has a bug such that it returns something else than False even when the game is not over.
Here is an implementation of the functions Terminal and Utility that do not produce the error you got, but make it work as expected.
In fact, I added all missing functions based on a board representation that is a string of 9 characters, and a game loop so you can play against the minimax algorithm:
import re
def Utility(board):
m = re.findall(r"([XO])(?:\1\1(?:...)*$|..\1..\1|...\1...\1|.\1.\1..$)", board)
return -1 if "O" in m else len(m)
def Terminal(board):
return "O X"[Utility(board)+1].strip() or not board.count(".") and "draw"
def turn(board):
return "OX"[board.count(".") % 2]
def Actions(board):
return (i for i in range(9) if board[i] == ".")
def Result(board, action):
return board[:action] + turn(board) + board[action+1:]
board = "." * 9
while True: # Interactive game between human and minimax
print(f"{board[:3]}\n{board[3:6]}\n{board[6:]}")
winner = Terminal(board)
if winner:
print(f"Winner: {winner}")
break
if turn(board) == "X":
action = int(input("Your move (0..8)? "))
if board[action] != ".":
print("Invalid move. Try again.")
continue
else:
action = player(board)
print("minimax chooses", action)
board = Result(board, action)
I've been working on a MCTS AI for a couple days now. I tried to implement it on Tic-Tac-Toe, the least complex game I could think of, but for some reason, my AI keeps making bad decisions. I've tried change the values of UCB1's exploration constant, the number of iterations per search, and even the points awarded to winning, losing, and getting to tie the game (trying to make a tie more rewarding, as this AI only plays second, and try to get a draw, win otherwise). As of now, the code looks like this:
import random
import math
import copy
class tree:
def __init__(self, board):
self.board = board
self.visits = 0
self.score = 0
self.children = []
class mcts:
def search(self, mx, player,):
root = tree(mx)
for i in range(1200):
leaf = mcts.expand(self, root.board, player, root)
result = mcts.rollout(self, leaf)
mcts.backpropagate(self, leaf, root, result)
return mcts.best_child(self, root).board
def expand(self, mx, player, root):
plays = mcts.generate_states(self, mx, player) #all possible plays
if root.visits == 0:
for j in plays:
root.children.append(j) #create child_nodes in case they havent been created yet
for j in root.children:
if j.visits == 0:
return j #first iterations of the loop
for j in plays:
if mcts.final(self, j.board, player):
return j
return mcts.best_child(self, root) #choose the one with most potential
def rollout(self, leaf):
mx = leaf.board
aux = 1
while mcts.final(self, mx, "O") != True:
if aux == 1: # "X" playing
possible_states = []
possible_nodes = mcts.generate_states(self, mx, "X")
for i in possible_nodes:
possible_states.append(i.board)
if len(possible_states) == 1: mx = possible_states[0]
else:
choice = random.randrange(0, len(possible_states) - 1)
mx = possible_states[choice]
if mcts.final(self, mx, "X"): #The play by "X" finished the game
break
elif aux == 0: # "O" playing
possible_states = []
possible_nodes = mcts.generate_states(self, mx, "O")
for i in possible_nodes:
possible_states.append(i.board)
if len(possible_states) == 1: mx = possible_states[0]
else:
choice = random.randrange(0, len(possible_states) - 1)
mx = possible_states[choice]
aux += 1
aux = aux%2
if mcts.final(self, mx, "X"):
for i in range(len(mx)):
for k in range(len(mx[i])):
if mx[i][k] == "-":
return -1 #loss
return 0 #tie
elif mcts.final(self, mx, "O"):
for i in range(len(mx)):
for k in range(len(mx[i])):
if mx[i][k] == "-":
return 1 #win
def backpropagate(self, leaf, root, result): # updating our prospects stats
leaf.score += result
leaf.visits += 1
root.visits += 1
def generate_states(self, mx, player):
possible_states = [] #generate child_nodes
for i in range(len(mx)):
for k in range(len(mx[i])):
if mx[i][k] == "-":
option = copy.deepcopy(mx)
option[i][k] = player
child_node = tree(option)
possible_states.append(child_node)
return possible_states
def final(self,mx, player): #check if game is won
possible_draw = True
win = False
for i in mx: #lines
if i == [player, player, player]:
win = True
possible_draw = False
if mx[0][0] == player: #diagonals
if mx[1][1] == player:
if mx[2][2] == player:
win = True
possible_draw = False
if mx[0][2] == player:
if mx[1][1] == player:
if mx[2][0] == player:
win = True
possible_draw = False
for i in range(3): #columns
if mx[0][i] == player and mx[1][i] == player and mx[2][i] == player:
win = True
possible_draw = False
for i in range(3):
for k in range(3):
if mx[i][k] == "-":
possible_draw = False
if possible_draw:
return possible_draw
return win
def calculate_score(self, score, child_visits, parent_visits, c): #UCB1
return score / child_visits + c * math.sqrt(math.log(parent_visits) / child_visits)
def best_child(self, root): #returns most promising node
treshold = -1*10**6
for j in root.children:
potential = mcts.calculate_score(self, j.score, j.visits, root.visits, 2)
if potential > treshold:
win_choice = j
treshold = potential
return win_choice
#todo the AI takes too long for each play, optimize that by finding the optimal approach in the rollout phase
First off, the purpose of this AI is to return an altered matrix, with the best play he could make in that circunstance. I find myself questioning if the MCTS algorithm is the reason behind all these broken plays, due to some possible mistakes in its implementation. With that said, in my eyes, the code does the following:
Check if the root already has its children, in case it has, choose the most promising.
Rollout a random simulation and save the result.
Update the leaf's score, its number of visits and the root's number of visits.
Repeat for 1200 iterations, in my example
Return the best move (matrix, child_node) possible.
Why is it not working? Why is it choosing bad plays instead of the optimal one? Is the algorithm wrongly implemented?
My mistake was choosing the node with the most visits in the expansion phase, when it should have been the one with the most potential according to the UCB1 formula. I also had some errors when it came to implementing some if clauses, as all the losses weren't being counted.
So I am making genetic algorithms where the fittest chromosome is the one that is all ones [1,1,1,1...]. I coded the whole thing below while debugging I found that crossover, mutation, generate_population works and I went through the code for my other functions and they all make sense. But whenever I run it I find that after the second or third generation all my other generations are the same. so my questions are that why is that happening because I went through my code and every new generation should have a different and "fitter" population and there should be variety among each population. Any help would be really appreciated. result image
import random
def generate_population(size):
population = []
for i in range(size):
individual = []
for g in range(64):
x = random.randrange(0,2)
individual.append(x)
population.append(individual)
return population
def fitnessFunc(individual):
fit = 0
for i in individual:
if i == 1:
fit += 1
else:
fit = fit
return fit
def choice_by_roulette(sorted_population, fitness_sum):
offset = 0
normalized_fitness_sum = fitness_sum
lowest_fitness = fitnessFunc(sorted_population[0])
if lowest_fitness < 0:
offset = lowest_fitness
normalized_fitness_sum += offset * len(sorted_population)
draw = random.uniform(0, 1)
accumulated = 0
for individual in sorted_population:
fitness = fitnessFunc(individual)+offset
probability = fitness / normalized_fitness_sum
accumulated += probability
if draw <= accumulated:
return individual
def sort_population_by_fitness(population):
return sorted(population, key=fitnessFunc)
def crossover(individual_a, individual_b):
for i in range(64):
pop = random.randint(1,2)
if pop == 1:
individual_a[i] = individual_b[i]
else:
individual_a = individual_a
return individual_a
def mutate(individual):
rand = random.randrange(0,10)
if rand == 5:
if individual[rand]==1:
individual[rand]=0
else:
individual[rand]=1
return individual
def make_next_generation(previous_population):
next_generation = []
sorted_by_fitness_population = sort_population_by_fitness(previous_population)
population_size = len(previous_population)
fitness_sum = sum(fitnessFunc(individual) for individual in population)
for i in range(population_size):
choice = choice_by_roulette(sorted_by_fitness_population, fitness_sum)
schoice = choice_by_roulette(sorted_by_fitness_population, fitness_sum)
if choice != None:
first_choice = choice
if schoice != None:
second_choice = schoice
individual = crossover(first_choice, second_choice)
individual = mutate(individual)
next_generation.append(individual)
return next_generation
population = generate_population(size=10)
generations = 1000
i = 1
while True:
print(f" GENERATION {i}")
for individual in population:
print(individual, fitnessFunc(individual))
if i == generations:
break
i += 1
population = make_next_generation(population)
best_individual = sort_population_by_fitness(population)[-1]
print(" FINAL RESULT")
print(best_individual, fitnessFunc(best_individual))
I found three major problems. Will try to break it down, but since I'm not completely sure what intended result is, it's hard to say for sure how it should be done.
Problem 1: Crossover function mutates parent generation "in place" while creating next generation. That means ~ 25 % of one parents genes will mutate towards other parents gene set.
Problem 2: make_next_generation() does not make sure two different individuals are sent to crossover. That will sometimes result in individuals passing over unchanged to the next generation.
Problem 3: mutate(individual) only affects one gene out of 64. (Guessing not on purpose)
import random
def generate_individual():
# Use list comprehensions
return [random.randrange(0,2) for g in range(64)]
def generate_population(size):
# Same result, only less code
return [generate_individual() for i in range(size)]
def fitnessFunc(individual):
# Yes, this line does the same work.
return sum(individual)
# fit = 0
# for i in individual:
# if i == 1:
# fit += 1
# The following two lines did nothing
# else:
# fit = fit
# return fit
There is a real problem in choice_by_roulette(). Don't know how it's supposed to work, though. Commented below.
def choice_by_roulette(sorted_population, fitness_sum):
offset = 0
normalized_fitness_sum = fitness_sum
lowest_fitness = fitnessFunc(sorted_population[0])
# Lower than 0? fitnessFunc() will never return that.
# Could it be lowest_fitness > 0?
if lowest_fitness < 0:
offset = lowest_fitness
normalized_fitness_sum += offset * len(sorted_population)
draw = random.random()
accumulated = 0
for individual in sorted_population:
fitness = fitnessFunc(individual)+offset
probability = fitness / normalized_fitness_sum
accumulated += probability
if draw <= accumulated:
return individual
def sort_population_by_fitness(population):
return sorted(population, key=fitnessFunc)
Crossover should not mutate old individuals(?). Since the same individuals may get picked more than once, they will have mutated ~ 50 % towards another individual within the same generation. This is probably your biggest issue.
def crossover(individual_a, individual_b):
for i in range(64):
pop = random.randint(1,2)
if pop == 1:
individual_a[i] = individual_b[i]
else:
individual_a = individual_a
return individual_a
My suggestion:
def crossover(individual_a, individual_b):
return [random.choice(genes) for genes in zip(individual_a, individual_b)]
Here you don't need to return anything. You're literally mutating individual in place. Also, only gene 5 can mutate, but maybe that's supposed to be?
def mutate(individual):
rand = random.randrange(0,10)
if rand == 5:
individual[rand] = 1 - individual[rand] # Toggle: 1-0=1, 1-1=0
# if individual[rand]==1:
# individual[rand]=0
# else:
# individual[rand]=1
def make_next_generation(previous_population):
next_generation = []
sorted_by_fitness_population = sort_population_by_fitness(previous_population)
population_size = len(previous_population)
fitness_sum = sum(fitnessFunc(individual) for individual in population)
for i in range(population_size):
choice = choice_by_roulette(sorted_by_fitness_population, fitness_sum)
schoice = choice_by_roulette(sorted_by_fitness_population, fitness_sum)
# This code will not work. first_choice will be user without being declared below if any of there are None.
# if choice != None:
# first_choice = choice
# if schoice != None:
# second_choice = schoice
# choice and schoice will sometimes be the same individual. That will definitely diminish the genetic diversity.
while choice == schoice:
schoice = choice_by_roulette(sorted_by_fitness_population, fitness_sum)
individual = crossover(choice, schoice)
mutate(individual)
next_generation.append(individual)
return next_generation
population = generate_population(size=10)
generations = 1000
for i in range(1, generations+1):
print(f" GENERATION {i}")
for individual in population:
print(individual, fitnessFunc(individual))
population = make_next_generation(population)
best_individual = sort_population_by_fitness(population)[-1]
print(" FINAL RESULT")
print(best_individual, fitnessFunc(best_individual))
I've been trying out to solve the monty hall problem in Python in order to advance in coding, which is why I tried to randomize everything. The thing is: I've been running into some trouble. As most of you probably know the monty problem is supposed to show that changing the door has a higher winrate (66%) than staying on the chosen door (33%). For some odd reason though my simulation shows a 33% winrate for both cases and I am not really sure why.
Here's the code:
from random import *
def doorPriceRandomizer():
door1 = randint(0,2) #If a door is defined 0, it has a price in it
door2 = randint(0,2) #If a door is defined either 1 or 2, it has a goat in it.
door3 = randint(0,2)
while door2 == door1:
door2 = randint(0,2)
while door3 == door2 or door3 == door1:
door3 = randint(0,2)
return door1,door2,door3 #This random placement generator seems to be working fine.
while True:
loopStart = 0
amountWin = 0
amountLose = 0
try:
loopEnd = int(input("How often would you like to run this simulation: "))
if loopEnd < 0:
raise ValueError
doorChangeUser = int(input("[0] = Do not change door; [1] = Change door: "))
if doorChangeUser not in range(0,2):
raise ValueError
except ValueError:
print("Invalid input. Try again.\n")
else:
while loopStart != loopEnd:
gameDoors = doorPriceRandomizer()
inputUser = randint(0,2)
if doorChangeUser == 0:
if gameDoors[inputUser] == 0:
amountWin += 1
loopStart += 1
else:
amountLose += 1
loopStart += 1
elif doorChangeUser == 1:
ChangeRandom = 0
while gameDoors[ChangeRandom] == gameDoors[inputUser]:
ChangeRandom = randint(0,2)
if gameDoors[ChangeRandom] == 0:
amountWin += 1
loopStart += 1
else:
amountLose += 1
loopStart += 1
print("Win amount: ",amountWin,"\tLose amount: ",amountLose)
What am I doing wrong? I really appreciate all help! Thanks in advance!
ChangeRandom = 0
while gameDoors[ChangeRandom] == gameDoors[inputUser]:
ChangeRandom = randint(0,2)
This doesn't do what you think it does. Instead of checking if the ChangeRandom door is the same as the inputUser door, this checks if the ChangeRandom door and the inputUser door have the same value -- that is to say they're either both winners or both losers.
That said, that's not even what you want to do. What you want to do is to find a door that's not the user's input that IS a loser door, then switch to the OTHER one that isn't the user's input. This could be implemented with minimal change to your code as:
other_wrong_door = next(c for c, v in enumerate(gameDoors) if v != 0 and c != inputUser)
new_door = next(c for c, _ in enumerate(gameDoors) if c != inputUser and c != other_wrong_door)
But honestly this merits a re-examining of your code's structure. Give me a few minutes to work something up, and I'll edit this answer to give you an idea of how I'd implement this.
import random
DOORS = [1, 0, 0]
def runonce(switch=False):
user_choice = random.choice(DOORS)
if user_choice == 1:
# immediate winner
if switch:
# if you won before and switch doors, you must lose now
return False
else:
new_doors = [0, 0] # remove the user-selected winner
new_doors = [0] # remove another loser
return bool(random.choice(new_doors))
# of course, this is always `0`, but
# sometimes it helps to show it. In production you
# wouldn't bother writing the extra lines and just return False
else:
if switch:
new_doors = [1, 0] # remove the user-selected loser
new_doors = [1] # remove another loser
return bool(random.choice(new_doors))
# as above: this is always True, but....
else:
return False # if you lost before and don't switch, well, you lost.
num_trials = int(input("How many trials?"))
no_switch_raw = [run_once(switch=False) for _ in range(num_trials)]
switch_raw = [run_once(switch=True) for _ in range(num_trials)]
no_switch_wins = sum(1 for r in no_switch_raw if r)
switch_wins = sum(1 for r in switch_raw if r)
no_switch_prob = no_switch_wins / num_trials * 100.0
switch_prob = switch_wins / num_trials * 100.0
print( " WINS LOSSES %\n"
f"SWITCH: {switch_wins:>4} {num_trials-switch_wins:>6} {switch_prob:.02f}\n"
f"NOSWITCH:{no_switch_wins:>4} {num_trials-no_switch_wins:>6} {no_switch_prob:.02f}")
You have gotten the mechanics of the problem wrong so you are getting the wrong result. I have rewritten the choice mechanics, but I am leaving the user input stuff to you so that you can continue to learn python. This is one of many ways to solve the problem, but hopefully it demonstrates some things to you.
def get_choices():
valid_choices = [0, 1, 2] # these are the values for a valid sample
shuffle(valid_choices) # now randomly shuffle that list
return valid_choices # return the shuffled list
def get_door(user_choice):
return user_choice.index(0)
def monty_sim(n, kind):
"""
:param n: number of runs in this simulation
:param kind: whether to change the door or not, 0 - don't change, 1 = change door
:return: (win_rate, 1 - win_rate)
"""
wins = 0
for i in range(0, n):
game_doors = get_choices()
user_choice = get_door(get_choices()) # use the same method and find user door choice
# so there are two branches.
# In both, a door with a goat (game_door = 1) is chosen, which reduce the result to
# a choice between two doors, rather than 3.
if kind == 0:
if user_choice == game_doors.index(0):
wins += 1
elif kind == 1:
# so now, the user chooses to change the door
if user_choice != game_doors.index(0):
wins += 1
# Because the original choice wasn't the right one, then the new
# must be correct because the host already chose the other wrong one.
win_rate = (wins / n) * 100
return win_rate, 100 - win_rate
if __name__ == '__main__':
n = 1000
kind = 1
wins, loses = monty_sim(n, kind)
print(f'In a simulation of {n} experiments, of type {kind} user won {wins:02f} of the time, lost {loses:02f} of the time')
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