2018-08-20/fki Reviewed for version 13. 27-jan-2011/FK 03-feb-2009/FK 07-nov-2006/FK --- Note: on August 2018 lab TAG was reviewed, modernized and brought into version 13. This led to fewer files, simpler software, and cleaner code. The following changes took place: - The Deedee example agent was removed. - The special properties 'user' and 'room' were removed from the Bailiff. - Bailiff and Dexter now have id and info strings that can be set from the commandline. These are for better diagnostics on the console. - Bailiff and Dexter now print commandline help and exit if they see '?', -h, or -help on the commandline. - Bailiff and Dexter had all of their Swing GUI removed. - The custom util package was removed. Bailiff and Dexter now parse their commandline arguments themselves. - Bailiff now uses java.util.logging.Logger instead of the old custom Logger. - Dexter now maintains a jump counter, which is printed with the debug messages. - The old requirement to use Java level 1.4 was removed. Compilation is now at Java 8. The rmic tool is no longer used. - The package name was changed from 'dsv.pis.gotag' to just 'tag'. - Build and run files for Windows (develop/bat, test/bin/pc) were reviewed, debugged, and refactored. --- This is lab TAG, the game of Tag for mobile agents. It is highly recommended that a good understanding of lab CHAT is acquired before TAG is attempted. This distribution contains the working source code for: tag.bailiff A Jini-aware execution service for mobile Java code. tag.dexter A small agent that jumps randomly between Bailiffs Compiling, building, and installing is all managed by the file build.xml, which is an ant script. Ant is a free software build tool which can be found at http://ant.apache.org/ When ant is successfully installed, open a command prompt/shell and change to the develop directory. Typing ant at the command prompt should compile the sources and compose the jar-files. The directories build and dist are created to hold the generated files. The build directory holds the Java class files and the dist (distribution) contains the jar-files. To install the jar-files in the test branch of the software tree, type the command ant install which copies the needed files over to test directories. Sometimes it is valuable to make sure everything has been recompiled and rebuilt fresh. To remove all generated files (your sources are not touched), give the command ant clean The above three commands are targets that can be found at the end of the build.xml file. On a Windows system you can compile and install by using the BAT-files in the develop/bat directory, and run the system from the BAT-files in the test/bin/pc directory. The TAG assignment The challenge is to create the Game of Tag for mobile software agents. The game of tag is a child's game, played minimally by two players. It is very simple. One player is choosen to be 'it'. That player's task is to run up to one of the other players and touch him or her, saying "You're it!", whereupon the 'it' property and the associated task is transferred to the other player. The players who are currently not 'it', try to evade being tagged, usually by running and hiding. Starting from the provided software, you must design and implement the game of tag for at least three mobile agents. The playfield consists of three or more Bailiffs (execution servers). You can modify the given sources freely, and add classes and interfaces as desired. There are two important requirements on which the game's implementation depend: (1) tagging can only be done between players in the same Bailiff (2) the tag (the 'it' property) must be passed reliably from one player to another. It must not be lost or duplicated during the transaction. From the given code (Dexter) we see how the Jini middleware can help each player to dynamically discover and communicate with Bailiffs. From (1) we realise that a player needs to know how the other players are distributed among the Bailiffs. The player being 'it' needs to find a Bailiff with players in it, move there, select a victim, and then attempt to tag that victim. The other players all want to know where the player being 'it' is located, so that they can avoid being tagged, by moving to another Bailiff if the player being 'it' arrives in their Bailiff. So, here is what is needed: (a) Each player can be either 'it' or 'not it', and depending on what it currently is, its behaviour changes appropriately. (b) A player needs cooperation from the Bailiffs so that is can: (1) Get a list of players currently located in a Bailiff (2) Query each player if they are 'it' or not. (3) Try to tag a player (c) From (b) it follows that each player must have a unique id, so that it can: (1) Recognise itself in a list of players (b.1) (2) Specify some other player (b.2 and b.3) (d) Finally, there must be some mechanism that determines exactly if a player can be tagged or not. This has to do with the way mobility actually works. When a mobile object (e.g. a player) 'moves' from one Bailiff to another, it does not really move at all. It sends a copy of itself to the other Bailiff, and if the copy was successful, the original object terminates its thread of execution and goes to garbage collection. Thus, if a player is tagged at the wrong moment, the copy of the player that moved away stays untagged, while the original object instance is tagged instead. When the original instance dies, it takes the 'it' property with it into oblivion, and the tag is lost from the game. There are several possible implementation strategies, and work can be divided between players and Bailiffs to reduce the workload of the players. This is a design choice, but you will need to expand the BailiffInterface to support the game, in particular b.1, b.2 and b.3. That said, the list returned in b.1 could contain additional information beside player id:s; for example, 'this is you' and 'this player currently is it'. That would reduce the number of additional remote method calls that a player otherwise would have to make. Other possible services offered by the Bailiff could be a predicate if it contains the player being it, or a function that returns the current number of players in it. The number of new methods in the Bailiff required to implement the game is usually no more than three, depending on the solution design. Let the Bailiff mediate player-to-player communication It is strongly advised that communication between players use the Bailiff as a proxy. The central message between players in the game of Tag is of course the tagging action itself. It could look something like this: // In this code example, AgentID shold be replaced with whatever is // chosen as the identifier datatype. The class java.util.UUID is a // good choice. // In the BailiffInterface, which is what a player sees: public boolean tag(AgentID aid) throws java.rmi.RemoteException; // Which the Bailiff implements thus: public boolean tag(AgentID aid) throws java.rmi.RemoteException { Agent a = lookup(aid); return a.tag(); } // In the player when being tagged: public boolean tag() { if (/* tag succeeded */) // update my state to be 'it' return true; else return false; } // In the player when trying to tag another player: ... BailiffInterface myBailiff // Bailiff I am in AgentID victim // ID of other player to tag ... try { if (myBailiff.tag(victim)) // update my state to be not it } catch (RemoteException rex) {} In the above example, the Bailiff uses the provided AgentID to locate the requested player, and then perform the local method call. Then it is the player being tagged that decides if it was tagged or not, and returns true or false accordingly. If the requested agent is no longer available in the Bailiff, the lookup will return null, and the calling player receives a NullPointerException wrapped inside a RemoteException. It should also be realised, that when the Bailiff is calling the player's tag() method, the player's own thread of execution is also active in the player object. So if the player is using, for example, a boolean variable to keep track of its 'it' status, the code that checks that variable for what to do and how to behave, should expect that status to go from 'not it' to 'it' at any time. Using the Bailiff as a message proxy is good, but it is not the only way. Other messaging schemes could be devised. However, there is a particularly dangerous bug-fest luring in another modification of the BailiffInterface. In that design, the Bailiff provides a reference to the requested player, and the tagging player calls the victim directly (except it does not): /* *** DO NOT USE *** */ // BailiffInterface public Agent getAgent(AgentID aid) throws java.rmi.RemoteException; // Bailiff public Agent getAgent(AgentID aid) throws java.rmi.RemoteException { return lookup(aid); } // Player being it ... Agent a = myBailiff.getAgent(victimID); // get the victim if (a != null && a.tag()) // tag it isIt = false; // I am no longer 'it' ... The programmer thinks: "Well, I know the it player and the victim are on the same Bailiff, so I receive a reference to the victim, so I can call its tag method to tag it." But here is the trap: the BailiffInterface is a remote interface. It cannot return a local reference. It will return a reference to a copy of the victim, and it is the copy that gets tagged. The original player object is unmodified, and so the tag is lost. One important consequence of having the Bailiff act as mediator, is that it must recognise the player class. Otherwise it cannot call a Player.tag() method. This is another necessary modification of the Bailiff, and it can be implemented in at least two ways: The first solution is to simply use the player class in the Bailiff code, and cast to it where required. A more elegant approach, however, is to have the player implement an interface which the Bailiff knows about. That way one can have players from different classes participate in the game together. Finally, the Bailiff will need a data structure that maps from player identifiers (AgentID) to the local objects that are the players themselves. The player should be added to the map when the Agitator starts, and removed when it stops, also when the player stops because of an exception. The data structure must be protected against concurrent modification, because at any time a present player may die or a remote player migrate to the Bailiff. A suitable instance of interface java.util.Map is java.util.HashMap. Another bug from past years is if the Bailiff fails to remove done players from the map it maintains. Not only will the map continue to grow and consume memory, but it will also accumulate a mausoleum of dead player instances, which can be tagged but will never tag back. Once a dead player has been tagged, the tag is lost. It must also be possible to create a list of the names of the active players currently in the Bailiff. For the namelist, start looking at method java.util.Map.keySet(). Do not send the whole map instance as the response, extract the names into something nice, like an array of String, or something which implements interface java.util.List. If you decide to annotate the list elements with information like, 'this is you' and 'this is the it player', you probably want to create an element class. Remember to protect against concurrent modification while extracting the names. Player behaviours The player has two hard states, being 'it' or 'not it'. In the 'it' state, it hunts down victims and tries to tag them. A successful predator would probably aim for the Bailiff with the most players in it, as this is a simple heuristic to implement. An untagged player, on the other hand, has more freedom in how to behave. Obviously it needs to watch out for the player being 'it', and attempt to escape if it arrives in the current Bailiff. But if not immediately threatened, it has a choice between staying put where it is, or hop over to some other safe Bailiff. If it decides to hop on, it has further choices over a random Bailiff, or one with few players, or one with many players. The behaviour or strategy exhibited by players, will dramatically affect the overall appearence of the game. Players that seek out company, will huddle together in some corner of the playfield, leaving most of the Bailiffs empty. Players that seek loneliness will spread out, automatically balancing the load on the available Bailiffs. There is no requirement to implement alternative non-tagged behaviours, but you are very welcome to experiment with them and observe the emergent system-wide effects. Player delays In order to be able to follow the progress of a game at human speeds delays are required. It is difficult to give good recommendations for these, but a general move delay of 3-5 seconds may be a good starting point. You may also want to give each player a bit of random variation on the order of 50-250 ms so that they slide out of lock-step behaviour. In particular, a player being 'it' could be awarded a slightly shorter delay to give it some edge and make successful tagging more likely. Player identification Players need to have universially unique names (identifers) so that they can be uniquely addressed. Let the agent generate its own UUID when it starts, and then keep this name while it plays (see java.util.UUID.randomUUID()). So, rather than having the Bailiff assign the agent a temporary id when it arrives, the Bailiff asks the agent what id it has. Doing the work First, study the provided sources and learn how the programs work. Then go back to the instruction earlier in this text, and start planning: - what to do - where to do it (i.e. which files do you need to modify) - how to do it (checklists, editors) - how to determine if it works (testing, debugging) You will need the Bailiff, and you will need to make changes to it. The player agent can be created by modifying Dexter, or by creating an entirely new agent. There is no right or wrong here, but it may be easier to recompile things if you keep Dexter and just change its code, even if that means a lot of changes. But your are free to make any changes you see fit, adding or dropping classes etc. When your plan is done, make sure you can compile and run the system BEFORE you start modifying it. Then implement the changes. If you can, start with something small and confirm that it works as you intended. When you get compilation errors, read them carefully and remember that the first error is usually the significant one. Subsequent errors often follow as a consequense of the first problem. When you get run-time exceptions, read them carefully and try to understand what is happening. Remember that exceptions are usually nested, meaning that the critical point of failure is to be found near the top of the list. An exception in the Bailiff will print in the Bailiff console, in the normal way. An exception in a player also prints in the Bailiff console, but if it is wrapped in a RemoteException then the exception actually happened in a Bailiff, or in a player hosted by a Bailiff: Online documentation for the Java API is here: http://download.oracle.com/javase/8/docs/api/ Examination Both labs are done in groups of two students. Groups with only one member are allowed. The grades given for the two labs combined are PASS or FAIL (godkänd eller underkänd). The grades are individual, which means that one group member may be passed and the other failed, if it does not seem credible that that member did contribute to the work. Both labs (CHAT and TAG) must be passed for a passing grade on the lab component of the course. Work which is not up to a passing standard but is judged to be salvagable may be required to provide additional material before being graded. For lab TAG you must do the following: - Implement the game of Tag using the provided material. - Write, print and submit a short report on your design choices, and problems encountered and solved. Remember to put the following on the title sheet: = ID2010/LAB TAG = Your names - Give an oral presentation and show a running implementation. The result must execute and demonstrate (textually or graphically) that the game is being played by three players on a playfield of three Bailiffs. [The group and the examiner sits down together at the computers. The group runs their system and explains briefly what they have done. The examiner asks questions as necessary.] Good luck and remember to have fun!