326 lines
13 KiB
Python
326 lines
13 KiB
Python
import random
|
|
import heapq
|
|
|
|
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
|
|
from node import Node
|
|
|
|
|
|
class Player(Creature):
|
|
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
|
|
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 = {
|
|
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)
|
|
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 -- 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.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
|
|
|
|
#- - - - bfs & successor - - - -#
|
|
def successor(self, append):
|
|
|
|
rotateLeft = AgentState(
|
|
append.get_x(),
|
|
append.get_y(),
|
|
append.get_direction().counterClockwise()
|
|
)
|
|
|
|
rotateRight = 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:
|
|
moveForward = AgentState(
|
|
append.get_x() + move_x,
|
|
append.get_y() + move_y,
|
|
append.get_direction()
|
|
)
|
|
else:
|
|
moveForward = None
|
|
|
|
return [
|
|
[actions["rotateLeft"], rotateLeft],
|
|
[actions["moveForward"], moveForward],
|
|
[actions["rotateRight"], rotateRight]
|
|
]
|
|
|
|
def heuristics(self, state, target_state):
|
|
# cost is initially step distance in manhattan metric
|
|
return abs(state.get_x() - target_state.get_x()) + abs(state.get_y() - target_state.get_y())
|
|
|
|
def graphsearch(self, fringe, explored, istate, succesorFunction, goalState):
|
|
finalActionList = []
|
|
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
|
|
|
|
tmpNode = (heapq.heappop(fringe))[1] # node
|
|
|
|
# build dictionary
|
|
# parent = tmpNode.get_predecessor() # fetch parent state
|
|
# tmpNode.set_predecessor(None) # clear predecessor - don't build a tree chain
|
|
# if parent is None:
|
|
# finalActionList.append([parent, tmpNode])
|
|
# else:
|
|
# finalActionList.append(
|
|
# [parent[1], tmpNode]) # pair(key, value) - key: parent state, value: current state + action
|
|
|
|
if tmpNode._state.get_x() == goalState.get_x() and tmpNode._state.get_y() == goalState.get_y():
|
|
while tmpNode._parent is not None:
|
|
finalActionList.append(tmpNode._action)
|
|
tmpNode = tmpNode._parent
|
|
finalActionList = list(reversed(finalActionList))
|
|
return finalActionList # TODO change step!
|
|
|
|
explored.append(tmpNode)
|
|
|
|
tmpList = succesorFunction(tmpNode._state)
|
|
for newState in tmpList:
|
|
_flag = True
|
|
_flagFringe = True
|
|
_flagExplored = True
|
|
|
|
if newState[1] is None:
|
|
continue
|
|
|
|
# calculating priority
|
|
monster = 0
|
|
if any([thing.isCreature for thing in self.model.grid.get_cell_list_contents([(newState[1].get_x(), newState[1].get_y())])]):
|
|
if newState[0] == 0:
|
|
monster = 10
|
|
p = self.heuristics(newState[1], goalState) + tmpNode._cost + monster + 1
|
|
|
|
r = 0
|
|
counter = 0
|
|
pos = 0
|
|
for fringeNode in fringe:
|
|
if fringeNode[1]._state.get_x() == newState[1].get_x() and fringeNode[1]._state.get_y() == newState[1].get_y() and fringeNode[1]._state.get_direction() == newState[1].get_direction():
|
|
_flagFringe = False
|
|
_flag = False
|
|
r = fringeNode[0]
|
|
pos = counter
|
|
counter = counter + 1
|
|
|
|
for exploredNode in explored:
|
|
if exploredNode._state.get_x() == newState[1].get_x() and exploredNode._state.get_y() == newState[1].get_y() and exploredNode._state.get_direction() == newState[1].get_direction():
|
|
_flagExplored = False
|
|
_flag = False
|
|
|
|
# if _flag:
|
|
# newState[1].set_predecessor(tmpNode)
|
|
|
|
if _flagFringe and _flagExplored:
|
|
newNode = Node(tmpNode, newState, tmpNode._cost + 1 + monster)
|
|
heapq.heappush(fringe, (p, newNode))
|
|
elif not _flagFringe and (p < r):
|
|
newNode = Node(tmpNode, newState, tmpNode._cost + 1 + monster)
|
|
fringe[pos][0] = p
|
|
fringe[pos][1] = newNode
|
|
|
|
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 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:
|
|
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.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):
|
|
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))
|