Agent Design Guide

Thank you for your interest in IAGO: Interactive Arbitration Guide Online.

What follows is a brief tutorial on how to design agents for IAGO. We will walk through the IAGO Core Agent, as well as some of the variations that can be found included in your copy of IAGO. If you have not already installed IAGO, either via the VM or on your own, please do that first, then return here. You may also look at the IAGO Default Agent, which is a much simpler agent without a lot of intelligence to it.

Part 1: What is an agent?

An IAGO agent is a computer player that human can negotiate with. IAGO agents are designed to act much like humans in many ways, and often use emotional tactics to try to one-up their opponents. They may even behave "irrationally" at times! Your IAGO agent can act however you like it to, but there are some general guidelines on desining agents that may help you get started.

First, it's important to realize that IAGO agents are fundamentally "reactionary". This means that they respond to Events that occur when the user takes certain actions. The IAGO platform groups these events into convenient types, which are discussed in detail here. To implement basic functionality, your agent should decide how to respond to at least SEND_MESSAGE, SEND_OFFER, SEND_EXPRESSION, and FORMAL_ACCEPT events. Please pull up the IAGOCoreVH.java file now, and refer to it as you read this design guide. You are free to (and encouraged) to edit or replace the Core Agent code as you see fit. It is merely a starting point.

Part 2: Sending and Receiving Emotions

While emotions may be the most subtle part of designing an agent, it can have a huge impact! Aggressive agents may be able to make gains in the short term, but will that work in repeated negotiations? To find, out you'll want your agent to be able to react to and send emotions.

When you receive an emotion from the human user, this is because they have hit one of the five emoticon buttons along the bottom of their screen. You will receive a SEND_EXPRESSION Event that contains a description of the emotion. Often times, you may want to respond with a message reacting to their event, or with a similar emotion yourself (mirroring), or even both! To do this, you'll need to create your own SEND_EXPRESSION or SEND_MESSAGE event. The latter is covered in Part 4 below. To send an expression, use the appropriate constructor as described here. As an agent designer, you have more flexiblity in emotions, as you have access to EIGHT emotions, and can choose the duration that they will appear on the agent avatar. Simply create your event, add it to the List of your response (see Part 6), and you're on your way to an expressive agent!

Here is the relevant code in the Core agent that deals with responding to an incoming expression with an expression of your own (and a message).


//what to do when player sends an expression -- react to it with text and our own expression
		if(e.getType().equals(Event.EventClass.SEND_EXPRESSION))
		{
			String expr = expression.getExpression(getHistory());
			Event e1 = new Event(History.VH_ID, Event.EventClass.SEND_EXPRESSION, expr, 2000, 0);
			resp.add(e1);

			Event e0 = new Event(History.VH_ID, Event.EventClass.SEND_MESSAGE, messages.getMessageResponse(getHistory(), game), 0);
			resp.add(e0);
			return resp;
		}

Note that we first determine the incoming type of Event. Since it is an Expression Event, we choose to create two response events of our own. These events are then added to the "resp" list (defined earlier) and are executed in order. For more on chaining events, see Part 5 below.

