diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..757fee3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.idea \ No newline at end of file diff --git a/actions.py b/actions.py deleted file mode 100644 index 62ab3f6..0000000 --- a/actions.py +++ /dev/null @@ -1,69 +0,0 @@ -#Funkcja konwertujaca wspolrzedne nastepnego pola do odwiedzenia -#na kolejke akcji do wykonania - -# tl - turn left, tr - turn right, fwd - forward, op - open -#queue - kolejka akcji do zrobienia, pole klasy Player -#path - kolejka pol do odwiedzenia, pole klasy Player - -#pole to element wziety z kolejki path - -def actionPlanner(self,pole): - x0 = self.pos[0] - y0 = self.pos[1] - x = pole[0] - y = pole[1] - if (x > x0): - if (self.direction == 1): - self.queue.insert("fwd") - elif (self.direction == 0): - self.queue.insert("tr") - self.queue.insert("fwd") - elif (self.direction == 2): - self.queue.insert("tl") - self.queue.insert("fwd") - else: - self.queue.insert("tr") - self.queue.insert("tr") - self.queue.insert("fwd") - elif (x < x0): - if (self.direction == 3): - self.queue.insert("fwd") - elif (self.direction == 0): - self.queue.insert("tl") - self.queue.insert("fwd") - elif (self.direction == 2): - self.queue.insert("tr") - self.queue.insert("fwd") - else: - self.queue.insert("tr") - self.queue.insert("tr") - self.queue.insert("fwd") - else: - if (y > y0): - if (self.direction == 0): - self.queue.insert("fwd") - elif (self.direction == 1): - self.queue.insert("tl") - self.queue.insert("fwd") - elif (self.direction == 3): - self.queue.insert("tr") - self.queue.insert("fwd") - else: - self.queue.insert("tr") - self.queue.insert("tr") - self.queue.insert("fwd") - else: - if (self.direction == 2): - self.queue.insert("fwd") - elif (self.direction == 3): - self.queue.insert("tl") - self.queue.insert("fwd") - elif (self.direction == 1): - self.queue.insert("tr") - self.queue.insert("fwd") - else: - self.queue.insert("tr") - self.queue.insert("tr") - self.queue.insert("fwd") - if (self.path.empty()): - self.queue.insert("op") diff --git a/hero.py b/hero.py deleted file mode 100644 index fc20566..0000000 --- a/hero.py +++ /dev/null @@ -1,319 +0,0 @@ -from mesa import Agent -from othercharacters import dice, Box, Creature, Armor, Weapon - -class Player(Creature): - def __init__(self, unique_id, model, n, s, a, w, maxhp, hp, weap, arm, g, w2, w3): - super().__init__(unique_id, model, n, s, a, w, maxhp, hp, weap, arm, g) - self.name = n - self.strength = s - self.agility = a - self.wisdom = w - self.maxHealth = maxhp - self.health = hp - self.gold = g - self.weapon1 = weap - self.weapon2 = w2 - self.weapon3 = w3 - self.armor = arm - self.isBox = False - self.isCreature = False - self.directions = [[0, 1], [1, 0], [0, -1], [-1, 0]] - self.direction = 0 - self.queue=[] - self.hasgoalchest=False - self.openedchests=0 - - def rotate(self, clockwise=True): - if clockwise: - self.direction = (self.direction + 1) % 4 - else: - self.direction = (self.direction + 3) % 4 - - def moveFwd(self): - possible_steps = self.model.grid.get_neighborhood( - self.pos, - moore=False, - include_center=False) - new_position = (self.pos[0] + self.directions[self.direction][0], - self.pos[1] + self.directions[self.direction][1]) - if new_position in possible_steps: - self.model.grid.move_agent(self, new_position) - print("moved to ", new_position) - # def move(self): # OLD - # possible_steps = self.model.grid.get_neighborhood( - # self.pos, - # moore=True, - # include_center=False) - # new_position = self.random.choice(possible_steps) - # self.model.grid.move_agent(self, new_position) - - def meleeAttack(self, opponent): - attackValue = self.strength + dice(6) - defenseValue = opponent.strength + opponent.armor.defence - damage = attackValue - defenseValue - if damage > 0: - opponent.health = opponent.health - (damage + self.weapon1.damage) - - def rangeAttack(self, opponent): - attackValue = self.agility + dice(6) - defenseValue = opponent.agility - damage = attackValue - defenseValue - if (damage > 0) and (damage + self.weapon2.damage - opponent.armor.defence > 0): - opponent.health = opponent.health - (damage + self.weapon2.damage - opponent.armor.defence) - - def magicAttack(self, opponent): - attackValue = self.wisdom + dice(6) - defenseValue = opponent.wisdom - damage = attackValue - defenseValue - if (damage > 0) and (damage + self.weapon3.damage - opponent.armor.mag_protection > 0): - opponent.health = opponent.health - (damage + self.weapon3.damage - opponent.armor.mag_protection) - - def fightOrFlight(self, opponent): - combat = True - while combat: - choice = dice(4) - print("dice rolled:", choice) - if choice == 1: - running_speed = self.agility + dice(6) - opponent_speed = opponent.agility + dice(6) - if running_speed > opponent_speed: - combat = False - print("Player ran away") - self.step() - else: - opponent.defaultAttack(self) - if self.health <= 0: - combat = False - print("Player died :/") - elif choice == 2: - self.meleeAttack(opponent) - if opponent.health > 0: - opponent.defaultAttack(self) - if self.health <= 0: - combat = False - print("Player died :/") - else: - combat = False - self.gold = self.gold + opponent.gold - opponent.gold = 0 - opponent.model.grid.remove_agent(opponent) - print("Fight won") - elif choice == 3: - self.rangeAttack(opponent) - if opponent.health > 0: - opponent.defaultAttack(self) - if self.health <= 0: - combat = False - print("Player died :/") - else: - combat = False - self.gold = self.gold + opponent.gold - opponent.gold = 0 - opponent.model.grid.remove_agent(opponent) - print("Fight won") - else: - self.magicAttack(opponent) - if opponent.health > 0: - opponent.defaultAttack(self) - if self.health <= 0: - combat = False - print("Player died :/") - else: - combat = False - self.gold = self.gold + opponent.gold - opponent.gold = 0 - opponent.model.grid.remove_agent(opponent) - print("Fight won") - - def openChest(self, chest): - self.gold = self.gold + chest.gold - print("Chest opened. Gold inside:", chest.gold) - chest.gold = 0 - self.openedchests+=1 - self.hasgoalchest=False - chest.model.grid.remove_agent(chest) - self.direction=0 #po osiągnięciu jednego celu 'restartuje sie' na szukanie ścieżki do kolejnego - # if isinstance(chest.loot,Armor): - # buffer = self.armor - # self.armor = chest.loot - # chest.loot = buffer - # if isinstance(chest.loot,Weapon): - # if chest.loot.type == "Melee": - # buffer = self.weapon1 - # self.weapon1 = chest.loot - # chest.loot = buffer - # elif chest.loot.type == "Range": - # buffer = self.weapon2 - # self.weapon2 = chest.loot - # chest.loot = buffer - # elif chest.loot.type == "Magic": - # buffer = self.weapon3 - # self.weapon3 = chest.loot - # chest.loot = buffer - - def findShortestPathToTarget(self): - visited = [] - precedessors = {} - queue = [self.pos] - found_target = False - - while queue: - cur_pos = queue.pop(0) - - #check for target - cell_contents = self.model.grid.get_cell_list_contents([cur_pos]) - if cell_contents and any([isinstance(thing, Box) for thing in cell_contents]): - found_target = cur_pos - self.hasgoalchest=True - break - - #enqueue safe unvisited neighbours - neighbours = self.model.grid.get_neighborhood( - cur_pos, - moore=False, - include_center=False) - for cell in neighbours: - #if cell hasn't been visited and the contents don't include creatures - if cell not in visited and not any([isinstance(thing, Creature) for thing in self.model.grid.get_cell_list_contents([cell])]): - queue.append(cell) - precedessors[cell] = cur_pos - - visited.append(cur_pos) - - if found_target: - path = [found_target] - while True: - if path[0] == self.pos: - break - precedessor = precedessors[path[0]] - path.insert(0, precedessor) - return path - - return "no chests left" - - # Funkcja konwertujaca wspolrzedne nastepnego pola do odwiedzenia - # na kolejke akcji do wykonania - - # tl - turn left, tr - turn right, fwd - forward, op - open - # queue - kolejka akcji do zrobienia, pole klasy Player - # path - kolejka pol do odwiedzenia, pole klasy Player - - # pole to element wziety z kolejki path - - def actionPlanner(self, cell_from, cell_to): - x0 = cell_from[0] - y0 = cell_from[1] - x = cell_to[0] - y = cell_to[1] - if (x > x0): - if (self.direction == 1): - self.queue.append("fwd") - elif (self.direction == 0): - self.queue.append("tr") - self.rotate(True) - self.queue.append("fwd") - elif (self.direction == 2): - self.queue.append("tl") - self.rotate(False) - self.queue.append("fwd") - else: - self.queue.append("tr") - self.rotate(True) - self.queue.append("tr") - self.rotate(True) - self.queue.insert("fwd") - elif (x < x0): - if (self.direction == 3): - self.queue.append("fwd") - elif (self.direction == 0): - self.queue.append("tl") - self.rotate(False) - self.queue.append("fwd") - elif (self.direction == 2): - self.queue.append("tr") - self.rotate(True) - self.queue.append("fwd") - else: - self.queue.append("tr") - self.rotate(True) - self.queue.append("tr") - self.rotate(True) - self.queue.append("fwd") - elif (y > y0): - if (self.direction == 0): - self.queue.append("fwd") - elif (self.direction == 1): - self.queue.append("tl") - self.rotate(False) - self.queue.append("fwd") - elif (self.direction == 3): - self.queue.append("tr") - self.rotate(True) - self.queue.append("fwd") - else: - self.queue.append("tr") - self.rotate(True) - self.queue.append("tr") - self.rotate(True) - self.queue.append("fwd") - elif (y < y0): - if (self.direction == 2): - self.queue.append("fwd") - elif (self.direction == 3): - self.queue.append("tl") - self.rotate(False) - self.queue.append("fwd") - elif (self.direction == 1): - self.queue.append("tr") - self.rotate(True) - self.queue.append("fwd") - else: - self.queue.append("tr") - self.rotate(True) - self.queue.append("tr") - self.rotate(True) - self.queue.append("fwd") - elif (len(self.path)==0): - self.queue.append("op") - - def step(self): - if self.health > 0: - print("position: ", self.pos) - #print("direction: ", self.direction) - if not self.hasgoalchest: #jeśli nie ma wyznaczonej skrzynki do której idzie to robi bfs żeby ją wyznaczyć - self.path=self.findShortestPathToTarget() - if self.path=="no chests left": - print("no chests left, life has lost meaning") - else: - print("the player should follow this path:", self.path) - for i in range(len(self.path)-1): #iteruje po kolejnych krotkach ze współrzędnymi pól - #actionPlanner ma się wykonać i-1 razy, bo dla ostatniego pola już nie - self.actionPlanner(self.path[i], self.path[i+1]) #dla każdego pola dodaje do kolejki kilka akcji potrzebnych żeby do niego dojść - print(self.queue) - self.direction=0 # bo po obrotach w wyznaczaniu ścieżki trzeba wrócić do orientacji takiej, jaka była na starcie wyznaczania - #to znaczy, że po otwarciu skrzynki też się zeruje żeby zacząć wyznaczanie i chodzenie od takiej samej - while len(self.queue)!=0: - self.action=self.queue.pop(0) - if self.action=="tr": - self.rotate(True) - print("tr'd, direction: ", self.direction) - break - elif self.action=="tl": - self.rotate(False) - print("tl'd, direction: ", self.direction) - break - elif self.action=="fwd": - self.moveFwd() - break - cellmates = self.model.grid.get_cell_list_contents([self.pos]) - if len(cellmates) > 1: - if isinstance(cellmates[0], Box): - self.openChest(cellmates[0]) - else: - opponent = cellmates[0] - print("Fighting") - self.fightOrFlight(opponent) - # print("HP: " + str(self.health) + " / " + str(self.maxHealth)) - print("Gold: " + str(self.gold)) - else: - print("HP: 0 / " + str(self.maxHealth)) diff --git a/model.py b/model.py deleted file mode 100644 index b4e2f13..0000000 --- a/model.py +++ /dev/null @@ -1,59 +0,0 @@ -from mesa import Model -from hero import Player -from othercharacters import Creature, Box, Wall, dice -from armory import WM1, A1, WR1, S1, WM2, A2 -from mesa.time import RandomActivation -from mesa.space import MultiGrid -# from mesa.datacollection import DataCollector -import random - -x = 10 -y = 10 -step_counter = 0 -boxes_number = 4 -creatures_number = 5 - - -class GameMap(Model): - def __init__(self, x, y): - self.grid = MultiGrid(x, y, False) - self.schedule = RandomActivation(self) # agenci losowo po kolei wykonują swoje akcje - # to jest potrzebne przy założeniu, że potwory chodzą? - self.boxes_number = boxes_number - self.creatures_number = creatures_number - self.running = True - # player = Player(1000, self) - player = Player(1000, self, "Janusz", 3, 3, 3, 20, 20, WM1, A1, 0, WR1, S1) - self.schedule.add(player) - x = self.random.randrange(self.grid.width) - y = self.random.randrange(self.grid.height) - self.grid.place_agent(player, (x, y)) - - for i in range(self.boxes_number): - box = Box(i, self) - # self.schedule.add(box) - x = self.random.randrange(self.grid.width) - y = self.random.randrange(self.grid.height) - if self.grid.is_cell_empty((x, y)): - self.grid.place_agent(box, (x, y)) - self.schedule.add(box) - else: - pass - - for i in range(self.boxes_number, - self.boxes_number + self.creatures_number): # taki range, żeby każdy agent miał poprawne unique_id - # creature = Creature(i, self) - creature = Creature(i, self, "Goblin", 1, 1, 1, 1, 1, WM2, A2, dice(6)) - x = self.random.randrange(self.grid.width) - y = self.random.randrange(self.grid.height) - if self.grid.is_cell_empty((x, y)): - self.grid.place_agent(creature, (x, y)) - self.schedule.add(creature) - else: - pass - - # self.datacollector=DataCollector #informacje o stanie planszy, pozycja agenta - - def step(self): - self.schedule.step() - # self.datacollector.collect(self) #na razie niepotrzebne diff --git a/nn_model.h5 b/nn_model.h5 new file mode 100644 index 0000000..d302982 Binary files /dev/null and b/nn_model.h5 differ diff --git a/othercharacters.py b/othercharacters.py deleted file mode 100644 index b558ec1..0000000 --- a/othercharacters.py +++ /dev/null @@ -1,86 +0,0 @@ -from mesa import Agent -import random - - -def dice(number): - return random.randint(1, number) - - - -class Wall(Agent): - def __init__(self, unique_id, model): - super().__init__(unique_id, model) - - def step(self): - pass - - -class Box(Agent): - def __init__(self, unique_id, model): - super().__init__(unique_id, model) - self.gold = 3 * dice(6) - self.isBox = True - self.isCreature = False - - def step(self): - pass - - -class Weapon(): - def __init__(self, name, type, damage): - self.name = name - self.type = type - self.damage = damage - - -class Armor(): - def __init__(self, name, defence, mp): - self.name = name - self.defence = defence - self.mag_protection = mp - - -class Creature(Agent): - def __init__(self, unique_id, model, n, s, a, w, maxhp, hp, weap, arm, g): - super().__init__(unique_id, model) - self.name = n - self.strength = s - self.agility = a - self.wisdom = w - self.maxHealth = maxhp - self.health = hp - self.gold = g - self.weapon1 = weap - self.armor = arm - self.isBox = False - self.isCreature = True - - def meleeAttack(self, opponent): - attackValue = self.strength + dice(6) - defenseValue = opponent.strength + opponent.armor.defence - damage = attackValue - defenseValue - if damage > 0: - opponent.health = opponent.health - (damage + self.weapon1.damage) - - def rangeAttack(self, opponent): - attackValue = self.agility + dice(6) - defenseValue = opponent.agility - damage = attackValue - defenseValue - if (damage > 0) and (damage + self.weapon1.damage - opponent.armor.defence > 0): - opponent.health = opponent.health - (damage + self.weapon1.damage - opponent.armor.defence) - - def magicAttack(self, opponent): - attackValue = self.wisdom + dice(6) - defenseValue = opponent.wisdom - damage = attackValue - defenseValue - if (damage > 0) and (damage + self.weapon1.damage - opponent.armor.mag_protection > 0): - opponent.health = opponent.health - (damage + self.weapon1.damage - opponent.armor.mag_protection) - - def defaultAttack(self, opponent): - if self.weapon1.type == "Meele": - self.meleeAttack(opponent) - elif self.weapon1.type == "Range": - self.rangeAttack(opponent) - else: - self.magicAttack(opponent) - diff --git a/sprites/hero.png b/sprites/heroE.png similarity index 100% rename from sprites/hero.png rename to sprites/heroE.png diff --git a/sprites/heroN.png b/sprites/heroN.png new file mode 100644 index 0000000..97cb342 Binary files /dev/null and b/sprites/heroN.png differ diff --git a/sprites/heroS.png b/sprites/heroS.png new file mode 100644 index 0000000..bc850be Binary files /dev/null and b/sprites/heroS.png differ diff --git a/sprites/heroW.png b/sprites/heroW.png new file mode 100644 index 0000000..6377d78 Binary files /dev/null and b/sprites/heroW.png differ diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/__pycache__/__init__.cpython-39.pyc b/src/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000..1ab9b42 Binary files /dev/null and b/src/__pycache__/__init__.cpython-39.pyc differ diff --git a/src/__pycache__/direction.cpython-39.pyc b/src/__pycache__/direction.cpython-39.pyc new file mode 100644 index 0000000..923230d Binary files /dev/null and b/src/__pycache__/direction.cpython-39.pyc differ diff --git a/src/agent/__init__.py b/src/agent/__init__.py new file mode 100644 index 0000000..dbf109d --- /dev/null +++ b/src/agent/__init__.py @@ -0,0 +1,4 @@ +from .hero import Player +from .map.gameMap import GameMap +from .model import * +from .state import AgentState diff --git a/src/agent/__pycache__/__init__.cpython-39.pyc b/src/agent/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000..b489ef8 Binary files /dev/null and b/src/agent/__pycache__/__init__.cpython-39.pyc differ diff --git a/src/agent/__pycache__/hero.cpython-39.pyc b/src/agent/__pycache__/hero.cpython-39.pyc new file mode 100644 index 0000000..6c15015 Binary files /dev/null and b/src/agent/__pycache__/hero.cpython-39.pyc differ diff --git a/src/agent/ga_chromosome.py b/src/agent/ga_chromosome.py new file mode 100644 index 0000000..1baaf31 --- /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, 3, 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/hero.py b/src/agent/hero.py new file mode 100644 index 0000000..308bc64 --- /dev/null +++ b/src/agent/hero.py @@ -0,0 +1,350 @@ +import random +import pandas as pd +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.agent.model import * +from src.agent.state import AgentState +from src.direction import Direction +from src.items.armory import WM9 +from src.treesearch.actionsInterpreter import ActionInterpreter +from src.agent.model.dice.dice import roll_the_dice +from src.treesearch.bfs import BFS +from src.nominalize import nominalize +import matplotlib.pyplot as plt +import numpy as np +import os +import PIL +import tensorflow as tf + +from tensorflow import keras +from tensorflow.keras import layers +from tensorflow.keras.models import Sequential + +import pathlib + +class Player(Creature): + def __init__(self, unique_id, model, n, s, a, w, max_hp, hp, weapon, arm, g, w2, w3, list_of_chests): + super().__init__(unique_id, model, n, s, a, w, max_hp, hp, weapon, arm, g) + self.name = n + self.strength = s + self.agility = a + self.wisdom = w + self.maxHealth = max_hp + self.health = hp + self.gold = g + self.weapon1 = weapon + self.weapon2 = w2 + self.weapon3 = w3 + self.armor = arm + self.isBox = False + self.isCreature = False + self.isPlayer=True + self.direction = Direction.N + self.queue = [] + self.has_goal_chest = False + self.opened_chests = 0 + self.__listOfChests = list_of_chests + self.__actionsCollection = [] + self.combat=False + self.opponent=None + self.strategy="PASS" + + def identify_content(self, chest): + dataset_url = "https://drive.google.com/uc?export=download&id=1b6w1FbupRmgVC-q9Lpdlg5OBK_gRKEUy" + data_dir = tf.keras.utils.get_file(fname='loot', origin=dataset_url, untar=True) + data_dir = pathlib.Path(data_dir) + + #image_count = len(list(data_dir.glob('*/*.jpg'))) + #print(image_count) + + batch_size = 32 + img_height = 180 + img_width = 180 + + train_ds = tf.keras.preprocessing.image_dataset_from_directory( + data_dir, + validation_split=0.2, + subset="training", + seed=123, + image_size=(img_height, img_width), + batch_size=batch_size) + + val_ds = tf.keras.preprocessing.image_dataset_from_directory( + data_dir, + validation_split=0.2, + subset="validation", + seed=123, + image_size=(img_height, img_width), + batch_size=batch_size) + + class_names = train_ds.class_names + #print(class_names) + + normalization_layer = layers.experimental.preprocessing.Rescaling(1. / 255) + + normalized_ds = train_ds.map(lambda x, y: (normalization_layer(x), y)) + image_batch, labels_batch = next(iter(normalized_ds)) + first_image = image_batch[0] + # Notice the pixels values are now in `[0,1]`. + #print(np.min(first_image), np.max(first_image)) + + #num_classes = 3 + + # Recreate the exact same model, including its weights and the optimizer + new_model = tf.keras.models.load_model('nn_model.h5') + + # Show the model architecture + #new_model.summary() + + # loss, acc = new_model.evaluate(test_images, test_labels, verbose=2) + # print('Restored model, accuracy: {:5.2f}%'.format(100 * acc)) + + object_url = chest.type + object_path = tf.keras.utils.get_file(chest.file_name, origin=object_url) + + img = keras.preprocessing.image.load_img( + object_path, target_size=(img_height, img_width) + ) + img_array = keras.preprocessing.image.img_to_array(img) + img_array = tf.expand_dims(img_array, 0) # Create a batch + + predictions = new_model.predict(img_array) + score = tf.nn.softmax(predictions[0]) + + print( + "This image most likely belongs to {} with a {:.2f} percent confidence." + .format(class_names[np.argmax(score)], 100 * np.max(score)) + ) + + return class_names[np.argmax(score)] + + def predict_strategy(self, opponent): + testcase = pd.DataFrame({'p_strength': nominalize(self.get_strength(),10), + 'p_agility':nominalize(self.get_agility(), 10), + 'p_wisdom':nominalize(self.get_wisdom(),10), + 'p_health':nominalize(self.get_health(),50), + 'p_melee_damage':nominalize(self.get_melee_damage(), 10), + 'p_ranged_damage':nominalize(self.get_ranged_damage(),10), + 'p_magic_damage':nominalize(self.get_magic_damage(),10), + 'p_armor_defence':nominalize(self.get_armor_defence(),5), + 'p_armor_magic_protection':nominalize(self.get_armor_magic_protection(),5), + 'e_strength':nominalize(opponent.get_strength(), 10), + 'e_agility':nominalize(opponent.get_agility(), 10), + 'e_wisdom':nominalize(opponent.get_wisdom(),10), + 'e_health':nominalize(opponent.get_health(), 50), + 'e_damage':nominalize(opponent.get_damage(),10), + 'e_armor_defence':nominalize(opponent.get_armor_defence(), 5), + 'e_armor_magic_protection':nominalize(opponent.get_armor_magic_protection(), 5), + 'e_attack_type':opponent.get_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 + print(r_dataframe) + result=robjects.r('predict(fights.id3, r_dataframe)') + print(result) + return result[0] + def describe_situation(self, opponent): + situation = {# 'p_strength': self.get_strength(), + # 'p_agility': self.get_agility(), + # 'p_wisdom': self.get_wisdom(), + # 'p_health': self.get_health(), + # 'p_melee_damage': self.get_melee_damage(), + # 'p_ranged_damage': self.get_ranged_damage(), + # 'p_magic_damage': self.get_magic_damage(), + # 'p_armor_defence': self.get_armor_defence(), + # 'p_armor_magic_protection': self.get_armor_magic_protection(), + 'e_strength': opponent.get_strength(), + 'e_agility': opponent.get_agility(), + 'e_wisdom': opponent.get_wisdom(), + 'e_health': opponent.get_health(), + 'e_damage': opponent.get_damage(), + 'e_armor_defence': opponent.get_armor_defence(), + 'e_armor_magic_protection': opponent.get_armor_magic_protection(), + 'e_attack_type': opponent.get_attack_type().upper()} + return situation + + def melee_attack(self, opponent): + attack_value = self.strength + roll_the_dice(6) + defense_value = opponent.strength + opponent.armor.get_defence() + damage = attack_value - defense_value + if damage > 0: + opponent.health = opponent.health - (damage + self.weapon1.get_damage()) + print("damage done by the player: ", damage) + + def range_attack(self, opponent): + attack_value = self.agility + roll_the_dice(6) + defense_value = opponent.agility + damage = attack_value - defense_value + if (damage > 0) and (damage + self.weapon2.get_damage() - opponent.armor.get_defence() > 0): + opponent.health = opponent.health - (damage + self.weapon2.get_damage() - opponent.armor.get_defence()) + print("damage done by the player: ", damage) + + def magic_attack(self, opponent): + attack_value = self.wisdom + roll_the_dice(6) + defense_value = opponent.wisdom + damage = attack_value - defense_value + if (damage > 0) and (damage + self.weapon3.get_damage() - opponent.armor.get_mag_protection() > 0): + opponent.health = opponent.health - (damage + self.weapon3.get_damage() - opponent.armor.get_mag_protection()) + print("damage done by the player: ", damage) + + def fight_or_flight(self, opponent, strategy): + self.combat = True + while self.combat: + if strategy=="MELEE": + self.melee_attack(opponent) + if opponent.health > 0: + opponent.default_attack(self) + if self.health <= 0: + self.combat = False + print("Player died :/") + else: + self.combat = False + self.gold = self.gold + opponent.gold + opponent.gold = 0 + opponent.model.grid.remove_agent(opponent) + print("Fight won") + elif strategy=="RANGED": + self.range_attack(opponent) + if opponent.health > 0: + opponent.default_attack(self) + if self.health <= 0: + self.combat = False + print("Player died :/") + else: + self.combat = False + self.gold = self.gold + opponent.gold + opponent.gold = 0 + opponent.model.grid.remove_agent(opponent) + print("Fight won") + elif strategy=='MAGIC': + self.magic_attack(opponent) + if opponent.health > 0: + opponent.default_attack(self) + if self.health <= 0: + self.combat = False + print("Player died :/") + else: + self.combat = False + self.gold = self.gold + opponent.gold + opponent.gold = 0 + opponent.model.grid.remove_agent(opponent) + print("Fight won") + else: + running_speed = self.agility + roll_the_dice(6) + opponent_speed = opponent.agility + roll_the_dice(6) + if running_speed > opponent_speed: + self.combat = False + print("Player ran away") + self.step() + else: + print("Player was too slow to run away") + opponent.default_attack(self) + if self.health <= 0: + self.combat = False + print("Player died :/") + + def open_chest(self, chest): + if chest.type == 1: + ch_gold = 3 * roll_the_dice(6) + self.gold = self.gold + ch_gold + print("------Chest opened. Gold inside:", ch_gold, "-----") + elif chest.type == 2: + self.weapon1 = WM9 + else: + self.health = 0 + # self.direction = 0 # po osiągnięciu jednego celu 'restartuje sie' na szukanie ścieżki do kolejnego -- NIE ZEROWAĆ OBROTU - to psuje goldState w bfs!!! + # if isinstance(chest.loot,Armor): + # buffer = self.armor + # self.armor = chest.loot + # chest.loot = buffer + # if isinstance(chest.loot,Weapon): + # if chest.loot.weapon_type == "Melee": + # buffer = self.weapon1 + # self.weapon1 = chest.loot + # chest.loot = buffer + # elif chest.loot.weapon_type == "Range": + # buffer = self.weapon2 + # self.weapon2 = chest.loot + # chest.loot = buffer + # elif chest.loot.weapon_type == "Magic": + # buffer = self.weapon3 + # self.weapon3 = chest.loot + # chest.loot = buffer + + def step(self): + if self.health > 0: + print("position: ", self.pos) + # print("direction: ", self.direction) + if not self.has_goal_chest: # jeśli nie ma wyznaczonej skrzynki do której idzie to robi bfs żeby ją wyznaczyć + # self.path=self.findShortestPathToTarget() + if len(self.__listOfChests) != 0: + # select and remove element from list + random_chest = random.choice(self.__listOfChests) + self.__listOfChests.remove(random_chest) + self.has_goal_chest = True + + current_state = AgentState(self.pos[0], self.pos[1], self.direction) + + goal_state = AgentState(random_chest[1][0], random_chest[1][1], self.direction) + # find way to goal state + treesearch_module = BFS(self) + self.__actionsCollection = treesearch_module.graphsearch([], + [], + current_state, + BFS.successor, + goal_state) + + if self.__actionsCollection is None: + raise Exception("CRITICAL ERROR - Algorithm error - Path doesn't exist!!! ://") + + else: + self.__actionsCollection = [action for action in self.__actionsCollection if + action is not None] # remove first None action + else: + raise Exception("WIN!!! :D") + + elif len( + self.__actionsCollection) == 0: # jeśli jest wyznaczona skrzynka - cel & nie ma akcji do wykonania - cel osiągnięty + self.has_goal_chest = False + + elif len( + self.__actionsCollection) != 0: # jeśli jest wyznaczona skrzynka - cel & są akcje do wykoannia to je realizuje + + action_index = self.__actionsCollection[0] # ignore -1 because it's None + self.__actionsCollection.remove(action_index) + + new_state = ActionInterpreter.interpret(action_index, + AgentState(self.pos[0], self.pos[1], self.direction)) + + self.model.grid.move_agent(self, (new_state.get_x(), new_state.get_y())) + self.direction = new_state.get_direction() + + #print("moved to - ", [new_state.get_x(), new_state.get_y()]) + + cellmates = self.model.grid.get_cell_list_contents([self.pos]) + if len(cellmates) > 1: + if isinstance(cellmates[0], Box): + decision = self.identify_content(cellmates[0]) + print("Content of chest: "+cellmates[0].address) + if (decision == 'coins') or (decision == 'weapons'): + print("I will open this chest!") + self.open_chest(cellmates[0]) + print("Type of opened chest: ", cellmates[0].type) + else: + print("Probably a trap - chest skipped!") + print("Type of skipped chest: ", cellmates[0].type) + self.opened_chests += 1 + self.has_goal_chest = False + cellmates[0].model.grid.remove_agent(cellmates[0]) + else: + self.opponent = cellmates[0] + self.strategy=self.predict_strategy(self.opponent) + print("Current enemy stats:\n", self.describe_situation(self.opponent)) + print("Strategy returned from the tree: ", self.strategy) + self.fight_or_flight(self.opponent, self.strategy) + else: + print("HP: 0 / " + str(self.maxHealth)) diff --git a/src/agent/map/__pycache__/gameMap.cpython-39.pyc b/src/agent/map/__pycache__/gameMap.cpython-39.pyc new file mode 100644 index 0000000..b3c5160 Binary files /dev/null and b/src/agent/map/__pycache__/gameMap.cpython-39.pyc differ diff --git a/src/agent/map/gameMap.py b/src/agent/map/gameMap.py new file mode 100644 index 0000000..1836a4d --- /dev/null +++ b/src/agent/map/gameMap.py @@ -0,0 +1,97 @@ +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 +from src.agent.model.creature import Creature + +from src.agent.model.box import Box + +from src.items.armory import *#WM1, A1, WR1, S1, WM2, A2 +from mesa.time import RandomActivation +from mesa.space import MultiGrid + +import random + +# from mesa.datacollection import DataCollector + +x = 10 +y = 10 +step_counter = 0 +boxes_number = 6 +creatures_number = 35 + +class GameMap(Model): + def __init__(self, x, y): + self.listOfChests = [] + self.grid = MultiGrid(x, y, False) + self.schedule = RandomActivation(self) # agenci losowo po kolei wykonują swoje akcje + self.boxes_number = boxes_number + self.creatures_number = creatures_number + self.running = True + 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) + print("player optimised stats:", s, a, w) + self.player = Player(1000, self, "Janusz", s, a, w, 50, 50, WM1, A1, 0, WR1, S1, self.listOfChests) + print('Player stats:',self.player.strength, self.player.agility, self.player.wisdom) + self.schedule.add(self.player) + x = self.random.randrange(self.grid.width) + y = self.random.randrange(self.grid.height) + self.grid.place_agent(self.player, (x, y)) + for i in range(self.boxes_number): + r_type = random.randrange(1, 4) + box = Box(i, self, r_type) + x = self.random.randrange(self.grid.width) + y = self.random.randrange(self.grid.height) + if self.grid.is_cell_empty((x, y)): + self.grid.place_agent(box, (x, y)) + self.schedule.add(box) + self.listOfChests.append([i, [x, y]]) # fetching chest position - [index, [OX, OY]] + else: + pass + + for i in range(self.boxes_number, + 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), 15, roll_the_dice(3), + WM2, A2, roll_the_dice(5)) + + x = self.random.randrange(self.grid.width) + y = self.random.randrange(self.grid.height) + if self.grid.is_cell_empty((x, y)): + self.grid.place_agent(creature, (x, y)) + self.schedule.add(creature) + else: + 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), 15, roll_the_dice(7), + WR4, A8, roll_the_dice(10)) + + x = self.random.randrange(self.grid.width) + y = self.random.randrange(self.grid.height) + if self.grid.is_cell_empty((x, y)): + self.grid.place_agent(creature, (x, y)) + self.schedule.add(creature) + else: + pass + #self.datacollector=DataCollector(model_reporters={"HP":self.player.health, "Gold":self.player.gold, "Position (x, y)":self.player.pos}) #informacje o stanie planszy, pozycja agenta +#other data: position, strength & other parameters + def get_list_of_chests(self): + return self.listOfChests + + def get_hp(self): + return self.player.health + def get_gold(self): + return self.player.gold + def get_position(self): + return str(self.player.pos)+str(self.player.direction.name) + + def step(self): + self.schedule.step() + #self.datacollector.collect(self) + #print(str(self.datacollector.model_reporters)) diff --git a/src/agent/model/__init__.py b/src/agent/model/__init__.py new file mode 100644 index 0000000..6d42ab3 --- /dev/null +++ b/src/agent/model/__init__.py @@ -0,0 +1,6 @@ +from .dice import * +from .armor import Armor +from .box import Box +from .creature import Creature +from .wall import Wall +from .weapon import Weapon diff --git a/src/agent/model/__pycache__/armor.cpython-39.pyc b/src/agent/model/__pycache__/armor.cpython-39.pyc new file mode 100644 index 0000000..af9d358 Binary files /dev/null and b/src/agent/model/__pycache__/armor.cpython-39.pyc differ diff --git a/src/agent/model/__pycache__/box.cpython-39.pyc b/src/agent/model/__pycache__/box.cpython-39.pyc new file mode 100644 index 0000000..f6499ac Binary files /dev/null and b/src/agent/model/__pycache__/box.cpython-39.pyc differ diff --git a/src/agent/model/__pycache__/creature.cpython-39.pyc b/src/agent/model/__pycache__/creature.cpython-39.pyc new file mode 100644 index 0000000..f91d40c Binary files /dev/null and b/src/agent/model/__pycache__/creature.cpython-39.pyc differ diff --git a/src/agent/model/__pycache__/wall.cpython-39.pyc b/src/agent/model/__pycache__/wall.cpython-39.pyc new file mode 100644 index 0000000..0747808 Binary files /dev/null and b/src/agent/model/__pycache__/wall.cpython-39.pyc differ diff --git a/src/agent/model/__pycache__/weapon.cpython-39.pyc b/src/agent/model/__pycache__/weapon.cpython-39.pyc new file mode 100644 index 0000000..eb4e9e8 Binary files /dev/null and b/src/agent/model/__pycache__/weapon.cpython-39.pyc differ diff --git a/src/agent/model/armor.py b/src/agent/model/armor.py new file mode 100644 index 0000000..09d8995 --- /dev/null +++ b/src/agent/model/armor.py @@ -0,0 +1,21 @@ +class Armor: + def __init__(self, name, defence, mp): + self.__name = name + self.__defence = defence + self.__mag_protection = mp + + def get_name(self): + return self.__name + + def get_defence(self): + return self.__defence + + def set_defence(self, new_defence): + self.__defence = new_defence + + def get_mag_protection(self): + return self.__mag_protection + + def set_mag_protection(self, new_mag_protection): + self.__mag_protection = new_mag_protection + diff --git a/src/agent/model/box.py b/src/agent/model/box.py new file mode 100644 index 0000000..4a507e0 --- /dev/null +++ b/src/agent/model/box.py @@ -0,0 +1,42 @@ +from mesa import Agent +import random +#from .dice.dice import roll_the_dice + +golds = [("https://drive.google.com/uc?export=download&id=1fWeew0jXZ1lZBmv6CG5viLGloJAex6ao",'moneta'), + ("https://drive.google.com/uc?export=download&id=1UrXbbfJhfCuDZSnxund7sVk40QMS3R2Q", 'moneta2'), + ("https://drive.google.com/uc?export=download&id=1qH0OP4X1NQqpHtUkwD8SryJtKCKcZzVe", 'moneta3'), + ("https://drive.google.com/uc?export=download&id=1b9tZf639mEWgiWq_EYyqjeKYPEMi7dX9", 'moneta4'), + ("https://drive.google.com/uc?export=download&id=1z9jt-j3aS1fRUgVA_t1zR7TS5QzuXW5b", 'moneta5')] +weapons = [("https://drive.google.com/uc?export=download&id=1TA-ObC33FaiHmgQ6i71Kcb32VrHsKUd1", 'miecz'), + ("https://drive.google.com/uc?export=download&id=1oyv15FSPJ84xx1tQLJW8Wlbb1JUx2xCy", 'miecz2'), + ("https://drive.google.com/uc?export=download&id=1Ha0eeLRLcidrMAN1P59V9zB8uSQq3GM5", 'miecz3'), + ("https://drive.google.com/uc?export=download&id=1GetUWnglUtqWqcK4sd5HsdVuaUpU5Ec_", 'miecz4'), + ("https://drive.google.com/uc?export=download&id=1RImo84OykYICvwfLycDEb5tr4tPbGVy1", 'miecz5')] +traps = [("https://drive.google.com/uc?export=download&id=1G-AxY712V-eT2ylW0VXn4o2V_4pvy7lz", 'kwiat'), + ("https://drive.google.com/uc?export=download&id=1i7MwzJRPBZ-KrCDqhT5RCXLstLlWCsx9", 'kwiat2'), + ("https://drive.google.com/uc?export=download&id=1zF7wQuG1gtQ796m6TM08FOUOVK8s_qhT", 'kwiat3'), + ("https://drive.google.com/uc?export=download&id=1qwrIThsoKg44b57JXvbXe--TIplacd-i", 'kwiat4'), + ("https://drive.google.com/uc?export=download&id=1YsqdQaLyD8Es4w09p4Zdw2CufNkl_avN", 'kwiat5')] + +class Box(Agent): + def __init__(self, unique_id, model, type): + super().__init__(unique_id, model) + #self.gold = 3 * roll_the_dice(6) + self.isBox = True + self.isCreature = False + self.isPlayer = False + self.type = type + r_img = random.randrange(0,4) + if type == 1: + self.address = golds[r_img][0] + self.file_name = golds[r_img][1] + elif type == 2: + self.address = weapons[r_img][0] + self.file_name = weapons[r_img][1] + else: + self.address = traps[r_img][0] + self.file_name = traps[r_img][1] + + def step(self): + pass + diff --git a/src/agent/model/creature.py b/src/agent/model/creature.py new file mode 100644 index 0000000..516fbb3 --- /dev/null +++ b/src/agent/model/creature.py @@ -0,0 +1,80 @@ +from mesa import Agent +from .dice.dice import roll_the_dice + + +class Creature(Agent): + def __init__(self, unique_id, model, name, strength, agility, wisdom, max_hp, hp, weapon, armor, gold): + super().__init__(unique_id, model) + self.name = name + self.strength = strength + self.agility = agility + self.wisdom = wisdom + self.maxHealth = max_hp + self.health = hp + self.gold = gold + self.weapon1 = weapon + self.armor = armor + self.isBox = False + self.isCreature = True + self.isPlayer = False + + def get_strength(self): + return self.strength + + def get_agility(self): + return self.agility + + def get_wisdom(self): + return self.wisdom + + def get_health(self): + return self.health + + def get_damage(self): + return self.weapon1.get_damage() + + def get_melee_damage(self): + return self.weapon1.get_damage() + + def get_ranged_damage(self): + return self.weapon2.get_damage() + + def get_magic_damage(self): + return self.weapon3.get_damage() + + def get_armor_defence(self): + return self.armor.get_defence() + + def get_armor_magic_protection(self): + return self.armor.get_mag_protection() + + def get_attack_type(self): + return self.weapon1.get_type() + def melee_attack(self, opponent): + attack_value = self.strength + roll_the_dice(6) + defense_value = opponent.strength + opponent.armor.get_defence() + damage = attack_value - defense_value + if damage > 0: + opponent.health = opponent.health - (damage + self.weapon1.get_damage()) + print("damage done by the monster: ", damage) + def range_attack(self, opponent): + attack_value = self.agility + roll_the_dice(6) + defense_value = opponent.agility + damage = attack_value - defense_value + if (damage > 0) and (damage + self.weapon1.get_damage() - opponent.armor.get_defence() > 0): + opponent.health = opponent.health - (damage + self.weapon1.get_damage() - opponent.armor.get_defence()) + print("damage done by the monster: ", damage) + def magic_attack(self, opponent): + attack_value = self.wisdom + roll_the_dice(6) + defense_value = opponent.wisdom + damage = attack_value - defense_value + if (damage > 0) and (damage + self.weapon1.get_damage() - opponent.armor.get_mag_protection() > 0): + opponent.health = opponent.health - (damage + self.weapon1.get_damage() - opponent.armor.get_mag_protection()) + print("damage done by the monster: ", damage) + def default_attack(self, opponent): + if self.weapon1.get_type() == "Meele": + self.melee_attack(opponent) + elif self.weapon1.get_type() == "Ranged": + self.range_attack(opponent) + else: + self.magic_attack(opponent) diff --git a/src/agent/model/dice/__init__.py b/src/agent/model/dice/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/agent/model/dice/__pycache__/dice.cpython-39.pyc b/src/agent/model/dice/__pycache__/dice.cpython-39.pyc new file mode 100644 index 0000000..c27cfdd Binary files /dev/null and b/src/agent/model/dice/__pycache__/dice.cpython-39.pyc differ diff --git a/src/agent/model/dice/dice.py b/src/agent/model/dice/dice.py new file mode 100644 index 0000000..a4afbcd --- /dev/null +++ b/src/agent/model/dice/dice.py @@ -0,0 +1,4 @@ +import random + +def roll_the_dice(number): + return random.randint(1, number) \ No newline at end of file diff --git a/src/agent/model/wall.py b/src/agent/model/wall.py new file mode 100644 index 0000000..859e0dc --- /dev/null +++ b/src/agent/model/wall.py @@ -0,0 +1,9 @@ +from mesa import Agent + + +class Wall(Agent): + def __init__(self, unique_id, model): + super().__init__(unique_id, model) + + def step(self): + pass diff --git a/src/agent/model/weapon.py b/src/agent/model/weapon.py new file mode 100644 index 0000000..fd90233 --- /dev/null +++ b/src/agent/model/weapon.py @@ -0,0 +1,14 @@ +class Weapon: + def __init__(self, name, weapon_type, weapon_damage): + self.__name = name + self.__type = weapon_type + self.__damage = weapon_damage + + def get_name(self): + return self.__name + + def get_type(self): + return self.__type + + def get_damage(self): + return self.__damage diff --git a/src/agent/state.py b/src/agent/state.py new file mode 100644 index 0000000..38a29dc --- /dev/null +++ b/src/agent/state.py @@ -0,0 +1,29 @@ +class AgentState: + def __init__(self, x, y, direction): + self.__x = x + self.__y = y + self.__direction = direction + + def get_x(self): + return self.__x + + def increment_x(self, value): + self.__x += value + + def set_x(self, x): + self.__x = x + + def get_y(self): + return self.__y + + def increment_y(self, value): + self.__y += value + + def set_y(self, y): + self.__y = y + + def get_direction(self): + return self.__direction + + def set_direction(self, direction): + self.__direction = direction diff --git a/src/decisiontree.py b/src/decisiontree.py new file mode 100644 index 0000000..2981d54 --- /dev/null +++ b/src/decisiontree.py @@ -0,0 +1,20 @@ +import rpy2.robjects as robjects +from rpy2.robjects.packages import importr +import pickle + +def create_model(): + RWeka = importr('RWeka') + robjects.r('WPM("refresh-cache")') + robjects.r('WPM("install-package", "simpleEducationalLearningSchemes")') + robjects.r('WPM("load-package", "simpleEducationalLearningSchemes")') + robjects.r('ID3<-make_Weka_classifier("weka/classifiers/trees/Id3")') + robjects.r('fights<-read.arff("C:/Users/X260/Downloads/currentdata.arff")') + robjects.r('fights.id3 <- ID3(strategy ~ p_strength + p_agility + p_wisdom+ p_health + ' + 'p_melee_damage + p_ranged_damage + p_magic_damage + p_armor_defence + p_armor_magic_protection + ' + 'e_strength+ e_agility + e_wisdom + e_health + e_damage + e_armor_defence + e_armor_magic_protection + ' + 'e_attack_type, data = fights)') + # robjects.r('.jcache(fights.id3)') + #robjects.r('save(fights.id3, file="tree.rda)') + # with open('tree.rda','wb') as f: + # pickle.dump(robjects.r('fights.id3'), f) + #todo: zapisać w rpythonie do pliku, raz zrobić i nie odpalać za każdym razem w serverze \ No newline at end of file diff --git a/src/dictionary/__pycache__/actions.cpython-39.pyc b/src/dictionary/__pycache__/actions.cpython-39.pyc new file mode 100644 index 0000000..1551064 Binary files /dev/null and b/src/dictionary/__pycache__/actions.cpython-39.pyc differ diff --git a/src/dictionary/actions.py b/src/dictionary/actions.py new file mode 100644 index 0000000..5fc1ee4 --- /dev/null +++ b/src/dictionary/actions.py @@ -0,0 +1,5 @@ +actions = { + "rotate_left": -1, + "move_forward": 0, + "rotate_right": 1 + } diff --git a/src/dictionary/directions.py b/src/dictionary/directions.py new file mode 100644 index 0000000..1483cbd --- /dev/null +++ b/src/dictionary/directions.py @@ -0,0 +1,8 @@ +from src.direction import Direction + +directions = { + Direction.N: [0, 1], + Direction.E: [1, 0], + Direction.S: [0, -1], + Direction.W: [-1, 0] + } diff --git a/src/direction.py b/src/direction.py new file mode 100644 index 0000000..69bf510 --- /dev/null +++ b/src/direction.py @@ -0,0 +1,16 @@ +from enum import Enum + + +class Direction(Enum): + N = 0 + E = 1 + S = 2 + W = 3 + + def clockwise(self): + v = (self.value + 1) % 4 + return Direction(v) + + def counter_clockwise(self): + v = (self.value - 1) % 4 + return Direction(v) diff --git a/src/items/__pycache__/armory.cpython-39.pyc b/src/items/__pycache__/armory.cpython-39.pyc new file mode 100644 index 0000000..ed818b7 Binary files /dev/null and b/src/items/__pycache__/armory.cpython-39.pyc differ diff --git a/armory.py b/src/items/armory.py similarity index 79% rename from armory.py rename to src/items/armory.py index d8a872e..7da919d 100644 --- a/armory.py +++ b/src/items/armory.py @@ -1,6 +1,5 @@ -from mesa import Agent, Model -import random -from hero import Weapon, Armor +from src.agent.model.weapon import Weapon +from src.agent.model.armor import Armor WM1 = Weapon("Log", "Melee", 1) WM2 = Weapon("Log", "Melee", 1) @@ -15,16 +14,16 @@ WM10 = Weapon("Axe", "Melee", 3) WM11 = Weapon("Axe", "Melee", 3) WM12 = Weapon("Battle axe", "Melee", 4) -WR1 = Weapon("Sling", "Range", 1) -WR2 = Weapon("Sling", "Range", 1) -WR3 = Weapon("Sling", "Range", 1) -WR4 = Weapon("Bow", "Range", 2) -WR5 = Weapon("Bow", "Range", 2) -WR6 = Weapon("Bow", "Range", 2) -WR7 = Weapon("Bow", "Range", 2) -WR8 = Weapon("Longbow", "Range", 3) -WR9 = Weapon("Longbow", "Range", 3) -WR10 = Weapon("Crossbow", "Range", 4) +WR1 = Weapon("Sling", "Ranged", 1) +WR2 = Weapon("Sling", "Ranged", 1) +WR3 = Weapon("Sling", "Ranged", 1) +WR4 = Weapon("Bow", "Ranged", 2) +WR5 = Weapon("Bow", "Ranged", 2) +WR6 = Weapon("Bow", "Ranged", 2) +WR7 = Weapon("Bow", "Ranged", 2) +WR8 = Weapon("Longbow", "Ranged", 3) +WR9 = Weapon("Longbow", "Ranged", 3) +WR10 = Weapon("Crossbow", "Ranged", 4) S1 = Weapon("Push", "Magic", 2) S2 = Weapon("Push", "Magic", 2) @@ -62,9 +61,9 @@ A16 = Armor("Magical Plate Armor", 3, 2) # C10 = Box(A8) # C11 = Box(A12) # C12 = Box(A14) - +# # Gracz = Player(1000, self, "Janusz",3,3,3,20,20,WM1,A1,0,WR1,S1) -# def __init__(self, unique_id, model, n, s, a, w, maxhp, hp, weap, arm, g): +# def __init__(self, unique_id, model, n, s, a, w, max_hp, hp, weapon, arm, g): # M1 = Creature("Goblin",2,2,1,10,10,WM2,A2,dice(6)) # M2 = Creature("Goblin",2,2,1,10,10,WM3,A3,dice(6)) # M3 = Creature("Goblin",2,2,1,10,10,WR2,A4,dice(6)) diff --git a/src/nominalize.py b/src/nominalize.py new file mode 100644 index 0000000..6050e87 --- /dev/null +++ b/src/nominalize.py @@ -0,0 +1,9 @@ +def nominalize(val, max_val): + return_value = "NONE" + if val > 0.66 * max_val: + return_value = "HIGH" + elif val > 0.33 * max_val: + return_value = "MEDIUM" + elif val > 0: + return_value = "LOW" + return return_value \ No newline at end of file diff --git a/src/tree/__pycache__/node.cpython-39.pyc b/src/tree/__pycache__/node.cpython-39.pyc new file mode 100644 index 0000000..14a4e69 Binary files /dev/null and b/src/tree/__pycache__/node.cpython-39.pyc differ diff --git a/src/tree/node.py b/src/tree/node.py new file mode 100644 index 0000000..a4a4e49 --- /dev/null +++ b/src/tree/node.py @@ -0,0 +1,24 @@ +class Node: + def __init__(self, parent, state_tuple, cost): + self._cost = cost + self._action = state_tuple[0] + self._state = state_tuple[1] + self._parent = parent + + def get_cost(self): + return self._cost + + def get_action(self): + return self._action + + def get_state(self): + return self._state + + def get_predecessor(self): + return self._parent + + def set_predecessor(self, predecessor): + self._parent = predecessor + + def __lt__(self, other): + return self._cost < other.get_cost() diff --git a/src/treesearch/__init__.py b/src/treesearch/__init__.py new file mode 100644 index 0000000..a58caa9 --- /dev/null +++ b/src/treesearch/__init__.py @@ -0,0 +1,3 @@ +from .bfs import BFS +from .actionsInterpreter import ActionInterpreter +from .heuristic import * diff --git a/src/treesearch/__pycache__/actionsInterpreter.cpython-39.pyc b/src/treesearch/__pycache__/actionsInterpreter.cpython-39.pyc new file mode 100644 index 0000000..e087dbd Binary files /dev/null and b/src/treesearch/__pycache__/actionsInterpreter.cpython-39.pyc differ diff --git a/src/treesearch/__pycache__/bfs.cpython-39.pyc b/src/treesearch/__pycache__/bfs.cpython-39.pyc new file mode 100644 index 0000000..b25d819 Binary files /dev/null and b/src/treesearch/__pycache__/bfs.cpython-39.pyc differ diff --git a/src/treesearch/actionsInterpreter.py b/src/treesearch/actionsInterpreter.py new file mode 100644 index 0000000..20b1af9 --- /dev/null +++ b/src/treesearch/actionsInterpreter.py @@ -0,0 +1,40 @@ +from src.agent.state import AgentState +from src.direction import Direction + + +class ActionInterpreter: + + @staticmethod + def interpret(action_index, default_state): + if action_index == -1: + return AgentState( + default_state.get_x(), + default_state.get_y(), + default_state.get_direction().counter_clockwise() + ) + elif action_index == 0: + move_x = 0 + move_y = 0 + + if default_state.get_direction() == Direction.N: + move_y = 1 + elif default_state.get_direction() == Direction.E: + move_x = 1 + elif default_state.get_direction() == Direction.S: + move_y = -1 + elif default_state.get_direction() == Direction.W: + move_x = -1 + + return AgentState( + default_state.get_x() + move_x, + default_state.get_y() + move_y, + default_state.get_direction() + ) + elif action_index == 1: + return AgentState( + default_state.get_x(), + default_state.get_y(), + default_state.get_direction().clockwise() + ) + else: + return default_state diff --git a/src/treesearch/bfs.py b/src/treesearch/bfs.py new file mode 100644 index 0000000..fd1f54e --- /dev/null +++ b/src/treesearch/bfs.py @@ -0,0 +1,140 @@ +import heapq + +from src.dictionary.actions import actions +from src.agent.state import AgentState +from src.direction import Direction +from src.tree.node import Node +from src.treesearch.heuristic.manhattan import manhattan + + +class BFS: + + def __init__(self, agent): + self.__agent = agent + + @staticmethod + def successor(append): + + rotate_left = AgentState( + append.get_x(), + append.get_y(), + append.get_direction().counter_clockwise() + ) + + rotate_right = AgentState( + append.get_x(), + append.get_y(), + append.get_direction().clockwise() + ) + + move_x = 0 + move_y = 0 + + if append.get_direction() == Direction.N: + move_y = 1 + elif append.get_direction() == Direction.E: + move_x = 1 + elif append.get_direction() == Direction.S: + move_y = -1 + elif append.get_direction() == Direction.W: + move_x = -1 + + if append.get_x() + move_x >= 0 and append.get_x() + move_x < 10 and append.get_y() + move_y >= 0 and append.get_y() + move_y < 10: + move_forward = AgentState( + append.get_x() + move_x, + append.get_y() + move_y, + append.get_direction() + ) + else: + move_forward = None + + return [ + [actions["rotate_left"], rotate_left], + [actions["move_forward"], move_forward], + [actions["rotate_right"], rotate_right] + ] + + def graphsearch(self, fringe, explored, istate, succesor_function, goal_state): + final_action_list = [] + + init_state = [None, istate] + root = Node(None, init_state, 0) + + heapq.heappush(fringe, (0, root)) # at beginning do nothing + + while len(fringe) != 0: + _flag = True + + if len(fringe) == 0: + return False + + tmp_node = (heapq.heappop(fringe))[1] # node + + # build dictionary + # parent = tmp_node.get_predecessor() # fetch parent state + # tmp_node.set_predecessor(None) # clear predecessor - don't build a tree chain + # if parent is None: + # final_action_list.append([parent, tmp_node]) + # else: + # final_action_list.append( + # [parent[1], tmp_node]) # pair(key, value) - key: parent state, value: current state + action + + if tmp_node.get_state().get_x() == goal_state.get_x() and tmp_node.get_state().get_y() == goal_state.get_y(): + while tmp_node.get_predecessor() is not None: + final_action_list.append(tmp_node.get_action()) + tmp_node = tmp_node.get_predecessor() + final_action_list = list(reversed(final_action_list)) + return final_action_list # TODO change step! + + explored.append(tmp_node) + + tmp_list = succesor_function(tmp_node.get_state()) + for new_state in tmp_list: + _flag = True + _flagFringe = True + _flagExplored = True + + if new_state[1] is None: + continue + + # calculating priority + monster = 0 + if any([thing.isCreature for thing in + self.__agent.model.grid.get_cell_list_contents( + [(new_state[1].get_x(), new_state[1].get_y())])]): + if new_state[0] == 0: + monster = 0 + p = manhattan(new_state[1], goal_state) + tmp_node.get_cost() + monster + 1 + + r = 0 + counter = 0 + pos = 0 + for fringeNode in fringe: + if fringeNode[1].get_state().get_x() == new_state[1].get_x() and fringeNode[ + 1].get_state().get_y() == new_state[1].get_y() and fringeNode[1].get_state().get_direction() == \ + new_state[1].get_direction(): + _flagFringe = False + _flag = False + r = fringeNode[0] + pos = counter + counter = counter + 1 + + for exploredNode in explored: + if exploredNode.get_state().get_x() == new_state[1].get_x() and exploredNode.get_state().get_y() == \ + new_state[1].get_y() and exploredNode.get_state().get_direction() == new_state[ + 1].get_direction(): + _flagExplored = False + _flag = False + + # if _flag: + # new_state[1].set_predecessor(tmp_node) + + if _flagFringe and _flagExplored: + new_node = Node(tmp_node, new_state, tmp_node.get_cost() + 1 + monster) + heapq.heappush(fringe, (p, new_node)) + elif not _flagFringe and (p < r): + new_node = Node(tmp_node, new_state, tmp_node.get_cost() + 1 + monster) + fringe[pos][0] = p + fringe[pos][1] = new_node + + return None diff --git a/src/treesearch/heuristic/__init__.py b/src/treesearch/heuristic/__init__.py new file mode 100644 index 0000000..1e52a18 --- /dev/null +++ b/src/treesearch/heuristic/__init__.py @@ -0,0 +1 @@ +from .manhattan import manhattan \ No newline at end of file diff --git a/src/treesearch/heuristic/manhattan.py b/src/treesearch/heuristic/manhattan.py new file mode 100644 index 0000000..a3944ba --- /dev/null +++ b/src/treesearch/heuristic/manhattan.py @@ -0,0 +1,4 @@ +# cost is initially step distance in manhattan metric + +def manhattan(state, target_state): + return abs(state.get_x() - target_state.get_x()) + abs(state.get_y() - target_state.get_y())