322 lines
13 KiB
Python
322 lines
13 KiB
Python
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, 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 graphsearch(self, fringe, explored, istate, succesorFunction, goalState):
|
|
finalActionList = []
|
|
fringe.append([None, istate]) # at beginning do nothing
|
|
|
|
while len(fringe) != 0:
|
|
_flag = True
|
|
|
|
if len(fringe) == 0:
|
|
return False
|
|
|
|
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:
|
|
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 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:
|
|
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))
|