Each of these events are "core" functionality. In short, no matter what kind of Expression the user sends, and why, this agent with always respond with an Expression and a Message. Which Expression and Message are used are determined by the respective Policies that are called here (the variables "expression" and "messages"). Let's dive into one possible Expression Policy we could use: the IAGONastyExpression!


	public String getExpression(History history)
	{
		Event last = history.getPlayerHistory().getLast();
		if(last.getType().equals(Event.EventClass.SEND_EXPRESSION)){
			if(last.getMessage().equals("sad"))
				return "angry";
			if(last.getMessage().equals("happy"))
				return "neutral";
			if(last.getMessage().equals("surprised"))
				return "neutral";
			if(last.getMessage().equals("angry"))
				return "angry";
		}
		...

As you can see, when we get the Expression from this Policy, it responds to sad or angry player Expressions with angry Expressions of its own. Similarly, it "responds" with a neutral expression to happy or surprised player Expressions. Note that responding with a neutral Expression isn't strictly necessary, since the agent image will already be showing a neutral expression by default.

Part 3: Sending and Receiving Offers

Offers are the core of a negotiation, and the part that may be the most familiar to you if you have been designing agents that compete against other agents. In IAGO, offers are made by specifying the location of objects on a 3 by X board, where X is the number of distinct items. Each of the 3 rows of the board represents items that are currently assigned to your agent, to the human opponent, or are "undecided" by being in the middle of the table. Whenever you craft an offer, you'll have to specify the location of ALL items. When you do this, the IAGO platform will also automatically create a message honestly describing the offer. For example, if you choose to split the 3 apples with 1 on each side and 1 in the middle, but choose to ignore the oranges entirely, the offer and the automatic message will reflect that: "How about I get one apple, and you get one apple?" (or similar). Note that incorrectly specifying the number of items can cause an exception, so be sure to debug carefully!

The Core Agent included in the source download has two example Behavior Policies. The Conceding Behavior attmepts to take the vast majority of the objects and then slowly relinquishes them, while the Repeated Favor Behavior leaves most items in the middle and splits them between players two at a time. Look at both Behaviors for examples on how you might create your agent. We'll go over a piece of the Conceding Behavior here:


		protected Offer getRejectOfferFollowup(History history) {

			//start from where we currently have conceded
			Offer propose = new Offer(game.getNumIssues());
			for(int issue = 0; issue < game.getNumIssues(); issue++)
				propose.setItem(issue,  concession.getItem(issue));


			int[] free = new int[game.getNumIssues()];
			int totalFree = 0;
			for(int issue = 0; issue < game.getNumIssues(); issue++)
			{
				free[issue] = concession.getItem(issue)[1];
				totalFree += concession.getItem(issue)[1];
			}

			if(totalFree > 0)  //concede free items
			{
				for (int issue = 0; issue < game.getNumIssues(); issue++)
				{
					propose.setItem(issue, new int[] {concession.getItem(issue)[0], 0, concession.getItem(issue)[2] + free[issue]});
				}
			}
			else
				return null;

			concession = propose;

			return propose;
		}

In this snippet, the agent determines what the last offer was that was proposed (the variable: "concession"). It then prepares a new offer ("propose"). It begins by reading the number of items that have not been allocated yet (are free, and in the middle of the table). It does this by reading the [1] position of the last proposal table. Then, if there are any free items, it concedes them all to the opponent. Otherwise, there is no concession! The Conceding Behavior has other parts where concessions occur--feel free to check it out.

IMPORTANT:You may receive information from the player that they accept or reject your offer. Great! That's important information on their preferences and strategy. However, note that offers are non-binding in IAGO. Accepting or rejecting an offer has no bearing on the status of items on the game board, nor does it "lock-in" offers. Locking-in of offers may only be done with the FORMAL_ACCEPT Event. FORMAL_ACCEPT events indicate that the current state of the board is acceptable to the party that send the Event. Changing the board will invalidate previous FORMAL_ACCEPTs. If both players send FORMAL_ACCEPTs, then the negotiation is over. FORMAL_ACCEPTs are ignored if the game board is not fully assigned (i.e., there are still items in the middle row).

Part 4: Sending and Receiving Messages

Messages and discussion are an important part of negotiation. While some may say that "talk is cheap", oftentimes the information gathered when talking with your negotiation partner may have a large bearing on strategy! In IAGO, messages are divided into many different SubClasses. These fall into three broad types: preferences, offer responses, and patter. There is no formal difference between each of the types (they are all represented by the SEND_MESSAGE event), but in designing an agent, it helps to consider them all.

Preferences are the most precise type of message. Preferences are either statements or questions send by the user to the agent that express some mathmatical relation between issues. For example, the user may say "I like apples more than oranges", or "Do you like pears least?". Since the current research goal of IAGO is not on natural language parsing, these preferences have been encoded into a specific class for your use here. If your SEND_MESSAGE event contains a non-null Preference object, you can use this class to decode the preference and respond accordingly. Note that preferences coming from the human always have a preference object encoded. The Core Agent responds to questions and statements with truthful answers about its own preferences, but your's does not have to (one year's competition winner lied!)!

