From 5b89648f69ec4b396d3528f25a29f44dd69fcd7c Mon Sep 17 00:00:00 2001 From: tubks Date: Wed, 16 Jun 2021 03:45:23 +0200 Subject: [PATCH] genetic algorithm with decision tree in progress --- server.py | 4 +- src/agent/ga_chromosome.py | 215 +++++++++++++++++++++++++++++++++++++ src/agent/map/gameMap.py | 12 ++- 3 files changed, 225 insertions(+), 6 deletions(-) create mode 100644 src/agent/ga_chromosome.py diff --git a/server.py b/server.py index 45ed5d0..b327243 100644 --- a/server.py +++ b/server.py @@ -5,7 +5,7 @@ from mesa.visualization.ModularVisualization import ModularServer #from src.decisiontree import create_model from src.direction import Direction from collections import defaultdict - +import src.agent.ga_chromosome as ga def player_representation(agent): portrayal = {"Shape": "sprites/heroE.png", @@ -55,10 +55,10 @@ class PlayerStats(TextElement): # return model.player.describe_situation(model.player.opponent, model.player.strategy) # else: # return "Not fighting" +ga.create_model() server = ModularServer(GameMap, [grid,PlayerStats()], "Map", {"x": 10, "y": 10}) -#create_model() server.port = 8081 server.launch() diff --git a/src/agent/ga_chromosome.py b/src/agent/ga_chromosome.py new file mode 100644 index 0000000..7514d96 --- /dev/null +++ b/src/agent/ga_chromosome.py @@ -0,0 +1,215 @@ +import numpy.random +import random +import pandas as pd +import os +import rpy2.robjects as robjects +from rpy2.robjects.packages import importr +from rpy2.robjects import pandas2ri +from rpy2.robjects.conversion import localconverter +from rpy2.robjects.packages import importr +from src.nominalize import nominalize +from src.decisiontree import create_model +MAX_COMBAT_TIME = 20 +attack_types=["MELEE", "RANGED","MAGIC"] +class EnemyStats: + def __init__(self): + self.strength = random.randint(1, 7) + self.agility = random.randint(1, 7) + self.wisdom = random.randint(1, 7) + self.max_health = random.randint(5, 30) + self.health=self.max_health + self.melee_wep_damage = 1 + self.ranged_wep_damage = 1 + self.magic_wep_damage = 2 + self.armor_defence = 0 + self.armor_magic_protection = 1 + self.attack_type=random.choice(attack_types) + + def meleeAttack(self, opponent): + attackValue = self.strength + random.randint(1, 6) + defenseValue = opponent.strength + opponent.armor_defence + damage = attackValue - defenseValue + if damage > 0: + opponent.health -= (damage + self.melee_wep_damage) + + def rangeAttack(self, opponent): + attackValue = self.agility + random.randint(1, 6) + defenseValue = opponent.agility + damage = attackValue - defenseValue + if (damage > 0) and (damage + self.ranged_wep_damage - opponent.armor_defence > 0): + opponent.health -= (damage + self.ranged_wep_damage - opponent.armor_defence) + + def magicAttack(self, opponent): + attackValue = self.wisdom + random.randint(1, 6) + defenseValue = opponent.wisdom + damage = attackValue - defenseValue + if (damage > 0) and (damage + self.magic_wep_damage - opponent.armor_magic_protection > 0): + opponent.health -= (damage + self.magic_wep_damage - opponent.armor_magic_protection) + + def reset(self): + self.health = self.max_health + +def try_combat(my_seed, p, e, player_att_type, enemy_att_type): + random.seed(my_seed) + current_iteration = 0 + while True: + if player_att_type == 0: + p.meleeAttack(e) + elif player_att_type == 1: + p.rangeAttack(e) + else: + p.magicAttack(e) + + if e.health<=0: + return p.health + + if enemy_att_type == 0: + e.meleeAttack(p) + elif enemy_att_type == 1: + e.rangeAttack(p) + else: + e.magicAttack(p) + + if p.health<=0: + return 0 + + current_iteration += 1 + if current_iteration >= MAX_COMBAT_TIME: + return 0 + p.reset() + e.reset() + +class PlayerStats(EnemyStats): + def __init__(self, s, a, w): + self.strength = 1+s + self.agility = 1+a + self.wisdom = 1+w + self.max_health = 50 + self.health = 50 + self.melee_wep_damage = 1 + self.ranged_wep_damage = 1 + self.magic_wep_damage = 2 + self.armor_defence = 0 + self.armor_magic_protection = 0 + + def predict_strategy(self, opponent): + testcase = pd.DataFrame({'p_strength': nominalize(self.strength,7), + 'p_agility':nominalize(self.agility, 7), + 'p_wisdom':nominalize(self.wisdom,7), + 'p_health':nominalize(self.health,50), + 'p_melee_damage':nominalize(self.melee_wep_damage, 10), + 'p_ranged_damage':nominalize(self.ranged_wep_damage,10), + 'p_magic_damage':nominalize(self.magic_wep_damage,10), + 'p_armor_defence':nominalize(self.armor_defence,5), + 'p_armor_magic_protection':nominalize(self.armor_magic_protection,5), + 'e_strength':nominalize(opponent.strength, 10), + 'e_agility':nominalize(opponent.agility, 10), + 'e_wisdom':nominalize(opponent.wisdom,10), + 'e_health':nominalize(opponent.health, 50), + 'e_damage':nominalize(opponent.melee_wep_damage,10), + 'e_armor_defence':nominalize(opponent.armor_defence, 5), + 'e_armor_magic_protection':nominalize(opponent.armor_magic_protection, 5), + 'e_attack_type':opponent.attack_type.upper(), + 'strategy':"PASS"}, index=[1]) + with localconverter(robjects.default_converter + pandas2ri.converter): + r_dataframe = robjects.conversion.py2rpy(testcase) + robjects.globalenv['r_dataframe']=r_dataframe + result=robjects.r('predict(fights.id3, r_dataframe)') + return result[0] + +def fitness_function(chromosome): + s=chromosome.count(1) + a=chromosome.count(2) + w=chromosome.count(3) + p=PlayerStats(s, a, w) + # wins=[0,0,0] + # current_seed=os.urandom(16) + # for i in range(3): + # random.seed(current_seed) + # while p.health>0: + # e=EnemyStats() + # try_combat(current_seed, p, e, i, e.attack_type) #walka iles razy + # if p.health>0: + # wins[i]+=1 + # p.reset() + # return max(wins) + wins=0 + while p.health > 0: + e=EnemyStats() + player_attack=p.predict_strategy(e) + try_combat(os.urandom(16), p, e, attack_types.index(player_attack), attack_types.index(e.attack_type)) #walka iles razy + if p.health>0: + wins+=1 + return wins + +# tournament selection +def selection(pop, scores, k=3): + # first random selection + selection_ix = numpy.random.randint(len(pop)) + for ix in numpy.random.randint(0, len(pop), k-1): + # check if better (e.g. perform a tournament) + if scores[ix] > scores[selection_ix]: + selection_ix = ix + return pop[selection_ix] + +# crossover two parents to create two children +def crossover(p1, p2, r_cross): + # children are copies of parents by default + c1, c2 = p1.copy(), p2.copy() + # check for recombination + if numpy.random.rand() < r_cross: + # select crossover point that is not on the end of the string + pt = numpy.random.randint(1, len(p1)-2) + # perform crossover + c1 = p1[:pt] + p2[pt:] + c2 = p2[:pt] + p1[pt:] + return [c1, c2] +# mutation operator +def mutation(bitstring, r_mut): + for i in range(len(bitstring)): + # check for a mutation + if numpy.random.rand() < r_mut: + # flip the bit + bitstring[i] = random.randint(1,4) +# genetic algorithm + +def genetic_algorithm(objective, n_bits, n_iter, n_pop, r_cross, r_mut): + # initial population of random bitstring + pop = [numpy.random.randint(1, 4, n_bits).tolist() for _ in range(n_pop)] # tworzy sie lista n_pop list szesciocyfrowych + # keep track of best solution + best, best_eval = 0, objective(pop[0]) + # enumerate generations + for gen in range(n_iter): + # evaluate all candidates in the population + scores = [objective(c) for c in pop] + # check for new best solution + for i in range(n_pop): + if scores[i] > best_eval: + best, best_eval = pop[i], scores[i] + print(">%d, new best f(%s) = %d" % (gen, pop[i], scores[i])) + # select parents + selected = [selection(pop, scores) for _ in range(n_pop)] + # create the next generation + children = list() + for i in range(0, n_pop, 2): + # get selected parents in pairs + p1, p2 = selected[i], selected[i+1] + # crossover and mutation + for c in crossover(p1, p2, r_cross): + # mutation + mutation(c, r_mut) + # store for next generation + children.append(c) + # replace population + pop = children + return [best, best_eval] +# define the total iterations +n_iter = 10 +# bits +n_bits = 6 +# define the population size +n_pop = 20 +# crossover rate +r_cross = 0.9 +# mutation rate +r_mut = 0.05 diff --git a/src/agent/map/gameMap.py b/src/agent/map/gameMap.py index 90d0942..c39e18d 100644 --- a/src/agent/map/gameMap.py +++ b/src/agent/map/gameMap.py @@ -1,6 +1,6 @@ from mesa import Model from mesa.datacollection import DataCollector - +import src.agent.ga_chromosome as ga from src.agent.hero import Player from src.agent.model.dice.dice import roll_the_dice @@ -30,7 +30,11 @@ class GameMap(Model): self.boxes_number = boxes_number self.creatures_number = creatures_number self.running = True - self.player = Player(1000, self, "Janusz", 5, 5, 5, 50, 50, WM1, A1, 0, WR1, S1, self.listOfChests) + best, score = ga.genetic_algorithm(ga.fitness_function, ga.n_bits, ga.n_iter, ga.n_pop, ga.r_cross, ga.r_mut) + s=1+best.count(1) + a=1+best.count(2) + w=1+best.count(3) + self.player = Player(1000, self, "Janusz", s, a, w, 50, 50, WM1, A1, 0, WR1, S1, self.listOfChests) self.schedule.add(self.player) x = self.random.randrange(self.grid.width) y = self.random.randrange(self.grid.height) @@ -51,7 +55,7 @@ class GameMap(Model): self.boxes_number + self.creatures_number-20): # taki range, żeby każdy agent miał poprawne unique_id # creature = Creature(i, self) creature = Creature(i, self, "Goblin", - roll_the_dice(3), roll_the_dice(3), roll_the_dice(3), roll_the_dice(3), roll_the_dice(3), + roll_the_dice(3), roll_the_dice(3), roll_the_dice(3), 15, roll_the_dice(3), WM2, A2, roll_the_dice(5)) x = self.random.randrange(self.grid.width) @@ -63,7 +67,7 @@ class GameMap(Model): pass for i in range(self.boxes_number + self.creatures_number-20, self.creatures_number): # taki range, żeby każdy agent miał poprawne unique_id creature = Creature(i, self, "Skeleton", - roll_the_dice(7), roll_the_dice(7), roll_the_dice(7), roll_the_dice(7), roll_the_dice(7), + roll_the_dice(7), roll_the_dice(7), roll_the_dice(7), 15, roll_the_dice(7), WR4, A8, roll_the_dice(10)) x = self.random.randrange(self.grid.width)