snake game as a multi-agent systemetd.dtu.dk/thesis/255329/bac10_01.pdf · snake game as a...
TRANSCRIPT
Snake Game as a Multi-Agent System
Abbas Amini
Kongens Lyngby 2010
IMM-B.Sc-2010
Supervisor: Jørgen Villadsen
Department: Department of Informatics and Mathematical Modeling
University: DTU
Date: 4th
January 2010
Page 2 of 74
Technical University of Denmark
Informatics and Mathematical Modeling
Building 321, DK-2800 Kongens Lyngby, Denmark
Phone +45 45253351, Fax +45 45882673
www.imm.dtu.dk
Page 3 of 74
Summary
This bachelor project is about multi-agent systems using Jason, which is a Java-based interpreter for an
extended version of AgentSpeak.
The properties of agents, the subject of multi-agent system and the programming language that is used in
multi-agent systems are described.
Besides Jason, Java is also used in order to simulate the agents and their environment. A case study is used
to put some properties of multi-agent systems in practice.
Page 4 of 74
Resumé
Denne bachelor projekt omhandler multi-agent systemer ved at bruge Jason, som er en Java-baseret
fortolker for en udvidet version af AgentSpeak.
Her beskrives agenternes egenskaber, emnet mutli-agent systmer og programmeringssproget, som bruges i
multi-agent systemer.
Udover Jason, Java er også brugt for at simulere agenterne og deres miljø. Et case study er valgt for at føre
nogle af multi-agent systemers egenskaber ud i praksis.
Page 5 of 74
Preface
This bachelor project was prepared at Informatics Mathimatical Modelling, The University of Denmark from
September 2009 to January 2010 as a part of fulfillment for the requirements for acquiring the B.Sc.
degree. This project takes a previous project “02122 Software Technology Project” as its starting point.
The project gives an introduction to multi-agent systems by describing the properties of agents and the
multi-agent system as a whole. The properties of the programming language used in multi-agent systems
are also described.
Then the case study is mentioned, and the way the case study is implemented is discussed in details. Finally
a conclusion is made, where the result of the project is summarised.
Lyngby, January 2010
Abbas Amini
Page 6 of 74
Acknowledgements
I thank my supervisor Jørgen Villadsen for his advice and guidance at the start of the project, and for his
comfort near the end of the project, where I was stressed.
Page 7 of 74
Contents
1 Introduction ................................................................................................................................................ 10
2 Introduction to Multi-Agent System .......................................................................................................... 10
2.1 Properties of Agents ........................................................................................................................ 10
2.1.1 An Agent should be Autonomous ............................................................................................... 11
2.1.2 An Agent should be Proactive ..................................................................................................... 12
2.1.3 An Agent should be Reactive ...................................................................................................... 12
2.1.4 An Agent should be Social ........................................................................................................... 12
2.2 Characteristic of a Multi-Agent System ........................................................................................... 13
2.2.1 Objections against Multi-Agent Systems .................................................................................... 14
2.2.1.1 Multi-agent systems compared to concurrent/distributed systems ..................................... 14
2.2.1.2 Multi-agent systems compared to AI .................................................................................... 14
2.2.1.3 Multi-agent system compared to economics/game theory .................................................. 15
2.2.1.4 Multi-agent systems compared to social science .................................................................. 15
2.3 Programming languages for multi-agent systems ........................................................................... 15
3 The BDI model ............................................................................................................................................ 16
3.1 Practical Reasoning .......................................................................................................................... 17
3.1.1 Deliberation and Intentions ........................................................................................................ 17
3.1.2 Means-Ends Reasoning ............................................................................................................... 18
4 Jason – An Extension of AgentSpeak .......................................................................................................... 19
4.1 Beliefs ............................................................................................................................................... 19
4.1.1 Annotation .................................................................................................................................. 19
4.1.2 Negation ...................................................................................................................................... 20
4.1.3 Rules ............................................................................................................................................ 20
4.2 Goals ................................................................................................................................................. 20
4.3 Plans ................................................................................................................................................. 21
4.3.1 Triggering Event – The first part of a plan .................................................................................. 21
4.3.2 The Context of a Plan .................................................................................................................. 22
4.3.3 The Body of a Plan ...................................................................................................................... 22
4.3.3.1 Actions ................................................................................................................................... 22
4.3.3.2 Achievement Goals ................................................................................................................ 23
Page 8 of 74
4.3.3.3 Test Goals ............................................................................................................................... 23
4.3.3.4 Remembered Notes ............................................................................................................... 23
4.3.3.5 Internal Actions ...................................................................................................................... 24
4.3.3.6 Expressions ............................................................................................................................ 24
5 Introduction to Previous Snake Game ....................................................................................................... 25
5.1 Task Description ............................................................................................................................... 25
5.2 The Overall Design of the Previous Project ...................................................................................... 25
5.2.1 Control of the Program ............................................................................................................... 26
6 Introduction to the Current Project ........................................................................................................... 27
6.1 Case Study ........................................................................................................................................ 27
7 The Overall Design ...................................................................................................................................... 30
7.1 The Java Part .................................................................................................................................... 30
7.2 The Jason Part .................................................................................................................................. 32
7.3 The Environment .............................................................................................................................. 32
8 Implementation .......................................................................................................................................... 33
8.1 The Java Part .................................................................................................................................... 33
8.1.1 The Arena .................................................................................................................................... 33
8.1.2 Definition of a Snake and the AI ................................................................................................. 34
8.1.3 Driver.java ................................................................................................................................... 36
8.2 The Jason Part .................................................................................................................................. 38
8.3 The Environment .............................................................................................................................. 39
8.4 Conclusion ........................................................................................................................................ 40
9 Further Improvements ............................................................................................................................... 41
10 Conclusion .................................................................................................................................................. 42
11 References .................................................................................................................................................. 43
12 Appendix A Source Code ....................................................................................................................... 44
12.1 Arena.java ........................................................................................................................................ 44
12.2 Direction.java ................................................................................................................................... 47
12.3 SpawnPoint.java ............................................................................................................................... 48
12.4 Driver.java ........................................................................................................................................ 49
12.5 GuiArena.java ................................................................................................................................... 54
12.6 GuiConstants.java ............................................................................................................................ 57
12.7 GuiModel.java .................................................................................................................................. 58
Page 9 of 74
12.8 GuiSnake.java ................................................................................................................................... 60
12.9 GuiView.java .................................................................................................................................... 62
12.10 ModelSnake.java .............................................................................................................................. 64
12.11 Move.java ......................................................................................................................................... 70
12.12 StartSnake.java ................................................................................................................................. 71
12.13 SnakeEnvironment.java ................................................................................................................... 72
12.14 sanke.asl ........................................................................................................................................... 74
12.15 snakes.mas2j .................................................................................................................................... 74
Page 10 of 74
1 Introduction _______________________________________________________________________________________
The project is about implementing a snake game as a multi-agent system.
In this report I will first introduce the multi-agent system, and then I will briefly describe the Jason
programming language. After that I will write a section about the previous project, which the current
project is based on. Afterwards I will introduce the current project and the case study that I have used.
Then there will be two sections describing the design and implementation details of the program. At the
end I will write about further improvements followed by a conclusion.
2 Introduction to Multi-Agent System _______________________________________________________________________________________
One of the types of software is functional programs. They take inputs, and on the basis of these inputs,
produce an output. A clear example is a compiler. They are the most common types of software, where
some instructions are given to them, and they just execute these instructions slavishly.
Unlike this type, there is another type of software, which is called reactive systems. They have to interact
with their environments; therefore they are more complex than the functional programs. Examples can be
computer operating systems, online banking systems, web servers, and so on.
Agents are subsets of reactive systems, which are not only reactive systems, but they have some other
properties too that make them very complex systems. One of the properties is autonomy, which means
that agents can decide how best to solve their tasks in their environments. More about properties of agents
will come in the next section.
2.1 Properties of Agents
Agents are systems that are situated in an environment. They sense their environment, and have a list of
actions that can perform by their effectors/actuators.
Page 11 of 74
Figur 1. An agent interacting with its environment (Taken from [1, p. 3])
The above picture shows how and agent interacts with the environment, where it is situated. There are also
implemented some plans in an agent. The actions that they decide to perform in their environment are
chosen by manipulating these plans.
These environments can be physical, like robots in a room, or they can be software environments. In both
cases agents have control over their parts each, so an agent does not have control over the whole
environment.
Besides having an environment, agents should have some other properties. Agents should be:
• Autonomous
• Proactive
• Reactive
• Social
Every one of these properties is discussed in the next sections.
2.1.1 An Agent should be Autonomous
We humans can decide to do anything we want. Of course there are limits for actions we want to perform,
according to the situation of the environment we live in, and our abilities and so on, but the things that we
Page 12 of 74
can do, and we decide to do, are in our own control. Our actions are not delegated from outside. So we
humans have the highest degree of autonomy.
On the other hand there are computer programs that only perform the things that we tell them to do. An
example can be text processing software. In these kinds of software there are no or very little autonomy.
Agents have also a degree of autonomy, but neither like us humans, and nor like other functional software.
It works in this way that we delegate goals to them, and according to plans that we give them, they decide
how to achieve these goals. However the plans are also given by us, but at the end the agents are the ones
that decide how to achieve the goals in a best way.
2.1.2 An Agent should be Proactive
If there is a system that has goals to achieve, but it does not try to achieve these goals unless it is invokes
from an external source, then we can not call this system a proactive system. When we say that agents
should be proactive, we mean that they should constantly try to achieve the goals that we delegate to
them, contrary to a program implemented in Java for example, where a method has to be invoked in order
to do something. We can at most call this kind of program a passive agent.
2.1.3 An Agent should be Reactive
Reactiveness is part of our daily life, and it is a common ability that we humans have. We use this ability
without thinking about it. For example we plan to take a particular bus to come on time to our meeting that
we have in a particular time. For any reason we can not manage to catch the bus, then automatically we
find an alternative way of transport, like taking a train or taking a taxi in order to come on time to our
meeting.
Agents should have a degree of reactiveness too, i.e. they should watch the changes in the environment
and act according to the changes. If they have in their plans to execute an action to achieve a goal, but the
environment changes such that this particular action is no longer possible to execute, then the agent have
to try to find an alternative way of achieving the goal. We can say that agents should be responsive to the
changes in their environments. However implementing a system that only is responsive to the changes in
an environment is not too hard, but when it is combined with autonomy, then it gets too complex to
implement. It means it is hard to find a balance between reactive and goal-directed behaviour.
2.1.4 An Agent should be Social
The property of being social is the property that makes it possible to have a multi-agent system. A system
containing more than one agent that communicates with each other. There are also other systems that can
communicate with each other by exchanging bytes or executing some methods on one another, but agents
Page 13 of 74
should be able to communicate at the knowledge level, like exchanging their beliefs, goals and plans. They
should have the ability to cooperate in their goals when it is necessary.
2.2 Characteristic of a Multi-Agent System
Until now we have described a single agent and its properties, though the last property, social ability, is a
precondition for a multi-agent system. Anyway a multi-agent system is a system, where multiple agents are
situated in an environment. The following figure shows a structure of a multi-agent system.
Figure 2. A multi-agent system (taken from [1, p. 6])
The figure shows that there are multiple agents in an environment. They have each a particular part of the
environment to influence called sphere of influence. Agents have relationships and interactions with each
other. In their relationships they can have different roles like being peers or one of them can have
Page 14 of 74
leadership over some others. An agent doesn’t usually know about all other agents in the environment, but
instead they have organisational relationships as shown in Figure 2. Some of them have knowledge about
some others, so the communication takes place between them in a necessary amount.
We can also see that in some cases spheres of influence overlap. In such cases the system becomes
complicated. These two agents that share a part of their spheres of influence, have to take each other’s
actions into consideration while they execute some actions. [1, p. 1-6]
2.2.1 Objections against Multi-Agent Systems
There have been discussions about some objections against multi-agent systems like rising questions about
differences between multi-agent systems and some other systems. Some of the important comparisons
and differences will be mentioned shortly in the following sections.
2.2.1.1 Multi-agent systems compared to concurrent/distributed systems
It is discussed whether multi-agent system is different enough than concurrent/distributed system, such
that it is worth our while to study about them separately. Multi-agent system is a subset of concurrent
system in a sense that it is also necessary here to pay attention to mutual exclusion, deadlock and livelock,
but there are two considerable differences:
First, because of autonomy, synchronization and coordination in multi-agent systems take place at run
time, while in concurrent systems these are hardwired at design time.
Second, in concurrent systems computing elements have a shared goal and make sure that the overall task
is fulfilled correctly, but in multi-agent systems every agent is mainly concerned with its own welfare.
2.2.1.2 Multi-agent systems compared to AI
There is a close relationship between AI and multi-agent systems. In past it was common to say that multi-
agent system is a subfield of AI, but the multi-agent systems community mean the opposite.
First of all it is assumed that multi-agent systems contain all the characteristics of AI, which is learning,
planning and so on. This is not accurate to say so, because multi-agent system is mostly computer science
and software engineering. There are parts of AI-features in it, but not all of them. For example in some
cases learning is unwanted in a multi-agent system while it is a part of AI-features.
Secondly agents in multi-agent systems should have social ability, which makes them capable of
cooperating and communicating with each other. This field was not discussed in AI until about 1980.
Page 15 of 74
2.2.1.3 Multi-agent system compared to economics/game theory
Game theory is a mathematical theory. One of the founders of computer science was also one of the
founders of game theory, but later on game theory was mostly used by economists to study the interaction
between economic entities in real world. Recently game theory has a close relationship with multi-agent
systems, such that it is used to analyse multi-agent systems. The question is whether it is proper to call
multi-agent system a subfield of economic/game theory. Before answering this question we have to take
two points into account:
Firstly most of the solution concepts in game theory are descriptive. According to these descriptive
concepts they give an optimal solution without telling how to compute it. It is usually hard to compute such
solutions, but multi-agent system research makes it possible to use tools of the computer science to find
some solutions to this computational problem.
Secondly some researchers have raised questions regarding the assumptions game theory makes to come
to the conclusion.
2.2.1.4 Multi-agent systems compared to social science
Social science discusses about understanding the human societies and multi-agent systems are artificial
societies. Social scientists are therefore interested in using multi-agent systems to model human societies.
Is multi-agent system not just a subset of social science? Well, to build a multi-agent system we can use
some of the properties of human societies, but it does not mean that we can model human society
precisely in all aspects, because human societies depend on many other parameters. Besides it does not
mean that making use of human societies is always the best way of building multi-agent systems (game
theory can also be used). Although multi-agent systems and social sciences are different braches, they have
a lot to say to each other. Multi-agent system is a tool to model a society and social science has a large
depot of concepts that can be used to understand multi-agent systems. [2, p. 8-11]
2.3 Programming languages for multi-agent systems
We now know about properties of agents and we have an overall idea about multi-agent systems. We have
to use a language, which gives us the possibility of implementing these properties in agents and also an
environment, where agents are situated. The following requirements must be valid for the language:
• The language must have the possibility of delegating goals to the agents. It should not be exact
instructions to the agents what they should do, but instead we should communicate with them in a
higher level. It means that we should describe the goals to them, independent of the ways of
achieving these goals. To put it briefly the language should support autonomy for the agents.
Page 16 of 74
• The language should support goal-directed behaviours. When goals are delegated to agents, it
should be possible for them to try to achieve these goals in a systematic way.
• The language should be able to produce a system, which is responsive to the changes of their
environment.
• The language should support goal-directed and responsive behaviour.
• The language should support communication and cooperation at the knowledge level, i.e. beliefs,
goals and plans.
AgentSpeak and Jason have these requirements and it is also this language combined with Java that is used
in this project. [1, p. 6-7]
3 The BDI model _______________________________________________________________________________________
The BDI model, which stands for belief-desire-intention model, is based on a model of human behaviour
developed by philosophers. The main source of this model is practical reasoning in human behaviours, and
it talks about the role of intentions in practical reasoning. The AgentSpeak architecture is based on BDI
agent model. We will briefly mention it in the following sections.
The central idea in BDI model is computer programs having mental state. By this we mean computer
systems having belief, desire and intention. How to distinguish between these three keywords, will be
discussed bellow:
Beliefs are information that the agent has about their environment and other agents. This information
however can be out of date or inaccurate. As an example for beliefs we can mention that in a system the
variable reactor32Temp holds the information about the temperature of reactor 32. So this reactor 32 is a
part of the environment in the system, and the variable is a kind of belief in the system. This example is
similar to the principle of beliefs in AgentSpeak.
Desires are all the wanted states that an agent has the possibility to achieve. It is notable to remember that
having a desire does not mean that an agent would 100% perform it, and it is also possible that an agent
have desires that are not compatible with other agents. Desires are actually options for the agents.
Page 17 of 74
Intentions are the wanted states that an agent has already chosen to achieve. When a goal is delegated to
an agent, it tries to achieve the goal by choosing the appropriate option. This chosen option is actually the
intention of the agent and then the agent has to perform some actions in order to achieve this intention.
3.1 Practical Reasoning
Now we know that belief, desire and intention are the main structure of AgentSpeak. The question is how
to use these concepts in order to produce an output? In other words how an agent should achieve the
chosen actions(/intentions)? Here practical reasoning comes to play. Practical reasoning is a process of
finding out what to do.
Practical reasoning is a matter weighing conflicting considerations for and against cometing
options, where the relevant considerations are provided by what the agent desires /value/
cares about and what the agent believes. (Quoted from [1, p. 17])
Human practical reasoning contains of two main parts: the first one is deliberation, which means trying to
achieve our intentions. The second one is means-ends reasoning, which means deciding how to achieve our
intentions.
3.1.1 Deliberation and Intentions
Deliberation, as mentioned before, is a process, which makes the agent to try to achieve the intentions.
Intention is a very important keyword in agents, so I will try to describe it a bit more.
First, when we have an intention, then we will naturally try to perform some actions that lead us to our
intention. If we have the intention of writing a book, then we have to provide pen and paper for ourselves,
set at a desk and try to think about how to start writing. But if we just go around with some friends for
shopping and so on, then we can say that we did not have the intention of writing a book at all.
The second property of intention is that it makes us keeping on trying to achieve it. If we simply drop the
intention without any reason, then we can again say that we did not actually have any intentions at the
beginning. If we have the intention of becoming a doctor, then we have to work towards achieving this
intention. However it is notable to mention that if reason of having a specific intention does not exist
anymore, then it is reasonable to drop the whole intention. For example I had the intention of becoming a
doctor, because I thought I would get an easy life, but then I find out that it is not the case. Now it is
rational to drop the intention of becoming a doctor. But if we fail to achieve an intention and we still persist
to achieve it, then it is rational to try again.
Page 18 of 74
The third property of intention is that it will set a limit to our future practical reasoning. It means that when
we decide to have a specific intention, then we will not perform some other actions that conflict with our
intention. We can also put it in this way that when an agent have some intentions, then other conflicting
intentions are not reasonable for the agent to try to achieve.
Finally intentions have a close relationship with the future beliefs, such that when we have intention we
belief that it is possible to achieve this intention, and we will eventually succeed in achieving it. It is also
possible to belief that we will fail in achieving our intention.
3.1.2 Means-Ends Reasoning
Means-ends reasoning talks about deciding how to achieve the intentions. It is done by performing some
actions in the environment. In the AI community means-ends reasoning is known as planning. In theory it
occurs in this way that the planning algorithm takes some input. These inputs are:
goals or intentions of the agent,
beliefs of the agent about its environment
and a list of actions that the agent can perform.
Then the planning algorithm produces an output, which is a plan. This plan contains the states that the
agent should go through in order to achieve its intentions. But making such a planning algorithm is very
costly, and even in some cases can get harder than NP-hard problems.
An alternative way is a simple idea that is quite useable in practice. We simply implement some plans for
the agent at design time, and the agent follows them at run time. Instead of collecting actions for the agent
we collect plans for them. It has appeared to be quite practical and it is this procedure that is used in
AgentSpeak. [1, p. 15-19]
Page 19 of 74
4 Jason – An Extension of AgentSpeak _______________________________________________________________________________________
I will write in this section shortly about the Jason language and the important parts of its syntax. Jason is an
extension of AgentSpeak. It has basically three main parts, which are beliefs, goals and plans.
4.1 Beliefs
Agents have a belief base, where its beliefs are stored. Beliefs are a collection of literals that are
represented by predicates. For example teacher(bob) is a predicate saying that Bob is a teacher. The term
bob refers to the individual Bob. Or another predicate can be likes(bob,swimming). This predicate says
that Bob likes swimming. So these kinds of predicates or their negations are stored in the agent’s belief
base. (I will write about negation later.) So in this example the agent believes that this fact (Bob likes
swimming) is true.
4.1.1 Annotation
There are some terms that can be associated with a particular belief. These are called annotations that
come after beliefs and are surrounded with brackets. An example can be busy(bob)[expires(summer)]. It
says that Bob is busy just until summer, or in other words the agent believes that Bob is busy, and this
busyness is no longer believed when summer starts.
There are other annotations too. One of the most important annotations is source annotation. It indicates
the source of a specific belief that an agent have. There are three sources an agent can get information
from.
1. Perception: An agent gains a specific belief by sensing its environment, so the source is actually
itself, but as it gets this information by observing its surroundings, the source is called percept. The
example shape(item1,square)[source(percept)] says that the agent believes that the shape of
a specific item that item1 refers to, is square, and the agent has got this belief by perception.
2. Communication between agents: Sometimes an agent gives some information to another agent in
a multi agent system. So the source of the information this agent has, is the other agent. For
instance likes(bob,swimming)[source(bob)] means that our agent believes that agent Bob likes
swimming, and this information is sent to our agent from Bob itself.
Page 20 of 74
3. Remembered notes: An agent adds a belief in its belief base while executing a plan. This belief can
be information about some action it has done, some interpreted action that needs to be resumed
or a promise it has made. It reminds itself about this information, so the source is itself. For
example we want an agent to remember when the room was vacuumed. In the plan body we add
+vacuumed(room,date(2009,12,20)). In the belief base of the agent this belief will be added:
vacuumed(room,date(2009,12,20))[source(self)].
4.1.2 Negation
I mentioned previously that a predicate or its negation is stored in an agent’s belief base as a belief. This
negation that we are talking about is strong negation. There are two kinds of negations, one is called
default negation denoted with the operator “not” (I will discuss about it later). The other one, which is
denoted by operator “~”, is called strong negation. When it is used with a predicate, the agent believes that
the predicate is false. For instance ~shape(item1,triangular) means that the agent believes that it is not
true that the shape of item1 is triangular.
4.1.3 Rules
Rules are used to conclude something new on the basis of something that is known or exists in the believe
base. A rule has two parts separated by the operator “:-”. The part at the right of this operator is called the
body of the rule. The body of a rule is very similar to the context of a plan; more about plan will come later.
An example of a rule is:
likely_shape(Itm,Shp)
:- colour(Itm,Shp)[source(Src)] & (Src == self | Src == bob).
Here Itm is a variable that can be any item and Shp is a variable that can be any shape. This rule says that
the most likely shape Shp an item Itm can have is either the shape that the agent has in its belief base as a
remembered note or the shape the agent has received from agent bob. So the body of the rule contains the
belief that the agent has in its belief base, and on the basis of this the agent gets another belief about the
likelihood of the shape of a specific item.
4.2 Goals
When an agent has a goal, it means that the agent wants to make some changes in its environment
according to the goal it has in order to achieve that goal.
There are two kinds of goals an agent can have, achievement goals denoted by the character “!” and test
goals denoted by the character “?”. Achievement goals are, as mentioned before, those goals that an agent
wants to achieve. For example the goal !vacuum(room) means that the agent has the goal that it should
Page 21 of 74
vacuum the room, so at this time the agent does not believe that the room is vacuumed, in other words the
agent does not believe that vacuum(room) is true. This goal is first achieved by executing actions that are
given as plans, which I will write about in the next section.
Test goals are used to retrieve and update information, which is in an agent’s belief base. For instance
?account_balance(AB) retrieves the information about what is the current balance of the bank account,
and at the same time it updates the variable AB according to the current information about the account.
4.3 Plans
Plans are the biggest part of Jason. Plans consist of three parts, which are the triggering event, the context
and the body. Here we can see the structure of a plan and the operators that separates the three parts of a
plan:
triggering_event : context <- body
I will describe every one of these parts shortly.
4.3.1 Triggering Event – The first part of a plan
Triggering event in a plan is the part, where changes in beliefs and goals occur. As mentioned before there
are two kinds of goals, achievement goals and test goals. So the triggering event deals with adding and
deleting beliefs and goals. The following table shows the six possible cases:
Notation Name
+l Belief addition
-l Belief deletion
+!l Achievement goal addition
-!l Achievement goal deletion
+?l Test goal addition
-?l Test goal deletion
(p. 44 in [1])
Belief addition and deletion is when a belief has to be added to or deleted from the agent’s belief base
according to the perceptions it gets by sensing its environment. Goal additions occur when a plan is
executed, or due to communication between agents.
Sometimes a plan for achieving a goal fails, and then the whole plan is dropped. In order to handle this plan
failure and prevent the plan to be dropped, the goal deletion is used. So one of the examples to handle this
Page 22 of 74
plan failure, is to tell the agent inside the goal deletion plan to retry the goal achievement. With regards to
test goals it is used to retrieve information from the agent’s belief base as mentioned earlier.
4.3.2 The Context of a Plan
The context of a plan is used to check whether a plan is capable of being executed. In the context of a plan
literals and logical expressions are used. The operators &, |, > and < can be used. Another operator, which
denotes default negation, is “not”. This can be used combined with strong negation:
Syntax Meaning
l The agent believes that l is true.
~l The agent believes that l is false.
not l The agent does not believe that l is true.
not ~l The agent does not believe that l is false.
(p. 46 in [1])
The operator not is used when the agent does not know anything about a literal. The context consists of
conjunction of the syntaxes in the above table.
An example of a plan context can be:
+!buy(Something)
: not ~legal(Something) & price(Something,P) & account_balance(B) & B > P
<- ...
The agent has to achieve the goal of buying something, but first it should go through the context. The thing
it should buy must not be illegal. Its price is P, and the bank account balance is B, such that B must not be
greater than P. When the context is valid the plan goes further to the body in order to achieve the goal.
4.3.3 The Body of a Plan
When the context of a plan is true, the body of the plan is executed. The body contains a sequence of
formulae that are separated by the symbol “;” and at the end the body is ended with “.”. The formulae
that come in the plan body are of six types that I will mention them here briefly.
4.3.3.1 Actions
One of these formulae is called “action”. This formula is defined in the body plan and is taken into action in
the environment. In the environment class, inside the method that executes actions, some actions are
performed in order to fulfil this formula. That is how the formula “action” is executed. For example in the
plan body we have the action rotate_left_arm(45) in order to tell our agent to rotate its left arm 45
Page 23 of 74
degrees. This action is performed in the environment class, such that in the method executeAction the
action of rotating left arm is executed, and according to this we would see in the GUI (if there is one) that
our agent rotates its left arm 45 degrees. But when this action is going to be executed, a feedback must be
given to the agent that whether the action was possible to be executed or not, and the plan does not go
further than this point until the agent gets the feedback. If the feedback tells the agent that the action was
not executed, then the plan fails.
4.3.3.2 Achievement Goals
The other formula that comes in the body of a plan is achievement goals. This is done when the agent
wants to achieve a new goal, and it is done by the operator “!” as mentioned before. When the body of the
plan comes to this formula, the plan is suspended until this goal is achieved, and then the plan will execute
the next formulae that it has in its body. For instance we have the sequence a1; !g; a2., which means
the formula a1 must be executed, then the goal g must be achieved before the formula a2 is executed. The
goal g would be achieved by executing another plan with a triggering event, a context and a body.
4.3.3.3 Test Goals
Another formula is test goals that are used in retrieving and updating information from the agent’s belief
base. There are some information that are updated in every cycle of actions, so it is useful to get the
newest information in a plan body and use it further in the current execution of the plan. For example the
position of an agent is updated repeatedly because the agent moves all the time in its environment, and we
need to get the current position of the agent just before executing a specific action. This information is
retrieved by a test goal that can look like this: ?position(X,Y). When for any reason the information
cannot be retrieved, then the interpreter in Jason tries to create an event that matches with a test goal
addition triggering event. It happens only if the programmer has implemented a plan that handles this
situation in this way, otherwise the test goal is considered to fail, and by that the whole plan is failed.
4.3.3.4 Remembered Notes
A remembered/mental note is another formula that can come in a body of a plan. I have written about
remembered notes earlier in the section about beliefs. I give an example here how to add a remembered
note to the belief base and how to remove it from the belief base if it is no longer necessary to have. For
instance the formula +clean(house,date(2009-12-09)) is added to the body of a plan. Then the agent
adds a remembered note in its belief base with the annotation source(self): clean(house,date(2009-12-
09))[source(self)]. But when an information is no longer necessary to have we delete it, for example,
with the formula:
-need_to_be_cleaned(house).
Page 24 of 74
We can also delete an old information and add a new one if the information is updated repeatedly. It is
done by the prefix “-+”. For instance we add -+position(X,Y) in the body of a plan in order to remove the
information about the old position of the agent and adding the information about the new position.
4.3.3.5 Internal Actions
I wrote earlier about the formula called “action”, which changes the environment and we can regard it as if
it is executed outside the agent’s mind. But the internal actions are actions that are executed within the
agent’s mind, i.e. it is executed in one reasoning cycle. There are some standard internal actions that Jason
has, but it is also possible to extend the language by defining more internal actions using Java for example.
These internal actions are used with a “.” character. For example if an internal action called find_path is
defined in a Java package called find, then we can use this internal action in the plan body in this way:
find.find_path. The standard internal actions that Jason has are started with the character “.”. For
example one of the famous standard internal actions is .send. This action is used when two agents
communicate with each other and send some information to each other.
4.3.3.6 Expressions
Each formula in the context and the body of a plan must have a Boolean value. In the context of a plan we
may need to use relational expressions that also have Boolean values. They can also be used in the body of
the plans. For example we can add in a plan context or body the expression “Y <= x*4”. As it is mentioned
before that a plan would not be executed when the context is not true, the same idea is valid for the body
of the plan. It means that the plan will fail if any formula in its body is false. [1, p. 32-55]
This was very brief description of the Jason language.
Page 25 of 74
5 Introduction to Previous Snake Game _______________________________________________________________________________________
The current project is based on a snake game that we made in a previous course called “02122 Software
Technology Project” in summer 2008. The project was implemented solely in java in a group of three
students. Some parts of the program were given beforehand, and we had to develop the program further. I
shortly introduce the project here, and afterwards I will describe the current project.
5.1 Task Description
The task was to make a classic snake game, where two or more snakes move on a plane area. The snakes
should avoid bumping against obstacles or each other. The player, who is able to control his/her snake
without bumping against obstacles in longest time, has won the game. The overall goal was the following:
• A user should be able to play against computer or other users.
• It should be possible for users to play against each other through a local network.
• The user should have different possibilities to vary the game, for example by using different
arrangements of obstacles.
• It should be possible to use the AI through a network against other groups that has made the same
project.
5.2 The Overall Design of the Previous Project
In the program there are some modules/packages that are as follows:
• snakes.ai: Controls a snake in the game via artificial intelligence.
• snakes.human: Controls a snake in the game via some keys that are pressed on a keyboard by a
human player.
• game.engine: Controls the progress of the game and implements rules of the game.
• gui.game3d/gui.game: Shows the game on the screen (contains game-controller).
• gui.options: Makes it possible for users to choose some sittings for the game (contains options-
controller).
• driver: Starts the game and distributes the control of the program (contains central controller).
Page 26 of 74
5.2.1 Control of the Program
While running the program it is one of the three controllers (game-controller, options-controller and
central controller) that has the control of the program. Figure 3 is a sequence diagram that shows the
delegation of control. The three controllers that can have the control of the program are shown. The
sequence diagram is divided in four parts marked with A, B, C and D.
• The part A shows how the options-
controller gets the control when the game
starts. It happens by showing a window,
where the user can select different options
for the game, like the speed, the number of
snakes, the map, and so on.
• The part B shows how the options-
controller tries to start the game, and this
action fails, but then the options-controller
retains the control of the program.
(Starting the game can fail for example if
there has not been selected a map, if there
are more players than allowed, or if the
selected server does not accept the
connection.)
• The part C shows how the options-
controller tries to start a game, and then
the game starts and the control is given to
the game-controller. When the game ends
the control is given back to the options-
controller.
• The part D shows how the program ends.
A complete run of the program would start with the part A, and after that parts B and C repeats an
arbitrary number of times, and at last the program ends with the part D. [6, p. 8]
Figure 3. A sequence diagram of the program control
Page 27 of 74
6 Introduction to the Current Project _______________________________________________________________________________________
The main goal is to work with multi agent system and make a snake game, where snakes are agents. In
other words the project is about working with multi agent system, where a snake game is used as case
study. As mentioned before the project is based on a previous snake game, which was implemented in
Java. I have taken the GUI from the previous snake game and made some changes in order to combine it
with Jason.
One of the main differences from the previous project is that in the current project humans cannot play
against each other or against an AI; instead there are only the snakes as multi agents that move around on
the field. I have decided to make this difference, because in a multi agent system it is not so relevant that
humans should be part of the system. It is rather about the agents that have to perform some actions in
their environment.
6.1 Case Study
The snake game is built of a field, which I will call arena from now on. On the arena some obstacles are
placed. One to four snakes can be created and placed on the arena. Snakes are seen as agents and when
they are created, they appear in the corners of the arena, and start moving. The arena is generated from a
text-file, so it is possible to choose different arenas for the game by choosing different text-files. More
details about generating arena will be mentioned later in the section about implementation.
The snakes increase in length by time during their movements, and they have to avoid bumping against
obstacles and each other. In this way they should try to live for the longest time possible. In their
movements they use an AI-algorithm, which will be mentioned in details in later sections.
Below there are two figures (Figure 4 and 5) that show the different states of the snake game.
Page 28 of 74
Figure 4. The arena and the snakes on their start positions.
Figure 4 is a screenshot of the snake game. It shows the arena, where obstacles have black colour. The four
snakes are shown at their start positions, i.e. the positions they appear when they are created. The dark
blue part is the head of the snake, and the light blue part is the body. At the start every snake is only two
units long.
Page 29 of 74
Figure 5. The arena and the snakes after some movements.
Figure 5 shows a state, where the snakes have moved on the arena for some time. We can also see that
their bodies have grown longer.
Page 30 of 74
7 The Overall Design _______________________________________________________________________________________
The program contains three main parts. One big part is the whole GUI part, which is implemented in Java.
This part contains the main frame, the construction of the arena, putting the arena on the main frame, the
definition of the snakes, the look of the snakes and placing the snakes on the arena.
The second part is implemented in Jason, where the snakes are defined as agents and the way they should
move on the arena is defined too.
The third part is a class called SnakeEnvironment.java that extends Environment from the package
jason.environment. This class is responsible to combine the GUI which is implemented in Java with the
Jason-code.
7.1 The Java Part
The Java part contains four packages called arena, driver, gui and modelsnake. There are twelve classes
distributed in these four packages. The package arena contains three classes called Arena.java,
Direction.java and SpawnPoint.java. Arena.java builds the arena by using a text file. The size of the
arena, the obstacles and the places, where the snakes should start are given in the text file.
Direction.java defines the four directions that a snake can have on the arena, and finally the class
SpawnPoint.java defines a spawn point, i.e. a place, where a snake starts its movement from when it is
created.
The package gui contains five classes, which are GuiArena.java, GuiConstants.java, GuiModel.java,
GuiSnake.java, GuiView.java. These classes are responsible for building the GUI of the program. The GUI
contains the main frame, where the arena is located. Here the arena, the obstacles and the snakes are
drawn, so it means that the whole look of the GUI is defined by these five classes.
The package modelsnake contains three classes, which are ModelSnake.java, Move.java and
StartSnake.java. The class ModelSnake.java defines a snake and its accessories like Id, name, colour,
body, next direction and last direction. The way a snake moves in one unit of time is defined in this class.
The algorithm for calculating the next direction of a snake in every movement is also defined here. The
class Move.java contains the information about the movement of a snake, which is used in updating the
movements of the snakes in the GUI. The last class StartSnake.java in this package contains the
Page 31 of 74
information about the properties of a snake just after it is created, and these start properties are then sent
to the GUI.
Finally the package driver contains a class called Driver.java. This class has a close co-operation with the
Jason environment. There are methods for creating the arena, the GUI and the snakes, and putting the
snakes on their places on the arena. It has also methods for updating the GUI after every movement of
snakes, and when a snake dies, it is removed from the list of snakes and from the GUI.
In order to make the connection between these packages clearer, I will
illustrate these relationships with some figures in the following, and the
role of the class Driver.java will also become clearer.
There are some methods inside the class Driver.java that does the
following:
1. First of all the GUI is created as an empty frame that only has a white
background and is ready for the elements to be put in it.
(see Figure 6)
2. Then the arena is created from a text file. The arena contains
obstacles and spawn points for the snakes. After creation of the
arena the information is sent to the GUI, and according to the
information the GUI shows the arena with its obstacles. The arena
contains also the spawn points on its corners and are ready for the
snakes to be put on these places.
(see Figure 7)
3. Finally the snakes are created and the information about them is
sent to the GUI. Then a spawn point is given for every snake and the
snakes are shown on the arena on these spawn points. The snakes
are now ready to start their movements form their start positions.
(see Figure 8)
Figure 6
Figure 7
Figure 8
Page 32 of 74
7.2 The Jason Part
The Jason implementation contains two parts, snake.asl and snakes.mas2j. snakes.asl defines a snake
as an agent and the way a snake should find its next position after every movement. The progress of the
game is controlled from here, i.e. the program keeps running until all the snakes are dead, and the speed of
the progress of the game is also determined here.
snakes.mas2j starts the Jason project, where the environment for the agents and the number of the
agents are chosen here.
7.3 The Environment
The class SnakeEnvironment.java defines the environment, where the agents are located. Here the GUI is
created by creating an instance of Driver. The perceptions are created for the agents and then the actions
are executed according to the perceptions. These actions call methods from class Driver.java in order to
create snakes, run the progress of the game, and calculate directions of the snakes after every movement. I
mentioned in a previous section that there is a close co-operation between the environment and
Driver.java, so now this co-operation is a bit clearer. More about environment will be mentioned in later
sections, where I will describe some important details about the implementation.
Figure 9. The overall structure of the game
GUI
Arena
ModelSnake
Agents Driver Environment
Java The Environment Jason
Page 33 of 74
Figure 9 shows an overall structure of the game. We can see the three main parts of the game. Inside the
Java part Driver is the link between the model and the GUI, and the interaction between Agents and The
whole Java part is done via Environment.
8 Implementation _______________________________________________________________________________________
In this section I will describe the implementation details about the important parts of the program. I will
start describing the Java part, and then the Jason part and finally I will mention how the environment
combines these two parts of the program.
8.1 The Java Part
Here I will describe how the arena is built and how the AI-algorithm is implemented. Then I will mention
some important methods in the class Driver.java.
8.1.1 The Arena
The class Arena.java is responsible for generating the arena. It is done in the constructor of the class.
public Arena(File file) throws IOException {
if (file == null)
throw new NullPointerException("parameter file is null");
BufferedReader map = new BufferedReader(new FileReader(file));
StringTokenizer tokens = new StringTokenizer(map.readLine(), ",\t");
tokens.nextToken(); // name
int width = Integer.parseInt(tokens.nextToken()); // width
int height = Integer.parseInt(tokens.nextToken()); // height
...
As we can see in the above code the constructor takes a text file as an input. The text file is read line by
line. The first line of the text file contains the name, the width and the height of the arena:
name, 30, 30
So it means that the width and the height of the arena are initialized as being 30 x 30.
In the following way the other lines of the text file is read:
...
ArrayList<Point> marks = new ArrayList<Point>();
for (int i = 0; i < height; i++) {
String row = map.readLine();
if (row == null) {
Page 34 of 74
break;
}
// Defines obstacles and spawn points
for (int j = 0; j < width && j < row.length(); j++) {
switch (row.charAt(j)) {
case '#': marks.add(new Point(j, i)); break;
case '^': spawnpoints.add(new SpawnPoint(new Point(j, i), Direction.NORTH));
break;
case '>': spawnpoints.add(new SpawnPoint(new Point(j, i), Direction.EAST));
break;
case '<': spawnpoints.add(new SpawnPoint(new Point(j, i), Direction.WEST));
break;
case 'v': spawnpoints.add(new SpawnPoint(new Point(j, i), Direction.SOUTH));
break;
}
}
}
size = new Point(width, height);
obstacles = marks;
}
As we see in the above code an ArrayList of points is created in order to save the obstacles in it. Then
every line of the text file is read and saved in a variable called row. Afterwards every row is checked. If the
character “#” is in a row, it is understood as being an obstacle and the characters “^”, “>”, “<” and “v” are
understood as being spawn points for the snakes with the given directions. The obstacles are saved in the
ArrayList marks, and the spawn points are saved in the ArrayList of spawnpoints, which is initialized as
a field in Arena.java.
Arena.java has some other methods too. There are methods for getting the size of the arena, getting
obstacles, getting spawn points and checking whether a point on the arena is an obstacle. There is also a
method called public Point pointInDirection(Point point, int direction). This method returns
the neighbour of a point, which is located in the given direction of the point.
8.1.2 Definition of a Snake and the AI
In the class ModelSnake.java a snake is defined, and the AI-algorithm is implemented here too. The
constructor of this class takes some parameters that are relevant for a snake. The signature of the
constructor is:
public ModelSnake(SpawnPoint spawnPoint, Arena arena, int id, String name,
Color color, int arenaSizex, int arenaSizey)
The parameters are a spawn point for the snake, the arena, an id for the snake, a name for the snake, a
colour for the snake and the size of the arena. In this constructor the body of a snake is defined as a linked
list of points. The head of a snake is added to the start of this list and the tail is added to the last of this list.
Page 35 of 74
Another important method in this class is public void makeMove(). This method defines a single
movement of a snake. This movement is done by giving the snake a new head and clipping its tail:
...
int tailsToCut = seq % 10 == 0 ? 0 : 1;
// Moves the head
Point newHead = arena.pointInDirection(getHead(), nextDirection);
body.addLast(newHead);
// Clips the tail
for(int i = 0; i < tailsToCut; i++) {
body.removeFirst();
}
...
The above code is part of the method makeMove(). The direction of the new head is determined by the
variable nextDirection, which is calculated by another method called calculateDirection(int
direction) that we will talk about later. An integer variable called seq is initialized to 1, and at the end of
this method this variable is increased by 1. Then the variable tailsToCut is used in clipping the tail and it is
defined by using seq, such that tailsToCut increases by 1 every time seq is increased by 10. It is done by
using the operator modulus. In this way the snake grows in length after every ten movements.
The method calculateDirection(int direction) contains the algorithm for the AI. It calculates the
direction of a snake in every movement. A snake can see four steps in every direction, and it checks the
“cost” of every point that is in its direction. Every point has a “cost”, it means that if the point is occupied,
the cost of the point is 4; if one neighbour of a point is occupied, the cost of the point is 1; if two
neighbours of a point are occupied, the cost of the point is 2, and so on. If all four neighbours of a point is
free, then the cost of the point is 0. So every point can have a cost from 0 to 4. Now the cost of a direction
is the sum of all the costs of the points in this direction. A snake chooses a direction with the minimum
cost. The following illustration shows this calculation:
Figure 10
Page 36 of 74
In Figure 10 the green points are the points that a snake can see in four steps if it should take its first step
towards north, i.e. up. In the same way it checks all the points in four steps if it should take its first step
towards east. These points are shown in Figure 11:
Figure 11
In this way it checks the costs of all the points in its four steps if it takes its first step towards one of the
directions north, east, south and west. The costs of all these points in every of these four directions are
added up, so we get an array of four numbers. The snake takes its next direction towards that direction
which has the minimum cost. (For more details see The section “Appendix” � “ModelSnake.java” )
8.1.3 Driver.java
The class Driver.java contains methods that are called in the environment when executing actions. First
of all I will describe what happens in the constructor of this class:
...
File map = new File("src/map.txt");
arena = new Arena(map);
freeSpawnPoints = arena.getSpawnPoints();
model = new GuiModel();
view = new GuiView(model);
// Build UI
view.buildUI();
sendArena(arena.getSize(), arena.getObstacles());
...
As we can see in the above code the map for the arena is taken from a text file. Then the arena is created
by using this map. The field freeSpawnPoints is an array of points, and it is set to the spawn points that are
in the arena. The model and the view of the game is created, and then the method buildUI(), which is in
the class GuiView.java, is called in order to build the frame. Finally the arena with its obstacles is sent to
the GUI by calling the private method sendArena(…), which is inside the Driver.java.
Page 37 of 74
The method createSnake(int id, String name, Color color) creates a snake. The body of this
method is as follows:
int index = freeSpawnPoints.size()-1;
SpawnPoint spawnPoint = freeSpawnPoints.remove(index);
ModelSnake snake = new ModelSnake(spawnPoint, arena, id, name,
color, arena.getSize().x, arena.getSize().y);
snakes.add(snake);
sendStartSnake(snake);
view.arena.repaint();
Every time a snake is created, a free spawn point is assigned to it, and this spawn point is then removed
from the array list of the free spawn points. The snake is added to the array list of snakes, which is
initialized as a field. Then the information about this snake is sent to the GUI by calling the private method
sendStartSnake(snake), so the GUI can show the snake according to this information. In order to show
the snake on the GUI, it is updated by calling the method repaint().
Another method called updateGame(int id) calls three other private methods:
public void updateGame(int id){
updateGameInfo(getLastMoves(id));
removeDeadSnakes();
removeDeadGuiSnakes();
}
The private method updateGameInfo(…) updates the GUI according to the movements that are calculated
for the snakes. The private method removeDeadSnakes() removes a snake form the array list of snakes
when it dies, i.e. its head is pumped against an obstacle, another snake or itself, and the private method
removeDeadGuiSnakes() removes this dead snake from the GUI.
Then we have the method calculateDir(int id) that calculates the direction of a snake by taking its id
as a parameter. It calculates the direction of the snake by calling the method calculateDirection(…)
from the class ModelSnake.java.
The last method that I want to describe here is move(int id). The body of this method looks like this:
for(ModelSnake es : snakes){
if(es.snakeId == id){
es.makeMove();
}
}
It takes the id of a snake, and It calls the method makeMove() from the class ModelSnake.java to perform a
movement for the snake with the given id.
Page 38 of 74
8.2 The Jason Part
The file snake.asl contains the code for a snake-agent. It has an initial goal and some plans. The initial goal
is to create a snake, and it is denoted by !create.
The plans define actions for creating and movement of snakes and calculating the direction of a snake in
every movement. The first plan @c is for achieving the goal of creating a snake:
@cr
+!create <- createSnake;
!move.
The body of this plan contains two formulae, namely an action createSnake and an achievement goal
!move. The action is executed in the environment and this new goal of moving a snake is achieved in
another plan with the triggering event +!move. The body of this plan contains three formulae:
@m
+!move <- .wait(300);
!calc_direction;
!move.
The internal action .wait(300) is used to wait a number of milliseconds, which is 300 in this case, and then
the achievement goal !calc_direction calculates the direction of the snake for the next movement.
Finally the goal of movement, !move, is added again.
There are four relevant plans for calculating the direction of a snake. In the contexts of these plans it is
determined whether a snake should move upwards, to the left, downwards or to the right. And the body of
every of these plans contains an action for the direction of the snake according to the context of the plan. I
mention the first one of these plans @cal1 as an example:
@cal1
+!calc_direction : upIsFree
<- up.
The context of this plan is a literal that checks whether the direction upwards is free. This check is done in
the environment. The body contains the action up, which moves the snake one step forward towards the
direction that is upwards. The structure of the three other plans @cal2, @cal3 and @cal4 for calculating the
direction is the same as @cal1. They calculate the other three directions, namely to the right, downwards
and to the left.
Page 39 of 74
8.3 The Environment
The class SnakeEnvironment.java extends Environment. This class defines the environment for the
agents. First of all the constant terms, which are used for perception, are instantiated. Then in the method
init(…) the GUI is created by making a new instance of Driver.
Besides this there are two important methods in this class that are createPercepts(int agId, String
agName) and executeAction(String agName, Structure action). The method for creating percepts
takes the agent id and the agent name as parameters. In this method the direction of a snake in its next
step is checked, and according to this direction the relevant percept for this snake is added. As an example I
mention one of the additions of the percepts:
...
for (ModelSnake ms : driver.snakes){
if (ms.snakeId == agId && ms.calculateDirection(ms.nextDirection) == Direction.NORTH){
driver.calculateDir(ms.snakeId);
addPercept(agName, lupFree);
}
...
}
As we can see in the above code excerpt, the direction of a snake with a specific id is checked by calling the
method calculateDirection from the class ModelSnake.java. If the snake has the direction north, i.e.
upwards, then the direction for this snake is calculated and finally the relevant percept is added for the
snake by using its name. According to this percept the action of moving upwards is executed in the method
executeAction(…). In the same way the other directions for a snake is checked, and the relevant percepts
are added.
In the method executeAction(String agName, Structure action), the action of creating a snake and
its movements towards one of the four directions is executed for every snake agent. The snakes are
identified by their ids. As an example I mention one of the executions of the actions:
...
else if (action.getFunctor().equals("up")) {
driver.updateGame(agId);
driver.move(agId);
logger.info("snake: "+agId+" executing: "+action);
}
...
In the above code it shows that if the action is “up” according to the percept, then the methods for
updating the GUI and moving the snake to next step is called from the class Driver.java. In the same way
the other actions are executed according to the corresponding percept.
Page 40 of 74
At the end of the method executeAction(…) the method createPercepts(agId, agName) is called in
order to create new percepts for the specific snake. It is worth mention that at the start of the body of the
method createPercepts(…) the method clearPercepts(agName) is called in order to clear the given
agents percept and then add a new one.
There is a private method called killTheAgent(int agId, String agName) that is called at the end of
the method executeAction(…). This method checks whether a snake is dead in the GUI-code, and if it is
the case, then the agent snake is also killed, so it is removed from the list of agents.
One of the errors that I met was a plan failure that seemed to be unexpected. When I ran the program,
after some movements one of the snakes suddenly stopped moving, and in the MAS Console a plan failure
for calculating direction was reported. After reading the chapter about environments in the book [1, p. 102-
114], I found out that when we add a percept for a specific snake agent, we have to call the method
addPercept(…) with the parameter that refers to agent’s name. For example when adding the percept of
moving upwards for a snake with a specific name, we call the method in this way: addPercept(agName,
lupFree). So the problem was that I called the method only with one parameter, which was the literal
lupFree.
8.4 Conclusion
In the section about java part I mentioned how the calculation of direction of a snake is done. I have made
this calculation different from the calculation that is made in the previous snake game. The idea is almost
the same, but here I have made it simpler. First In the previous snake game it was possible to choose how
many steps forward the snake should predict in different directions. It was done by giving a value to a
variable called DEPTH. Secondly in the previous snake game a snake saw the other snakes’ predicted paths
as obstacles. So the plan was to implement the AI in Jason, and because the previous AI was too
complicated, I made it simpler. Then I found out that even when I make it simpler it is still complicated
enough to implement it in Jason. Many for-loops and ArrayLists are used. So my conclusion is that this
kind of AI is worth the effort to implement in Java rather than Jason.
In the previous project the AI-snakes with their movements are seen as one whole system. The progress of
the game is run by creating an instance of Ticker. In every “tick” all the snakes move one step forward. Here
in current project every snake is seen as an agent, and their movements are performed parallel. For further
improvements like giving every snake some specific tasks or perform communication between snakes, it is
optimum to implement the program as a multi-agent system. In the current state of the program I still
prefer the Jason-approach rather than java-approach, because the program acts the same as if it was
Page 41 of 74
implemented solely in java, and also the programmer has the freedom of using the possibilities of multi-
agent system for further improvements. Moreover the executions of agents’ actions are multi-threaded,
which is more efficient than the java implementation, where the whole system is run in a single thread.
9 Further Improvements _______________________________________________________________________________________
As mentioned in earlier sections one of the most important properties of agents is social ability, i.e.
communication between agents. This project can be improved such that this property is added to the
agents. For example while snakes are moving on the arena, some apples can appear on the arena on
random places. These apples can be shown as red points. So when a snake meets these apples, it should
eat it until it is full. A snake can become full after eating three apples for example. The point is here that
when a snake meets an apple while it is full, it should tell the other snakes about the position of this apple.
It can be done by an internal action that can look like this: .broadcast(tell,apple(X,Y)). Then the
snakes that are still hungry move towards that apple. The one that gets the apple first, eats it. So the
system could continue in this way.
Another example: Instead of these apples there could be a number of mice as other agents on the arena.
Then the snakes could cooperate with each other in order to catch these mice.
In either example there are possibilities for communication between agents. Any other example can be
taken into consideration, but the point is that there is place for more improvements, especially in the field
of communication. It is worth mentioning that the communication between agents can also be established
via a network. It can be done by having a server side to which clients can be connected. So there is also
place for improvement of the program by adding the possibility of network communication.
Page 42 of 74
10 Conclusion _______________________________________________________________________________________
My primary goal in this project was to make a multi-agent system, where a snake game is used as case
study. If I mention the goal in a bit more details, the goal was to take the previous snake game and turn it
into a multi-agent system, such that the most possible parts, which are worth the effort, would be
implemented in Jason. I worked with the java part a lot in order to separate the GUI from the previous
project and adjust it to make it work with Jason. Then I used a lot of time on implementing the AI-
algorithm. So the project ended with having most of the code in java, and a little part in Jason. My plan was
to have more features for the game (some of the things I mentioned in the section “9 Further
Improvements”), but I didn’t have the time to add these features.
I should mention that the AI is not the best of all; it is a simple AI, so during the functional test of the
program I met a situation, where the snake pumped its head against an obstacle while it had a free way
forward. It happens in some situations, where other ways are surrounded with other snake bodies, but
there is a little narrow way out. The snake cannot find that way; instead it bumps its head against an
obstacle or against its own body or another snake. That is because the AI cannot see more than four steps
in every direction.
I described about what the program contains and what it can in the section “6.1 Case Study”. I have tested
the program many times functionally, i.e. by running it and looking at the GUI and the printouts in the MAS
Console. The program runs as expected, I mean that the agents perform the right actions according to the
perceptions they get. I have naturally found the errors by the functional tests, and corrected them. As an
example I have mentioned one of the errors at the end of the section “8.3 The Environment”. So the
current state of the program is the same as it is described in section 6.1.
Page 43 of 74
11 References _______________________________________________________________________________________
[1] Rafael H. Bordini, Jomi Fred Hübner and Michael Wooldridge, 2007, programming multi-agent systems
in AgentSpeak using Jason pp. 1-114 and 179-193, John Wiley & Sons, Ltd.
[2] Michael Wooldridge, An Introduction to Multiagent Systems pp. 1-14 and 65-88, John Wiley & Sons,
Ltd.
[3] http://jason.sourceforge.net/JasonWebSite/Jason%20Home.php (16.12.2009)
[4] http://jason.sourceforge.net/mini-tutorial/getting-started/ (16.12.2009)
[5] http://jason.sourceforge.net/api/index-all.html (16.12.2009)
[6] The report “Snakes” written in the course “02122 Software Technology Project” in summer 2008.
Page 44 of 74
12 Appendix A Source Code
_______________________________________________________________________________________
Here is all the source code of the program.
12.1 Arena.java package arena;
import java.awt.Point;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
/**
* @author hhl
*
* This class generates the arena.
*
*/
public class Arena {
/**
* A point with two coordinates that is used to give the size of the arena
*/
private Point size;
/**
* A list of obstacles
*/
private ArrayList<Point> obstacles;
/**
* A list of spawn points
*/
private List<SpawnPoint> spawnpoints = new ArrayList<SpawnPoint>();
/**
* Reads a text file that the arena is generated from.
* Defines the height and the width of the arena.
* Defines which symbols in the text file should be converted to obstacles
* and spawn points.
*
* @param file The text file that should be read
* @throws IOExeption If the chosen map cannot be read
*/
public Arena(File file) throws IOException {
if (file == null)
throw new NullPointerException("parameter file is null");
Page 45 of 74
BufferedReader map = new BufferedReader(new FileReader(file));
StringTokenizer tokens = new StringTokenizer(map.readLine(), ",\t");
tokens.nextToken(); // name
int width = Integer.parseInt(tokens.nextToken());
int height = Integer.parseInt(tokens.nextToken());
ArrayList<Point> marks = new ArrayList<Point>();
for (int i = 0; i < height; i++) {
String row = map.readLine();
if (row == null) {
break;
}
//Defines Obstacles and spawn points
for (int j = 0; j < width && j < row.length(); j++) {
switch (row.charAt(j)) {
case '#': marks.add(new Point(j, i)); break;
case '^': spawnpoints.add(new SpawnPoint(new Point(j, i), Direction.NORTH));
break;
case '>': spawnpoints.add(new SpawnPoint(new Point(j, i), Direction.EAST));
break;
case '<': spawnpoints.add(new SpawnPoint(new Point(j, i), Direction.WEST));
break;
case 'v': spawnpoints.add(new SpawnPoint(new Point(j, i), Direction.SOUTH));
break;
}
}
}
size = new Point(width, height);
obstacles = marks;
}
/**
* Returns the size of the arena.
*
* @return the coordinates for the height and the width of the arena
*/
public Point getSize() {
return new Point(size);
}
/**
* Returns a list of obstacles
*
* @return a list of coordinates for the obstacles
*/
public ArrayList<Point> getObstacles() {
ArrayList<Point> res = new ArrayList<Point>();
for (Point p : obstacles)
res.add(new Point(p));
return res;
}
/**
* Returns a list of spawn points
*
* @return a list of coordinates for the spawn points
*/
public List<SpawnPoint> getSpawnPoints() {
return new ArrayList<SpawnPoint>(spawnpoints);
Page 46 of 74
}
/**
* Defines the four directions for a point on the arena: right (east),
* left (west), up (north) and down (south). And returns the neighbor point
* that is in the direction the point had.
*
* @param point A point on the arena
* @param direction One of the four directions on the arena
* @return the neighbor point for a point that is in one the four directions.
*/
public Point pointInDirection(Point point, int direction) {
Point p = new Point(point);
switch(direction) {
case Direction.NORTH:
if (p.y == 0) p.y = size.y - 1;
else p.y--;
break;
case Direction.EAST:
if (p.x == size.x - 1) p.x = 0;
else p.x++;
break;
case Direction.SOUTH:
if (p.y == size.y - 1) p.y = 0;
else p.y++;
break;
case Direction.WEST:
if (p.x == 0) p.x = size.x - 1;
else p.x--;
break;
}
return p;
}
/**
* Checks whether a point is an obstacle or not
*
* @param point A point on the arena
* @return true if a point is an obstacle, otherwise false
*/
public boolean isObstacle(Point point) {
for(Point o : obstacles) {
if(point.equals(o))
return true;
}
return false;
}
}
Page 47 of 74
12.2 Direction.java package arena;
/**
* Defines the directions that a snake can move in on the arena
*
*/
public class Direction {
/**
* The movement upward (north)
*/
public static final int NORTH = 0;
/**
* The movement to the right (east)
*/
public static final int EAST = 1;
/**
* The movement downward (south)
*/
public static final int SOUTH = 2;
/**
* The movement to the left (west)
*/
public static final int WEST = 3;
/**
* Returns the opposite direction
*
* @param direction
* @return returns the opposite direction
*/
public static int getOppositeDirection(int direction) {
return (direction + 2) % 4;
}
}
Page 48 of 74
12.3 SpawnPoint.java package arena;
import java.awt.Point;
/**
* Defines a spawn point
*
*/
public class SpawnPoint {
/**
* A point with coordinates, where a spawn point in placed
*/
public final Point head;
/**
* A direction in a spawn point that indicates the direction of a snake that
* is assigned to this spawn point
*/
public final int direction;
/**
* Generates a spawn point
*
* @param head
* @param direction
*/
SpawnPoint(Point head, int direction) {
this.head = head;
this.direction = direction;
}
}
Page 49 of 74
12.4 Driver.java package driver;
import gui.GuiModel;
import gui.GuiSnake;
import gui.GuiView;
import java.awt.Color;
import java.awt.Point;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JOptionPane;
import modelsnake.ModelSnake;
import modelsnake.Move;
import modelsnake.StartSnake;
import arena.Arena;
import arena.SpawnPoint;
/**
* This class contains method-calls for creating the GUI and the snakes, and
* the progress of the game.
*/
public class Driver
{
/**
* A list of spawn points
*/
private static List<SpawnPoint> freeSpawnPoints;
/**
* The arena
*/
public static Arena arena;
/**
* The model of the GUI
*/
private static GuiModel model;
/**
* The view of the GUI
*/
private static GuiView view;
/**
* A list of snakes
*/
public static ArrayList<ModelSnake> snakes = new ArrayList<ModelSnake>();
/**
* Creates a map for the GUI, initializes the arena, free spawn points,
* the model of the GUI and the view of the GUI.
* Builds the GUI, and sends the information about arena to the GUI.
*/
public Driver(){
try {
File map = new File("src/map.txt");
Page 50 of 74
arena = new Arena(map);
freeSpawnPoints = arena.getSpawnPoints();
model = new GuiModel();
view = new GuiView(model);
// Build UI
view.buildUI();
sendArena(arena.getSize(), arena.getObstacles());
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "Incorrect map file", "Error",
JOptionPane.ERROR_MESSAGE);
}
}
/**
* Creates a snake and sends its start information to the GUI.
*
* @param id The id of a snake
* @param name The name of a snake
* @param color The color of a snake
*/
public static void createSnake(int id, String name, Color color) {
int index = freeSpawnPoints.size()-1;
SpawnPoint spawnPoint = freeSpawnPoints.remove(index);
// Create the snake
ModelSnake snake = new ModelSnake(spawnPoint, arena, id, name,
color, arena.getSize().x, arena.getSize().y);
snakes.add(snake);
sendStartSnake(snake);
view.arena.repaint();
}
/**
* Updates the GUI according to the information about movements of a snake,
* removes dead snakes from the list of snakes and remove dead snakes from
* the GUI.
*
* @param id The id of a snake
*/
public void updateGame(int id){
updateGameInfo(getLastMoves(id));
removeDeadGuiSnakes();
removeDeadSnakes();
}
/**
* Creates a snake.
*/
public void createS(int id) {
createSnake(id, "AI", Color.BLUE);
}
/**
* Copies the end point of the arena and the obstacles.
*
* @param arenaSize The size of the arena
Page 51 of 74
* @param obstacles A list of obstacles
*/
private static void sendArena(Point arenaSize, ArrayList<Point> obstacles) {
// Copy end point and obstacles
model.arenaEndPoint = new Point(arenaSize.x - 1, arenaSize.y - 1);
model.obstacles = new ArrayList<Point>(obstacles);
view.arena.repaint();
}
/**
* Sends the start information about a snake to the GUI. This method is
* called in createSnake.
* Sender informationer til en klient om en slange, som er tilmeldt spillet.
* Denne metode bliver kaldt i createSnake.
*
* @param ms A snake, whose informations should be sent
*/
private static void sendStartSnake(ModelSnake ms) {
StartSnake ss = ms.getStartSnake();
snakeJoined(ss);
}
/**
* Creates the Gui snake according to the information about the model snake.
*
* @param ss The snake in its start state
*/
private static void snakeJoined(StartSnake ss) {
GuiSnake gs = new GuiSnake(ss.snakeId,ss.name,ss.color);
gs.body = new LinkedList<Point>(ss.body);
model.snakes.add(gs);
}
/**
* Moves a snake one step forward
*
* @param id The id of a snake
*/
public static void move(int id){
for(ModelSnake ms : snakes){
if(ms.snakeId == id){
ms.makeMove();
}
}
}
/**
* Calculates the direction of a snake
*
* @param id The id of a snake
*/
public static void calculateDir(int id){
for(ModelSnake ms : snakes){
if(ms.snakeId == id){
ms.nextDirection = ms.calculateDirection(ms.nextDirection);
}
}
}
Page 52 of 74
/**
* Updates the GUI.
*
* @param snakeMoves A list of movements of snakes
*/
private static void updateGameInfo(ArrayList<Move> snakeMoves) {
// Update Snakes
for(Move m : snakeMoves) {
GuiSnake gs = model.getSnakeWithId(m.snakeId);
gs.updateSnake(m.newHead, m.decrement);
model.morphSnakes();
}
view.arena.repaint();
}
/**
* Returns a list of snakes' positions in the last movement
*
* @param id The id of a snake
* @return a list of the snakes' id and coordinates of the positions of the
* snakes'head in the last movement
*/
private static ArrayList<Move> getLastMoves(int id) {
ArrayList<Move> moves = new ArrayList<Move>();
for(ModelSnake ms : snakes) {
if(ms.snakeId == id){
moves.add(ms.getLastMove());
}
}
return moves;
}
/**
* Checks whether a point is occupied by either a snake or an obstacle
*
* @param point A point on the arena
* @return true if a point is occupied by a snake, otherwise the point that
* is occupied by an obstacle is returned.
*/
public static boolean pointIsOccupied(Point point) {
// Occupied by a snake
for(ModelSnake ms : snakes) {
if (ms.isPartOfSnake(point))
return true;
}
// Occupied by an obstacle
return arena.isObstacle(point);
}
/**
* Finds and removes a dead snake by first adding it to a list of dead
* snakes and then removing all the elements of this list.
*/
public void removeDeadSnakes() {
ArrayList<ModelSnake> snakesToRemove = new ArrayList<ModelSnake>();
// Finds and removes the dead snake
for(ModelSnake ms : snakes) {
if(ms.isDying()) {
snakesToRemove.add(ms);
}
Page 53 of 74
}
snakes.removeAll(snakesToRemove);
}
/**
* Removes a dead snake from the GUI.
*/
public void removeDeadGuiSnakes(){
ArrayList<Integer> livingSnakes = new ArrayList<Integer>();
for(ModelSnake ms : snakes) {
if(!ms.isDying()){
for(GuiSnake gs : model.snakes){
if(ms.snakeId == gs.snakeId){
livingSnakes.add(gs.snakeId);
}
}
}
}
// Remove dead snakes
ArrayList<Integer> snakeIdsToRemove = new ArrayList<Integer>();
for(GuiSnake gs : model.snakes)
snakeIdsToRemove.add(gs.snakeId);
snakeIdsToRemove.removeAll(livingSnakes);
for(Integer i : snakeIdsToRemove) {
JOptionPane.showMessageDialog(null, "a snake died");
model.snakes.remove(model.getSnakeWithId(i));
}
}
}
Page 54 of 74
12.5 GuiArena.java package gui;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import javax.swing.JPanel;
/**
* This class draws the arena, snakes and obstacles.
*/
public class GuiArena extends JPanel {
private static final long serialVersionUID = -4043391564584248592L;
/**
* The model of the GUI
*/
GuiModel model;
/**
* The width and the height of the arena indicated by pixels
*/
int pxWidth, pxHeight;
int propX, propY;
/**
* Initializes the GUI model.
*
* @param model The model of the GUI
*/
public GuiArena(GuiModel model) {
this.model = model;
}
/**
* Draws the the components of the GUI.
*
* @param g The graphic instance that are used for drawing
*/
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Update available pixels to model
updateDimensions(this.getWidth(), this.getHeight());
this.setBackground(Color.WHITE);
// Arena grid
this.drawArena(g);
// Obstacles, snakes
this.drawObstacles(g);
this.drawSnakes(g);
}
/**
Page 55 of 74
* Draws the arena, i.e. the vertical and the horizontal lines on the arena.
*
* @param g The graphic instance that are used for drawing
*/
private void drawArena(Graphics g) {
// Vertical lines
g.setColor(Color.LIGHT_GRAY);
int counter = -2;
for(int i = 0; i <= this.pxWidth && counter < model.arenaEndPoint.y;
i = i + propX)
{
counter++;
g.drawLine(i, 0, i, propY*(model.arenaEndPoint.y+1));
}
// Horizontal lines
counter = -2;
for(int i = 0; i <= this.pxHeight && counter < model.arenaEndPoint.x;
i = i + propY)
{
counter++;
g.drawLine(0, i, propX*(model.arenaEndPoint.x+1), i);
}
}
/**
* Draws the snakes' bodies and heads.
*
* @param g The graphic instance that are used for drawing
*/
private void drawSnakes(Graphics g) {
// Bodies
for(GuiSnake s : model.snakes) {
for(Point p : s.body) {
g.setColor(s.bodyColor);
this.drawPoint(p,g);
}
}
//Heads
for(GuiSnake s : model.snakes) {
g.setColor(s.headColor);
this.drawPoint(s.getHead(),g);
g.setColor(Color.BLACK);
g.drawString(""+s.snakeId+" "+s.name,
s.getHead().x*propX+(propX/2),
s.getHead().y*propY+(propY/2));
}
}
/**
* Draws the obstacles on the arena.
*
* @param g The graphic instance that are used for drawing
Page 56 of 74
*/
private void drawObstacles(Graphics g) {
g.setColor(Color.BLACK);
for(Point o : model.obstacles) {
this.drawPoint(o,g);
}
}
/**
* Draws a point by giving it a fill. This method is used in drawSnakes and
* drawObstacles.
*
* @param o A point
* @param g The graphic instance that are used for drawing
*/
private void drawPoint(Point o, Graphics g) {
int sx = (o.x * propX);
int sy = (o.y * propY);
g.fillRect(sx,sy,propX,propY);
}
/**
* Updates the dimensions of the arena by enlarging the pixels.
*
* @param width The width of the arena
* @param height The height of the arena
*/
private void updateDimensions(int width, int height) {
this.pxWidth = width;
this.pxHeight = height;
this.propX = pxWidth / model.arenaEndPoint.x;
this.propY = pxHeight / model.arenaEndPoint.y;
}
}
Page 57 of 74
12.6 GuiConstants.java package gui;
/**
* This class contains the constants that are in used in the GUI.
*
*/
public class GuiConstants {
/**
* The name of the program that is shown as the title of the frame
*/
public static final String PROGRAM_NAME = "Snakes";
/**
* The width of the frame
*/
public static final int FRAME_WIDTH = 609;
/**
* The height of the frame
*/
public static final int FRAME_HEIGHT = 600;
}
Page 58 of 74
12.7 GuiModel.java package gui;
import java.awt.Point;
import java.util.ArrayList;
import arena.Direction;
/**
* The model of the GUI
*/
public class GuiModel {
/**
* The end point of the arena
*/
public Point arenaEndPoint;
/**
* A list of the GUI-snakes
*/
public ArrayList<GuiSnake> snakes;
/**
* A list of the obstacles
*/
public ArrayList<Point> obstacles;
/**
* Initializes the end point of the arena, snakes and the obstacles
*/
public GuiModel() {
this.arenaEndPoint = new Point(1,1);
this.snakes = new ArrayList<GuiSnake>();
this.obstacles = new ArrayList<Point>();
}
/**
* Returns the GUI-snake with the given Id
*
* @param snakeId The Id of a snake
* @return the GUI-snake with the given Id
*/
public GuiSnake getSnakeWithId(int snakeId) {
for(GuiSnake gs : snakes) {
if(gs.snakeId == snakeId)
return gs;
}
return null;
}
public String directionInWords(int direction) {
String r = "";
switch(direction) {
case Direction.NORTH: r = "North"; break;
Page 59 of 74
case Direction.EAST: r = "East"; break;
case Direction.SOUTH: r = "South"; break;
case Direction.WEST: r = "West"; break;
}
return r;
}
/**
* Enables a snake to go out of the edge of of the arena and enter the arena
* from the opposite site
*/
public void morphSnakes() {
for(GuiSnake s : snakes) {
if(s.getHead().x < 0)
s.getHead().x = arenaEndPoint.x;
if(s.getHead().x > arenaEndPoint.x)
s.getHead().x = 0;
if(s.getHead().y < 0)
s.getHead().y = arenaEndPoint.y;
if(s.getHead().y > arenaEndPoint.y)
s.getHead().y = 0;
}
}
}
Page 60 of 74
12.8 GuiSnake.java package gui;
import java.awt.Color;
import java.awt.Point;
import java.util.LinkedList;
/**
* Defines the GUI-snake
*/
public class GuiSnake {
/**
* The id of a snake
*/
public int snakeId;
/**
* The color of the head of a snake
*/
Color headColor = null;
/**
* The color of the body of a snake
*/
Color bodyColor = null;
/**
* The name of a snake
*/
String name;
/**
* A linked list of points that define the body of a snake
*/
public LinkedList<Point> body;
/**
* Creates a snake with its Id, name and a color
*
* @param snakeId The id of a snake
* @param name The name of a snake
* @param headColor the color of the head of a snake
*/
public GuiSnake(int snakeId, String name, Color headColor) {
this.headColor = headColor;
int r = headColor.getRed();
int g = headColor.getGreen();
int b = headColor.getBlue();
r = Math.min(0xFF, r + 0x90);
g = Math.min(0xFF, g + 0x90);
b = Math.min(0xFF, b + 0x90);
bodyColor = new Color(r, g, b);
this.snakeId = snakeId;
this.name = name;
body = new LinkedList<Point>();
}
Page 61 of 74
/**
* Moves a GUI-snake by adding a new head and removing its tail.
*
* @param newHead A point that defines a new head for the snake
* @param decrement An integer value that tells about after how many
* movements the tail should not be removed. In this way the snake
* increases in length.
*/
public void updateSnake(Point newHead, int decrement) {
// Move Head
body.addLast(newHead);
// Move tail
for(int i=0; i<decrement; i++)
body.removeFirst();
}
/**
* Returns the head of a snake
*/
public Point getHead() {
return body.getLast();
}
/**
* Returns the tail of a snake
*/
public Point getTail() {
return body.getFirst();
}
}
Page 62 of 74
12.9 GuiView.java package gui;
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* Defines the view of the game, i.e. the frame that contains the GUI of the
* game
*/
public class GuiView extends JFrame {
private static final long serialVersionUID = -5203643666282706054L;
/**
* The GUI arena
*/
public GuiArena arena;
/**
* The panel in which the arena is placed
*/
JPanel arenaPanel;
/**
* The model of the GUI
*/
GuiModel model;
/**
* Initializes the model of the GUI
*
* @param model the model of the GUI
*/
public GuiView(GuiModel model) {
this.model = model;
}
// Build the user interface
public void buildUI() {
// Configurates the frame
this.setTitle(GuiConstants.PROGRAM_NAME);
this.setLocation(30,30);
this.setSize(GuiConstants.FRAME_WIDTH,GuiConstants.FRAME_HEIGHT);
this.setLayout(new BorderLayout());
// Panels
this.createPanels();
this.setVisible(true);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
/**
Page 63 of 74
* Creates the panels
*/
private void createPanels() {
arena = new GuiArena(model);
arenaPanel = new JPanel();
arenaPanel.setLayout(new BorderLayout());
arenaPanel.add(arena, BorderLayout.CENTER);
this.add(arenaPanel, BorderLayout.CENTER);
}
}
Page 64 of 74
12.10 ModelSnake.java package modelsnake;
import java.awt.Color;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import arena.Arena;
import arena.Direction;
import arena.SpawnPoint;
import driver.Driver;
/**
* This class defines a snake and the AI
*
*/
public class ModelSnake {
/**
* A snake's Id
*/
public int snakeId;
/**
* A snake's name
*/
private final String name;
/**
* A snake's color
*/
private final Color color;
/**
* A linked list of points that defines a snake's body
*/
LinkedList<Point> body;
/**
* A direction that a snake moves in after a movement
*/
public int nextDirection;
/**
* A snake's last direction of movement
*/
private int lastDirection;
/**
* It is used to clip the tail of a snake when the snake moves forward
*/
private int lastDecrement;
/**
* The arena
Page 65 of 74
*/
final Arena arena;
/**
* A counter that is used for increasing the length of a snake
*/
int seq = 1;
/**
* Generates a snake by creating its body and head, and adding the head
* to the body.
*
* @param spawnPoint A spawn point for the snake
* @param arena The arena on which the snake moves
* @param id The id of the snake
* @param name The name of the snake
* @param color The color of the snake
* @param arenaSizex The x-coordinate of the size of the arena
* @param arenaSizey The y-coordinate of the size of the arena
*/
public ModelSnake(SpawnPoint spawnPoint, Arena arena, int id, String name,
Color color, int arenaSizex, int arenaSizey) {
this.arena = arena;
snakeId = id;
this.name = name;
this.color = color;
// Default start directions
body = new LinkedList<Point>();
// Head
Point head = spawnPoint.head;
// Adds the head to the body
body.addLast(head);
// The tail
Point tail = arena.pointInDirection(head,
Direction.getOppositeDirection(spawnPoint.direction));
// Adds the tail to the body
body.addFirst(tail);
lastDirection = spawnPoint.direction;
lastDecrement = 1;
nextDirection = lastDirection;
}
/**
* Performs a movement for a snake in a movement by moving the head and
* clipping the tail.
*/
public void makeMove() {
int tailsToCut = seq % 10 == 0 ? 0 : 1;
Page 66 of 74
// Moves the head
Point newHead = arena.pointInDirection(getHead(), nextDirection);
body.addLast(newHead);
// Clips the tail
for(int i = 0; i < tailsToCut; i++) {
body.removeFirst();
}
lastDirection = nextDirection;
lastDecrement = tailsToCut;
seq++;
}
/**
*
* @return snakeId The id of the snake
*/
public int getSnakeId(){
return snakeId;
}
public void increaseLength(){
// The tail
Point tail = new Point();
// Adds the tail to the body
body.addFirst(tail);
}
/**
* Returns a snake's position in the last movement
*
* @return a snake's id and the coordinates of the position of its head in
* the last movement.
*/
public Move getLastMove() {
Move lastMove = new Move();
lastMove.snakeId = snakeId;
lastMove.newHead = new Point(getHead());
lastMove.decrement = lastDecrement;
return lastMove;
}
/**
* Returns a snake's head
*
* @return the coordinates of the position of a snake's head
*/
Point getHead() {
return body.getLast();
}
/**
* Creates a snake, whose start information should be sent to the GUI, and
* returns it
*
* @return the information about the created snake
Page 67 of 74
*/
public StartSnake getStartSnake() {
StartSnake ss = new StartSnake();
ss.body = new LinkedList<Point>();
for (Point p : body)
ss.body.addLast(new Point(p));
ss.snakeId = snakeId;
ss.name = name;
ss.color = color;
return ss;
}
/**
* Checks whether a snake is dead
*
* @return true if a snake is dead, otherwise false.
*/
public boolean isDying() {
// Remove the head temporarily, because the snake otherwise would always
// bump against its own head.
Point head = body.removeLast();
boolean res = Driver.pointIsOccupied(head);
body.addLast(head);
return res;
}
/**
* Checks whether a point is part of a snake's body
*
* @param point A point on the arena
* @return true if the points are part of the snake's body, otherwise false.
*/
public boolean isPartOfSnake(Point point) {
for(Point b : body) {
if(point.equals(b))
return true;
}
return false;
}
/**
* Defines the direction of a snake after a movement
*
* @param direction One of the four directions on the arena
*/
void setNextDirection(int direction) {
// The snake can not move into itself (the next last element).
// If it does, then it will move towards the direction that it had
// in the last movement.
if(direction != Direction.getOppositeDirection(lastDirection)) {
nextDirection = direction;
}
}
/**
* Defines the four directions for a point on the arena: right (east),
* left (west), up (north) and down (south). And returns the neighbor point
Page 68 of 74
* that is in the direction the point had.
*
* @param point A point on the arena
* @param direction One of the four directions on the arena
* @return the neighbor point for a point that is in one the four directions.
*/
private Point pointInDirection(Point point, int direction) {
Point p = new Point(point);
switch(direction) {
case Direction.NORTH:
if (p.y == 0) p.y = arena.getSize().y - 1;
else p.y--;
break;
case Direction.EAST:
if (p.x == arena.getSize().x - 1) p.x = 0;
else p.x++;
break;
case Direction.SOUTH:
if (p.y == arena.getSize().y - 1) p.y = 0;
else p.y++;
break;
case Direction.WEST:
if (p.x == 0) p.x = arena.getSize().x - 1;
else p.x--;
break;
}
return p;
}
/**
* Calculates the direction of a snake and returns the calculated direction.
*
* @param nextDirection The calculated direction of a snake
* @return the calculated direction of a snake
*/
public int calculateDirection(int nextDirection){
int calculatedDirection = nextDirection;
int cost = 0;
ArrayList<Point> h = new ArrayList<Point>();
ArrayList<Integer> costs = new ArrayList<Integer>();
h = getHeads(getHead());
for (int i = 0; i < h.size(); i++) {
cost = 0;
if (Driver.pointIsOccupied(h.get(i))) {
cost = 4;
}
for (int dir = 0; dir < 4; dir++) {
if(Driver.pointIsOccupied(pointInDirection(h.get(i),dir))){
cost++;
}
}
costs.add(cost);
}
Page 69 of 74
int var = 0;
ArrayList<Integer> costs1 = new ArrayList<Integer>();
for (int j = 0; j < 4; j++) {
var = 0;
for (int i = 0; i < h.size()/4; i++) {
var += costs.get(i);
}
costs1.add(var);
for (int j2 = 0; j2 < h.size()/4; j2++) {
costs.remove(0);
}
}
for (int i = 0; i < 4; i++) {
if (Collections.min(costs1) == costs1.get(i)){
calculatedDirection = i;
break;
}
}
if(calculatedDirection != Direction.getOppositeDirection(lastDirection)) {
nextDirection = calculatedDirection;
}
return nextDirection;
}
/**
* This method lists the points, which are in the predicted directions of
* a snake. This method is used in calculateDirection.
*
* @param point The head of the snake
* @return a list that contains the points, which are in the predicted
* directions of a snake.
*/
private ArrayList<Point> getHeads(Point point){
ArrayList<Point> h = new ArrayList<Point>();
for (int nextDir = 0; nextDir < 4; nextDir++) {
Point p = pointInDirection(point, nextDir);
for (int i = 0; i < 4; i++) {
h.add(p);
h.add(pointInDirection(p, i));
for (int j = 0; j < 4; j++) {
Point p2 = pointInDirection(p, i);
h.add(pointInDirection(p2, j));
for (int k = 0; k < 4; k++) {
Point p3 = pointInDirection(p2, i);
h.add(pointInDirection(p3, k));
}
}
}
}
return h;
}
}
Page 70 of 74
12.11 Move.java package modelsnake;
import java.awt.Point;
import java.io.Serializable;
/**
* Information about movement of a snake.
*/
public class Move implements Serializable {
/**
* This field is required by Serializable.
*/
private static final long serialVersionUID = 6852770254778524601L;
/**
* A point that should be added to the front end of a snake.
*/
public Point newHead;
/**
* A number of point that should be removed from a snake's tail.
*/
public int decrement;
/**
* The id of the snake that should be modified
*/
public int snakeId;
/**
* For debugging.
*/
public String toString() {
return "Move[" + snakeId + "](" + newHead + "," + decrement + ")";
}
}
Page 71 of 74
12.12 StartSnake.java package modelsnake;
import java.awt.Color;
import java.awt.Point;
import java.io.Serializable;
import java.util.LinkedList;
/**
* Information about a snakes start properties.
*/
public class StartSnake implements Serializable {
/**
* This field is required by Serializable.
*/
private static final long serialVersionUID = 5469298050091108338L;
/**
* A linked list of points that form the snake. The last point is the head
* of the snake, and the first point is the tail of the snake.
*/
public LinkedList<Point> body;
/**
* The id of the snake.
*/
public int snakeId;
/**
* The name of the snake
*/
public String name;
/**
* The color of the snake.
*/
public Color color;
/**
* The direction of the snake.
*/
public int direction;
}
Page 72 of 74
12.13 SnakeEnvironment.java // Environment code for project snakes.mas2j
import jason.asSyntax.*;
import jason.environment.*;
import java.util.logging.*;
import javax.swing.UIManager;
import java.util.ArrayList;
import java.awt.Color;
import javax.swing.JOptionPane;
import modelsnake.ModelSnake;
import driver.Driver;
import arena.Direction;
public class SnakeEnvironment extends Environment {
private Logger logger =
Logger.getLogger("snakes.mas2j."+SnakeEnvironment.class.getName());
static Driver driver;
/** constant terms used for perception */
private static final Literal lupFree = ASSyntax.createLiteral("upIsFree");
private static final Literal lrightFree = ASSyntax.createLiteral("rightIsFree");
private static final Literal ldownFree = ASSyntax.createLiteral("downIsFree");
private static final Literal lleftFree = ASSyntax.createLiteral("leftIsFree");
/** Called before the MAS execution with the args informed in .mas2j */
@Override
public void init(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {}
driver = new Driver();
}
public void createPercepts(int agId, String agName){
clearPercepts(agName);
for (ModelSnake ms : driver.snakes){
if (ms.snakeId == agId && ms.calculateDirection(ms.nextDirection)
== Direction.NORTH){
driver.calculateDir(ms.snakeId);
addPercept(agName, lupFree);
}
else if (ms.snakeId == agId && ms.calculateDirection(ms.nextDirection)
== Direction.EAST){
driver.calculateDir(ms.snakeId);
addPercept(agName, lrightFree);
}
else if (ms.snakeId == agId && ms.calculateDirection(ms.nextDirection)
== Direction.SOUTH){
driver.calculateDir(ms.snakeId);
addPercept(agName, ldownFree);
// }
else if (ms.snakeId == agId && ms.calculateDirection(ms.nextDirection)
== Direction.WEST){
driver.calculateDir(ms.snakeId);
addPercept(agName, lleftFree);
}
}
}
Page 73 of 74
@Override
public synchronized boolean executeAction(String agName, Structure action) {
int agId = getAgId(agName);
if (action.getFunctor().equals("createSnake")) {
driver.createSnake(agId, agName, Color.BLUE);
logger.info("executing: "+action);
createPercepts(agId, agName);
} else if (action.getFunctor().equals("up")) {
driver.updateGame(agId);
driver.move(agId);
logger.info("snake: "+agId+" executing: "+action);
} else if (action.getFunctor().equals("right")) {
driver.updateGame(agId);
driver.move(agId);
logger.info("snake: "+agId+" executing: "+action);
} else if (action.getFunctor().equals("down")) {
driver.updateGame(agId);
driver.move(agId);
logger.info("snake: "+agId+" executing: "+action);
} else if (action.getFunctor().equals("left")) {
driver.updateGame(agId);
driver.move(agId);
logger.info("snake: "+agId+" executing: "+action);
}
else {
logger.info("executing: "+action+", but not implemented!");
}
killTheAgent(agId, agName);
createPercepts(agId, agName);
return true;
}
/** Called before the end of MAS execution */
@Override
public void stop() {
super.stop();
}
private int getAgId(String agName) {
if (agName.length() < 6){
return 1;
}
return (Integer.parseInt(agName.substring(5)));
}
private void killTheAgent(int agId, String agName){
for (ModelSnake ms : driver.snakes){
if (ms.snakeId == agId && ms.isDying()) {
getEnvironmentInfraTier().getRuntimeServices().killAgent(agName);
}
}
}
}
Page 74 of 74
12.14 sanke.asl // Agent snake in project snakes.mas2j
/* Initial goals */
// A goal for creation of a snake
!create.
/* Plans */
// Plan for creating a snake
@cr
+!create <- createSnake;
!move. // Goal for performing a movement
// Plan for movement
@m
+!move <- .wait(200);
!calc_direction; // Goal for calculating the direction
!move.
// Plan for calculation of direction
@cal1
+!calc_direction : upIsFree
<- up.
@cal2
+!calc_direction : rightIsFree
<- right.
@cal3
+!calc_direction : downIsFree
<- down.
@cal4
+!calc_direction : leftIsFree
<- left.
12.15 snakes.mas2j /* Jason Project */
MAS snakes {
infrastructure: Centralised
environment: SnakeEnvironment
agents: snake #2;
}