Offer responses are a simple accept or reject statement. They are triggered by two buttons on the left-side of the screen, near the offer table. They send a message indicating acceptance or rejection of an offer, and are completely non-binding (see Part 3 above). However, the Core Agent using the Repeated Favor Behavior makes use of acceptances by using them to base future offers on--you may as well! Acceptances and rejections are standard messages with associated SubTypes.

Patter responses are accessed through the menu on the right side of the screen, like preferences. But instead of using the relational GUI, patter responses are simply pre-scripted sentences that the human player may say. Each has a Message Code corresponding to its place in the list in the GameSpec class, to make parsing the incoming messages easier. The Core Agent responds to all messages with some kind of response--look at the switch statements in the Expression and Message Policies. And note that while the human is limited to a small set of utterances, your agent may send any kind of message it wishes (and therefore, does not need to encode a Message Code).

Let's take a brief look at how a "NiceFreeMessage" Policy deals with users expressing preferences:


	case PREF_INFO:
	case PREF_REQUEST:
	case PREF_SPECIFIC_REQUEST:
	case PREF_WITHHOLD:
		sc = Event.SubClass.PREF_INFO;
		if (p == null && !isWithholding)
		{
			p = utils.randomPref();
			issue1 = p.getIssue1();
			issue2 = p.getIssue2();
			relation = p.getRelation();
			isQuery = false;

		}
		else if (p == null && isWithholding) {
			str = "I don't think it best to reveal my intentions yet. Maybe if you did first...";
			sc = Event.SubClass.PREF_WITHHOLD;
			break;
		}
		if (p.getRelation() == Relation.BEST)
		{
			issue1 = utils.findMyItemIndex(game, 1);
			issue2 = -1;
			relation = Relation.BEST;
			isQuery = false;
			str = "I like " + utils.getSpec().getIssuePluralText().get(issue1) + " the best.";

		}
		else if (p.getRelation() == Relation.WORST)
		{
			issue1 = utils.findMyItemIndex(game, game.getNumberIssues());
			issue2 = -1;
			relation = Relation.WORST;
			isQuery = false;
			str = "I like " + utils.getSpec().getIssuePluralText().get(issue1) + " the least.";
		}
		else
		{...}
		break;
		if (relation != null) {
			resp.encodePreferenceData(new Preference(issue1, issue2, relation, isQuery), gs);
		}
		return resp;

Here, the agent looks at the last Preference ("p"). We know that the associated Event had a Preference encoded in it, which means the player just expressed something about their preferences, like: "I like apples best". This Policy determines the Relation that the player used and tries to truthfully mirror a similar statement about the agent's preferences. In the case that the Relation was a binary one (meaning >, <, or =), the agent determines which issues were discussed, then responds accordingly. Later, the agent creates an Event object to send back to the user, and uses a helper function to enocde Preference data.

Note that this file has lots of good examples of using the SubClass system to encode messages back to the user:


Event e1 = new Event(this.getID(), Event.EventClass.SEND_MESSAGE, Event.SubClass.OFFER_REJECT, messages.getRejectLang(getHistory(), game), (int) (700*game.getMultiplier()));

Part 5: Advanced Topics: Simulating Active Behavior

Because of the Event system, IAGO agents are, by nature, passive. They cannot take unprompted action. Fortunately, there are two tools that we can use in developing our agent that alleviate this problem. The first is the delay parameter. Whenever you create an action for the agent to take, you may include a millisecond delay as an optional parameter in the Event constructor. Each delay will cause the agent to pause before taking that action. This will be presented to the user as a busy wait or "lag", so delays should be kept short! However, they can be used to make the agent seem more human. A human cannot send an expression and an offer and a message all in the same instant, so why should your agent? By chaining several Events together with a delay, you could, for example, cause the agent to pause for 2 seconds, send an offer, pause for another second, send a message, and then pause for a half second, and finally smile. You may, instead, opt to do these instantly.

