Skip to content

Commit

Permalink
Merge pull request #196 from matrx-software/hotfix-1.1.1
Browse files Browse the repository at this point in the history
Hotfix 1.1.1
  • Loading branch information
thaije authored Jul 7, 2020
2 parents f4c74fa + 8aa22c8 commit 5471faa
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 49 deletions.
20 changes: 10 additions & 10 deletions matrx/agents/agent_brain.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def __init__(self, memorize_for_ticks=None):
self.__memorize_for_ticks = memorize_for_ticks

# The central state property (an extended dict with unique searching capabilities)
self.__state = None
self._state = None

def initialize(self):
""" To initialize an agent's brain.
Expand Down Expand Up @@ -380,7 +380,7 @@ def is_action_possible(self, action, action_kwargs):

@property
def state(self):
return self.__state
return self._state

@property
def memorize_for_ticks(self):
Expand Down Expand Up @@ -527,32 +527,32 @@ def _get_action(self, state, agent_properties, agent_id):
self.agent_properties = agent_properties

# Update the state property of an agent with the GridWorld's state dictionary
self.__state.state_update(state)
self._state.state_update(state)

# Call the filter method to filter the observation
self.__state = self.filter_observations(self.__state)
if isinstance(self.__state, dict):
self._state = self.filter_observations(self._state)
if isinstance(self._state, dict):
raise ValueError(f"The filter_observation function of "
f"{self.agent_id} does not return a State "
f"object, but a dictionary. Please return "
f"self.state.")

# Call the method that decides on an action
action, action_kwargs = self.decide_on_action(self.__state)
action, action_kwargs = self.decide_on_action(self._state)

# Store the action so in the next call the agent still knows what it did
self.previous_action = action

# Get the dictionary from the State object
filtered_state = self.__state.as_dict()
filtered_state = self._state.as_dict()

# Return the filtered state, the (updated) properties, the intended actions and any keyword arguments for that
# action if needed.
return filtered_state, self.agent_properties, action, action_kwargs

def _fetch_state(self, state_dict):
self.__state.state_update(state_dict)
state = self.filter_observations(self.__state)
self._state.state_update(state_dict)
state = self.filter_observations(self._state)
filtered_state_dict = state.as_dict()
return filtered_state_dict

Expand Down Expand Up @@ -649,7 +649,7 @@ def _set_messages(self, messages=None):
self.received_messages.append(received_message)

def _init_state(self):
self.__state = State(memorize_for_ticks=self.memorize_for_ticks, own_id=self.agent_id)
self._state = State(memorize_for_ticks=self.memorize_for_ticks, own_id=self.agent_id)

@staticmethod
def __check_message(mssg, this_agent_id):
Expand Down
17 changes: 14 additions & 3 deletions matrx/agents/agent_types/human_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,18 +138,29 @@ def _get_action(self, state, agent_properties, agent_id, user_input):
# actions
self.agent_properties = agent_properties

# first filter the state to only show things this particular agent can see
filtered_state = self.filter_observations(state)
# Update the state property of an agent with the GridWorld's state dictionary
self._state.state_update(state)

# Call the filter method to filter the observation
self._state = self.filter_observations(self._state)
if isinstance(self._state, dict):
raise ValueError(f"The filter_observation function of "
f"{self.agent_id} does not return a State "
f"object, but a dictionary. Please return "
f"self.state.")

# only keep user input which is actually connected to an agent action
usrinput = self.filter_user_input(user_input)

# Call the method that decides on an action
action, action_kwargs = self.decide_on_action(filtered_state, usrinput)
action, action_kwargs = self.decide_on_action(self._state, usrinput)

# Store the action so in the next call the agent still knows what it did
self.previous_action = action

# Get the dictionary from the State object
filtered_state = self._state.as_dict()

# Return the filtered state, the (updated) properties, the intended actions and any keyword arguments for that
# action if needed.
return filtered_state, self.agent_properties, action, action_kwargs
Expand Down
74 changes: 43 additions & 31 deletions matrx/agents/agent_utils/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ def __init__(self, own_id, memorize_for_ticks=None):
self.__decays = {}

def state_update(self, state_dict):

# If decay does not matter, we simply use the given dictionary
if self.__decay_val <= 0.0:
# Set the previous and new state
self.__prev_state_dict = self.__state_dict.copy()
self.__state_dict = state_dict.copy()

# Set the "me"
self.__me = self.get_self()

# Return self
return self

# Else: decay does matter so we need to handle knowledge decay
prev_state = self.__state_dict.copy()
state = state_dict.copy()

Expand All @@ -33,43 +47,41 @@ def state_update(self, state_dict):
# Get the ids of all objects that are not perceived any more
gone_ids = set(prev_state.keys()) - set(state.keys())

# Handle knowledge decay if decay actually matters (e.g. that stuff need to be memorized)
if self.__decay_val > 0:
# Decay the decays of all objects that are not perceived any longer
for obj_id in gone_ids:
self.__decays[obj_id] = max((self.__decays[obj_id] - self.__decay_val), 0)

# Reset the decays of all objects that are still perceived
for obj_id in persistent_ids:
self.__decays[obj_id] = 1.0

# Add all new decays of the newly perceived objects
for obj_id in new_ids:
self.__decays[obj_id] = 1.0

# Check for non-zero decays and flag them for keeping (this now also includes all new_ids as we just added
# them).
to_keep_ids = []
for obj_id, decay in self.__decays.items():
if decay > 0:
to_keep_ids.append(obj_id)
else: # remove all zero decay objects, this reduces the self.__decays of growing with zero decays
self.__decays.pop(obj_id)
to_keep_ids = set(to_keep_ids)

# If decay does not matter, flag all objects for keeping that are newly or still perceived
else:
to_keep_ids = new_ids.union(persistent_ids)
# Decay the decays of all objects that are not perceived any longer
for obj_id in gone_ids:
self.__decays[obj_id] = max((self.__decays[obj_id] - self.__decay_val), 0)

# Reset the decays of all objects that are still perceived
for obj_id in persistent_ids:
self.__decays[obj_id] = 1.0

# Add all new decays of the newly perceived objects
for obj_id in new_ids:
self.__decays[obj_id] = 1.0

# Check for non-zero decays and flag them for keeping (this now also
# includes all new_ids as we just added them).
to_keep_ids = []
for obj_id, decay in self.__decays.items():
if decay > 0:
to_keep_ids.append(obj_id)
# remove all zero decay objects, this reduces
# the self.__decays of growing with zero decays
else:
self.__decays.pop(obj_id)
to_keep_ids = set(to_keep_ids)

# Create new state
new_state = {}
for obj_id in to_keep_ids:
# If the object id is in the received state, pick that one. This makes sure that any objects that were also
# in the previous state get updated with newly perceived properties
# If the object id is in the received state, pick that one. This
# makes sure that any objects that were also in the previous state
# get updated with newly perceived properties
if obj_id in state.keys():
new_state[obj_id] = state[obj_id]
# If the object id is not in the received state, it may still be in the previous state (e.g. because its
# decay was non-zero). Though this only happens when decay is set.
# If the object id is not in the received state, it may still be
# in the previous state (e.g. because its decay was non-zero).
# Though this only happens when decay is set.
elif obj_id in prev_state.keys():
new_state[obj_id] = prev_state[obj_id]

Expand Down
9 changes: 7 additions & 2 deletions matrx/api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,24 +460,29 @@ def fetch_context_menu_of_other():
data = request.json

# check that all required parameters have been passed
required_params = ("agent_id_who_clicked", "clicked_object_id", "click_location")
required_params = ("agent_id_who_clicked", "clicked_object_id", "click_location", "agent_selected")
if not all(k in data for k in required_params):
return return_error(code=400, message=f"Missing one of the required parameters: {required_params}")

agent_id_who_clicked = data['agent_id_who_clicked']
clicked_object_id = data['clicked_object_id']
click_location = data['click_location']
agent_selected = data['agent_selected']

# check if agent_id_who_clicked exists in the gw
if agent_id_who_clicked not in gw.registered_agents.keys() and agent_id_who_clicked != "god":
return return_error(code=400, message=f"Agent with ID {agent_id_who_clicked} does not exist.")

# check if the selected agent exists
if agent_selected not in gw.registered_agents.keys() :
return return_error(code=400, message=f"Selected agent with ID {agent_selected} does not exist.")

# ignore if called from the god view
# if agent_id_who_clicked.lower() == "god":
# return return_error(code=400, message=f"The god view is not an agent and thus cannot show its own context menu.")

# fetch context menu from agent
context_menu = gw.registered_agents[clicked_object_id].create_context_menu_for_other_func(agent_id_who_clicked,
context_menu = gw.registered_agents[agent_selected].create_context_menu_for_other_func(agent_id_who_clicked,
clicked_object_id,
click_location)

Expand Down
6 changes: 5 additions & 1 deletion matrx/objects/env_object.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import matrx.defaults as defaults

import warnings

class EnvObject:

Expand Down Expand Up @@ -78,6 +78,10 @@ def __init__(self, location, name, class_callable, customizable_properties=None,
if not hasattr(self, "obj_id"):
self.obj_id = f"{self.obj_name}_{_next_obj_id()}".replace(" ", "_")

if "#" in self.obj_id:
warnings.warn("Note: # signs are not allowed as part of an agent or object ID, as it breaks the MATRX frontend. Any hashtags will be removed from the ID..")
self.obj_id = self.obj_id.replace("#", "")

# Make customizable_properties mutable if not given.
if customizable_properties is None:
self.customizable_properties = []
Expand Down
3 changes: 2 additions & 1 deletion matrx_visualizer/static/js/context_menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ var matrx_url = 'http://' + window.location.hostname,
post_data = {'agent_id_who_clicked': lv_agent_id,
'clicked_object_id': obj_id,
'click_location': [x,y],
'self_selected': object_selected == lv_agent_id}
'self_selected': object_selected == lv_agent_id,
'agent_selected': object_selected}

// if no one or only ourselves are selected, send to matrx_context_menu_self,
// otherwise send to matrx_context_menu_other
Expand Down
3 changes: 3 additions & 0 deletions matrx_visualizer/static/js/gen_grid.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ function draw(state, world_settings, new_messages, accessible_chatrooms, new_tic

// add this agent to the dropdown list
if (obj.hasOwnProperty('isAgent')) {

console.log("adding selection listener:", obj);
populate_god_agent_menu = true;
pop_new_chat_dropdown = true;

Expand Down Expand Up @@ -813,6 +815,7 @@ function draw_bg_tiles() {
function add_selection_listener(object) {
$(object).click( function() {

console.log("clicked on selection listener");
var obj = $(this);
var obj_id = obj.attr('id');

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import setuptools

requires = ['Flask==1.1.1',
requires = ['Flask>=1.1.1',
'jsonpickle',
'Flask-SocketIO>=3.3.2',
'numpy>=1.15.4',
Expand Down

0 comments on commit 5471faa

Please sign in to comment.