diff --git a/actions.py b/actions.py index 62ab3f6..389ab12 100644 --- a/actions.py +++ b/actions.py @@ -1,69 +1,47 @@ -#Funkcja konwertujaca wspolrzedne nastepnego pola do odwiedzenia -#na kolejke akcji do wykonania +# Funkcja konwertujaca wspolrzedne nastepnego pola do odwiedzenia -# 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 +from state import AgentState +from direction import Direction -#pole to element wziety z kolejki path +def actionsInterpreter(actionIndex, defaultState, directions): -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") + if actionIndex == -1: + return AgentState( + defaultState.get_x(), + defaultState.get_y(), + defaultState.get_direction().counterClockwise() + ) + elif actionIndex == 0: + move_x = 0 + move_y = 0 + + if defaultState.get_direction() == Direction.N: + move_y = 1 + elif defaultState.get_direction() == Direction.E: + move_x = 1 + elif defaultState.get_direction() == Direction.S: + move_y = -1 + elif defaultState.get_direction() == Direction.W: + move_x = -1 + + return AgentState( + defaultState.get_x() + move_x, # directions[defaultState.get_direction()[0]], - is not subscriptable ??? + defaultState.get_y() + move_y, # directions[defaultState.get_direction()][1], - is not subscriptable ??? + defaultState.get_direction() + ) + elif actionIndex == 1: + return AgentState( + defaultState.get_x(), + defaultState.get_y(), + defaultState.get_direction().clockwise() + ) + else: + return defaultState + + +actions = { + "rotateLeft": -1, + "moveForward": 0, + "rotateRight": 1 +} diff --git a/direction.py b/direction.py new file mode 100644 index 0000000..67d353e --- /dev/null +++ b/direction.py @@ -0,0 +1,15 @@ +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 counterClockwise(self): + v = (self.value-1)%4 + return Direction(v) \ No newline at end of file diff --git a/hero.py b/hero.py index fc20566..cbf26f5 100644 --- a/hero.py +++ b/hero.py @@ -1,8 +1,15 @@ +import random + from mesa import Agent + from othercharacters import dice, Box, Creature, Armor, Weapon +from actions import actions, actionsInterpreter +from state import AgentState +from direction import Direction + class Player(Creature): - def __init__(self, unique_id, model, n, s, a, w, maxhp, hp, weap, arm, g, w2, w3): + def __init__(self, unique_id, model, n, s, a, w, maxhp, hp, weap, arm, g, w2, w3, listOfChests): super().__init__(unique_id, model, n, s, a, w, maxhp, hp, weap, arm, g) self.name = n self.strength = s @@ -17,35 +24,18 @@ class Player(Creature): 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) + self.directions = { + Direction.N : [0, 1], + Direction.E : [1, 0], + Direction.S : [0, -1], + Direction.W : [-1, 0] + } + self.direction = Direction.N + self.queue = [] + self.hasgoalchest = False + self.openedchests = 0 + self.__listOfChests = listOfChests + self.__actionsCollection = [] def meleeAttack(self, opponent): attackValue = self.strength + dice(6) @@ -129,10 +119,10 @@ class Player(Creature): self.gold = self.gold + chest.gold print("Chest opened. Gold inside:", chest.gold) chest.gold = 0 - self.openedchests+=1 - self.hasgoalchest=False + 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 + #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 @@ -151,160 +141,172 @@ class Player(Creature): # self.weapon3 = chest.loot # chest.loot = buffer - def findShortestPathToTarget(self): - visited = [] - precedessors = {} - queue = [self.pos] - found_target = False + #- - - - bfs & successor - - - -# + def successor(self, append): - while queue: - cur_pos = queue.pop(0) + rotateLeft = AgentState( + append.get_x(), + append.get_y(), + append.get_direction().counterClockwise() + ) - #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 + rotateRight = AgentState( + append.get_x(), + append.get_y(), + append.get_direction().clockwise() + ) - #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 + move_x = 0 + move_y = 0 - visited.append(cur_pos) + 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 found_target: - path = [found_target] - while True: - if path[0] == self.pos: - break - precedessor = precedessors[path[0]] - path.insert(0, precedessor) - return path + 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: + moveForward = AgentState( + append.get_x() + move_x, + append.get_y() + move_y, + append.get_direction() + ) + else: + moveForward = None - return "no chests left" + return [ + [actions["rotateLeft"], rotateLeft], + [actions["moveForward"], moveForward], + [actions["rotateRight"], rotateRight] + ] - # Funkcja konwertujaca wspolrzedne nastepnego pola do odwiedzenia - # na kolejke akcji do wykonania + def graphsearch(self, fringe, explored, istate, succesorFunction, goalState): + finalActionList = [] + fringe.append([None, istate]) # at beginning do nothing - # 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 + while len(fringe) != 0: + _flag = True - # pole to element wziety z kolejki path + if len(fringe) == 0: + return False - 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") + tmpState = fringe.pop(0) + + # build dictionary + parent = tmpState[1].get_predecessor() #fetch paren state + tmpState[1].set_predecessor(None) # clear predecessor - don't build a tree chain + if parent is None: + finalActionList.append([parent, tmpState]) 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") + finalActionList.append([parent[1], tmpState]) # pair(key, value) - key: parent state, value: current state + action + + + if tmpState[1].get_x() == goalState.get_x() and tmpState[1].get_y() == goalState.get_y(): + return finalActionList + + explored.append(tmpState) + + tmpList = succesorFunction(tmpState[1]) + for newState in tmpList: + _flag = True + + if newState[1] is None: + continue + + for fringeState in fringe: + if fringeState[1].get_x() == newState[1].get_x() and fringeState[1].get_y() == newState[1].get_y() and fringeState[1].get_direction() == newState[1].get_direction(): + _flag = False + + for exploredState in explored: + if exploredState[1].get_x() == newState[1].get_x() and exploredState[1].get_y() == newState[1].get_y() and exploredState[1].get_direction() == newState[1].get_direction(): + _flag = False + + if _flag: + newState[1].set_predecessor(tmpState) + fringe.append(newState) + + + return None 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") + # 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 len(self.__listOfChests) != 0: + # select and remove element from list + randomChest = random.choice(self.__listOfChests) + self.__listOfChests.remove(randomChest) + self.hasgoalchest = True + + currentState = AgentState(self.pos[0], self.pos[1], self.direction) + + goalState = AgentState(randomChest[1][0], randomChest[1][1], self.direction) + # find way to goal state + self.__actionsCollection = self.graphsearch([], + [], + currentState, + self.successor, + goalState) + + if self.__actionsCollection is None: + raise Exception("CRITICAL ERROR - Algorithm error - Path doesn't exist!!! ://") + + else: #build list from dictionary by last element + goalActionsList = [] + #keysList = list(self.__actionsCollection.keys()) + #valuesList = list(self.__actionsCollection.values()) + + stateWithChest = self.__actionsCollection[-1] # fetch last item + + #stateWithChest: + # [ + # [0] key - parent ActionState: object, + # [1] value - node successor: + # { + # [0] action: string - action to get state + # [1] AgentState: object - current state + # } + # ] + + goalActionsList.append(stateWithChest[1][0]) # save action + tmpState = stateWithChest[0] + while tmpState is not None: # iterate while key (parent state) != None + + index = 0 + for valeState in self.__actionsCollection: # find new key(parent status) index in array data + if valeState[1][1].get_x() == tmpState.get_x() and valeState[1][1].get_y() == tmpState.get_y() and valeState[1][1].get_direction() == tmpState.get_direction(): + break + index += 1 + + + goalActionsList.append(self.__actionsCollection[index][1][0]) # get action + tmpState = self.__actionsCollection[index][0] # get state - and next we will find key equal it's value + + self.__actionsCollection = list(reversed(goalActionsList)) + self.__actionsCollection = [action for action in self.__actionsCollection if action is not None] # remove first None action 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 + 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.hasgoalchest = False + + elif len(self.__actionsCollection) != 0: # jeśli jest wyznaczona skrzynka - cel & są akcje do wykoannia to je realizuje + + actionIndex = self.__actionsCollection[0] # ignore -1 because it's None + self.__actionsCollection.remove(actionIndex) + + newState = actionsInterpreter(actionIndex, AgentState(self.pos[0], self.pos[1], self.direction), self.directions) + + self.model.grid.move_agent(self, (newState.get_x(), newState.get_y())) + self.direction = newState.get_direction() + + print("moved to - ", [newState.get_x(), newState.get_y()]) + cellmates = self.model.grid.get_cell_list_contents([self.pos]) if len(cellmates) > 1: if isinstance(cellmates[0], Box): @@ -313,7 +315,7 @@ class Player(Creature): opponent = cellmates[0] print("Fighting") self.fightOrFlight(opponent) - # print("HP: " + str(self.health) + " / " + str(self.maxHealth)) + # 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 index b4e2f13..c67a5ea 100644 --- a/model.py +++ b/model.py @@ -16,6 +16,7 @@ creatures_number = 5 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 # to jest potrzebne przy założeniu, że potwory chodzą? @@ -23,12 +24,13 @@ class GameMap(Model): 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) + player = Player(1000, self, "Janusz", 3, 3, 3, 20, 20, WM1, A1, 0, WR1, S1, self.listOfChests) 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) @@ -37,6 +39,7 @@ class GameMap(Model): 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 @@ -54,6 +57,9 @@ class GameMap(Model): # self.datacollector=DataCollector #informacje o stanie planszy, pozycja agenta + def get_listOfChests(self): + return self.listOfChests + def step(self): self.schedule.step() # self.datacollector.collect(self) #na razie niepotrzebne diff --git a/server.py b/server.py index eeb22fa..3b46704 100644 --- a/server.py +++ b/server.py @@ -18,5 +18,5 @@ server = ModularServer(GameMap, [grid], "Map", {"x":10, "y":10}) -server.port = 8521 +server.port = 8080 server.launch() \ No newline at end of file diff --git a/state.py b/state.py new file mode 100644 index 0000000..9e39e5b --- /dev/null +++ b/state.py @@ -0,0 +1,36 @@ +class AgentState: + def __init__(self, x, y, direction, predecessor = None): + self.__x = x + self.__y = y + self.__direction = direction + self.__predecessor = predecessor + + 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 + + def get_predecessor(self): + return self.__predecessor + + def set_predecessor(self, predecessor): + self.__predecessor = predecessor \ No newline at end of file