The second tool for taking action is the TIME Event. IAGO sends TIME events to the log every 5 seconds. In part, this allows for easier analysis of the data due to timestamping, but it can also be used to make agents that react to the passage of time! The Core Agent that is included in your download has an example of this technique. In the Core Agent, an internal memory is kept of the last few events. If too many TIME Events have passed with no other Events in between, the agent takes action by prompted the user with a message. Your agent may do the same!

Finally, a note about the OFFER_IN_PROGRESS Event. This Event is used in two senses, both of which are strictly optional in basic agent design. The first sense is to use it as a signal. Whenever a human user begins moving items on the board to craft an offer, an OFFER_IN_PROGRESS Event is triggered. Your agent can choose to respect this by refraining from interrupting the user and sending its own offers or messages. No one likes a pushy person! On the other hand, you could choose to design an agent that DOES interrupt, especially if it thinks the incoming OFFER is sure to be an upleasant one.

You may also decide to send your own OFFER_IN_PROGRESS Event to the user. Once triggered, this Event will cause the human player's screen to show flashing dots in the text log, to indicate that you are busy. These dots will automatically disappear once your agent takes another action. This can be used to stall for time, or to merely indicate that they should wait for your agent. Let's look at one of the longer sequences of Events in the Core Agent:


		Event eExpr = new Event(History.VH_ID, Event.EventClass.SEND_EXPRESSION, expression.getUnfairEmotion(), 2000, 0);
		resp.add(eExpr);
		Event e0 = new Event(History.VH_ID, Event.EventClass.SEND_MESSAGE, Event.SubClass.OFFER_REJECT, messages.getVHRejectLang(getHistory(), game), 0);
		resp.add(e0);
		Event e3 = new Event(History.VH_ID, Event.EventClass.SEND_OFFER, behavior.getNextOffer(getHistory()), 700);
		if(e3.getOffer() != null)
		{
			Event e1 = new Event(History.VH_ID, Event.EventClass.OFFER_IN_PROGRESS, 0);
			resp.add(e1);
			Event e2 = new Event(History.VH_ID, Event.EventClass.SEND_MESSAGE, Event.SubClass.OFFER_PROPOSE, messages.getProposalLang(getHistory(), game), 3000);
			resp.add(e2);
			this.lastOfferSent = e3.getOffer();
			resp.add(e3);
		}

Here, the player has just sent an offer that the agent has judged to be less than fair. In reponse, the agent first waits for 2 seconds, then creates an Expression as dictated by its Expression Policy. It sends that simultaneously with language from its Message Policy indicating rejection. Then, 700ms later, it computes its next offer. Assuming the agent returns an offer, it then signals the GUI to activate the Offer In Progress dots, sends the offer, and some proposal text, following a 3 second delay. Feel free to get creative with chaining Events!

Part 6: Code Organization

Our Core Agent organizes its code into three main areas that are paralleled by the three main types of offers described above. By separating the core functionality from the Message, Behavior, and Emotion policies, you can develop multiple agents quickly that can have one aspect of their personality swapped out quickly. The Core module of the agent deals with things that all agents might do--like respond to FORMAL_ACCEPTs and deal with a prolonged period of no activity. By merely invoking the appropriate methods from the Policy classes, the details of exactly WHAT the agent says, for example, can be abstracted--clearly if the agent has received an offer it ought to say SOMETHING, however. This is not the only way to organize code, and you are under no obligation to follow it. Merely create a Java Package (for example: edu.YOUR_INSTITUTION.agents.MY_AGENT_NAME), create an agent, have that agent extend GeneralVH or IAGOCoreVH, and override the three required methods inherited from that class. That's it!

Enjoy your agent development, and please feel free to consult the Javadocs or use the IAGO support portal for more information!

Part 7: Advanced Topics: Agents for Repeated Games

IAGO 2.1 (Desdemona) supports configuration files that include multiple GameSpecs. This means that several games can be played back to back, with the user negotiating with your agent repeatedly. This means that naive strategies that are aggressive in early games may not be as effective later!

