Changes between Version 3 and Version 4 of WikiStart


Ignore:
Timestamp:
Mar 8, 2021, 9:31:37 AM (4 years ago)
Author:
Wouter Pasman
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • WikiStart

    v3 v4  
    1 This is the public wiki of the BW4T-Matrx project.
    2 
     1
     2
     3[[PageOutline]]
     4= Welcome to BW4T-Matrx
     5
     6This is our repo for agents for BW4T running on matrx
     7
     8You can copy the code using
    39{{{
    410git clone https://tracinsy.ewi.tudelft.nl/BW4T-Matrx-CollaborativeAI/
    511}}}
     12
     13
     14see also https://tracinsy.ewi.tudelft.nl/trac/Matrx for our clone of matrx
     15
     16More info on matrx core http://docs.matrx-software.com/en/master/autoapi/matrx/world_builder/index.html#matrx.world_builder.WorldBuilder.add_agent
     17
     18
     19= Usage
     20
     21== Installation
     22* git clone the project as above
     23* (optional) set up your virtual environment
     24* pip install -r requirements.txt
     25
     26
     27== Setup
     28You can change the runsession.py or runtournament.py script that contains the setup settings for the run.
     29
     30=== runsession settings
     31
     32* Edit {{{bw4t/runsession.py}}} program with your favourite text editor
     33* If you added a new TeamXXAgent, import it
     34* locate the section containing the agents list, like this
     35{{{
     36agents = [
     37    {'name':'agent1', 'botclass':RandomAgent, 'settings':{'slowdown':3, 'colorblind':True}},
     38    {'name':'agent2', 'botclass':RandomAgent, 'settings':{'slowdown':2, 'shapeblind':True}},
     39    {'name':'human1', 'botclass':Human, 'settings':{'slowdown':1}}
     40    ]
     41
     42}}}
     43* Edit this section as needed. Each line is creating one bot. Names must be unique. Botclass must match the class name of the agent you need. Slowdown X means that this agent slows by a factor X, the minimum number is 1.
     44* save the file. Make sure it remains plain ASCI text and don't change the end-of-line markers.
     45
     46=== runtournament settings
     47* Edit {{{bw4t/runtournament.py}}} program with your favourite text editor. Edit the agents as described with the runsession settings above.
     48* Additionally there is a {{{teamsize}}} variable. Set it to the desired team size for the sessions.
     49
     50The tournament will create and run all possible teams consisting of <teamsize> agents selected from the agents list.
     51
     52== Run
     53While running, do not open the csv log file that the session/tournament is writing to: doing this may block the file for writing and cause the session/tournament to halt with a write error.
     54
     55=== Run Session
     56* run {{{python runsession.py}}}
     57* In a web browser (eg safari or firefox) go to http://localhost:3000
     58* Go to god mode and start the simulation by pressing the white triangle at the top
     59* The log file is written to world1 directory.
     60
     61=== Run Tournament
     62* run {{{python runsession.py}}}
     63* The log file is written to worldXX directory
     64* You can not use the browser to see the progress with tournaments
     65
     66= Human agent
     67A human bot is controlled through the web browser. Go to http://localhost:3000 and select the humanbot you want to control. Now you can navigate the humanbot using the keyboard:
     68
     69||keyboard letter||effect||
     70||WASD||move agent||
     71||Q||grab an object||
     72||E||to drop object||
     73||R||to open a door||
     74||F||to close it||
     75 
     76Humans can also send and receive messages. Click on the balloons-icon at the top right. An additional area will appear at the right or at the bottom of the screen (notice, in many browsers this remains invisible because they hide the scroll bar). You can type your own messages and select agent(s) to adress to.
     77
     78= Implementing your agent
     79To implement an agent for BW4T, all you need is to create a class that extends the {{{bw4t/BW4TBrain}}}.
     80To do this, you must implement {{{filter_bw4t_observations}}} and {{{decide_on_bw4t_action}}}.
     81
     82If you implement __init__, you must call super().init(settings) with the original settings as first call in your init function.
     83
     84If you implement initialize, you must call super().initialize() with the original settings as first call in your initialize function.
     85
     86You cannot override get_log_data because we use it already for logging the outcomes of the game
     87
     88
     89With that done, you can import and use your agent in runsession, check [[wiki:#Setup]].
     90
     91The following sections give some details about the two functions to implement
     92
     93== filter_bw4t_observations
     94This class is called every tick, with the new State object.
     95You should process here all observations, because observations may be visible only for one tick.
     96
     97Normally this function returns the state. If you return a modified state here, then decide_on_bw4t_actions will receive this modified state.
     98
     99== decide_on_bw4t_action
     100This function is called every time your agent can take an action. This may not be every tick.
     101The decide_on_bw4t_action receives the state as parameter.
     102The agent code decides on this and returns what the next action is, in the form of a tuple {{{(action, params)}}}.
     103
     104The following actions  and params are possible in BW4T:
     105||Action||required params||
     106||'OpenDoorAction'||'door_range':1, 'object_id':doorId||
     107||'MoveNorth' 'MoveEast' 'MoveSouth' 'MoveWest'|| ||
     108||GrabObject||'object_id':blockId ||
     109||DropObject||'object_id':blockId ||
     110||None (do nothing)|| ||
     111
     112
     113
     114== BW4TBrain
     115All agents for BW4T are required to extend our BW4TBrain.
     116Implementing a BW4TBrain is slightly different from implementing a normal AgentBrain (the default superclass for agents):
     117
     118* decide_on_bw4t_action replaces decide_on_action. BW4T agents must not override decide_on_action
     119* filter_bw4t_observations replaces filter_observations. Agents must not override filter_observations
     120* agent action parameters are restricted, they can not set 'remove_range', 'grab_range', 'door_range' and 'action_duration'.
     121* If an agent raises an exception, only a message is printed but the world continues running. This is because (1) the agent may be able to recover in the next round (2) other agents that are still running may be still able to complete the task. **You should however ensure such errors do not occur in the first place.**
     122
     123The following values are enforced for the parameters for all actions
     124||action parameter||value||
     125||grab_range||1||
     126||max_objects||3||
     127||action_duration||set by the agent slowdown setting||
     128
     129
     130
     131== State info
     132When implementing your agent, you receive a State object.
     133It contains a dictionary representing a gridworld state. The gridworld is a 2D grid with a set of objects/tiles, each at a given 'location' on the grid. Some tiles like walls are fixed, some tiles like bots and blocks can be moved
     134The keys are unique IDs from the Matrx system, and the values again are dictionaries. The values dictionary contents depend on the type of tile. Typically agents will receive something like this:
     135{{{
     136dict: {
     137'agent_0_in_team_0_344':
     138  {
     139    'isAgent': True,
     140    'team': '',
     141    'name': 'Agent 0 in Team 0',
     142    'obj_id': 'agent_0_in_team_0_344',
     143    'location': (1, 1),
     144    'is_movable': True,
     145    'action_set': ['MoveSouthWest', 'OpenDoorAction', 'MoveWest', 'MoveSouth', 'GrabObject', 'MoveEast', 'MoveNorthEast', 'DropObject', 'MoveNorth', 'RemoveObject', 'MoveNorthWest', 'Move', 'MoveSouthEast', 'CloseDoorAction'],
     146    'carried_by': [],
     147    'is_human_agent': False,
     148    'is_traversable': True,
     149    'class_inheritance': ['BlockWorldAgent', 'AgentBrain', 'object'],
     150    'is_blocked_by_action': False,
     151    'is_carrying': [],
     152    'sense_capability': {<class 'matrx.objects.agent_body.AgentBody'>: 2, <class 'builder.CollectableBlock'>: 2, '*': inf},
     153    'visualization': {'size': 1.0, 'shape': 1, 'colour': '#92f441', 'depth': 100, 'opacity': 1.0, 'show_busy': False},
     154    'current_action': None,
     155    'current_action_args': {},
     156    'current_action_duration': -1000000,
     157    'current_action_started_at_tick': -1000000
     158  },
     159  'human_0_in_team_0_345':{
     160    'isAgent': True,
     161    'key_action_map': {'w': 'MoveNorth', 'd': 'MoveEast', 's': 'MoveSouth', 'a': 'MoveWest', 'q': 'GrabObject', 'e': 'DropObject', 'r': 'OpenDoorAction', 'f': 'CloseDoorAction'},
     162    'team': '',
     163    'name': 'Human 0 in Team 0',
     164    'obj_id': 'human_0_in_team_0_346',
     165    'location': (3, 1),
     166    'is_movable': True,
     167    'action_set': ['Move', 'MoveWest', 'GrabObject', 'MoveSouthEast', 'MoveNorthWest', 'MoveNorthEast', 'MoveEast', 'MoveSouth', 'OpenDoorAction', 'RemoveObject', 'MoveSouthWest', 'DropObject', 'MoveNorth', 'CloseDoorAction'],
     168    'carried_by': [],
     169    'is_human_agent': True,
     170    'is_traversable': False,
     171    'class_inheritance': ['HumanAgentBrain', 'AgentBrain', 'object'],
     172    'is_blocked_by_action': False,
     173    'is_carrying': [],
     174    'sense_capability': {<class 'matrx.objects.agent_body.AgentBody'>: 2, <class 'builder.CollectableBlock'>: 2, '*': inf},
     175    'visualization': {'size': 1.0, 'shape': 1, 'colour': '#92f441', 'depth': 100, 'opacity': 1.0, 'show_busy': False},
     176    'current_action': None,
     177    'current_action_args': {},
     178    'current_action_duration': -1000000,
     179    'current_action_started_at_tick': -1000000
     180  },
     181  'world_bounds_-_wall@(23,_4)_0': {
     182    'room_name': 'world_bounds',
     183    'name': 'world_bounds - wall@(23, 4)',
     184    'obj_id': 'world_bounds_-_wall@(23,_4)_0',
     185    'location': (23, 4),
     186    'is_movable': False,
     187    'carried_by': [],
     188    'is_traversable': False,
     189    'class_inheritance': ['Wall', 'EnvObject', 'object'],
     190    'visualization': {'size': 1.0, 'shape': 0, 'colour': '#000000', 'depth': 80, 'opacity': 1.0}
     191  }
     192
     193  'world_bounds_-_wall@(0,_5)_0': {'room_name': 'world_bounds', ...}
     194  'room_0_-_wall@(4,_4)_94': {'room_name': 'room_0', name:...}
     195  'room_0_-_wall@(5,_7)_102': {
     196    'room_name': 'room_0',
     197    'name': 'room_0 - wall@(5, 7)',
     198    'obj_id': 'room_0_-_wall@(5,_7)_102',
     199    'location': (5, 7),
     200    'is_movable': False,
     201    'carried_by': [],
     202    'is_traversable': False,
     203    'class_inheritance': ['Wall', 'EnvObject', 'object'],
     204    'visualization': {'size': 1.0, 'shape': 0, 'colour': '#8a8a8a', 'depth': 80, 'opacity': 1.0}
     205  }
     206  'room_0_-_wall@(6,_4)_101'
     207  ....
     208  'room_0_-_door@(7,_7)_109': {
     209    'is_open': False,
     210    'room_name': 'room_0',
     211    'name': 'room_0 - door@(7, 7)',
     212    'obj_id': 'room_0_-_door@(7,_7)_109',
     213    'location': (7, 7),
     214    'is_movable': False,
     215    'carried_by': [],
     216    'is_traversable': False,
     217    'class_inheritance': ['Door', 'EnvObject', 'object'],
     218    'visualization': {'size': 1.0, 'shape': 0, 'colour': '#640000', 'depth': 80, 'opacity': 1.0}
     219  },
     220
     221  'room_0_area_110: {
     222    'room_name': 'room_0',
     223    'name': 'room_0_area',
     224    'obj_id': 'room_0_area_110',
     225    'location': (5, 5),
     226    'is_movable': False,
     227    'carried_by': [],
     228    'is_traversable': True,
     229    'class_inheritance': ['AreaTile', 'EnvObject', 'object'],
     230    'visualization': {'size': 1.0, 'shape': 0, 'colour': '#0008ff', 'depth': 80, 'opacity': 0.1}
     231  }
     232
     233  ...
     234
     235
     236  'room_1_-_wall@(5,_7)_118': {'room_name': 'room_0', name:...}
     237  ........................
     238  'Drop_off_0_338', {'drop_zone_nr': 0, 'is_drop_zone': True, 'is_goal_block': False, 'is_collectable': False, 'name': 'Drop off 0', 'obj_id': 'Drop_off_0_338', 'location': (12, 21), 'is_movable': False, 'carried_by': [], 'is_traversable': True, 'class_inheritance': ['AreaTile', 'EnvObject', 'object'], 'visualization': {'size': 1.0, 'shape': 0, 'colour': '#878787', 'depth': 80, 'opacity': 1.0}}
     239  'Drop_off_0_339',
     240  'Drop_off_0_340',
     241  'Collect_Block_341',
     242  'Collect_Block_342',
     243  'Collect_Block_343', {'drop_zone_nr': 0, 'is_drop_zone': False, 'is_goal_block': True, 'is_collectable': False, 'name': 'Collect Block', 'obj_id': 'Collect_Block_343', 'location': (12, 21), 'is_movable': False, 'carried_by': [], 'is_traversable': True, 'class_inheritance': ['CollectableBlock', 'EnvObject', 'object'], 'visualization': {'size': 0.5, 'shape': 1, 'colour': '#0008ff', 'depth': 85, 'opacity': 0.5}}
     244
     245  'World': {
     246    'nr_ticks':0,
     247    'curr_tick_timestamp':1609927984782732,
     248    'grid_shape':(24,25),
     249    'team_members': ['agent_0_in_team_0_344', 'human_0_in_team_0_345'],
     250    'world_ID': 'world_1',
     251    'vis_settings': {'vis_bg_clr': '#C2C2C2', 'vis_bg_img': None}}
     252}
     253}}}
     254
     255The World object is somewhat special. The other objects contain a 'obj_id', 'class_inheritance" field and other fields that make it possible to identify the type of object contain in here.
     256=== Blocks
     257Blocks only appear when the agent is near the block (within block_sense_range, see setup section), you get an additional item like
     258
     259{{{
     260Block_in_room_6_330:{
     261  'is_drop_zone': False,
     262  'is_goal_block': False,
     263  'is_collectable': True,
     264  'name': 'Block in room_2',
     265  'obj_id': 'Block_in_room_2_318',
     266  'location': (20, 5),
     267  'is_movable': True,
     268  'carried_by': [],
     269  'is_traversable': True,
     270  'class_inheritance': ['CollectableBlock', 'EnvObject', 'object'],
     271  'visualization': {'size': 0.5, 'shape': 1, 'colour': '#0dff00', 'depth': 80, 'opacity': 1.0}}
     272}}}
     273
     274'is_goal_block' is set for the "GhostBlock" objects that are placed in the dropzone. These objects contain also a 'visualize' property with 'colour', 'shape' and 'size' values that must match the block that is to be dropped in this zone.
     275
     276If you move a block, it keeps the same name eg 'Block_in_room_6_330' even though it is not in the said room anymore.
     277
     278=== Room doors
     279The room door objects can have only one roomid assigned. Therefore you can assume that all rooms connect to the corridor.
     280
     281=== State functions
     282
     283state has some utility functionality, for example
     284
     285||get_all_room_names()||the available room_name properties on the map||
     286||get_room_objects('room_1') ||gives the areas (tiles) that make up room 1||
     287||get_traverse_map()||returns dict {(0,0):False, (0,1):True...} where (X,Y) is the map coordinate and the boolean indicates whether the location can be traversed.||
     288
     289You can create a crude map of the environment with the get_traverse_map() like this
     290{{{
     291        print("Extracting the map")
     292        map=state.get_traverse_map()
     293        width, length = state.get_world_info()['grid_shape']
     294        for y in range(length):
     295            for x in range(width):
     296                if map[(x,y)]:
     297                    print("*",end="")
     298                else:
     299                    print(" ", end="")
     300            print()
     301}}}
     302
     303
     304=== The object fields
     305Most fields are undocumented and you will have to figure it out yourself or even reverse engineer using the matrx code. Here are a few:
     306||location||a tuple (x,y) with the x,y location of the object on the grid
     307||isAgent||set when the object contains info about an agent||
     308||class_inheritance||This is the chain off MATRX used classes the object inherits from. Meaning that a value of [class_B, class_A, EnvObject, object] means that the dictionary with properties comes from a Class_B instance, which inherits from Class_A, which in turn inherits from MATRX’ base object class EnvObject. Finallt, EnvObject in turn inherits from ‘object’; the base Python class from which everything in Python inherits*. So with the snippet {{{state[“some_object_id”][“class_inheritance”][0]|}}} you get the string name of the class definition of the object with ID “some_object_id”.||
     309
     310= Path Planner
     311There is a simple path planner available in matrx, called Navigator. It takes (x,y) coordinates as input, and it needs a state_tracker as well. You can use it like this
     312{{{
     313    state_tracker = StateTracker(agent_id=self.agent_id)
     314    navigator = Navigator(agent_id=self.agent_id,
     315            action_set=self.action_set, algorithm=Navigator.A_STAR_ALGORITHM)
     316}}}
     317
     318To use the navigator you need something like
     319
     320{{{
     321    navigator.reset_full()
     322    navigator.add_waypoints([targetloc])
     323}}}
     324
     325navigator has a is_circular option to create a circular path that loops back to the first waypoint after reaching the last. Check the python documentation for details.
     326
     327If you are not using reset_full, old path points may remain and the path may not work
     328
     329Now you can extract the next steps using
     330{{{
     331  self._state_tracker.update(state)   
     332  action=navigator.get_move_action(state_tracker)
     333or
     334}}}
     335It will take the current agent location as start point and navigate to the waypoint you can add later. The StateTracker contains some processed map so better
     336
     337Check the PatrollingAgent example to see how to use it.
     338FAIK it can only reach reachable places, it will not consider opening doors and refuse planning if the end point can not be reached. Once a door is open, path planning will work
     339
     340
     341
     342= Duration of actions
     343BW4TAgents can not actively use duration of actions. This section is only here to explain how duration of actions actually works.
     344
     345In the gridworld/BW4T world, all time is measured in ticks. Actions and messages are all executed at a single tick. These ticks are independent from the wall time clock and thus this world can be slowed down without changing the behaviour. In a typical simulation, around 10 ticks are executed in a second, but this may differ depending on the CPU speed and load.
     346
     347In grid_world, all actions you can also set 'action_duration'. The minimum is 1, if you set it smaller matrx will change it to 1. This field indicates after how many ticks the action will actually be performed, so 10 means that nothing happens the coming 9 ticks and the action will then be performed in tick 10 from now.
     348
     349In BW4T agents can NOT change the action_duration, because BW4T uses the action_duration field to control the overall speed of an agent. Your agent will raise an exception if you try to set action_duration.
     350
     351'duration' works like this: once an agent decides to do an action by returning this from the decide_on_action function, e.g. move north, with an action duration of 5, that agent will be "busy" for 4 ticks, and do the action on the 5th. The agent being busy is indicated with the gears animation in the frontend.
     352
     353What this means for the agent is that when the agent is busy with an action, the agent can only perceive the world, but not decide on a new action: only filter_observations is called for agents that are busy with an action, and decide_on_action is skipped. In that manner the situation you propose is impossible, as the agent is blocked from deciding on any new actions once it has initiated an action that takes multiple ticks.
     354
     355= The next required block
     356
     357The BW4T world only accepts the placed blocks if only the correct blocks are placed in the right places in the right order. So it is not acceptable if other blocks are placed on the dropzone as well, nor is placement accepted if the order of placement was incorrect.
     358
     359Since agents only can see the blocks when they are very close to the blocks, agents can usually not determine the order in which the blocks were placed, thus not whether the placement order is correct. So if all blocks are there and the game is not completed, the only solution then is to re-place all the blocks.
     360
     361= Block Shapes
     362The blocks have a number of options for their shape
     363||shape number||shape||
     364||img field||as in the image data?||
     365||0||rectangle||
     366||1||triangle||
     367||2||circle||
     368
     369= Communication
     370A basic communication mechanism is provided to all agents.
     371Use this to send a message:
     372
     373{{{
     374    msg = Message(content='Delivering block'), from_id='me' )
     375    self.send_message(msg)
     376}}}
     377
     378This example message is not too informative but you can extend it to make it more useful. The from_id is a required field but can contain anything. It probably is useful to put the agent ID there. Check the matrx.messages.Message class for more details.
     379
     380If you do not specify {{{to_id}}}, your message will be broadcasted to all agents, including yourself.
     381
     382Received messages appear some time later (probably the next tick) in {{{self.received_messages}}} list. The received messages remain in the list only one tick so you need to scan them every tick if you don't want to miss messages. (but see [https://github.com/matrx-software/matrx/issues/240 issue])
     383
     384= Log files
     385In the world_1 directory  a log file is written for each session. It's  in the CSV file format, with ";" as the column separator. Its first line contains the column information. Each row contains the info for one tick.
     386
     387||Column||meaning||
     388||done||contains True if the goal has been achieved, else False. ||
     389||Agent*_msgs||contain the number of messages sent by the agent in the previous tick||
     390||Agent*_acts||The action done by the agent in this tick||
     391||tick_nr||The tick value for this row||
     392
     393= Running the tests
     394To run the tests using eclipse pyDev,
     395
     396* Set the PYTHONPATH, add the root of project as source folder
     397* All test functions must have names test_XXX. Otherwise it don't work
     398
     399With that, right click on the project and select 'run as / python unit test'.