You may design agents that store information about user behaviors by attending to the GAME_START and GAME_END events. Our sample Core_Agent does not store any information across games, but does change its initial greeting message in later games! See if you can design more advanced behaviors! Note that the History is preserved across all games, but the GameSpec is NOT. Therefore, attempting to access Events that are tied to the GameSpec (such as Offers!) after a GAME_END event will cause errors. There is nothing stopping you from aggregating data in your own data structures before the GAME_END though!

Try this basic exercise. In Game2 only, have your agent respond to all Smile emoticons by saying "This is the Xth time you've smiled". By keeping a count, and attending to the GAME_START and GAME_END events, you should be able to do this!

FAQs

I need sudo access for the IAGO VM!

The password for the VM is "iago".

I'm getting a message that the "opponent has left" or the "connection was terminated early" when I try to play IAGO!  What should I do?

Your code is throwing an exception.  Refer to either Tomcat's logs or Eclipse's debugger.

I think I've found a bug.  Is that true, and how can I get it fixed?

While we've tried to make a stable release of IAGO for your use, there are occasionally bugs.  Please report these via the support portal.

I don't like the idea of MessagePolicies and BehaviorPolicies etc. being separate.  Can't I just put it all in one class?

Sure, so long as your code isn't cluttered!  Your agents just need to extend GeneralVH and implement all the override methods.

It looks differerent in Internet Explorer 6.0 than Chrome!  What gives?

We try to support as many modern browsers as we can.  However, some older browsers may lead to slight visual distortion.  IE 11 is the only currently supported IE browser by Microsoft.

I'm competing in ANAC! How will I submit my code? 

Additional instructions for submitting and compiling your code will be made available closer to the deadline. Generally, you will submit your agent .java file (which extends GeneralVH.java) and any supporting files. These should be in a new package, following your own naming convention. You will NOT submit any WebContent or .js files, nor will you submit any implementing classes of GameSpec.java.  For now, feel free to test your code locally.  The ANAC deadline will be in July 2021, subject to change.

I made some changes to items in edu.usc.ict.iago.views for testing.  Will those changes be preserved for ANAC?

No.  While it's fine to test differnt types of games by altering ViralGameSpec, the organizers will provide their own GameSpec implementation, with point values and other settings prepared by us.  Your agent and any associated classes should simply extend GeneralVH and work with various types of points and issues.

I see a lot of data being outputted to the Eclipse console, but it's hard to read there. Can I just write it to a file?

Sure. See this tutorial for a quick way to do this. Note that all of this information is also available through reading the Tomcat log file.

I've changed the agent name in several places, but it's still showing up as "Brad"!

There are a couple of places to check. First, has your class that implements "GameSpec.java", which is by default "ViralGameSpec.java" set the return data for getEndgameMessage()? Please note that these changes will not be kept for the competition. Only changes made within your agent class, such as getArtName() (which should always be either "Brad", "Rens", "Laura", or "Ellie") and agentDescription() can be made permanently.

I'm trying to configure my Gmail to be a server for sending out game summaries for me to look at to help with debugging and it wont work.

Note: Email support is suspended in IAGO 2.1. Everyone's email server is different, but the following settings worked for the gmail servers we used for testing. This code was inserted into the GameBridge.java constructor. Don't forget all this data is available in the console output as well, so it's not strictly necessary to set this up.

System.setProperty("mail.smtp.ssl.enable", "true");
ServletUtils.setCredentials("yourAccount@gmail.com", "password", "Your Human Name", "true", "smtp.gmail.com", "465");

Do note that you should enable SSL as per above! Gmail requires this, as it is more strict that other servers.

What are message codes, as alluded to by getMessageCode in the Event class?

Message codes are a deprecated feature. Please use the Event.SubClass system. The following message is preserved for reference: Message codes are a way of quickly determining what message a user has sent to the agent without doing a lot of natural language processing or String comparisons. Message codes 0 - 12 are reserved for the natural language utterances specified in the menu. These are listed in the example code in ResourceGameSpec.java. So, for example, message code 4 corresponds to "Accept this or there will be consequences". Message code 100 is reserved for an offer rejection, and message code 101 is reserved for an offer acceptance.