Simple Computer Games
Simple Computer Games
Using Java
Contents i
i
ii CONTENTS
B References 429
Index 437
iv CONTENTS
Dr. John B. Smith at UNC Chapel Hill for helping me realize that the random ideas I had about introductory
programming texts were aspects of an event-driven introduction to programming. Also for being my academic
and thesis advisor.
Lynn Andrea Stein’s Rethinking CS101 project for starting a discussion (in my head) about what expectations
in introductory programming books should be.
Kim B. Bruce and the Java: An eventful approach gang at Williams College for reviving my interest in an
event-driven introduction to computer programming.
Game names, game company names, and video game program names are the property of their respective
trademark or copyright holders.
Preface
Motivation
This says that a floating point number has at least one digit to the left of the decimal point (thus .1 is not
valid according to this template) and if there is a decimal point, there must be at least one digit to the right
of the decimal point. The decimal point (and any following digits) are optional. The negative sign is optional
and, by this definition, a leading plus sign is not valid.
The point of using these templates is that they are unambiguous and show exactly what is valid when
declaring some particular Java language structure.
One note is that we will use simplified templates early in the book, templates which do not include all of
the complexities permitted in a given structure. As we make use of the complex features we will repeat the
template with the more complex parts. Thus the last template presented in the book is the most complete. To
aid in studying, the templates presented in each chapter are repeated at the end of the chapter in the summary
and the most complete templates are all collected in Appendix C.
v
Chapter
1
Getting Started: What’s in a Game?
While some computer scientists are sometimes loathe to hear it, creating a computer program is as much art
as it is science. Converting an idea for a computer program into a programmable description of that idea is
a design task; in fact, that phase of development is called the design phase by developers. Designers of all types
have rules that they apply yet there remains an aesthetic dimension which cannot be learned from a book; it
is learned only through practice.
This is actually a very wonderful thing: computer programming, from scratch to running code, involves
both creative and analytic capabilities. It uses all the parts of the programmer’s brain that can be brought to
bear.
Learning the aesthetic component of a design skill requires practice and evaluation of the results. One of
the most important parts of the practice is to play with the elements, trying different combinations and trying
for different effects. This book uses computer games as a programming area to teach general computer pro-
gramming. The preface discusses the approach in detail, but there are two primary reasons for this: computer
games encompass all the hard problems in computer science and making computer games will trick you into
playing with the computer programs.
Playing the computer games in this book will motivate you to improve them; some improvements are
suggested in the text or exercises but you are sure to come up with other, better, changes on your own. To
tweak what happens in the computer game you will have to tweak what happens in the computer program.
This chapter begins with an overview of how this book is designed. It then examines the parallels between
a game and a computer program, starting by looking for a usable definition of a game. The following chapters
begin giving you the tools to build your own game using the Freely Available Networked Game Engine (FANG)
and the Java programming language.
1
2 CHAPTER 1. GETTING STARTED: WHAT’S IN A GAME?
Structure
The book begins with a definition of what a game is; that is what the next section of this chapter is all about. It
then ties that definition to a definition of a computer program. Just before the chapter ends, you are given the
source code for a game, Meteors, an Asteroids clone. You are given the game and a brief explanation so that
you can modify the code. The point of this book is that you only learn design skills and programming skills by
practicing them.
The program given in this chapter is also an example of the spiral approach taken by the text. You will be
shown new features of the programming language, the game engine, computer science, as soon as you could
possibly use them. In later sections and chapters, the same topics will be revisited so that you learn why they
work the way they do and, more importantly, how to design and build them yourself.
So, in this chapter you see a Java program and receive a recipe for compiling and running the program.
The terms “compile” and “run” are both given about a sentence of definition and the structure of the pro-
gram is presented in two or three paragraphs. It is obvious that for someone without computer programming
experience, they will not understand what is going on. They will, hopefully, be able to compile and run the
program, however. And then, with some guidance in the end-of-section questions, modify it. At this point, if
anything goes wrong, students will not know enough to fix it.
Chapter 2 again presents the overall structure of a Java program, this time with three to five times as much
explanation. Now some of the “magic incantations” which you used in making the program run will be clearer.
The book uses a game engine/learning library called FANG (the Freely Available Network Game engine).
FANG provides a lot of support for beginning students for writing games. It is, initially, another collection
of magic incantations, protecting the student from the sharp corners inside of Java. As you gain familiarity
with Java concepts while using FANG, some of the pieces of FANG will be peeled back. Three of the last four
chapters of the book involve writing game programs that do not use FANG at all.
The first two chapters are atypical: they present new concepts in a very broad context. This is because
you need to learn a little bit about how computers work in order to understand the commands you type in to
the machine.
The remaining chapters begin by setting a much more limited context: they begin by describing a par-
ticular game. The game in each chapter requires some new aspect of programming. You begin using objects
defined in FANG controlled by rules defined in FANG. One game requires defining your own rules for FANG
objects. Another has a large number of similar objects, motivating the development of object containers. In
each case, the game design discussion at the beginning of the chapter leads to a program design discussion
and a realization of what we have not yet learned to do. This gives you a place to hang the new programming
concepts and Java techniques.
This book is about the programming concepts first, about Java programming skills second, and finally about
simple computer games. The simple is there so that you can focus on the underlying structure, the introductory
computer science, that makes the games possible. The computer games are there to give you feedback as you
learn the computer science concepts.
Definitions
Before proceeding, we should examine that last statement above: Do we need a definition of a game? In The
Art of Game Design [Sch08], Jesse Schell spends almost two pages talking about academics, game researchers,
demanding detailed game definitions, definitions which do not interest practicing game designers.
1.2. WHAT’S IN A GAME? 3
The rant stung as the authors are both academics who study and teach with games. As Schell worked
through the next dozen pages, he found some merit in the struggle to define games: a definition provides a
mental framework for holding the pieces we learn about game design and programming. Having a working
definition of a game also makes it easier to exploit parallels between game and program design and imple-
mentation; it lets us analogize from one field to the other.
Dictionary Definitions
When getting started with a definition, the dictionary is often a rewarding place to start.
The Oxford English Dictionary provides an interesting starting place with “4. a. a diversion of the nature
of a contest, played according to rules, and displaying in the result the superiority either in skill, strength,
or good fortune of the winner or winners.”[OED71] Alternatively, “8. c. the apparatus for playing particular
games.”[OED89] Each of these definitions provide useful pieces: a game has rules and is played with apparatus
of some sort. If you think about a simple computer game, the game has virtual apparatus of some kind and
some sort of rules.
Other dictionaries have the same general definition though the American Heritage Dictionary is one non-
specialized dictionary providing a mathematical definition: “A model of a competitive situation that identi-
fies interested parties and stipulates rules governing all aspects of the competition, used in game theory to
determine the optimal course of action for an interested party” [AHD00]. Again we see rules but these rules
apply to the party playing the game rather than to the apparatus in the game. This definition might be useful
when developing strategies, ways of playing games, particularly when constructing computer opponents.
The current popularity of game design in general and the study of video games in particular means that
there is a considerable specialized literature on games and play. Perhaps their definitions are better suited to
our need.
Literature Definitions
Salen and Zimmerman, in Rule of Play, relate play and game by noting that not all languages have two different
words for the two concepts. Further, they note that, “Games are a subset of play,” and “Play is a component
of games.” [SZ04] Of all the play we do, only some of it is games (the first statement) yet when we use a game,
we play it (the second statement). This permits us to look at definitions of play as well as games.
Johan Huizinga, a Dutch anthropologist, published his Homo Ludens (“Man the Game Player”) in 1938, of-
fering one of the first and broadest academic definitions of play:
[Play is] a free activity standing quite consciously outside “ordinary” life as being “not serious,”
but at the same time absorbing the player intensely and utterly. It is an activity connected with no
material interest, and no profit can be gained by it. It proceeds within its own proper boundaries
of time and space according to fixed rules and in an orderly manner. It promotes the formation of
social groupings, which tend to surround themselves with secrecy and to stress their difference
from the common world by disguise or other means. [Hui55]
What does this have that our dictionary definitions lack? It talks about play as being outside of ordinary
life, bounded in time and space; he goes on to talk about the “magic circle” which people enter when they
begin a game, the magic circle containing the rules which apply while the players are in it. The magic is further
reflected in the total absorption of players while the game lacks seriousness or real world consequences.
David Parlett, a game historian, offers a definition of formal games in the The Oxford History of Board Games:
game. To achieve that object is to win. Hence a formal game, by definition, has a winner; and
winning is the “end” of the game in both senses of the word, as termination and as object.
Means. It has an agreed set of equipment and of procedural “rules” by which the equipment is
manipulated to produce a winning situation. [Par99]
This definition captures the idea of challenge or competition in a game as well as explicitly mentioning
both the game rules and the game equipment.
There are literally dozens of attempts to define game and play in the emerging game studies literature
as well as in game design guides, books that are less academic but more focused on helping budding game
designers learn the skill of game design.
Jesse Schell, in The Art of Game Design examines a number of definitions before settling on our final and
shortest definition of play and games:
This is of particular resonance for a computer programmer because computer science is, fundamentally,
problem solving. All a computer program is is a formal description of how to solve a problem. This definition
does not go into detail about the rules and equipment but that is what the rest of Schell’s book is about.
Game Studies
Game studies is a fairly young field of academic study having grown up in parallel with the rise of the
video game industry. Prior to the 1980s, only a handful of historians and anthropologists studied games
and play and their definition. Johan Huizinga, in his 1938 Homo Ludens (quoted in the chapter), offered a
book length examination of games across cultures and across history.
Several different approaches to the study of games have emerged. Anthropologists remain interested
in the meaning of games to the people who play them. Sociologists and economists study how people in-
teract with games as systems. New media scholars study games as artistic constructs, examining how
they communicate their different messages. Computer scientists, game designers, and other technolo-
gists focus on how games are made and the industry that makes them. In the following descriptions of
specific research, notice how interconnected the different approaches are.
Study of the meaning and impact of games on players includes looking at games as learning tools,
games as simulations, and how games change their players. The military has been interested in games in
the first two senses here since before World War II with the use of flight simulators. As early as the 1940s
analog computers were used to calculate the outcomes of the settings of flight controls.
Learning with games has begun moving into the mainstream with Gee’s What Videogames Have to Teach
Us About Learning and Literacy[Gee03] being a recent attempt to look at what players learn from the games
they play and how that experience can be used to reach modern students. The negative impact of games
is also a focus of this approach. Grand Theft Childhood: The Surprising Truth About Violent Video Games[KO08]
by Kutner and Olson is a popular press book surveying this research. This research impacted the games
presented in this book in that there is a conscious effort to limit the amount of violence in any of the
game designs. This is a personal, designer-specific decision, but the book attempts to create games with
playable mechanics without a violent veneer.
Sociologists look at the social constructs surrounding games. Some study massively-multiplayer on-
line games (MMOs) and the societies that arise within them. Others look at the “mod communities”, the
groups that form around different games to modify and extend them. Mia Consalvo’s Cheating: Gaining
Advantage in Videogames[Con07] looks at players who subvert the social contract in multiplayer games.
Another interesting approach to examining MMOs is taken by economist Edward Castranova in Synthetic
1.2. WHAT’S IN A GAME? 5
A Working Definition
Looking at these sample definitions, we can extract our own more general and more detailed definition:
A game is a collection of components that interact according to a set of rules presenting the player
with a meaningful choices to make in determining the outcome of a struggle with the environ-
ment or another player.1
The “meaningful choices” is an attempt to capture the problem solving in Schell’s definition; these are
also where the absorption of Huizinga’s definition emerges. The “outcome” of the game reflects the contest
elements in Parlett’s definition and implies we will want to have ways of keeping score.
This definition gives us names for the parts of a game, a way to talk about how a game is built. It also gives
us names for the parts of a computer game.
The remainder of this section explores components and rules as they appear in games. Following sections
explore how they apply to computer programs and computer games.
This chapter talks about computer programs but it does not include any; do not use that as an excuse to
skip it. It is not that long and the insight you gain from this parallel presentation will help you explore the
computer programs presented in all of the following chapters.
Things: Components
Components are the things used to play a game. A traditional game of checkers, for example, has sixty-four
squares in two colors arranged in an 8 8 alternating square, a checkerboard, and twenty-four checkers, also
in two colors.
There are two flavors of components: passive components (like those in checkers) and active components
which move on their own (due to physics) or add something new to an ongoing game.
1 This definition fails to address the “entertaining” and “amusing” portions of the dictionary definitions; these dimensions are sub-
jective and difficult to quantify. This book uses games to teach software design and development; while some mention will be made of
“fun”, the player experience is more properly the domain of game design. Interested students are referred to the references at the end
of the book.
6 CHAPTER 1. GETTING STARTED: WHAT’S IN A GAME?
Passive Components
Passive components are place holders, used to keep track of the game’s progress. The checkers and board
illustrated above, the playing cards in bridge or poker, or the peg board in cribbage are all examples of passive
components. The players of the game manipulate the passive components according to the rules of the game.
Active Components
Active components are components which “make decisions” about how to contribute to the game. The “de-
cision maker” differs for different components: dice and marbles move according to the laws of physics (in-
teracting with the table, gravity, one another, and friction); collectible cards have their automatic actions
enumerated on them or listed in the rules for the game.
Cubic dice, when rolled, bounce and rub on the surface to provide the players with a random num-
ber, something which was not there before. Other games’ active components include the marbles in Milton
Bradley’s Hungry Hungry Hippos™or the creature cards in the various collectible card games (Wizards of the
Coast’s Magic: The Gathering™or Nintendo’s Pokémon™).
Actions: Rules
Rules determine how the components of the game interact: the starting configuration of components,
legal moves to change the configuration, how the configuration is evaluated for scoring, and how to know
when the game ends. The rules also determine the winning player and even if there is a winning player.
Consider traditional checkers again. The rules specify the opening configuration shown in Figure 1.2.
Players then alternate moving one checker at a time diagonally onto an adjacent, unoccupied square. Initially,
all checkers may only move toward their opponent’s rear rank; checker pieces are promoted when they reach
their opponent’s rear rank and as kings can move in any diagonal direction. Checkers may jump an opposing
checker if they are adjacent to it and there is an unoccupied square just past it in a direction that checker
could normally move. The game ends when one player loses his last checker or cannot make a legal move; the
player unable to move loses the game. The rules prescribe each player’s goal within the game as well as how
checkers and board squares interact.
1.3. ACTIVE AND PASSIVE: RULES FOLLOWERS 7
A set of rules can be applied to different components without changing the game: chess can be played on
a pocket board, with life-sized pieces, or even on a piece of paper by mail. The components differ in scale but
not in kind; the same set of rules means the same game.
4 4 7 Q
A
A 4 4
7
Q
The same is not true of games sharing only components. Both bridge and “Go Fish!” use a deck of 52 cards
divided into four suits with thirteen values each. The relationship between the components, embodied in the
rules, is different. The initial configuration, sets of legal moves, and even each player’s goals differ. This is a
different game.
Review 1.2
(a) You are trying to create your own game to play with two six-sided dice and 2 or more players. The game
is made up of rounds. The first round begins by each player rolling the dice and marking down the number
that is the sum of the two dice. This number is the starting score. After everyone has had a chance to roll the
dice, the first round ends. During each successive round, the player with the lowest score rolls first and then
the player with the next lowest score rolls next, and so on until all players have rolled during the round. After
each roll, the sum of the die rolls is added to the previous sum. The first player to reach or exceed 100 wins
the game. According to our definition, is this a game? Why or why not?
(b) Chris Crawford, in The Art of Game Design[Cra84], differentiates between a puzzle and a game: a puzzle
presents the player with a configuration and rules for changing the configuration but the configuration is
fixed and to be solved. A game has interactivity in that the configuration changes over time with or without
intervention of the player. Does our definition capture this difference? Or would our definition admit a puzzle
as a game? Is such a difference important?
(c) Can you name three games played primarily with a pair of six-sided dice? Describe the rules of each. How
are turns different between the games? How are winning conditions different between the games? Could any of
the rules work with twelve-sided dice instead of six-sided?
Consider players A and B playing a game of checkers. As they alternate turns, each follows the rules as
part of making there turn. When player A makes his move he considers the rules of the checkers; he is, of
course, also considering how he is going to win. He follows the rules of checkers in making a legal move and
the rules of his strategy by picking his “best” move from those available. After player A moves, player B takes
over, following the rules of checkers and her own strategy for the duration of her turn. Thus A and B together
provide the rules follower for their game.
What if we add a new active component, a moderator who mediates the players’ interactions with the pas-
sive components. The players limit their interaction to the moderator and the moderator, following the rules
of the game, interacts with the remaining components.
The Moderator
Instead of moving components on their own, the checkers players in a game with a moderator take turns
submitting their moves to the moderator. The moderator then follows the rules of checkers.
To keep the moderator as simple as possible, we enhance the checkers: each checker indicates all cur-
rently legal moves and, when moved, which opposing checkers (if any) are to be removed and whether or
not the moving checker should be promoted. The movement rules of checkers are embedded in the checkers
themselves; they are followed by the moderator.
The moderator’s rules are simple and generic:
while current player has at least one legal move
ask current player to pick a checker
if checker is not legal
back to top of loop
else
ask player to pick move
if move is not legal
back to top of loop
else
make move
change current player
Though the moderator’s rules say “checker”, if that is changed to “component” then these rules are com-
pletely generic. They do not depend on what game is being played: the moderator follows the game loop.
How does that match the loop above? The moderator, an example of a rule follower, do not actively show
the state of the game (players look at the board) but otherwise follows the above loop fairly directly.
The only thing the moderator knows about checkers is that players alternate making legal moves and
that a player loses when they have no more legal moves. This is because we have placed all the complicated
1.3. ACTIVE AND PASSIVE: RULES FOLLOWERS 9
movement rules inside the components that are moved. A turn-based game like checkers, one with no active
components other than the moderator, can be played with a single rule follower. The moderator asks each
player in turn for his action, waiting and doing nothing while they think about the move. The moderator then
follows his rules (and those embedded in the checkers) according to the current player’s request. Players
concentrate on their personal strategies or rules for winning the game (more on strategies in the next section);
the moderator concentrates on enforcing the rules to play the game. It is important to note the distinction
between the rules of the game (which are inherent in checkers) and the strategy each player employs.
Note that each active component in a game has its own rule follower. In Hungry Hungry Hippos, every
marble is moving according to the rules of physics at the same time. Each hippo waits for a player to tell it to
move (by pressing their button) but any player can tell her hippo to move at any moment and the hippo must
respond by biting into the arena. The marbles respond to gravity (and being hit by hippos), so the laws of
physics determine their motion; each of these components moves independently and has, for our purposes,
its own rule follower.
Rule followers are event-driven. That means that they act in response to things that happen. The mod-
erator asks a question and then waits for an answer, continuing with his work only when an answer is given.
The hippos are similar. The marbles, too, are waiting for things to happen to them (being hit by another mar-
ble, being swallowed by a hippo) but rather than sitting still while waiting, the marbles also move around on
the board. So the two patterns of action we see with the rule followers here are waiting, doing nothing, for
some trigger event to set off a sequence of rules or the continuous following of rules. In either case, each
independent active component in the game has its own rule follower.
This section defined a game as a collection of components and a collection of rules that determine how the
components interact, presenting the player with meaningful choices influencing the outcome of the game.
Players (in this section) are outside of the game and interact with the components either one at a time or
simultaneously. Components are either active or passive. Active components have rule followers that permit
them to act simultaneously and they react to certain events. Central control for a game can either be manifest
in a particular component or can be provided by the players collectively. What players choose to do is guided
by their strategies, the rules they use to try to win the game.
Retrogaming
Retrogaming, also know as old-school gaming or classic gaming, is the hobby (or obsession) of playing
games designed for a bygone era of gaming hardware. Some play, like one of the authors, because the
fondly recall when Space Invaders and PacMan took over their lives. Some play to experience some of the
best crafted games ever designed. Some play to explore specific designers’ work. And, finally, some play
because retrogames have been introduced on modern game platforms, bringing the Golden Age of arcade
games alive for a new generation.
Retrogames are played in three ways, each providing a different take on the original experience: on
retro hardware, in emulation, and in ports. Retro hardware is the purist’s choice: find original game
systems, game or console, restore them to working order, and play your heart out just as the game was
designed.
Looking at games from the early 1970s it is important to remember that game machines were not
general purpose computers that happened to play games. They were custom-built, special-purpose com-
puters that only played a specific game. This was true of the quarter-draining machines in the local arcade
(a market niche virtually created by Space Invaders and PacMan) and it was true of the first generation of
home game consoles.
From 1972 to 1976 home game systems like Magnivox’s Odyssey and Atari’s Pong had one to five
games hardwired into them; hardwired in this cast to be taken literally because the circuit boards and
custom logic chips really were wired to play only a few different games. In 1976 Fairchild Electronics
introduced the Channel F, the first home game console with a computer at its heart. Games were bough
10 CHAPTER 1. GETTING STARTED: WHAT’S IN A GAME?
Retrogaming (contd.)
on cartridges which plugged in and provided the program for the computer to run. It was also the first
home console to have enough memory to provide computer-controlled opponents for games.
The following year Atari introduced the Atari 2600 (also known as the Video Computer System
(VCS)). The 2600 was a runaway best seller, creating the real market for video game consoles spawning
hundreds of game titles from dozens of publishers.
Generations of consoles are referred to by the number of bits the computer processes at one time;
more bits means more power. The 8-bit generation included the VCS, Mattel’s Intellivison, and, after the
Video Game Crash of 1984, the original Nintendo Entertainment System (NES). The Super NES and the
Sony Playstation (redubbed the PSone in 200) were 16-bit winners. While the bit-size wars have ended,
the computing and graphics power of consoles has continued to grow in each successive generation.
Retrogamers, longing to play the good old games but unable to find or afford the original hardware,
sometimes play the original software on a console emulator. An emulator is a program, running on a gen-
eral purpose computer which “behaves like” the original hardware. The original game software instructs
the emulator just as it would the console and the emulator drives the modern hardware to make it play a
game like the original. The idea of hardware virtualization is very similar to emulation. In virtualization
the machine software behaves as if it were a complete computer of the same type as the one it is actually
running on; emulation is used when the two types of computer are different.
The Multiple Arcade Machine Emulator (MAME) [MAM09] is an emulator that can play retrogames
from the 8-bit through modern eras on current PCs. As a legal note, the game software for the original
system is, quite probably, under copyright and is not free to download and share.
Retrogames are also available as ports. A port is when a computer program written for one type of
computer is translated into a program for a different computer; what the program does remains the same
though how it does it is changed. Games are ported to gain revenue from popular titles. It is also done to
make use of games designed for limited hardware: these games are well-suited to the computing power
now available in mobile devices. Ports are also popular on Internet distribution channels like the Xbox
Arcade and the PlayStation Network; the titles are inexpensive and appeal to a casual gamer demo-
graphic.
The games you will build in this book owe a great deal to the Golden Age (1977-1983, approximately),
the 8-bit generation. Tight constraints on graphics and computing, like the linguistic limitations when
writing haiku, drove designers to create masterpieces of minimalist beauty. The games here pay homage
to the designers of that era but the limitations are used to keep the focus on computer programming.
You, the reader, still get to create fun, playable games while you learn the fundamentals of computer
programming.
Review 1.3
(a) Many sports games have referees. What is the role of a referee in a game such as baseball or basketball (or
some other sport)? How is the role of the referee similar to that of the moderator described in this section?
you to understand how the game works; the rest of the book covers the same ground with detailed explana-
tions.
A Java program is a description of rules written in the Java programming language. The Java program-
ming language is written in words, some of which are special keywords, or words defined and required by the
language, and some of which are identifiers or names for things which the programmer can choose.
To read a Java program, know that code that comes after a // on a line or between /* and */ is a comment.
A comment is ignored by Java (it is only there for us humans). In this book, comments are typeset in italics:
// this is a single line comment - next line is a blank line
In this book, Java keywords are typeset in boldface. Identifiers and all of the punctuation are typeset in a
normal face. The following listing shows two keywords, an identifier, and a block: a block is any section of the
program enclosed inside of curly braces, { and }. When to use a block will be explained in the next chapter.
public class DemonstrationClass {
A complete Java listing using the Game type defined in FANG along with two spacesprites types, the Ship
and the Meteor, is given below.
1 import spacesprites.*; // get all space sprites
2 import fang2.core.Game;
3
28 }
Important things to notice: the name after the keyword class in line 5 matches the name of the file where
the code is stored (without the .java at the end); there are two nested blocks, the outer one running from
the end of line 6 all the way to line 26 and the inner one running from the end of line 11 to line 25; and an
identifier can be asked to perform an action (of which it is capable) by having the name of the identifier, a dot,
the action, and any information needed by the action in parentheses.
To make a Java program run is a two step process: first you must compile (translate) the Java program
into a simpler form and then you must execute (start) the simpler form with the java program. The following
commands assume that you have a command prompt, a computer shell program where you can type commands.
In the following, the prompt, the characters printed by the computer to tell you it is ready for a command,
appear in italics, what you type is shown in normal type.
The prompt shows the folder where you are; the tilde, ~, is the current user’s home directory (folder) so
the source code for the class was installed directly below the home directory of the user of the code. The
-classpath parameter includes a dot, a colon, and then the ppath (folder location) of the fang2.jar file where
the FANG library code is provided.
~/Chapter01% javac -classpath .:/usr/lib/jvm/fang2.jar Meteors.java
Assuming all goes well with typing in the classpath (where Java looks for library information used by the
program), the first command will result in a slight pause (while the javac program, the Java compiler, translates
the code we see in the listing above into a simpler language which is machine-friendly) and the prompt will
be printed again. No news is good news.
The second line runs the java (not javac) program. When the program runs, then you should see a window
similar to that in Figure 1.4.
In FANG, everything above the four buttons at the bottom of the window is the game canvas; FANG game
objects such as ship and rock are drawn on the game canvas. The triangle in the lower-left of the window is
ship. Looking at the code in Listing 1.1, the location of ship, set in line 13, is (0.2, 0.8).
FANG objects are located by their center point. The center of ship is one-fifth of the window width from
the left edge of the window (0.2) and four-fifths of the window height from the top edge (0.8). The width and
height of the canvas are always treated by FANG as 1.0 and 1.0, no matter what size the window actually is.
(0.0, 0.0) is the upper-left corner of the window and (1.0, 1.0) is the lower-right corner (the y-axis is inverted;
values get bigger the further down the screen you go).
A FANG game is started by clicking on the Start button just below the game canvas on the left. When
Meteors begins, the meteor begins moving (its speed and direction are randomly selected). The ship is con-
trolled by the keyboard with left and right arrows turning it, the up arrow applying thrust in the current
direction, and space bar shooting little blue lazer balls that shatter meteors into smaller and smaller pieces.
The two numbers at the top of the screen are a count of the number of lives remaining for your ship
(upper-left corner) and the score (one point per hit on a meteor). The game keeps going even after all meteors
are destroyed or the ship is destroyed. Detecting the end of game and doing something reasonable in that
circumstance takes more code.
The one thing we will try changed, though, is the number an size of the meteors. Listing 1.2 creates three
meteors.
1 import spacesprites.*; // get all space sprites
2 import fang2.core.Game;
3
21 playersShip.showLives();
22 playersShip.showScore();
23 addSprite(playersShip); // add ship to game
24
The three lines for constructing a meteor are about the same as they were in the previous listing. The only
difference is that two meteors, rockA and rockB are constructed with different sizes: the rock in the last game
(and rockC in this game) started at size 3; they broke in half three times. Sending in 2 or 4 (as in lines 25 or 29)
changes the starting size and the number of levels of splitting.
Lines 30 and 35 show a new command which Meteor supports (all screen objects in FANG support it; you can
try changing the color of playersShip if you like) is setColor. Our game has a named rule, getColor which,
given a name of a color in quotes, returns the color. This then goes to the setColor method to change the
color of the object on the screen. Figure 1.5 shows the result of a few moments play, all the meteors scooting
around.
This section is just a hint at what you can do with FANG. One of the goals of the text is to get you started
with all the eye-candy and support in FANG, to get you playing with Java. As the book goes on, you will learn
how to get past using FANG and understand Java much better. The important thing to do is to start playing
with the code.
Review 1.4
(a) Looking at Listing 1.4, what line number would you change to have the playerShip start in the upper-left
rather than the lower-left corner? What would be an appropriate change?
(b) What do you think you would change in Meteors.java to change the gameplay so that the meteor does
not collide with the player’s ship?
(c) Assume you wanted to make major modifications in Meteors.java, so major that the result would really
be a different game. Assume you copy the Java file to NewSpaceGame.java. What is the very first change you
would have to make so that the program could compile?
1.5. STRATEGIES: WINNING A GAME 15
devised to support any of these goals; in this section we assume a strategy is selected to win the game.
3 Exceptions such as “Pick any legal move at random.” are unlikely to produce very good winning strategies.
16 CHAPTER 1. GETTING STARTED: WHAT’S IN A GAME?
Review 1.5
(a) Tic-tac-toe is a simple game with some basic strategies. Using your own personal experience or through
some internet research, describe two strategies you can use to help you win at tic-tac-toe.
What is in a Computer?
At a minimum, a computer consists of a memory and a processor. In order to communicate with the outside
world, the computer also needs some form of input/output (I/O) devices like keyboards, mice, joysticks, and
a video monitor.
The important thing to note is that for each different function the computer loaded a new set of rules, a new
program. The program which is running, the instructions which are being loaded and executed, determines
how different parts of memory are interpreted and therefore what the computer is capable of. We will return
to this description in greater detail in the next chapter.
What instruction does the CPU fetch next? Normally the CPU fetches the instruction directly following
the current instruction in memory. One of the amazing powers of the modern computer is that the CPU can
change the address of the next instruction to any valid RAM address. The next instruction can be determined
by the result of some calculation. On any given clock cycle, the computer can interpret any memory location
as an instruction. Selecting what to do next, high-speed execution, and the ability to interpret numbers in
memory in multiple ways combine to make modern computers general purpose information processors (and
decent game platforms!).
Multitasking
Interestingly, a computer program can have multiple rules being followed at the same time even if the com-
puter has only a single CPU. How? Consider our game-mastered checkers game again. Imagine that rather
than two players playing checkers there are eight players who wish to play checkers. How could one moder-
ator satisfy all of the players?
The moderator could line up the players and play a game for the first pair and then, when that game is done,
play a game for the second pair, and then help the third and fourth pairs. By serving each pair sequentially, the
moderator has helped four pairs play checkers. Unfortunately, though, it takes four times as long as playing
one game to finish all four (assuming, for the moment, that all checkers games last the same amount of time).
An alternative scheme would be for the moderator to phone three other moderators, inviting them down
so that all four games could be played, in parallel, using four identical sets of components. This approach
requires only the amount of time it takes to play one game to finish all four games; it is quite costly in terms
of checkers, boards, and moderators.
Consider a hybrid approach: have four sets of checkers components (pieces, boards) but only one moder-
ator. The four sets of checkers represent the four games and the four pairs can play simultaneously but the
moderator divides his time between the four games. That is he can ask the player in game one for his move
and then, while waiting for the answer he can make a pending move in game two, announce a victory in game
three, check that the player in game four has made a legal choice of checker and then return to listening for
a move in game one.
This division of attention takes advantage of the fact that in the original game the moderator spent most
of his time waiting for things to happen. Using what in one game was waiting time to service a different game
means that four simultaneous games take only a little bit longer than a single game. This is an example of
multitasking, working on multiple tasks by using down time in one task to take care of another task.
Multitasking is not getting something for nothing. Imagine very fast players selecting moves as quickly
as the moderator can make them. This would take all the moderator’s time just to service one game. If all
four pairs were that fast, multitasking would actually take longer than running the four games in sequence.
1.6. WHAT IS IN A COMPUTER PROGRAM? 19
Multitasking might still be a good idea as the last pair in line would get to start their game sooner than if they
had to wait for the three other games to finish.
Similarly, a single CPU can multitask, providing a computer program with multiple parallel rule followers,
each getting a slice of the attention of the CPU. This is important because a single computer program might
need multiple active components. Imagine a program that reads from the keyboard and receives information
from the network at the same time; each of these activities should have its own rule follower so that no input
is missed.
A rule follower in a computer program is also known as a thread of control. Will a program with multiple
threads of control run many times slower than one with a single thread of control? Yes and no. If all of
the threads of control have very complex sequences of rules that work on very small components, then it is
possible that each thread of control is trying to use the full capacity of the CPU. Such threads are processor
bound and this situation is equivalent to the players making moves as fast or faster than the moderator.
Most programs spend at least some of their time waiting for events that occur slowly, in terms of CPU
speeds: waiting for data read from the network, from a hard drive or optical drive, waiting for a human being
to press a key. Human reaction speed is hundreds millions of times slower than the time it takes the CPU to
follow a single rule; a lot of work can be provided to a ready thread while another thread waits for an event.
While multiple threads of control will not execute as fast as if the machine were truly parallel (a CPU per
thread of control), it will run much faster than if each thread of control were executed from beginning to
end one after the other. It also has the advantage that the different threads of control can communicate and
cooperate to provide the user with a compelling experience.
Levels of Abstraction
A computer game is both a game and a computer program. A computer game is two different collections of
components and rules. It has components and rules at two different levels.
Designing a computer game is really two different design tasks: designing a game and then translating the
components and rules of the game into virtual components and rules in a particular computer programming
language.
Translation typically involves moving from a very high level of abstraction to a more concrete level by
specifying more details. This section introduces some of the general techniques for specifying rules and com-
ponents in computer games (and other computer programs, too). The remainder of the book can be read as
the continued expansion of techniques mentioned here.
Designing a game requires defining the components and the rules. It is possible to enumerate the compo-
nents4 . Rules, however, are more difficult to define. A first thought might be to list all of the configurations
of the components and how to get from one to another. Unfortunately, the number of configurations for even
a small number of components can grow astronomically: with a 3x3 grid, each empty or containing an “X” or
an “O” there are almost twenty thousand component configurations5 . Some of the configurations are illegal
in play (the board containing nine “X”s is counted though you could never see it in play) but the exponential
growth in configurations means that rules must be expressed more compactly.
Instead of exhaustively listing all game configurations, simple rules are typically built up into more com-
plex rules. The rules for combining rules can be considered problem solving techniques6 . Very simple rules (i.e.,
pick a checker, capture a piece, roll two dice) are combined into more useful rules using five simple techniques:
sequence, selection, iteration, delegation, and abstraction.
For example, in Listing 1.3 on page 8 the rule follower asks a player to select a checker and then select a
spot to move the checker and then the checker is moved. This simplification is an example of the simplest
rule writing technique: sequence. Do this and then do this other thing and then do that. This is the simplest
mechanism for combining simple rules into more complex rules.
4 infinite games are beyond the scope of this book
5 39 = 19683
6 These problem solving techniques are based on Nell Dale’s problem solving techniques from the Turbo Pascal days (see [DW92])
20 CHAPTER 1. GETTING STARTED: WHAT’S IN A GAME?
The previous example is an oversimplification of the original processing done by the moderator. A player
selects a checker and the moderator determines if it is legal or not. If it is, then the moderator asks for a
target square; if it is not, the game master asks again for a checker to move. This illustrates the second rule
combining mechanism: selection. Two rules can be combined with some condition that can be tested and one
or the other of the rules will be executed but not both.
The moderator’s complete program actually repeats a sequence of two selection statements and a smaller
sequence multiple times. Such repetition combines rules using the iteration rule writing technique: a rule can
be combined with some sort of condition and the rule will be repeated until the condition is met (or so long
as the condition is met; these two are logically indistinguishable).
The additional rule writing techniques were not used in the game master’s “program”. Delegation is the
creation of a new named rule. It would be possible to use the following definition:
define move for player
ask player to pick a checker
if checker is not legal fail
ask player to pick move
if move is not legal fail
make move
succeed
This defines a new rule for the moderator, a rule called move. That rule can be applied to any player and
will be applied to each player in turn:
while game not done
if move for current player succeeds
change current player
This new rule shortens the moderator’s main routine at the cost of defining the move command. When you
learn to name new commands in Java there will be more discussion of when it makes sense to use delegation
and when it makes sense to include code directly inline.
The final rule combining technique, abstraction, is the packaging of an entire game, a “mini-game” as a
component in another game. Examples of this will be provided as we study how to define our own types in
Java.
The earlier description of checker’s components brings up one possible problem in describing a game’s
components: scale. That is, is the checkerboard a component that is divided into squares (the squares are
part of a component) or are the squares components that are assembled into a checkerboard (the board is
a collection of components rather than a component itself). The answer to the question is one of scale and
picking the right scale to examine a game (or a computer program) is something of an art rather than a science;
both of the above views are valid and it might be nice to be able to switch between them at various moments
(when moving a given checker it seems the squares are important; when selecting a checker to move the
checkerboard’s configuration is most important).
Interpreting Memory
The RAM contains both instructions (fetched by the CPU in the fetch-execute-store loop) and data (loaded in
the execute phase and then replaced in the store phase). How can you tell what a given location in the RAM
means? The answer is that you can’t. That is, a given value in a given place in the RAM only has a particular
meaning if you know the context in which it should be interpreted.
The need to know how to interpret a memory location has implications in how we write computer pro-
grams: when we set aside memory to hold data we must tell Java what type of component we will put in that
location. That way when we read or write the actual values stored there we know the context in which it
should be interpreted. We will return to this when we begin assigning types to memory locations.
1.7. SUMMARY 21
A computer program is a collection of virtual components that reside in the computer’s memory and in-
teract according to a collection of rules also stored in the computer’s memory. The rules are followed (active
components are executed by) the computer’s processor. A single processor can be shared between many active
components (threads of control) because most active components have pauses built into their rules sequence
(waiting for input from the user or reading from one of the slower components in the memory) and those
pauses can be used to overlap execution of multiple active components. Active component’s sequencing can
be modified by events that the computer detects. For game purposes the primary events of interest come from
input devices such as keyboards and mice or from other computers connected to the network.
Review 1.6
(a) Between RAM and hard drives, which tends to have a larger capacity? Which tends to be more expensive
per gigabyte (GB)?
(b) Consider eating at a small restaurant with only 4 tables and 16 chairs. Is it necessary to hire 16 waiters to
serve each patron individually? Why not? About how many waiters would be required? How does a restaurant
with fewer than 16 waiters serve all 16 customers who may be in the restaurant at the same time? (insert
footnote) There is a video game called Diner Dash where aspiring waiters can practice.
(c) Do some internet research to find out how much memory a computer used for gaming needs. Cite at least
two sources in coming up with your answer.
(d) Describe how to draw the letter A using a combination of only the following instructions (where X can be
replaced by a number):
Assume the pen starts up and after drawing the letter, the pen should finish up.
(e) Take your answer to the previous question and randomly reorder the instructions, then follow them. Do
you get an A? Why not? A similar thing might happen if you happen to place the correct set of instruction for
a computer program in the wrong order.
1.7 Summary
In this chapter we developed a simple definition for a game: a game is a collection of components that interact
according to a set of rules. Components are the things used to play a game. Rules describe how the components
of the game interact.
Rules specify the starting configuration of components, legal moves to change the configuration, how the
configuration is evaluated for scoring, how to know when the game ends, and how to determine the winner.
The same game can be played with different components: whether you use poker chips, different denom-
inations of coins, painted manhole covers or bottle caps, the movement and capture rules make the game
checkers. Alternatively, multiple games can be played with the same components interacting according to
different rules: solitaire, poker, go fish, or rummy are all played with the same deck of cards but they are very
different games because the specified rules are different.
A rule follower (moderator) can automate the rules of a game. The rules for playing the game are distinct
from a strategy for winning the game. A rule follower only knows the rules of the game and how to move it
22 CHAPTER 1. GETTING STARTED: WHAT’S IN A GAME?
forward. Each active component can be considered to have a rule follower assigned to it (even rule followers
which just follow the laws of physics).
A computer program is a collection of components that interact according to a set of rules. Both components
and rules are stored in the computer’s memory (RAM). Rules are followed by the computer’s processor (CPU).
Rule followers in a computer program are called threads of control. A single processor can simulate multiple
threads of control by rapidly switching from one thread of control to another, giving each a tiny slice of its
attention. The result is multitasking which can make use of otherwise wasted CPU time.
Rules, for a game or a computer program, are almost always built up. Simple, single action, rules are com-
bined using the five problem solving techniques: sequence, selection, iteration, delegation, and abstraction.
Sequence is doing one thing after another. Selection is evaluating some condition and doing one thing or
another depending on the condition’s value. Iteration is repeatedly doing something until some condition is
met. Delegation is naming a part of a program to make a new command. Abstraction is the creation of a new
component, a component that encapsulates its own rules into a sort of mini-game within a game.
Review Exercise 1.2 Describe the components and rules of Chinese Checkers.
Review Exercise 1.5 Compare the components and rules from the previous two questions.
Review Exercise 1.6 Ignoring whether a position is legal or not and assuming an unlimited number of both
colors of checkers, how many board positions are possible on a checker board?
Review Exercise 1.7 Consider the game Tic-Tac-Toe. Describe what a rule follower (moderator) would do to
supervise a game between two players.
Review Exercise 1.8 Consider the game Battleship7 . Each player begins with a 10 10 grid of squares and a
fleet of five ships of varying lengths (lengths appear after each ship’s name): aircraft carrier(5), battleship(4),
cruiser(3), submarine(3), and destroyer(2). To set up the game, each player places their fleet in their grid,
each ship placed horizontally or vertically across the given number of squares.
Players then take turns selecting squares in their opponents grid, shooting at any ship occupying the
square. The goal is to sink the enemy’s fleet before a player’s own fleet is sunk.
Formally describe the components and rules for this game.
Review Exercise 1.9 How would you add a rule follower to Battleship?
Review Exercise 1.10 If the ships and the grid are the components of Battleship, consider modifying the rules:
Describe how you would modify the rules to make the game more difficult.
Describe how you would modify the rules to make the game easier.
How would you modify the rules to permit players of different skill level to compete evenly (be sure to
note your definition of even).
How could you use the same components to play a completely different game?
7 A version of Battleship is produced by Hasbro Games but the game predates the Milton Bradley Games (now Hasbro) 1967 release by
at least 50 years.
1.7. SUMMARY 23
Review Exercise 1.11 For Battleship, it is possible to vary the components while keeping approximately the
same rules.
Describe how you would modify the components to make the game harder.
Describe how you would modify the components to make the game easier.
Can you make the game work between opponents of differing skill levels by modifying just the compo-
nents?
Review Exercise 1.12 You have been asked to stage a game of checkers during the half-time of a football game
in a large, outdoor stadium. Describe the components you would use. Would there be any need to modify the
rules of the game?
Chapter
2
Designing Your First Program
A game is a collection of components with associated rules governing their interaction, providing the player
with meaningful choices to solve the problem present by the opposition, be it the game environment or an op-
posing player. Computers can only process number but components, rules, environments and even opposing
players’ strategies can be encoded, modeled, and executed in the memory of a modern computer.
This chapter introduces the Java programming language, discussing the structure of Java computer pro-
grams and object-oriented programming practices. The simple computer games in this book are supported by
the Freely Available Networked Game (FANG) Engine. FANG provides types of objects representing games,
players, screen objects, and even behaviors for the screen objects. This chapter focuses on the structure of a
handful of games using only a few of FANG’s features. The next chapter looks at a much broader selection of
game objects leading in Chapter 4 to writing a game with all behavior explicitly programmed.
2.1 BasketBall
A game, whether on a computer or not, begins with an idea. Similarly, a computer program, whether a game
or not, begins with an idea. The idea is translated into a design, a description of what the game or program
should do and look like. The translation of a finished design into a concrete product depends on technique
rather than design skills.
Most chapters in this book begin by introducing a game design, an idea for a simple computer game. The
game components and rules are explained so that the reader and the author have, generally, the same game
in their mind. The design is followed by an inventory of concepts required to implement the game with special
attention paid to new, unfamiliar concepts.
“To implement” means to accomplish, to make real. Thus implementing a game design is making the
parts and writing the rules; implementing a computer program is writing the code that makes the design
work. Implementation of a video game design is actually another layer of design: a computer program must
be designed to play the given game before the program can be implemented. The remainder of each chapter
introduces the computer science and Java techniques that are new with that chapter’s game. The chapter ends
with a finished game program.
The Meteors game in Chapter 1 is less than a page in length yet it had a zooming spaceship, laser blasts,
and meteors which split in half when shot. FANG provides high-level tools for building a game by expressing
connections between behaviors and screen objects. This chapter again uses these highly abstract building
25
26 CHAPTER 2. DESIGNING YOUR FIRST PROGRAM
blocks to build a game with mouse input, a bouncing ball, and even a target on the screen, all in less than
seven pages of code1 .
The game we are designing is BasketBall, a take on the carnival game of making a free-throw to win a prize.
Design diagrams using a “handwritten” font to emphasize the conceptual nature of the work, accompany most
game designs. The final game will look more or less like the drawing.
BasketBall
ov e s end of
m
y e r's M ouse lick, launch
Pla n th ey c
w h e
line;
n ce s it off
u
e d c od e b o lates f
ovid
NG pr sket, and sim
u
m ad e up o
F A et
l.
Th e b a , off th e b a A b ask tangles
al s rec
th e w th ree
.
gravity
Looking at Figure 2.1, notice that most of the writing on the drawing is done at an angle. That is to indicate
comments rather than text which is part of the game. The title at the top is also, typically, not part of the game
itself but appears as the title of the window decorations around the game in screen shots.
BasketBall has three components: the basket, the ball, and the “launcher”. When the game starts, the
player has control of one end of the line representing the launcher. It indicates the direction and force with
which they are launching the ball. When they click, the game launches the ball with the appropriate initial
velocity.
As mentioned in the design comments, the ball is also influenced by a constant acceleration due to gravity
(toward the bottom of the screen) and it bounces off of the basket and the edges of the screen.
What do you need to know how to do to implement BasketBall using the FANG engine? You have seen one
Java program using FANG but were not supposed to look too closely at it. This time around you examine the
structure of a Java program first with a minimal program which just draws a square on the screen and then one
which draws the basket. Placing the basket on the screen requires understanding FANG screen coordinates.
The chapter code provides two classes, types of components defined for inclusion in a Java program,
BasketBallLauncherSprite and BasketBallSprite. A sprite is the name, in the FANG library, for elements
1 Not to worry: we will be looking, in detail, at about two pages of code, including two simpler programs along the way to making our
game.
2.2. JAVA 27
visible on the screen.We will see how to include them in the game and how to modify their associated behav-
iors.
One interesting thing about computer games is that you can model things which you cannot experience in
real life. If we want to see how much fun the free-throwing on Jupiter would be, we can increase the gravity.
And if we would rather make the game more like billiards, we can turn gravity off completely. But first, on to
Java.
Review 2.1
(a) What does it mean when the writing on a design diagram is slanted?
(b) In your own words define implementation. How would you implement bridge (or poker or any other familiar
card game) given a description of its rules. This implementation is in the real world, not the computer.
(c) Looking at the description of what the BasketBallSprite will do in the game, bounce and fall, is there
anything you think is missing? If so, what?
2.2 Java
Java is an example of an object-oriented programming language. What that means, in a nutshell, is that every
rule in a program is associated with some object; an object is, in computer science terms, what we have been
calling a component. The remainder of the book will endeavor to use component when talking about games
and object when talking about objects inside a computer program. Since we are designing computer games,
there may be some grey areas; we will err on the side of calling them objects.
Every object is of some particular type. In Java an object type is a class. The rules followed by all objects of
a give class are defined once in the definition of the class.
This is analogous to the idea of a Pawn type of component for a game of chess. The rules of moving any
pawn are part of the definition of the type while the color, starting location, and current location of any given
Pawn is specific to that pawn. The Pawn type is analogous to a class in Java and each of the sixteen pawns in
the game are analogous to objects, each of the type Pawn.
One powerful feature of object-oriented languages like Java is support for programmer-defined classes.
Java has many classes built into the language and it ships with a staggering number of standard libraries,
packages of class definitions which can be included into user programs. This book also uses the Freely Available
Networked Game (FANG) Engine. FANG provides classes for components like game, player, and screen objects.
A program written in Java is not in a form that a computer can directly execute. The Java program must
be translated into a more computer-friendly (and human-unfriendly) form by a program called a compiler. We
will mention the compiler in this section; when we look at how to actually run a Java program there will be
more to say (as there will in the next chapter’s section on how computer work).
Syntax
Java is an artificial yet, to you, a foreign language. Learning a new language requires learning a grammar,
how concepts are strung together, and a vocabulary, a collection of the words used to express the concepts.
Computer scientists make this split at a finer level, talking about the syntax or structural rules and the semantics
or meaning of the resulting program. Syntax is the rules for what constitutes a well-formed program. A
program is well-formed if it has no structural errors; a well-formed program may or may not mean, or do,
anything at all.
Getting started with Java requires learning about the syntax, how to write a well-formed computer pro-
gram. That means having a language to talk about language. The next section introduces the overall structure
of a Java program along with the Java templates used to describe Java syntax.
28 CHAPTER 2. DESIGNING YOUR FIRST PROGRAM
The name of the feature is in angle brackets so that we, readers of the template, can tell the difference
between words that are actually part of the template and words that represent other syntactic structures. The
:= symbol is read “is defined as”. The first line of a class definition shows both literal words and syntactic
structures used in a definition:
<class-definition> := public class <class-name>
The two words, ‘‘public’’ and ‘‘class’’ are part of the definition of the class, keywords that form part
of the vocabulary (they are set aside in the language definition) and the grammar (their position tells the Java
translator program that this is the beginning of a class definition) of the Java language. The <class-name> rep-
resents another template value, a value which has some specified structure with a value that can be different
every time the <class-definition> template is used.
Before finishing the class definition template, we will take a look at the template for <class-name>. It
illustrates how we can specify a variable length structure.
<class-name> := <identifier>
<identifier> := <letter-or-underscore><letter-or-digit-or-underscore>*
Notice how <class-name> is really just an alias, a different name, for an <identifier>. Identifiers are user
chosen names and appear in a lot of different places in Java; we will use more descriptive names for them,
identifying what it is that they name, in most of our templates.
The two templates inside our definition, <letter-or-underscore> and <letter-or-digit-or-underscore>,
represent single characters chosen from the named sets of characters. That is, a <letter-or-underscore>
could be the ’_’, the ’A’, the ’p’, or the ’Z’ character, among others.
Notice that we used single quotes, ’, around individual characters. The single quotes are not part of the
character itself. They serve to focus our eyes on the character and make clear what we are talking about. The
two template names are touching; this is significant because identifiers cannot contain spaces.
What is the star at the end of the template? It means “zero or more of the preceding item”.
That is, <letter-or-digit-or-underscore>* means “zero or more items from the template called
<letter-or-digit-or-underscore>”. So an identifier begins with a letter or an underscore and has any num-
ber of letters, digits, or underscores after that.
A class definition is
<parent-class-name> := <identifier>
<statements> := <statement>*
This template says that a <class-definition> is defined to be the two words public and class followed by
an identifier naming the class followed by the word extends followed by another identifier naming a “parent”
class (whatever that relationship means in Java). After the parent class name, comes a block of statements en-
closed in curly braces. <statements> is a list of zero or more <statement>s. We will have to leave <statement>
2.2. JAVA 29
undefined for the moment; it is the fundamental unit of which Java rules are composed and we will define it
a little bit more in each chapter.
The spacing and indenting in the template is that permitted by Java and expected by the author of this
book. Java is free-form in that it tends to ignore extra spaces. The space between public and class is not extra;
without the space, Java would see the word publicclass, a word it does not understand and it will signal an
error. Because Java is free-form, the two words could appear fifteen spaces apart or even on separate lines2 .
The spacing and indentation shown is that used when using the template in Java.
This is a lot of syntax just to be able to talk about syntax. Templates define language constructs by naming
the construct (name inside angle brackets) and use the := symbol to begin the definition. The definition is
composed of literal characters and other templates (which are replaced by something matching their defini-
tion). Literal characters or words match exactly what they say. There is one character that is neither a literal
or a template name: ’*’ is a template symbol that means zero or more of the preceding item.
These syntax templates use an Extended Backus-Naur Form (EBNF) . The form is named for John Backus
and Peter Naur and has been used to describe programming language syntax since the 1950s.
A Whole Program
Listing 2.1 is a very short example program. You can see that lines 5-14 match the <class-definition> tem-
plate. This, of course, assumes that the lines between the curly braces match <statements>; they do.
1 import fang2.core.Game;
2 import fang2.sprites.RectangleSprite;
3
Java class files are named for the public class they contain: the class Square must be in a file named
Square.java. Notice that Java is case-sensitive so uppercase and lowercase letters are different. The names
must match exactly.
Lines 4 and 7 begin with //. The // characters begin an end-of-line comment. A comment is something that
is ignored by Java. An end-of-line comment marks everything from the // through the end of the current line
as a comment; in this book comments are typeset using an italic typeface. Lines 4 and 7 are, as far as Java is
concerned, empty. The comments are there for humans reading the code. They serve as guideposts for other
programmers; there will be a lot more on comments (including a Java template for the two different forms of
comments) later.
Listing 2.1 as a whole matches the Java template for a <class-file>:
<class-file> := <imports>
2 The term “spaces” is used to mean “whitespace” or characters whose printed form just moves where the next character will print.
The space, ’ ’, the tab (no good way to write it now), and the end-of-line character are all whitespace.
30 CHAPTER 2. DESIGNING YOUR FIRST PROGRAM
<class-definition>
<imports> := <import>*
The contents of a .java file are zero or more import lines (<imports> is a list of zero or more of the <import>
template; the import template is a line importing one library or library class) followed by the definition of the
public class.
The import statements begin with the obvious keyword and end with a semicolon (you will see a lot
of semicolons in Java). The <import-class-path> is the name of the class or library to import. Line 1,
import fang2.core.Game; imports the Game class (the last name, before the semicolon, is a class name) from
the core library which is part of the fang library. The name is read backwards.
By using import, we have access to two classes in our program, Game and RectangleSprite. The Game class is
the parent class (or base class) for our Square game. That comes from the naming of the identifier after extends
in the <class-definition> template. What is a base class?
A base class is a type which the class being defined is extending. Think about it in terms of board games.
Battleship is a game. Electronic Battleship extends the standard Battleship game; everything you can do with
Battleship (play, clean up, lose pieces) you can also do with Electronic Battleship except that the new game might
do some things better. Or at least differently. Rather than calling out a shot location and the other player
answering “Hit” or “Miss”, putting the shot marker on your board tests whether or not it is a hit and plays an
appropriate sound effect. The rules for Electronic Battleship could mostly consist of “See Battleship rules for this
section.”
The same thing, to an extent, is true in Java when one class extends another. The child class gets all the
rules defined for the parent class automatically. So, since Game can be translated and run on the computer, so
can Square.
The results of compiling and running Square are in Figure 2.2. The screenshot is in grey scale; the actual
square is in a color referred to as FANG Blue.
define advance
update game state
call setup
while (not game over)
show game state
get input from user
call advance
A method, sometimes called a procedure or subroutine, is a new command. The language used by our mod-
erator is pretty close to English but there is an attempt to keep each instruction simple. A method is a named
definition of a new instruction composed of multiple simpler instructions. By giving the new instruction a
name we can use it as a “regular” instruction in the description of the game loop below.
When the moderator is following the instructions, they begin at the top and execute them one after the
other, in sequence. Sometimes, it is useful to interrupt that order and execute a different sequence of instruc-
tions, returning to the point of interruption after the different sequence is finished. Consider a World War II
wargame such as PanzerBlitz. The rules include the following for the German player’s turn:
32 CHAPTER 2. DESIGNING YOUR FIRST PROGRAM
2. German player announces which of his units are attacking which Russian units,
and what attack techniques are being used.
3. German player resolves all Normal combat, rolls the die once for each attack. Ger-
man player flips face-down all firing units, as they are fired, to signify that they
may not move.
This turn sequence, taken from page 11 of the rules, specifies what happens each time through the game
loop. In particular, the first actions for the German player are listed in phases 1-3 here. In step 3 the player
“resolves all Normal combat”. That is a reference to page 5 (and following) where the resolution of normal
combat is explained. The sequential following of the rules on page 11 is interrupted to do combat resolution;
the player keeps track of where they were, though, and returns to page 11 when the combat is over to continue
with step 4. The interruption of the flow on page 11 is calling the rule define on page 5, the method previously
defined.
The new setup method is called exactly once before the game starts. Once the game starts, advance is called
over and over to advance the game toward its conclusion. While simplified, this loop matches the internal
operation of a FANG game almost exactly. The Game class defines two empty methods, setup and advance. We
must provide new, non-empty redefinitions if we want our game to do anything.
The first line in the listing, @Override tells the Java compiler that this method redefines an already defined
version of the program in the parent class (Game). The compiler checks that there is such a method and will
throw an error if we say we are overriding and there is no method to override. This helps protect us from
typographic errors in the name or parameter list of the method.
The second line is the method header. The method header consists of the access level (the keyword public),
the return type (the keyword void), the name (setup) and the list of parameters (() — an empty parameter
list; more on just what parameters are below). Detailed presentation of method definition is left for Chapter 6.
For now, we will just define the four parts of the function header that appear here:
public is a Java keyword used to indicate that the thing so labeled is visible and usable outside of the
current class. Since the game loop sketched above runs somewhere inside of FANG, the setup method must
be publicly available. There are other settings as we will see in future.
Methods can behave like mathematical functions, returning calculated values. Methods returning a value
must specify the type of object they return; void is a special type indicating that the method does not return
any value at all3 .
3 This is the empty type or the type with no values, not to be confused with the term null, the value meaning no value provided. null
plays an important part in Java and is discussed later.
2.2. JAVA 33
The name is a valid Java identifier (no spaces, case-sensitive). To override an existing method the name
and parameter list must match the previous definition exactly.
The parameter list is between the parentheses; in this case there is nothing between the parentheses so
there are no parameters. Parameters are values passed in to a method to permit it to behave differently each
time it is called. Parameters are discussed in detail over the next four chapters.
The body of a method comes between the curly braces (opening and closing on lines 9 and 13, respectively).
The curly braces serve as a container for the code they enclose. setup is called once, before the game begins
running, by FANG. When a method is called, the calling method is suspended and the body of the method
begins execution with the first line in the body; if no control statements change the flow, execution continues
in sequence through the body of the method.
Square’s setup method declares a local variable, a label for a RectangleSprite called rectangle, and as-
signs a newly created RectangleSprite to the variable. The word new asks Java to create an object somewhere
in memory (we neither know nor care where); the identifier following new is a class name followed by a (pos-
sibly empty) parameter list for the code that makes a new object. Line 10 creates a new RectangleSprite.
Line 11 uses the label given to the new rectangle to tell it to set its position (to call a method defined by the
RectangleSprite class; it is like telling a Pawn component to move to a given location on the chessboard). Fi-
nally, in line 12, the label is used to add the new rectangle to the game so that it will be displayed. Only screen
objects which are added to the game will be displayed when the game loop displays the game’s current state.
Screen Coordinates
Two-dimensional coordinates are often expressed as a pair of distances: the distance along the x-axis from
the origin to the coordinate point and the distance along the y-axis from the origin. Each point on the plane
has coordinates of the form (x, y) and every pair of numbers refers to a specific point.
Given a point, (x, y), there are several questions that must be answered to find the point it corresponds to:
What is the origin (where is (0, 0))? What is the unit of measure in each dimension (they need not be identical)?
What are the unit vectors of the two dimensions (where do the axes point)?
FANG adopts standards from computer graphics in expressing game locations: The origin is the upper-left
corner of the visible game-play area; the x-axis points from left to right (values increase from left to right);
the y-axis points down the screen (values increase as you move from top to bottom of the screen).
What units of length are available? It would be possible to use the tiniest picture elements or pixels as a unit
of measure, except that no two screens are likely to have exactly the same pixel size. It would also be possible,
by querying the computer, to figure out what size pixels it has in some more traditional measure of length
and then convert all screen distances to millimeters or inches.
Both of these absolute measurement systems have a similar problem: the size of the game (and the coordi-
nates of all locations) change when the game window is resized. On modern computers, most windows have
“resizing handles” and can, at any time, be used to resized the window. If the square is a given number of
millimeters down from the top, if the window is the given size the square is centered; a window resized to
twice the height would have the square near the top rather than centered.
Instead of absolute units defined outside the game, x and y dimensions are measured relative to the current
size of the screen. That means that the upper-left corner, the origin, has the coordinates (0.0, 0.0); the
decimal point in each number reminds us that we are using fractions to express screen locations. The lower-
right corner is (1.0, 1.0) (one screen width to the right of the origin and one screen height below the origin).
Figure 2.3 shows the coordinates of the origin and the bottom-right corner. It also shows two long, thin
rectangles crossing at the center of the screen, (0.5, 0.5).
Looking back at Listing 2.1, the coordinate parameters make sense.
10 RectangleSprite rectangle = new RectangleSprite(0.5, 0.5);
11 rectangle.setLocation(0.5, 0.5);
In line 10 the keyword new tells the system to make a new object, the type of which is determined by
the type name following new. Thus new RectangleSprite... constructs a new RectangleSprite, whatever that
is. Notice that we imported the type from fang2.sprites at the beginning of the Java file (line 2). The two
parameters passed along with the type are the width and height of the rectangle in screens. Thus (0.5, 0.5)
on line 10 means the rectangle is a square half a screen wide and half a screen high.
FANG screen objects, sprites, are located by their centers. When you create a new RectangleSprite, it
is located at the origin. The center of the rectangle is at the origin of the screen. That is not were we want
it displayed so we set its location. rectangle.setLocation(0.5, 0.5) in line 11 calls a method defined for
rectangle, a method called setLocation. The two parameters are the x-coordinate and the y-coordinate where
the rectangle should be located. The center of the rectangle is moved to the center of the screen, creating the
image seen in Figure 2.2.
Review 2.2
(a) Write the Java/FANG code to construct a RectangleSprite as wide as the screen and 75% as high as the
screen.
2.3. CREATING EXECUTABLE PROGRAMS 35
(d) What symbols are used to indicate the start and end of a method body?
Different CPUs are found at the center of different computing devices (game consoles, PCs, PDAs, cell
phones, etc.) and different CPUs have different machine languages. As was said before, the contents of mem-
ory must be understood in context; the same sequence of values mean something completely different in a
different machine language. An advantage to a high-level language is that different compilers can be written,
each of which translates the same high-level program into a different machine code file, each specialized for a
particular CPU and machine language.
Once a high-level program is translated into a computer’s machine language, that machine code can be
loaded into the computer’s memory and executed by the operating system. Operating systems (such as Mi-
crosoft’s Windows Vista, the open source Linux, and Apple’s OSX) are an important field of study in computer
science. For our purposes, they are programs that run on the computer that can start, stop, and manage other
computer programs. Operating systems also permit programs to create directories (or folders), read and write
files, and use the network. The operating system provides an environment in which our programs run and
can access the various resources of the computer and the Internet.
By analogy, compiling is similar to translating an entire book from one language to another. If we consider
translating an encyclopedia, translating the entire thing is a lot of work. The user of the translated encyclo-
pedia may not even be interested in the entire text, just the article they look up along with those articles it
leads them to. It might be more efficient to translate portions of the book only as they are needed. This would
require an interpreter, fluent in both the source and target languages, alongside the reader but the translation
time would be minimized.
This same approach can be used in the computer: a program in a higher-level language can be interpreted,
each part being translated just in time for it to be used. Parts that are never used are never translated. Note
that the interpreter, here a program like the compiler, treats the higher-level program as input along with
the actual input processed by the program.
Compiling has a speed advantage over interpretation if the program will be run multiple times: the high-
level representation of the program need only be translated once. Interpretation has a flexibility advantage
if the program is going to change often: each time the interpreter is run, the most current version of the
high-level representation is executed.
There is a hybrid approach where high-level programs are compiled into bytecode, a lower-level language
that is not any computer’s machine language and the bytecode representation is then interpreted by a pro-
gram that understands bytecode and runs in the local machine language. Each bytecode is interpreted each
time it is executed and it is still possible to have bytecode files that are out-of-date (the higher-level program
has changed but not yet been compiled).
While this approach appears to capture the worst of the other two approaches, there is a different flexibil-
ity advantage: a simple interpreter can be provided for each different machine language. This means the same
bytecode file can be interpreted (executed) on multiple machines. Bytecode interpreters are both simpler to
build and faster to run than high-level language interpreters. Much of the work of a standard interpreter is
done in compiling into bytecode so each bytecode executes very quickly.
The hybrid approach is analogous to the use of a particular human language for standard communication
within a given field. The Medieval Church used Latin for scripture and communications across Europe be-
cause its clergy spoke a wide variety of languages. Rather than having to have an interpreter for every pair of
languages (so a Polish speaker could communicate with a Spanish speaker or a Frankish speaker could com-
municate with a Saxon), the Church could communicate with speakers of any language so long as there was an
interpreter from Latin to the given vulgar tongue. Just as the cost of adding a new language to the Church’s
collection was minimized, the cost of adding a new computer to the list of machines where a given bytecode
runs means implementing just a bytecode interpreter.
The hybrid compiler is simpler because it only needs to know one target language, the bytecode. The
bytecode serves as the lingua franca for multiple CPUs, a language of exchange that they all, through their
simple interpreters, can understand. This is very useful when computer programs are shared over the Internet
to multiple, different kinds of computers.
Java is a successful example of a bytecode compiled language. In order to run a Java program which we
write, we must go through a write-compile-run cycle: we must write and save the Java source code; we must
2.3. CREATING EXECUTABLE PROGRAMS 37
provide the Java code to the javac Java compiler which translates it into bytecode; we then provide the byte-
code file to java, the bytecode interpreter on our local machine. One thing to keep in mind is that there is no
direct connection between the Java and bytecode files; you must remember to compile the program for any
changes to appear in the interpreted program.
<comment> := /*
<anyNumberOfCommentLines>
*/
Since comments are treated as whitespace by the Java language processor (more on the processor in a
moment), they are not part of the rules that a Java program follows. Instead, they are there solely for the
programmers who come after you. There will be more on writing good comments throughout the book. For
the moment keep in mind that your comments should document the design decisions you have made and guide
the reader to how you thought about the program you wrote.
Basket
Listing 2.4 shows our first step toward making the BasketBall program. Like all FANG game programs, Basket
extends fang2.core.Game; we can call it Game in line 8 because we imported the class in line 1. As we saw when
looking at the enhance game loop in Section 2.2, FANG provides two empty methods, setup and advance, which
we are expected to override in order to give our game rules that do something. Just like in Square, Basket will
only override the setup method.
1 import fang2.core.Game;
2 import fang2.sprites.RectangleSprite;
3
4 /**
5 * Draw a basket (for ”basketball”) on the screen.
6 */
7 public class Basket
8 extends Game {
2.3. CREATING EXECUTABLE PROGRAMS 39
9 /**
10 * Setup the sprites (and anything else) necessary for our game.
11 * Use the new operator to construct three RectangleSprites. Each is
12 * sized and located in screens (screen is 0.0 to 1.0 from left to right
13 * across and 0.0 to 1.0 down from the top to bottom (y-axis is backward).
14 */
15 @Override
16 public void setup() {
17 // width, height (in screens); location is (x, y), in screens
18 RectangleSprite rectangleBottom = new RectangleSprite(0.25, 0.05);
19 rectangleBottom.setLocation(0.625, 0.475);
20 addSprite(rectangleBottom);
21
setup is a public method (accessible from the rest of FANG), returning nothing (return type void) and
having an empty parameter list. The method header on line 16 says all of this. The body of setup is everything
inside the curly braces on lines 16 and 29; when setup is called by FANG the lines in the body are executed in
sequential order. The nine (non-blank, non-comment) lines in setup are grouped, for easy reading, into three
sets of three.
Each block of three declares a label for a RectangleSprite and uses new to construct a new
RectangleSprite. The one labeled bottom is wide and short, 0.25 screens wide and 0.05 screens high. The
other two are thin and tall with backboard taller than frontboard. Each is then positioned on the screen. They
all have their bottom at 0.5 screens down from the top. The front and back board overlap the bottom by a
little bit to give them pretty location numbers.
How can we go from a Java, a high-level language, to bytecode, a lower-level language, and then have the
bytecode interpreted? The next two sections address this.
Compiling
Remembering that Java uses a hybrid compiled/interpreted model we must:
2. Save the program in a file named Basket.java (the name of the public class and the file must match).
3. Compile the .java (high-level) file to produce a .class (bytecode, low-level) file. Basket.java will pro-
duce Basket.class.
This book demonstrates commands you would type at the command-line on your computer. If you are
running an integrated development environment like Eclipse, Netbeans, or IntelliJ you can do exactly the same
thing by pressing a couple of buttons. If you are using the JavaWIDE on-line IDE, you only need to save a
newly created .java page and JavaWIDE will compile the program for you, returning a page with your program
embedded right in it.4 .
Opening a command shell in the folder where we have saved .java, we run the Java compiler with the
javac command:
What is -classpath and the stuff after it? It is a parameter for the compiler telling it where Java program and
library files live. It is a colon-separated list of folder (directory) names. With two elements, this list tells the
Java compiler to look in the current directory and in the fang2.jar Java archive file5 . The fang2.jar archive
was downloaded from www.fangengine.org; that website also contains instructions and tutorials on installing
the FANG engine.
It is possible for the compiler to detect and report certain types of errors; we will examine some of these
in future chapters. For the moment, read the error message very carefully and examine the given line number
and the ones just before it. A compiler works from the beginning of a file to the end and it may not be able to
report an error until it has become completely confused. Thus the reported location of the error is often after
the actual location. Make sure that the contents of the file exactly match the code shown in Listing 2.4.
Running
After the javaccommand completes successfully, you will find a new file in the current directory:
Basket.class. This is the bytecode file generated by the compiler. The Java bytecode interpreter, java, is
able to start any Java bytecode file which has a special main routine; one big thing provided by FANG is a de-
fault main routine which creates and runs a Game (or Game-derived) component. The interpreter expects the
name of the component to run; it also requires a -classpath parameter to be able to find non-standard Java
libraries (such as FANG).
~/Chapter02% java -classpath .:/usr/lib/jvm/fang2.jar Basket
Figure 2.6 shows Basket running. The screen is divided into two parts: the top, black portion, the game
field itself containing three FANG blue rectangles (which overlap and look like one big fishhook) and the row
of buttons across the bottom of the screen6 .
When a FANG Game-derived program is run in Java, FANG creates the screen spaces that you see, sets up
the game , and then draws the game on the screen. The game does not actually begin until you press the Start
button in the lower-left corner of the game window. Pressing Start for Basket has FANG update the game
regularly but, because the update rules for the game are empty, nothing appears to happen.
In fact, when you press Start, the only change is the label on the button: it changes from Start to Pause.
This happens whenever you start your game; this permits you to pause the game at any time you like.
The other three buttons permit you to turn on or off the Sound, display any Help for the current game,
and to Quit the current game, closing the window completely.
4 Take a look at http://www.fangengine.org/ for step-by-step tutorials in setting up various environments. JavaWIDE, which has
the parent folder of the current folder. By definition, the topmost folder is its own parent.
6 Due to printing limitations in this book, screen shots will appear primarily in shades of grey; on the screen the top portion is black
Review 2.3
(a) Programming languages like C and C++ are compiled into machine code and do not need to be translated as
they are executed. Other languages such as Javascript are translated as they are executed (called interpreted).
Is the Java programming language compiled, interpreted, or both?
(b) What does it mean to compile a Java program? In your answer properly use the terms ”source code”,
”bytecode” and ”compiler”.
(c) Type in the program in Listing 2.1, but leave out line 11. Compile and run the program. Describe what you
see that is different and why you think this happens.
(d) Type in the program in Listing 2.1 but leave out line 12. Compile and run the program. Describe what you
see that is different and why you think this happens.
(e) As a new programmer the messages your compiler gives you will be hard to understand at first. One way
to start understanding them better is to intentionally put errors in programs and see what the compiler says
42 CHAPTER 2. DESIGNING YOUR FIRST PROGRAM
is wrong. In this way when you encounter the compiler message due to an unintentional error, you have a
better chance at figuring out what went wrong.
Type in the program in Listing 2.4. Change a part of the program that will make the program have an error
when you try to compile it. Try to compile the program and see what message you get. Fix the error, note
down what you did to cause the error and write down what message the compiler gives. Repeat this two more
times (try to get three different error messages).
2.4 FANG
FANG is the Freely Available Networked Game Engine7 . FANG is an educational game engine designed to
support beginning students in writing simple computer games early in their careers.
This section discusses what an application framework is (game engines are a special kind of application
framework) and why you would want to use one when learning Java. We then dissect the FANG name and see
why it is a good choice for a beginning programming course.
What is a Framework?
An application framework is a combination of at least one programming language and a library which provides
the infrastructure for building a particular type of application. Application frameworks are typically spe-
cialized for use in a particular environment meaning for a particular computer family or operating system.
Examples of application frameworks include Cocoa for the Machintosh’s OSX operating system, the Mozilla
Web browser framework, and the OpenOffice.org framework for building office applications. The important
thing to note is that an application framework provides a higher level of abstraction than just a programming
language and, as discussed above in relation to high-level languages, computer programmers use abstraction
to vanquish complexity.
Freely Available
FANG is an example of open-source software(OSS). Open-source means that programmer(s) provide the high-
level language representations of their programs along with compiled, low-level representations. The high-
level source code, complete with comments, makes it easy for programmers to read, understand, and even
modify open-source software.
Open-source software is in contrast to closed-source or proprietary software where the software vendor
makes only the compiled or machine-language representation of the software available. Without the source
code, difficult reverse-engineering is required to be able to modify the software function and many vendors
require licensees to agree to not reverse-engineer their products before they can install them.
FANG is also an example of free software. The FANG software license9 is designed so that you, the user,
are free to use it as you wish. You can run it for any purpose (including figuring out how it works). You can
modify it for your own purposes. You can freely distribute your changes to other people so that they can take
advantage of your improvements. The only requirements on you are that you must acknowledge that your
code includes or is based on FANG (just as FANG does, in particular in the fang2.util package where some
amount of code is based on work done by the Association for Computer Machinery’s Java Working Group on a
different pedagogical toolkit).
Notice that the free in free software is freedom for you, the user and how you choose to interact with the
software. This is free as in “free speech”: you are free to use speech as you see fit.
Free software is a subset of open-source software; you cannot exercise the freedoms the license promises
unless you have access to the high-level language version of the software.
Free software has many benefits. Think back to the first computer game you played and really loved. Now,
does that computer program run on modern hardware? If all you have is a DVD/CD10 with the machine code
for the game, you have to install it on the right kind of computer (machine code is specific to a particular CPU)
and, perhaps, a particular version of an operating system. If you had the source code you could, potentially,
compile it for your current machine and operating system. If it was free software you would have the source
and the freedom to compile it if you wanted to.
What if just recompiling the program failed to get it to work for you? The game might be tied to the speed
of the processor or some particular type of graphics or networking hardware which is no longer available.
With the source code, some knowledge, and a whole lot of patience, you could, at least potentially, fix the
game and keep it running. When you have the source code and a free license, you can fix any bugs that come
along and repurpose the software as you see fit.
Now, imagine if the software was a word processor rather than a game. If all of your term papers were
saved using version 1.0 of the software and just before you apply to graduate school, the company ships version
2.0, revoking support for version 1.0. Further, what if version 2.0 could not read version 1.0 files. How can you
assemble a portfolio of your term papers if you do not have a free license for the word processor? With open
source software and formats, you can do what you want with whatever version of the software you want to
(as long as you follow the licensing requirements). With proprietary software you have only the options the
vendor chose for you.
FANG is free and open-source software; freely available and free for you to modify.
9 Gnu Public License v3.0
10 Or, heaven forfend, a floppy disk.
44 CHAPTER 2. DESIGNING YOUR FIRST PROGRAM
Networked
Application frameworks provide infrastructure for particular types of applications. One thing they can pro-
vide is a communications infrastructure. FANG provides a network infrastructure that makes writing net-
worked games, games with multiple players at multiple locations, almost as easy as writing single player
games.
Game Engine
A game engine is a special kind of application framework. A game engine provides infrastructure for a particular
genre of games. Commercial engines such as Valve’s Source Engine or Epic Games’ Unreal Engine provide
sound, physics, graphics, lighting, networking, and other subsystems to licensees of the software.
FANG was designed as a teaching engine from the get go. Thus it provides well-documented, clear source
code. It also provides graphics, networking, and sound subsystems to support the creation of simple, 2-
dimensional arcade style games. Because it is open source and flexible, it is possible to write different genres
of games but it can be a lot more work than making a game in the genre the engine was built for.
Because FANG was designed as a teaching engine, as we progress through the book, we will peel back some
layers of the abstraction provided by FANG and peek “under the hood”; you will get a chance to see how the
concepts we use in our games are used inside the game engine as well. You will also be able to, if you wish,
extend the game engine. That is the other benefit of open-source software: anyone in the community can
contribute back to make the software better.
Review 2.4
(b) Describe the flexibility present in using open-source software that is not present when using closed-source
proprietary software?
4 /**
5 * Throw a basketball sprite. BasketBallLauncher knows about
6 * BasketBallSprite so we just add the launcher. When the mouse is
7 * clicked, the launcher launches a basket ball along the given line
8 * (gravity and bouncing change its course).
9 */
10 public class BasketBall
11 extends Game {
2.5. FINISHING UP BASKETBALL 45
12 /**
13 * Setup the sprites (and anything else) necessary for our game.
14 */
15 @Override
16 public void setup() {
17 // width, height (in screens); location is (x, y), in screens
18 RectangleSprite rectangleBottom = new RectangleSprite(0.25, 0.05);
19 rectangleBottom.setLocation(0.625, 0.475);
20 addSprite(rectangleBottom);
21
Lines 30-33 are the new code. Before finishing, setup adds a fourth sprite to the game, a
BasketBallLauncherSprite. Line 32 calls the constructor for the new kind of sprite specifying a location on
the screen, (0.05, 0.5); that is just off the left edge of the screen, halfway between the top and the bottom
of the screen. A launcher is a “stretchy line”, a line where one end is fixed (at the anchor point given to the
constructor) and the other end tracks the mouse.
When the program is run, a small orange dot is visible at the given location until the player presses Start.
When the player presses Start, the launcher begins keeping track of the player’s mouse. An example moment
while tracking is in Figure 2.7.
The BasketBallLauncherSprite has something called a transformer attached to it. Transformers provide
behaviors such as tracking where the mouse is (which is one of the transformers hooked to the launcher) and
detecting when the mouse is clicked and things like that. More on playing with transformers below.
When the player finally decides to click the mouse, the BasketBallLauncherSprite creates a new
BasketBallSprite and gives it an initial velocity based on the angle and length of the line that represented the
launcher. The launcher also removes itself from the game so that the player gets to launch one ball and then
watch it move around. A static figure, failing to capture the kinetic action of this little game, is in Figure 2.8.
How can we change how this game works? All the behavior is hidden inside of BasketBallSprite and
BasketBallLauncherSprite. In the spirit of free software, we have the source code. We can look at it and mod-
ify it. We will only look at one small section of BasketBallLauncherSprite, a method called mouseClickedAt.
You could probably guess that this method is called when the mouse is clicked and the launcher is in the game.
42 @Override
43 public void mouseClickedAt(Location2D location, int mouseButton) {
44 BasketBallSprite projectile = new BasketBallSprite();
45 // start the basketball at the anchor end of launcher
46 CHAPTER 2. DESIGNING YOUR FIRST PROGRAM
46 projectile.setLocation(getAnchor());
47 // launch velocity is double the length of the line
48 projectile.setVelocity(getVector().multiply(2.0));
49 // 270 degrees - straight down; 0.6 screens/second^2
50 projectile.setAcceleration(new Vector2D(270, 0.4));
51 // add new sprite to game
52 Game.getCurrentGame().addSprite(projectile);
53 // banish launcher
54 removeFromCanvas();
55 }
Notice that the method never uses its parameters. You can see that it is overriding some other definition
of the method so it has to have the same parameter list even if it is not going to use any of the parameters.
Also notice that there are a lot of comments in the method. Almost every line has a short comment before it
saying what it does.
2.5. FINISHING UP BASKETBALL 47
So, let’s say we wanted to double the force of gravity. What line would we change? Well, setAcceleration
on the ball sets the gravity field. So if we changed the 0.4 on line 50 to, say, 0.8, it would double the acceleration
due to gravity. To turn gravity off, we could use 0.0 instead.
Notice that the last line of the method asks the game to remove the launcher (when a sprite wants to get
out of a game it should call removeFromCanvas). What if you commented out that line? A chance to have a
million basketballs (which don’t intersect with one another).
What if we wanted to change how hard the ball bounces off the basket or the edge (or, even, other bas-
ketballs)? We could look inside of BasketBallSprite. The method we are interested in is the constructor, the
method that is called when new is used (in line 44 in the listing above). Inside the constructor three different
transformers are built and hooked together.
40 public BasketBallSprite() {
41 super(DEFAULT_DIAMETER, DEFAULT_DIAMETER);
42 bouncer = new BounceInsideRectangleTransformer(this,
43 0.0, 0.0, 1.0, 1.0, // dimensions of screen
44 new VelocityTransformer(new Vector2D()));
45 // amount of bounce off edges
48 CHAPTER 2. DESIGNING YOUR FIRST PROGRAM
46 bouncer.setElasticity(0.66);
47 spriteBouncer = new BounceClassTransformer(this, bouncer);
48 spriteBouncer.add(RectangleSprite.class);
49 // amount of bounce off sprites
50 spriteBouncer.setElasticity(0.25);
51
The three transformers of interest to us are assigned to the variable names bouncer, spriteBouncer, and
gravity. The ball is very dead when it bounces off of the basket but pretty lively when it bounces off of
the edges of the screen. Those two aspects of bouncing are determined by the elasticity of spriteBouncer
(for bouncing off RectangleSprites) and bouncer (for bouncing off the edges of the screen). Right now
the ball bounces off the top of the screen as well as the other edges. The four numbers passed to the
BounceInsideRectangleTransformer on line 43 are the minimum x, minimum y, maximum x, and maximum y
coordinates of the box inside which the ball bounces. So if you wanted the top to be above the screen, give it
a negative value. You could also move the floor up or one of the walls in11 .
So, you have now seen the insides of two classes which extend sprites. The next chapter goes in more
depth into the available sprites and how you can use them.
Review 2.5
(a) Compile and run BasketBall. Try to launch the ball so that it ends up inside the basket.
(b) Starting with the BasketBallLauncherSprite as given, modify it so that the game has no gravity. Is the
game more or less fun than the original? Can you try different levels, say between 0.0 and double the original
gravity? Which one is most fun to you. (Note: Use a broad definition of “fun” for this exercise since the game
is quite simple.)
(c) Modify BasketBallLauncherSprite so that you can launch more than one basketball.
(d) The BasketBallSprite is a Java class which extends OvalSprite. That is why it is round. What do you think
would happen if you changed OvalSprite (in both the import line and in the extends line) to RectangleSprite?
Try it and see.
2.6 Summary
Java
Java programs are written in plain text. Each class defines the rules for all objects of that type. A class file
must have the same name as the public class defined inside the file. Java is case-sensitive so uppercase and
lowercase letters are considered different.
The name of a Java class can be any valid Java identifier. A valid identifier begins with a letter (or an
underscore) which is followed by any number of letters or digits (or underscores). This is also shown below in
a Java language template for <identifier>.
11 Be
careful moving the left wall in. If the ball starts outside of the defined rectangle, the physical modeling used will break. The
program will run but the behavior of the ball is not predictable.
2.6. SUMMARY 49
A Java class file begins with any required import statements. The next non-comment line is public class
and the class name. A class is defined as extending some other class. This means that the child class can do any-
thing the parent class can. Thus a BasketBallSprite which extends OvalSprite can do anything an OvalSprite
can do, including being added to the game and have its location set.
A method is a named group of instructions forming an effectively new instruction. The header of a method
has four parts:
public void setup() {
//^ ^ ^ ^ ^ - beginning of method body
//| | | | - the parameter list
//| | | - the name of the method (an identifier)
//| | - the return type of the method
//| - the access level of the method (public is all we know)
Java code is normally executed in sequence, the order in which it is written. The normal order is inter-
rupted when a method name appears in the code: this is a method call. When a method is called, the method
containing the call is suspended and execution moves to the body of the method definition starting with the
first line and normally continuing in sequence until the end of the method body. When execution finishes the
body of the method, execution returns to the spot where the method was called and continues from there.
FANG
The Freely Available Networked Game Engine (FANG) is
• Free software
• A game engine — an application framework designed around making simple two-dimensional video
games.
50 CHAPTER 2. DESIGNING YOUR FIRST PROGRAM
The fang2.core.Game class is the parent class for all FANG simple computer games. It provides two empty
methods for games to override: setup, called once before the game is Started with the button on the FANG
window and advance, called once every time through the game loop.
FANG provides screen objects called sprites and behavior objects called transformers. The BasketBall game
uses both to simulate a ball being shot, bouncing, and falling due to gravity.
Java Templates
<identifier> := <letter-or-underscore><letter-or-digit-or-underscore>*
<class-name> := <identifier>
<parent-class-name> := <identifier>
<class-file> := <imports>
<class-definition>
<imports> := <import>*
<comment> := // <commentFromHereToEndOfLine>
<comment> := /*
<anyNumberOfCommentLines>
*/
Review Exercise 2.1 What does the annotation @Override mean right before the header line of a method?
Review Exercise 2.2 What is the difference between high-level and low-level programming languages?
Review Exercise 2.6 When does FANG call each of the following methods (if you define one):
2.6. SUMMARY 51
(a) setup
(b) advance
Review Exercise 2.7 What does the compiler do with comments? For whom are comments written?
Review Exercise 2.8 Explain why it is important to “document your intent” in comments rather than just
describing the Java code.
Review Exercise 2.9 What is the screen coordinate for each of the following:
(a) the upper-right corner of the game screen?
(b) the middle of the left side of the game screen?
(c) the center of the screen?
Review Exercise 2.10 Where on the screen (what set of points) are the screen coordinates the same? That is,
where are the x-coordinate and the y-coordinate equal?
Review Exercise 2.11 What are the screen coordinates of a point one-third from the left edge and two-thirds
from the top of the screen?
Programming Problems
Programming Problem 2.1 Modify Square.java from this chapter so that instead of drawing a square it
instead draws a circle. (Hint: Look at BasketBallSprite.)
Programming Problem 2.2 Modify BasketBall.java so that instead of launching from the middle of the
left-hand side of the screen, the basket ball launcher launches from very close to the bottom-left corner of the
screen. You should be able to do this changing just one line.
Programming Problem 2.3 Modify BasketBallSprite.java so that the right-hand edge of the “gym” (the
bouncing rectangle) is one screen past the right edge of the screen. What happens when you miss the basket
now?
Programming Problem 2.4 If you modified BasketBallLauncherSprite.java so that you can launch unlim-
ited BasketBallSprites into the game, it would be nice to have them bounce off of each other. That requires
changing BasketBallSprite’s constructor.
(a) Find line 48 in BasketBallSprite.java. The line reads
48 spriteBouncer.add(RectangleSprite.class);
What happens if you comment the line out? Try it and see.
(b) Copy line 48 (so there are two calls to the add method). Change the second copy so that instead of
saying RectangleSprite it says BasketBallSprite (leave the .class part after the class name). Now
see what happens with multiple BasketBallSprites.
Programming Problem 2.5 Start with BasketBall.java and try the following. Though it only says to compile
it once, you will want to compile and test each of your changes as you go along.
(a) Copy BasketBall.java to a new .java file, BackwardBall.java.
(b) Compile and run BackwardBall.
(c) Move the BasketBallLauncherSprite from the left-hand to the right-hand side of the screen.
52 CHAPTER 2. DESIGNING YOUR FIRST PROGRAM
The last two chapters presented short, simple game programs. They were simple because of the high-level of
abstraction at which you were able to work. Using a Meteor class or a BasketBallLauncher class is working with
classes that have both an appearance and fairly complex behaviors already attached. It is time to start drilling
down into the lower-levels of abstraction. This chapter surveys the sprite and transformer classes provided by
FANG, presenting in the process a large collection of short proto-games, programs that are not games by our
definition; most lack any problem to be solved or choices for the “player” while the run. We will also drill
down, way down, into how computers work and what, exactly, a digital, binary, general-purpose computer
is. Finally, the chapter ends with a presentation of how to use the Java language’s documentation (which also
applies to the FANG documentation) to find the names of classes and methods.
53
54 CHAPTER 3. FANG: A SURVEY OF CLASSES
60
40
40
80
20
10
0 0
MPH
Digital electronic signals (like those inside the computer) are more robust than analog signals. It is much
simpler to build a circuit that can differentiate between the presence or absence of 3.3VDC than it is to build
a circuit that can differentiate between all the values between 0 and 3.3VDC. This also explains why modern
digital computers are binary (using the base-2 number system; more on this later): it is easier to differentiate
between 0 and 3.3VDC rather than being able to tell 0 from 0.33 from 0.66 from 0.99 and so on to have eleven
discrete values.
While there have been analog computers, almost all modern computers are digital. Each memory location
in the computer can hold a number of discrete states. You might ask how a machine, limited to holding entries
from a small set of possible values, can be a general-purpose machine. The answer to that question comes in
two parts: how many states can a sequence of discrete entries take on and what do those states mean?
Note that you are holding a book in your hands, one written in the English language. Ignoring the pictures
for the moment, how many different symbols can any given location in the book contain? That is, counting
from the beginning of the book, what value might the one thousandth character in the text have? The ten
thousandth? Any, randomly selected location? There are only about 75 different values possible (a space, 26
lowercase letters, 26 uppercase letters, 10 digits, and a handful of punctuation characters; this is a computer
science book so the punctuation is more varied than in a book by Mary Shelley).
Yet, this sequence of alphabet characters, this book, purports to teach you about building simple computer
games; no matter how simple the games are, the concept being taught is moderately complex. The sequence
of characters, drawn from a fairly limited alphabet, conveys a great amount of information. And a different
sequence drawn from the same limited alphabet might teach you how to bake fabulous deserts or tell you the
story of Frankenstein: or the Modern Prometheus.
The same thing happens inside the computer: its memory, composed of bytes1 which each draw their
1 A byte is eight binary digits (or bits) wide; each bit can contain a 0 or a 1 so the total number of combinations that a byte can hold is
2 2 2::: eight times or 28 = 256. A byte can directly encode the numbers from 0 to 255 which can be interpreted in different ways.
3.1. HOW COMPUTERS WORK 55
value from one of 256 possible values, is arranged as a large sequence of bytes. Modern machines have main
memories (RAM) measured in gigabytes2 or billions of bytes. To put that in perspective, a page of this book
contains about five thousand characters drawn from an alphabet about a quarter of the size of a byte’s possible
values. That means a page could be stored in just about a kilobyte (1024 bytes). That means a computer with
a one gigabyte memory could store almost a million pages in the RAM at one time.
Note that the memory inside the computer, the random access memory is not fixed like the ink on this
page. Instead, it can change over time. Thus it can store a different selection of a million pages at any given
moment and, by following instructions, it can change any of those characters on any of those pages, changing
what million pages are stored in a split second.
While each byte can be considered a number on the range 0-255, the interpretation or meaning of the
contents of memory depends on what part of the computer is reading the value as well as on the current
instructions being executed by the CPU. We will now take a closer look at the parts of the computer.
Parts of a Computer
Figure 3.2 shows an abstract block diagram of a computer. The middle of the diagram is the Central Processing
Unit (CPU). The CPU is, literally, the rules follower for the computer. It follows a very simple cycle: fetch the
contents of a location in the main memory, determine what the contents of that location mean in terms of the
instructions the CPU knows how to follow, fetch any contents of memory that are needed by the instruction,
and execute the instruction.
Random
Input/Output
Access
CPU
Devices
Memory
Disk Drives
Network
2 In computer science we use standard power of ten prefixes (i.e., giga-, mega-) to refer to the nearest power of two: a kilobyte is
210 = 1024 bytes; a megabyte is 220 = 1048576; a gigabyte = 230 = 1073741824.
56 CHAPTER 3. FANG: A SURVEY OF CLASSES
The main memory is random access in the sense that any byte can be accessed in the same amount of time.
Individual bytes are addressed with their distance from the beginning of memory (memory location 0); no
matter how large a byte’s address, it can be accessed as quickly as the byte at location 0.
What would a non-random access memory look like? Consider a similar addressing scheme, where each
chunk of data was labeled with its distance from the beginning of the memory but have it stored on a reel of
magnetic tape. Let us compare a three byte access pattern in the RAM and on our tape.
In the computer, the cost of accessing the byte at location 0 followed by the byte at location 1000000
followed by the byte at location 100 is three memory access times, all the same and all very quick.
Assuming the tape begins completely rewound, the same access sequence on the tape reel is very quick
for byte 0 (the tape is positioned to read that location); then the tape must move 999999 memory locations
past the read head and read the byte at location 1000000; finally, the tape must stop and rewind 9999001
locations to read the final byte. Even if moving the tape and not reading it is somewhat faster than reading
each byte, moving forward and backward about two million locations certainly takes longer than it would to
read locations 0, 1, and 2, one after another (or even 0, 100, and 1000). In RAM, all sequences are equally fast3 .
The CPU can change the values stored in the RAM very quickly but the contents of the RAM are volatile:
they go away when the power is turned off. Computers use disk drives to provide longer-term storage; disk
storage includes optical (CD/DVD), magnetic (regular hard and floppy disks), and solid-state (memory stick,
thumb drive) drives. These devices are slower but larger than the RAM. They are also non-volatile meaning
that changes to the content persist across power-off cycles.
On the right side of the drawing are several input/output devices (I/O). These include the screen, the key-
board, game controllers, and even the network interface. These devices can provide information for a running
computer program or be driven to display information from the running program for the computer user.
The instructions executed by the CPU are very simple. They are things such as: get the value of a given
memory address, add two numbers, store a result to a given memory address, and if the result of addition was
negative, continue execution at a different address.
For example, the following sequence of instructions would load the contents of two memory locations and
then put the larger of the two values into a different location in memory4 . The number to the left of each line
is the memory location where that instruction begins (each is assumed to take exactly 4 bytes).
1248 load contents of memory location 100 to x
1252 load contents of memory location 104 to y
1256 if x > y jump to memory location 1268
1260 store contents of y to memory location 108
1264 jump to memory location 1272
1268 store contents of x to memory location 108
1272 ...<more instructions here>...
This sequence of six instructions makes sure that the value stored at 108 is the larger of the values stored
at 100 or 104. It takes six instructions to do something a human could do on inspection. A modern computer
is powerful because it can execute these instructions billions of times per second. Simple arithmetic and
decisions, done quickly enough, permit the computer to run any program from an operating system to a word
processor to a real-time strategy game. It all depends on the contents of the computer’s memory and what it
means.
3 And then there was cache. Cache memory is a very, very fast memory that sits between the RAM and the CPU; the way it works
changes the statement that all sequences are equally fast (close memory accesses are quicker, in general). Ignoring cache, the statement
stands.
4 The numbers in the example are base 10 rather than binary; in the computer the instructions and memory locations would be
Review 3.1
(a) Do the Xbox 360, Playstation 3 and Wii have operating systems? Do some internet research to find the
answer? Cite your sources when you answer this question.
Direction Pad
(D-Pad)
Analog Stick
(d) When you type a term paper into your favorite word processor and then your cat turns the machine off
you lose what you typed. What kind of memory was the word processor using to store the contents of your
paper?
Where does the paper get moved when you select File | Save?
(e) How many bits are in one byte? What does bit mean?
(f) What does binary mean? How does it apply to your computer?
(h) Can you give an example of information in real life where context determines how it is interpreted?
Perhaps something that you and your teacher would both be able to read but which would mean something
different to each of you.
Note on Java syntax: Sometimes it is important to be able to talk about a specific definition of a method.
That is, the definition of the method that appears inside a specific class. For example, many different sprites
support the getX method. We might want to talk about the version (if any) defined in RectangleSprite as
opposed to the version defined in OvalSprite. The Java notation for this is to use the name of the class, a dot,
and then the name of the method. Thus RectangleSprite.getX is the version defined in RectangleSprite and
OvalSprite.getX is the version defined in the OvalSprite class. We will use the name of the method without
any class name whenever possible but must be more specific in some instances.
Creating Objects
Java objects have types and Java types are classes. The name of a class typically begins with a capital letter
(more on naming in the next chapter). In order to use an object of a given type you must create an instance of
that object.
A high-level analogy of the relationship between classes and instances is the relationship between
blueprints for a car and any given instance of that type of car. The blueprints describe the features every
car has, the services it can provide like “accelerate” and “open driver’s door”. Each car has a “level of gas”
method, too; while all cars share the capability of reporting gas, each will report its own, specific, level of gas.
An object constructor is a special, a method used by the Java system to create an object of a given type.
A constructor is called by following the new operator with the name of the type you want to create. We The
following examples will include calling constructors; in Chapter 6 and following we will work on defining
constructors for our own classes.
Naming an Instance
After creating a new object it is important to be able to have that object do things. A rectangle might need to
have its color set or a transformer might need to change the associated velocity. What a given object can do
depends on the public methods it defines.
In Java a local variable is an identifier which can be associated with any object of a given type. A local
variable is declared by stating the type to which it can refer and its name. The Java template for a local
variable is:
<type-name> := <identifier>
<variable-name> := <identifier>
This template introduces a little more EBNF notation: the [ and ] in the template are like the *, notation
for the template and not representing themselves in the resulting Java code. Where * means zero or more
copies of what comes before, the square brackets mean “what is inside is optional”. That is, there can be zero
or one copy of the = <expression> part of the <local-variable-declaration> template.
A <local-variable-declaration> can appear inside of the body of any method, between the curly braces
following the method header for any method.
What is an <expression>? Along with <statement>, <expression> is one of the more complicated elements
of a Java program. For the moment, we know of one definition:
<actual-parameter-list> := [<expression> [, <expression>]*]
expressions. So, we now know a couple of more definitions for <expression>: it could also be a number. How
can we indicate that a template has more than one possible match? One more template symbol: | indicates
that the template matches the stuff before the vertical bar or it matches the stuff after the vertical bar; read
it as “or”.
<expression> := new <type-name>(<actual-parameter-list>) \|
<number>
This reads as “a <expression> is either the keyword new followed by a type and parameter list or it is a
<number>.”
An example of declaring a local variable inside of setup:
... // inside of some Game-extending class
public void setup() {
RectangleSprite sq = new RectangleSprite(1.0, 1.0);
...
}
The name of the local variable is sq. It can refer to RectangleSprite objects. The result of calling new
refers to an object of the given type so new RectangleSprite(1.0, 1.0) is an expression which has the type
RectanleSprite (there will be much more on expressions and their types in the following chapters). The =
can be read as “is assigned the value of” or “refers to”. So, the line declaring sq reads: “Create a local variable
named sq which can refer to RectangleSprites and assign the value of a brand new RectangleSprite as big as
the screen to it.”
After the assignment, we can use the name, sq to call methods defined in the RectangleSprite class. We
will look at some specific methods in this chapter; in the last section we will cover how you can figure out
what methods and classes are available in FANG and Java.
Calling a Method
An object has methods. An objects methods are the commands that it understands. Going back to our checkers
game, a checkers piece knows how to move one square, how to jump, and how to be promoted. You can tell
it what to do if you have a label for it. That is, if one of my checkers is my favorite, I could ask it to move
forward to the left (a square might have two squares “forward” and two squares “back” so left and right, from
the player looking at their back row, disambiguates them) by saying favorite.move(forwardLeft). Here the
name of the object is favorite, the name of the method is move, the parameter list has one entry, whatever
forwardLeft is. The dot is how Java can tell where the identifier for the object and the identifier for the method
name end and begin.
So, to set the color of the rectangle sprite we created in the previous example, we would use sq, a dot, and
setColor. The parameter expected by setColor is a Color (more on this type at the end of the chapter). To get
a color, Game provides a method, getColor which takes a string naming the color we want. So, ”red” is a string
(notice the double quotes) naming the color red. So, to set the rectangle to red we use the following line:
sq.setColor(getColor(”red”));
This line shows that we can call a method “on” another object by naming the object and using the dot and
the method name. It also shows that we can call a method on the current object, the Game-extending class object
where setup resides. We will use both of these ways of calling methods throughout the rest of this chapter.
Games
Game or, more precisely, fang2.core.Game is a class provided by FANG. It is the standard starting point for
creating a game because after compiling any Game-extending class can run as an application (or, if embedded
3.2. FANG BASICS 61
in a Web page, as an applet; see http://www.javawide.org/ for free Web space where FANG programs can
be automatically built). The Game class provides all the code to put a window on the screen, put four buttons
across the bottom, setup the game, and run a game loop.
The game is setup with a call to the setup method (full method header: public void setup()). Game defines
setup, expecting classes that extend Game to override it.
To override is to provide a new, more specific, definition of a method. When a Meteors game starts, FANG
does all the necessary construction of objects for the window and buttons and internal structures. Then it calls
setup. Remember, calling a method means using the name of the method as a statement in another method,
one which is running. When the method name is encountered, the running method, the caller, is interrupted
but its place is remembered and the called method begins execution. When the called method finishes (when
execution runs off the closing curly brace at the end of the method body), Java returns to the remembered
location in the caller and continues execution.
What happens if there are more than one method with the same name? That is, what if Meteors.setup
and Game.setup both exist? Java looks at the actual type of the object on which the method is being called, the
Meteors object constructed automatically by FANG, and looks to see if the method it needs is defined in that
class, Meteors: it is so that version is chosen.
Consider Meteors and the main game loop. In the loop, FANG calls the advance method every time through
the loop (full method header: public void advance(double seconds)). Again, to find which advance method
to actually call, Java begins looking for Meteors.advance. There is no such method (we never defined one back
in Chapter 1). Java then, using the extends part of the class definition, looks for the method in the parent
class, Game.advance. Game.advance is defined (and has an empty body so it does nothing) so Java calls that
version every time through the game loop. If there had been no version in Game, Java would have looked at
Game’s parent class and then that class’s parent class and so on until it reaches the “root” class, Object. Object
is the ultimate parent of all objects in Java and we will have some more to say about it later.
To create a FANG game we must:
• extend fang2.core.Game.
• Override (redefine) setup if we want any new sprites added to the game.
• Override (redefine) advance if we want to do anything to update the game state each time through the
game loop.
We will see some other features of Game objects as we look at sprites and transformers.
Sprites
In computer graphics, a sprite is a two-dimensional visual object that moves in front of the background of
the screen. If you consider the game Asteroids, the basis for Meteors, the ship, the lasers, the rocks, and any
power-ups used in the game are all sprites; they are visual objects that move in front of a background (empty
in this case).
Historically, sprite was coined for an early 1980s video display controller from Texas Instruments [Whi92].
The sprite was named for flying pixies or fairies of that name because the generated image flew over the
television picture. The sprite was a god-send for sportscasters everywhere.
In addition to movement, sprite hardware, which was common throughout the 1980s, also supported colli-
sion detection (it could tell whether or not two sprites overlapped). In the spirit of that “Golden Age of Gaming”
technology, visual game components in FANG extend the fang2.sprite.Sprite class and have names ending
in Sprite.
62 CHAPTER 3. FANG: A SURVEY OF CLASSES
RectangleSprite
As we have seen, a RectangleSprite is constructed by calling new, naming the type of object (RectangleSprite)
to construct and specifying the width and height of the rectangle. We have declared local variables, labels for
referring to the new object, when we construct one.
1 import fang2.core.Game;
2 import fang2.sprites.RectangleSprite;
3
Recalling Square.java from the previous chapter, we see that line 10 begins with
RectangleSprite rectangle.
Consider building a simple square “target” picture like that in Figure 3.4. The picture is a series of alter-
1
nating colored squares, centered on the screen, in blue and gold, each one 10 of the screen smaller than the
one around it. With 5 squares, the result should look something like:
Given that RectangleSprite has a setColor method and to set the color of a rectangle called rectangle to
blue or to gold is simply:
rectangle.setColor(getColor(”gold”);
// or
rectangle.setColor(getColor(”blue”);
Before we go on and write the program, think about how you would specify each of the rectangles: what
would its width and height be? What would be the location of each sprite?
5 Yes, “or underscore” should appear with “letter”. We will agree not to mention underscores except when naming constants which
is covered in Chapter 4. It is simpler to write letters and letters or digits and we will not make much use of underscores in our naming.
3.2. FANG BASICS 63
7 /**
8 * Create, color, and draw five rectangles centered at the center of
9 * the screen. Add to the game in largest to smallest order.
10 */
11 @Override
12 public void setup() {
13 RectangleSprite one = new RectangleSprite(0.1, 0.1);
14 one.setColor(getColor(”gold”));
15 one.setLocation(0.5, 0.5);
16 addSprite(one);
17
Lines 16-19 declare a local variable, one, an set its value using a new expression to create a square 0.1
screens on a side. Then the square is colored gold, located at the center of the screen, and added to the game
(addSprite means that the game loop will draw the sprite). These four lines will be very familiar by the end
of this chapter as we will use them with multiple different kinds of sprites.
For this program, the following twenty or so lines do the same thing four more times. Each rectangle is a
different size and every other one is given a different color.
Five local variables are declared an initialized using new to construct squares from 0.1 to 0.5 screens across
(sounds like the sizes match those in the picture). They are located at the center of the screen, alternately
colored blue and gold, and each is added to the game with a call to addSprite. Those four lines are going to
become very familiar through out this chapter as we create, locate, color, and add sprites to the various games.
Five rectangles are created in sizes from 0.1 screen to 0.5 screens across. They are alternately colored blue
and gold. They are all centered at the center of the screen. And each, after it is sized, colored, and positioned,
is added to the game. Thus we should see five squares of alternating colors. Running the program we see in
Figure 3.5 that there is only one gold rectangle visible. What happened to other four rectangles?
Games are two-dimensional with an x-axis running from 0.0 to 1.0 from left to right and a y-axis running
from 0.0 to 1.0 from top to bottom. This is also a stacking or layering order. Most drawing programs (Inkscape,
Adobe Illustrator, Adobe Photoshop, etc.) have a similar ordering.
The first sprite drawn on the screen is behind the second sprite which is behind the third and so on. You
could imagine that sprites were made of construction paper and each is added to the scene covering up any-
thing it overlaps. We were counting on this behavior when we designed the target using five squares. Each
“ring” would need four rectangles without the overlap.
Look back at Listing 3.2. The lines in the setup method are executed in sequence, starting with line 16 and
running through line 39. The addSprite lines, the lines which add a sprite to the scene, are executed in order
from smallest to largest. Sprites are stacked in the order they are added to the scene. The other lines, where
the sprites are colored, located, or even constructed, do not impact the stacking order. The addSprite lines
execute in the reverse of the order we desire.
The names, one through five are arbitrary. Reversing chunks of code which construct, color, position,
and add each sprite results in the following version of Target03 (only setup is show; the rest is analogous to
Target01).
14 five.setColor(getColor(”gold”));
15 five.setLocation(0.5, 0.5);
16 addSprite(five);
17
30 two.setLocation(0.5, 0.5);
31 addSprite(two);
32
Keep stacking order in mind when figuring out the order to add sprites to the screen. If you need to add a
sprite below all other sprites in the game, Game also has an addBottom method which works just like addSprite
but it adds the sprite to the bottom of the stacking order. Overlapping sprites can be used to create interesting
pictures with very simple geometric objects.
Round: OvalSprite
An OvalSprite is specified like a RectangleSprite with its width and height. Thus CircleTarget03.java makes
a circular target like Target03.java:
1 import fang2.core.Game;
2 import fang2.sprites.OvalSprite;
3
31 addSprite(two);
32
The code (called CircleTarget03 to match Target03; CircleTarget 01 and 02 are left as exercises for the
interested reader) constructs five circles6 in alternating colors. They are added to the scene from largest to
smallest, thus they overlap the right way.
What else can we do with layering? What if we draw some things in the background color? That would
appear to erase any part of other sprites they overlapped. What would the following setup draw?
6A circle is a special case of an oval, one where both diameters are the same. An circle is to an oval as a square is to a rectangle.
68 CHAPTER 3. FANG: A SURVEY OF CLASSES
(The rest of the program is just what you would expect, import for Game and OvalSprite and a public class
named for the file, Oval. Many of the following sprite examples will just show the setup method, starting with
the method header (just after the @Override line).)
Figure 3.7 shows the output: left and right are white circles, each centered half-way between the center
and the obvious edge of the screen.
The white oval is wide and short, centered on the whole screen. Thus without black, the picture would be
a oval centered and two circles above it. What does black do to the picture? It is between the layers with white
and the circles. Thus the circles are in front of it (and are not modified). The white oval is occluded by black
but not entirely. The edge of black touches the center of the screen (and thus, the center of the white oval).
That means the bottom of the white oval is visible with a curved section cut out of the top.
One skill you should work on is being able to read a group of sprite coordinates and picturing what it looks
like. This will help you in designing complicated shapes out of simple geometries.
The next section takes a break from presenting sprites, instead looking at modifying rectangles and ovals
with different transformers, objects that add animation behaviors to sprites. Then, the section after presents
a mix of new sprites and new transformers to give you a broader pallet when working with FANG.
Transformers
Sprites represent screen objects. A given sprite can have any number of transformers associated with it. A trans-
former transforms the sprite in some way: some, like the VelocityTransformer modify the sprite’s location;
others, like the SpinTransformer change the rotation; some change the scale or the color, ScaleTransformer
and ColorInterpolatorTransformer finally, some like KeyboardTransformer filter and modify when or how
other transformers are applied.
design goals in making the transformers was creating many small, single-purpose transformers which can be
used together. Since moving and wrapping around are two different actions, this requires two transformers.
1 import fang2.core.Game;
2 import fang2.sprites.OvalSprite;
3 import fang2.transformers.VelocityTransformer;
4 import fang2.transformers.WrapTransformer;
5
15 addSprite(oval);
16
Listing 3.6 creates a small OvalSprite, giving it the label oval, giving it the color cornflower blue, and
positioning it at the middle of the screen. Then two transformers are constructed and added to the sprite.
Notice that the transformer classes are in the fang2.transformers package.
Line 20 calls the VelocityTransformer constructor. The constructor needs a direction and a speed, the
direction and speed with which associated sprites will be moved. The direction 0.0 degrees points to the right
of the screen (along the positive x-axis) and rotation goes in the anti-clockwise direction. So, 25.0 degrees is
slightly up and to the right. The length of the velocity is screens per second. A transformer is added to a sprite
with a call to addTransformer; line 21 adds the velocity transformer to oval.
What does it mean to have velocityTransformer added to oval? It means that once the game is started (by
pressing the Start button), the transformer will begin updating the location of the sprite, animating it with
the given velocity.
A WrapTransformer wraps one edge of the screen around to the opposite side. When a sprite moves com-
pletely off the screen in one direction, the wrap transformer moves it to the corresponding point on the oppo-
site side. Line 24 creates the WrapTransformer; it has no parameters since it always does the same thing. The
sprite moves around the screen as if the screen were mapped onto a giant donut (technical term, torus).
There is no Circle.java screenshot: either the circle would be in the middle of the screen before the Start
of the game or it will be shown at some other spot on the screen during the game. Neither image serves to
improve understanding of these two transformers. Instead you should run the program and try changing the
values passed in to the VelocityTransformer constructor to see if you can change how the circle moves.
SpinTransformer
Most transformers can be applied to multiple sprites at the same time. So, if we wanted to take Target03 and
have all of the boxes spin together, one SpinTransformer added to all five of the RectangleSprites suffices.
The following listing shows only setup; the only difference in the rest of the program from Target03 is the
addition of the appropriate import line.
14 public void setup() {
15 RectangleSprite five = new RectangleSprite(0.5, 0.5);
16 five.setColor(getColor(”gold”));
17 five.setLocation(0.5, 0.5);
18 addSprite(five);
3.2. FANG BASICS 71
19
The new lines are at the end of setup, 40-46. Line 41 declares a local variable of type SpinTransformer
having the name spinner; remember that a variable is declared with a type name (a class name, typically)
followed by an identifier. We could call the variable groundhog, yodel, or just plain x. There is much more on
naming in Chapter 4 but for now you can think of the names you choose as descriptions for human readers of
your code. Choosing a name that describes what the thing it labels is for, in this case it is a SpinTransformer,
meant to spin all of the squares in the target.
So, spinner is assigned a value, the value returned from new when a SpinTransformer is constructed.
The parameter passed to SpinTransformer is the number of degrees per second to rotate. As always, posi-
tive rotation is in the anti-clockwise direction. The next five lines then add the new transformer to all five
RectangleSprites.
This shows a feature of transformers: most of them are designed to work with multiple different
sprites at the same time. This permits you to have an entire group of, say, lemmings, all using the same
VelocityTransformer to move them all in the same direction. It is even possible to use a single WrapTransform
to wrap every sprite on the screen. Figure 3.8 shows the target as it is spinning. Again, it is much more
interesting to watch while it is running.
To use a transformer you must
• addTransformer to the sprite. That is done by calling the addTransformer method using the sprite vari-
able:
<sprite-variable-name>.addTransformer(<transformer-variable-name>);
The next section surveys sprites and transformers available in FANG, showing some different combina-
tions.
More of Everything
Words: StringSprite
The StringSprite class is for displaying words, numbers, and anything else composed of characters on the
screen. We will use it for displaying things like the score of a game or the number of seconds left in a level.
Hello.java demonstrates how to create a StringSprite.
3.2. FANG BASICS 73
1 import fang2.core.Game;
2 import fang2.sprites.StringSprite;
3
Line 10 constructs a StringSprite. The constructor takes a string, in double quotes, which is the text
displayed by the sprite, and a scale, the height of a line of text in screens. The width of the sprite is determined
by FANG and depends on the width of the text contents at the given letter size.
StringSprite has a method, setText, which also takes a string as a parameter. It permits you to change
the value displayed by the sprite. A string is a sequence of characters between double quotes, ” characters.
Inside the double quotes you may have any characters except for double quotes (it marks the end of the string)
and new line characters (the quotes must be on the same line). We will see how to encode these characters
below.
Figure 3.9 shows what Hello looks like when run. As with other sprites, you can create any number of
StringSprites and add them to the game. Inside a string you can use special character sequences: \n (“slash
en”) stands for a new line so the current string well start a new line; \” stands for the quote character. Both of
these sequences have two characters in them but stand for only one character in the string.
1 import fang2.core.Game;
2 import fang2.sprites.StringSprite;
3
21
The colors of the four sprites in Strings.java are different, drawing attention to the two colored lines
(only one of which really stands out in the text)7 . The text is also emphasized by increasing its size.
Finally, ZoomingHello shows the ScaleTransformer at work. It starts with the Hello program and adds a
new ScaleTransformer to the StringSprite. The ScaleTransformer constructor takes three numeric param-
eters: the seconds the transformation will take, the starting scale in screens and the ending scale in screens.
StringSprite scale is the height of one line. So, the string starts very small (0.05 or a twentieth of the screen
high) and grows (or zooms) to fairly large (0.50 or half of the screen high). There is not screenshot of the
animation.
1 import fang2.core.Game;
2 import fang2.sprites.StringSprite;
3 import fang2.transformers.ScaleTransformer;
4
7 This quote is an homage to JRR Tolkien’s “Do not meddle in the affairs of wizards, for they are subtle and quick to anger.” [Tol54]
The current author was unable to find any authoritative source for the dragon quote.
76 CHAPTER 3. FANG: A SURVEY OF CLASSES
Additional features of StringSprites will be introduced as we use them (see, in particular, Chapter 10
where we build a game of hangman).
21 }
HexAndLine.java draws an orange hexagon centered on the screen and crosses it with a yellow green line.
Line 12 in the listing calls the setScale method on the hexagon; this method takes a size, in screens, and
scales the sprite to that size. All sprites support setScale so we could have used it to set the scale of any of the
RectangleSprite, OvalSprite, and StringSprite we created in other programs. We had to use it here because
the regular polygon version of the PolygonSprite constructor does not have a scale parameter.
A VelocityTransformer can be modified by making it bounce. The reason bouncing is associated with
a velocity and wrapping isn’t is that the velocity needs to be turned around when something is hit. So the
BounceInsideRectangleTransformer takes a sprite, the sprite to check for hitting the edge, and a velocity
transformer. Notice the use of lowercase: bouncing transformers work with any transformer which provides
a velocity to a sprite so VelocityTransformer and AccelerationTransformer and other bounce transformers
would also work.
1 import fang2.core.Game;
78 CHAPTER 3. FANG: A SURVEY OF CLASSES
2 import fang2.sprites.LineSprite;
3 import fang2.sprites.PolygonSprite;
4 import fang2.transformers.BounceInsideRectangleTransformer;
5 import fang2.transformers.KeyboardTransformer;
6 import fang2.transformers.VelocityTransformer;
7
Listing 3.12 shows a modified HexAndLine: at the end of setup a VelocityTransformer is constructed and
then modified by a BounceInsideRectangleTransformer. By default, the rectangle the sprite bounces in is
bounded by 0.0 and 1.0 in the x and y dimensions: it matches the screen. Listing 2.7 at the end of the last
chapter demonstrates how the minimum and maximum values can be included to have the sprite bounce
inside a larger or smaller rectangle.
Red, green, and blue: primaries? The computer projects light out of its screen and into your eye (just as
a color television set does). Rather than using the subtractive primary colors, red, yellow, and blue, a system
projecting rather than reflecting light uses a different set of primary colors. The subtractive primaries are
called that because each subtracts some frequencies of light from what it reflects. When you mix all of them
together you get black (or, with most paint sets, a dark, muddy brown). The red, green, and blue (RGB) additive
primaries each add frequencies to what the screen is projecting and mix together to make white.
We have used different colors for our different examples yet avoided including the Color class. That is
because the Game class has a getColor method which returns a Color. We have used that method and passed
its results to setColor for a sprite or setBackground for the game.
3.2. FANG BASICS 79
Colors can be specified by name (see Appendix D for a listing of all color names; the list includes those
colors shown for the Web at http://www.w3schools.com/tags/ref_colornames.asp), by specifying the Web
numeric value, or by specifying the numeric value for the RGB and, optionally, the opacity channel.
Outlining Sprites
One other feature sprites support is outlining their shapes. The following setup method creates two outlined
ovals and rotates one of them 45 degrees. The outlines are different colors so we can tell the original from the
rotated oval in the screenshot.
19 originalWing.setOutlineColor(getColor(”white”));
20 originalWing.setOutlineThickness(0.01);
21 originalWing.showOutline();
22 originalWing.setLocation(0.36, 0.6);
23 addSprite(originalWing);
24
Lines 23-25 (and 31-33) demonstrate how to turn out outlining: you specify the outline color, the outline
size (in screen widths) and, finally, tell the sprite to show the outline. After you specify an outline, scaling the
sprite will scale the thickness of the outline as well. This is consistent with the way most vector art programs
handle scaling.
80 CHAPTER 3. FANG: A SURVEY OF CLASSES
The screenshot for LeftWing.java shows the original oval, outlined in white, and the rotated oval, outlined
in black. This shows that positive rotation is clockwise. The screenshot also shows that the color used to fill
the ovals is translucent.
Look at line 22. The color is created with the call getColor(”Wheat”, 128). The name, ”Wheat”, is one
described in the appendix. The number, 128, specifies how opaque the color should be. If the value were 0, the
wheat color would be completely transparent and if it were 255 the wheat color would be completely opaque.
The higher the number, the more opaque the color. This means that when we don’t specify how opaque to
make a color, it is given an opacity of 255.
Review 3.2
(a) Does the order in which you add sprites matter? Why or why not?
(c) Which of the following are NOT valid colors? (Hint: See the appendix on available colors)
(a) Light Rose
(b) Rose
(c) Dark Brown
(d) Dark Green
(e) Mauve
(d) When you use the FANG Engine are you limited to the named colors? Why or why not?
82 CHAPTER 3. FANG: A SURVEY OF CLASSES
(e) Try writing a program for Figure 3.4 without having any sprites overlap. (Hint: As mentioned in the text,
all but the innermost ring will require four rectangles.)
(f) What would happen if, in Circle.java, you commented out lines 24 and 25? How would the circle react
when it went off of the screen?
(g) How would you modify SpinningTarget03.java so that just the center rectangle spun? Try it and see.
Could you make the inner and outer rectangles rotate in opposite directions? How?
Figure 3.14 shows the first page of the documentation with three panes:
8 Hence the keyword extends.
9 http://java.sun.com/javase/6/docs/api/
10 http://www.fangengine.org/index.php/API
3.3. EXAMINING A PUBLIC PROTOCOL 83
1. Packages Pane Java collects classes into packages; this pane permits you to select a package to view. If
you select a package, the classes in the Classes Pane will be limited to only those classes in the selected
package; All Classes returns to the all class view for the given library.
2. Classes Pane An alphabetical listing of all of the classes in the currently selected package (or all the classes
in the whole library).
3. Documentation Pane The documentation for the selected class or, if no class has yet been selected, a sum-
mary of the available packages.11
Also at the top of the page is a long, indented sequence of class names. This is the inheritance hierarchy
or list of ancestor classes of Game. Game extends GameRedirection which extends GameLoop which. . .and so on.
Notice that the top of the hierarchy, the farthest back ancestor of Game is java.lang.Object; all classes in Java
extend (through some number of intermediate classes) Object. Java makes such extensive use of the java.lang
package that it is automatically imported into every Java program without an import line.
The text after the declaration of the class describes how the Game class was intended to be used. This
is a comment documenting the intent of the FANG programmers in making the class. Below the screen is a
11 The “API Specification” in the title of the page stands for application programmer’s interface; this use of the word interface is similar
to our use of protocol. The API is the collection of classes and methods provided by a library.
84 CHAPTER 3. FANG: A SURVEY OF CLASSES
series of tables, the Field Summary which lists any public fields, the Constructor Summary which lists all public
constructors, and the Method Summary listing all of the public methods.
Fields, as we discussed above, are labels for values stored in an object. We have only seen how to declare
private fields. Some classes have public fields and if they do, they are listed, along with a comment about
what they are used for, in the Field Summary. We will look at how to set the visibility of fields as we study how
to write our own classes.
The Constructor Summary lists all of the forms that can follow new to construct a new object of the given
class. Figure 3.16 shows the top of the Sprite class page. The Constructor Summary shows that a raw Sprite (the
parent class of all sprites in FANG) can be created with an empty parameter list or a parameter list containing
a Shape. Notice that Shape is a hyperlink, linked to the standard Java documentation (Shape happens to be in
the java.awt package).
The Method Summary is similar to the Constructor Summary in that it lists the return type, name, and pa-
rameter list of every public method. Figure 3.17 shows the Sprite page further down, in the middle of the
Method Summary; this is where you can look up the parameter list of methods you might want to call. What if
we wondered about how setColor is declared and what parameter(s) it expects?
In the middle of the figure you can see that the setColor method has a void return type, is named setColor
(we knew that), and takes one parameter of type Color (which is called color; Java is case-sensitive so the two
names are different. We will discuss naming in detail in the next chapter). What is Color? Is it a FANG or a Java
class? What would we import if we wanted to declare a Color field in our class? Again, notice that types are
hyperlinked to the documentation for the class. Clicking on the link takes us to the page shown in Figure 3.18.
The setColor name is also hyperlinked (as is the name of every method). In the Method Summary is a one-
line description of what the method does. If you need to know more, clicking on the name will take you to
detailed documentation on that method. Below the Method Summary table all of the constructors and methods
have detailed entries and the name in the summary table is linked to the detail.
Figure 3.18 shows the documentation page for the Color type. The package containing the class appears
right before the name of the class in the title. If this package is not java.lang, then you must import the class
3.3. EXAMINING A PUBLIC PROTOCOL 85
in order to use it in your program. Looking at this page, it is easy to see where the import line we have been
used
import java.awt.Color;
Java’s documentation is very thorough and quite well linked (class names lead to the documentation page
for that class). With upwards of 2000 classes, however, finding just the one you need can be daunting; reading
package and class descriptions, even at random, can increase your grasp of these essential types.
disagree, then both are probably wrong.” This is a great bumpersticker in that it is fairly short, pithy, and
deeper than it seems.
The recommendation that you document your intent rather than your implementation makes it easier
for the code to evolve without changing the commented behavior. Quality code requires constant vigilance
against letting errors creep in.
Review 3.3
(a) Using the FANG documentation, what package contains the Location2D class?
(b) What opening and closing markers are used around a Java documentation comment in your code?
(c) How many classes are in the fang2.transformers package? What is the name of the last one alphabetically?
(d) Why should comments reflect intent rather than just restate what the code does?
3.4 Summary
Method Description
addBottom(<sprite>) Add the given sprite to the game. The stacking order is below
all sprites already added to the game.
addSprite(<sprite>) Add the given sprite to the game. The stacking order is above
all sprites already added to the game.
getColor(<color-name>) Get the color named in the string. The name must be a color
that FANG understands. The string is checked in a case-
insensitive manner (so “Misty Rose” and “misty rose” are
the same color) and has all spaces squeezed out (so even
“mIStyROse” is the same color as the other two).
interpreted in context: the same bit pattern in memory might represent the number 65, the letter ’A’, or the
beginning of Beethoven’s Ninth Symphony.
RAM memory is random access in that accessing any given address is just as fast as accessing any other
memory address. RAM is fairly expensive (per byte stored) and is volatile: it loses its value when power
is turned off. Hard disk drives use magnetic storage to provide larger, cheaper (per byte stored), and non-
volatile storage. DVD/CD use optical storage and flash disks (USB thumb drives) use a non-volatile memory
technology.
Input/Output (I/O) is provided by keyboards, game controllers, microphones, monitors, network cards, and
printers. The operating system is a program which interacts with the component parts of a computer and can
control other computer programs, starting, stopping, monitoring, and, perhaps, pausing them.
CPU instructions are written in machine language, numbers encoding the specific instructions understood
by a particular processor. Since instructions are numbers in memory, they can be manipulated by other in-
structions: computer memory contains both instructions and data for the instructions to process and the two
can be interchanged at will.
Memory is broken into addressable units which are collections of bits (binary digits). Eight bits grouped
together are a byte and a byte can hold 256 different values. Computer memories are measured in binary
kilobytes, megabytes, and gigabytes: 210 , 220 , 230 respectively.
FANG
FANG is a collection of packages, all subordinate to the fang2 package. fang2.core contains classes central
to FANG’s function such as Game. fang2.sprites contains all standard sprites; a sprite is a screen visible ob-
ject. fang2.transformers is home to all of the transformers provided by FANG; a transformer adds automatic
behavior to a sprite or sprites.
Game
fang2.core.Game is the class a programmer extends to construct a FANG game. The class has two methods game
programmers overload: setup and advance. It also has helper methods, a couple of which are summarized
below.
Sprites
Sprites are screen visible, movable objects. FANG sprites have a location, measured from the upper-left corner
of the screen to the center of the sprite, a scale in screens, and a rotation. Each different kind of sprite has
different constructor parameters but all sprites support a certain standard set of methods. The next two tables
list the sprites we saw in this chapter (with constructor parameters) and then the standard sprite methods.
88 CHAPTER 3. FANG: A SURVEY OF CLASSES
Method Description
setColor(<color>) Set the color to the given color. Parameter is
java.awt.Color; Game.getColor returns the right type.
setLocation(<x>, <y>) Locate the sprite at the given location (coordinates in
screens). Location is based on sprite center.
setScale(<scale>) Set the scale of the sprite in screens. 1.0 means as big as the
screen. On StringSprite, refers to the height of a line of
text.
setRotationDegrees(<degrees>) Set the rotation of the sprite to the given number of degrees
anti-clockwise.
addTransformer(<transformer>) Add the given transformer to the group of transformers as-
sociated with the given sprite.
removeTransformer(<transformer>) Remove the given transformer to the group of transformers
associated with the given sprite. (We did not give examples
in this chapter.)
Transformers
Transformers, extenders of fang2.core.Transformer provide behaviors to sprites to which they are “at-
tached”. In general, one transformer can be associated with any number of sprites and any sprite can have
any number of transformers. The following table lists different transformer classes with their constructor
parameters.
<method-call> := [<object>.]<method-name>(<actual-parameter-list>)
Java and FANG provide extensive documentation. The documentation is generated from the comments
inside the .java source files. It is provided in the form of hyperlinked pages listing all of the packages in a
3.4. SUMMARY 89
library, all the classes in a package, and all the methods in a class. It is the best way to find out what methods a
given class supports. It provides links to the types used for parameters and comments from the programmer
on how the method (or class) should be used.
At the top of a class documentation page is the extends list of all the classes from java.lang.Object to
the documented class. It also shows the package name that must be imported to use the given class.
Java Templates
<type-name> := <identifier>
<variable-name> := <identifier>
<object-name> := <identifier>
<method-name> := <identifier>
<method-call> := [<object>.]<method-name>(<actual-parameter-list>).
Review Exercise 3.1 Describe the function of each of the following parts of a modern computer
(a) Central Processing Unit (CPU)
(b) Random Access Memory (RAM)
90 CHAPTER 3. FANG: A SURVEY OF CLASSES
Review Exercise 3.2 What does volatile mean? How does it apply to computer memory?
Review Exercise 3.3 Why do hard drive manufacturers insist that KB, MB, and GB refer to 1,000, 1,000,000,
and 1,000,000,000 bytes respectively, rather than the values which computer scientists prefer?
Review Exercise 3.6 How do you set the location of a Sprite extending class?
Review Exercise 3.9 What command would you use to create a LineSprite that ran from the upper-right
corner down to the lower-left corner of the screen?
Review Exercise 3.10 How many different named shades of gray does FANG recognize.
Review Exercise 3.11 In the following line of code, what is the name of the object on which a method is being
called, what is the name of the method, and what is the parameter list for the method?
someStringSprite.setText(”Call me Ishmael.”);
Review Exercise 3.12 When does FANG call each of the following methods (if you define one):
(a) public void setup()
(b) public void advance(double secondsSinceLastCall)
Review Exercise 3.13 What kind of transformer would you use to:
(a) Turn a OvalSprite into a billiard ball?
(b) Turn a PolygonSprite around and around?
(c) Make a moving sprite come back on the screen on the opposite side it moved off of?
(d) Make a sprite get smaller and smaller?
Review Exercise 3.14 Look up the class TimeLimitedTransformer in the FANG documentation. What parame-
ters do the constructor take? What does the import line for the class look like?
Review Exercise 3.15 Look up the class ColorTransformer in the FANG documentation. What is it for? What
are the parameters to its constructor? What import line would you use to use ColorTransformer in your pro-
gram?
3.4. SUMMARY 91
Programming Problems
Programming Problem 3.1 Starting with Hello, create a program that displays you first name centered in
the top half of the screen and your last name centered in the bottom half of the screen. Both names should be
displayed in different colors.
Programming Problem 3.2 Using Target03.java as a starting point, make a program, TriangleTarget03 that
displays five triangles with alternating colors, each about a tenth of a screen larger than the one within it. Pick
two contrasting colors of your choice and have all the triangles centered in the screen.
Programming Problem 3.3 Based on ZoomingHello.java write a program that shows your name shrinking
onto the screen. Your name should be written in green and should start up as tall as the screen. Then, over
thirty seconds, it should shrink down to one percent of the screen high.
Programming Problem 3.4 Write a program, BilliardBalls, which adds two different colored OvalSprites
to the game. Each sprite should move around the screen, bouncing off the edges. One should start moving up
and to the right, the other should start moving up and to the left.
To think about before programming: What kind of transformer(s) will you need? Can the sprites share
transformer(s)?
Programming Problem 3.5 Write a program, Jet, which uses simple geometric shapes to draw a “jet” plane
on the screen. The jet should be about a quarter of the screen in size. Animate the jet so that it flies around
the screen, wrapping around from one side to the other.
To think about before programming: What kind of transformer(s) will you need? How may sprites will you
need? Can the sprites share transformer(s)?
Our working definition of game is: a collection of components and associated rules governing their interaction.
Together the components and rules present the player with meaningful choices that shape the outcome of the
game.
The last two chapters presented Java programs using FANG. We have seen the overall structure of a Java
program, how to import FANG (and Java) libraries, and how to construct sprites and transformers.
To define our own game, we override the two methods setup and advance. In previous chapters we have
constructed components and used predefined rules to make games; not many of our programs (except Meteors
and, perhaps, Basketball) presented the player with meaningful choices. A big reason for that is we do not
know how to have Java make a selection, choosing one set of rules or another based on some condition.
This chapter focuses on designing a simple game, as simple a game as possible. Designing the program
to play the game requires examining the list of problem solving techniques presented in the first chapter,
sequence, selection, iteration, delegation, and abstraction. The five techniques lead to a discussion of what an
algorithm is and how top-down and bottom-up problem solving approaches work in computer programming.
Along the way we will define just what an algorithm is and finish our simple game.
93
94 CHAPTER 4. DECIDING WHAT HAPPENS: IF
A single-player game has the fewest players possible. The computer controls the environment within the
game and any opponents; the computer provides the rule followers for any elements of the environment or
opponents requiring them.
Computerized opponents require strategies; the program must have rules for both the game and winning
the game. This violates our current goal of simplicity. A computer game where the computer enforces the
rules is a form of solitaire. Note that many arcade games from the 1980s and 1990s (the Golden Age of video
games to many) are solitaire games.
The minimum number of components is also one; it is difficult to imagine rules where a single component
on the screen constitutes what we would consider a game. The fewest components in a viable game is two.
Now for the rules. We will consider games where the player directly interacts with at least one of the
components. Thus one component in some way represents the player. The other component and the screen
represent the “rest” of the game: the state of the game is communicated to the user through the single re-
maining component.
Many games are possible with just these simple components. Gameplay depends on many things: the
computer component could move or remain stationary; the user could control the position, facing, speed, or
size of their component; when moving, components might interact with each other or the edges of the screen
in different ways.
If the computer component moves on its own, the player can be tasked either with catching it or avoiding
it. Our first game, NewtonsApple, will have the player catching the computer-controlled component.
In NewtonsApple, the player plays (moves) a component representing the famous physicist, Sir Issac New-
ton. The computer will randomly “drop” apples by placing them along the top of the screen and moving them
toward the bottom. While limited to moving from side-to-side, the player must move Sir Issac so that each ap-
ple falls on his head. NewtonsApple’s score will be the ratio of the number of apples caught to the total number
of apples dropped. Figure 4.1 draws a picture of the game play area.
dropped
t er C ontrol ed
Compu
A pples
A pples
Newton
l ed
Contro
Mous e
There is a common sense dictum that when you don’t understand some problem, draw a picture. This
holds particularly true in game and computer program design. This sort of diagram, a picture of the screen
with components and some notes on their behavior, captures many of our design decisions more clearly and
compactly than natural language. The design task is explicitly about taking a general idea and specifying it,
understanding it well enough to be able to make the game or program. Deep understanding is also required to
permit us to imagine how the game could be changed to make it more fun or how to translate it into a different
medium.
Chapter 1 discussed the idea of differentiable outcomes, winning or losing. Differentiable outcomes imply
some way of keeping score. In the text description of the game this was mentioned as “the ratio of the number
of apples caught to the total number of apples dropped.” Our diagrams help us realize when important things
are missing: we have added a third component, the score. When designing a game or a program, it is important
to make everything explicit.
Scores are an important part of a game; players use them as feedback on how well they are doing and can
compare them with other players for an indication of relative abilities. Our score tracks “trials” and “suc-
cesses”. This means that whenever an apple is dropped (the program selects a random spot for it to fall from)
the number of trials is incremented; whenever an apple is caught, the number of successes is incremented.
We will use different scoring metrics in other chapters.
define advance
do nothing
setup
while (not game over)
displayGameState
getUserInput
advance
displayGameState is where FANG goes through the list of all sprites that have been added to the game
and draws them on the screen1 . The getUserInput step is done to see what has happened with the mouse or
keyboard. The advance step is where, based on the current game state and the user’s input, the new state of
the game is calculated. Then the whole loop begins over again with displaying the new game state.
The game state is the current condition of the game. You could consider the game state for chess to be the
current configuration of the board, the time on the chess clocks for each player, and, possibly, the list of moves
that got us to this point. The state of a game of tennis is the score, who hit the ball last, the location and “state”
of each player (tired, thirsty, distracted, etc.), and the location and state of the ball (velocity, bounciness, etc.).
Overriding
Figure 4.2 shows two classes which extend Game: NewtonsApple from this chapter and ZoomingHello from the
last chapter. Each box represents a class (named at the top of the box), below it are listed the methods defined
1 FANG uses the painter’s algorithm, drawing each sprite in order from the back to the front; this is why sprites added later overlap
sprites added earlier. Note that it is possible to reset the ordering of the sprites.
96 CHAPTER 4. DECIDING WHAT HAPPENS: IF
Game
public void setup()
public void advance(double seconds)
ex
te
nd
s
ds
ZoomingHello
e xte n
public void setup()
NewtonsApple
public void setup()
public void advance(double seconds)
in the class (not all of them but all the ones we are interested in). The arrows, labeled extends indicate that
the class at the end of the arrow extends the class the arrow points to.
One key thing to keep in mind when looking at the core game loop description above is that setup and
advance are Java methods, part of a Game object. In the last two chapters we saw that the Java syntax for
calling a method is [<object-name>.]<method-name>(<actual-parameter-list>). So, somewhere inside of
FANG lurks the following code:
Game currentGame = new /* MAGIC CONSTRUCTOR! */;
...
currentGame.setup();
...
/* we don’t know the syntax for this yet */
while ... {
...
currentGame.advance(timeSinceLastCall);
...
}
What is a magic constructor? It is one which constructs the Game-extending class which we are actually
running. That is, when the command-line (leaving aside the -classpath) is java ZoomingHello, the magic con-
structor line becomes new ZoomingHello() and when the command-line reads java NewtonsApple, the magic
constructor becomes new NewtonsApple().
When Java runs ZoomingHello, what happens when it calls currentGame.setup? Java begins at the bottom
of the extends diagram with the class that came after new. Starting at ZoomingHello, Java seeks a definition of
setup. Java sees one, ZoomingHello.setup and calls that method (the version we wrote to add the StringSprite
and ScaleTransformer to the game).
Inside the game loop, Java does the same thing with currentGame.advance. Starting at ZoomingHello and
working up the extends arrows, it searches for the first (lowest) definition of the method advance. There is
none in ZoomingHello so the search continues with the class it extends, Game. There is a definition of advance
4.2. COMPUTER PROGRAM (GAME) DESIGN 97
in game so each time through the FANG game loop Game.advance is called. The advance method defined there
does nothing.
The same discussion applies to NewtonsApple, the program we are writing in this chapter. currentGame is
really a NewtonsApple so the search starts at that class. Both both methods are overridden in NewtonsApple.
Thus the lowest, most “extended” version of any overridden method is called.
It is public so the FANG engine can call it. It returns void (or nothing at all). It is called advance and it has a
single element in its parameter list. That value is a double, a number with a fractional part. When defining
a parameter list each element in the list looks like a local variable declaration, a type name followed by an
identifier. Instead of using an = to assign the parameter a value, the corresponding parameter in the call will
be used to give it a value. More on parameters getting values later.
secondsSinceLastCall is a long name but it is an attempt to say what the parameter means: FANG tries
to call advance twenty times a second but there might be times when it does not hit that rate exactly. So,
secondsSinceLastCall has a value very near 0.05 (seconds) but FANG calculates the actual time since the last
call. The value is passed in to advance so that it can be used for calculating things like how far the apple has
fallen since the last time it was moved.
Before we fill in the body of the advance method, the next section discusses program (and game) design in
light of different problem solving techniques.
Review 4.1
(a) According to our definition, is Newton’s Apple a game? Why or why not?
(b) Two methods of the Game class are typically overridden when writing a game. Chapter 2 covered simple
programs overriding just the setup method.
This section describes the other method that is typically overridden. What is the name of that method?
What is that methods header?
(c) What value does the parameter have when FANG calls public void advance(double secondsSinceLastCall)?
What is the meaning of the floating point number which is passed?
(d) Approximately how frequently is the advance method called by the game engine?
(e) If a very complex advance method took 0.1 seconds to execute could the game engine call the advance
method at the normal rate?
author of a cookbook: you do not take a handful of fresh ingredients and create a fine meal; you describe how
any competent cook could take a handful of fresh ingredients and create a fine meal.
What makes this design? What does it have in common with, for example, game design? It seems, looking
at the various design fields, that they all have the designer indirectly creating experiences. That is, a graphic
designer uses art and technique to create a poster (or computer interface or billboard or...) but the poster’s
purpose is to make the viewer want to purchase a particular brand of soft drink. Similarly, the computer
programmer writes code that helps the user accomplish some task, solve some problem. There is a level of
indirection in the thing being built.
So, what is a game designer building and what purpose does it have. Falling back on Jesse Schell’s The Art of
Game Design [Sch08], he claims that game designers build games in order to induce players to have a particular
experience. He goes on to talk about the four basic elements of a game: aesthetics, story, mechanics, and
technology. I mention these elements here because if we become good at software design these same elements
can be applied (perhaps giving short-shrift to story).
The capitalized entries are from the sections of the rules[Dar35]. Notice that the setup is done sequentially.
The body of the game loop is also done in sequence, each player taking their turn and then passing the dice
to their left. As part of each player’s turn, what they are able to do is selected according to the type of space
they landed on. The game loop iterates until only one player remains in the game. When a player performs an
action on the square they landed on, how to do that is delegated to the appropriate section of the rules. This
particular game does not have a good example of abstraction.
Notice that the delegation even has parameters:
BUYING PROPERTY. . .Whenever you land on an unowned property you may buy that property from
the Bank at its printed price. You receive the Title Deed card showing ownership; place it face up
in front of you.
The emphasis was added. The whole buying procedure depends on what unowned property you land on.
That property is the one which may be purchased, it is the one which determines the price, and it is the
property that determines what title card the player receives.
This loop is also similar to the standard game loop. Players look at the board, roll dice to update their
position on the board (state), decide what to do on the landing square (input), leaving their token and title
cards where the next player can see them when they go to move.
Notice that when you design a game, any game, you start with the general game you want to build, consid-
ering the four essential elements of aesthetics, story, mechanics, and technology, and then you describe how
things work at that high level. Odds are, when considering the highest level of the game, you are not thinking
100 CHAPTER 4. DECIDING WHAT HAPPENS: IF
about what color the dice should be or whether the attacker rolls first or second. Those decisions are further
down and would be part of defining how a player makes their move.
Thus a hierarchical approach where details are pushed off to another level is used when designing games
as well as when designing software.
Review 4.2
(a) Designing a program and writing a program both involve problem solving, but at different levels. Which
one is problem solving at a higher level (using instructions will less detail)?
4.3 Sequence
Sequential execution of a computer program means following instructions in exactly the order in which they
appear. In Java, the order in which the source code is written is the normal order for imposing sequence.
Inside the body of a method, the statements are executed in the order written from the first one just inside
the opening curly brace to the last one before the close. Look at the setup method from the Target03 program
from the last chapter:
12 public void setup() {
13 RectangleSprite five = new RectangleSprite(0.5, 0.5);
14 five.setColor(getColor(”gold”));
15 five.setLocation(0.5, 0.5);
16 addSprite(five);
17
Stacking order of sprites depends on the order in which they are passed as parameters to addSprite. What
order are these sprites passed to addSprite? When FANG calls setup, whatever calling method is executing is
4.3. SEQUENCE 101
suspended (but it remembers where it was) and execution moves to the first line of setup, line 13. After line
13 finishes, line 14 is executed and then 15 and 16 and 172 . This is putting together a solution by specifying
exactly what to do first, second, third, and so on.
So, in what order are the sprites added to the game? In line number order of the addSprite lines: lines 16,
21, 26, 31, and 36 in that order or five, four, three, two, and one, largest to smallest.
The only difference between the setup methods of games in the previous chapter and NewtonsApple is that
NewtonsApple does not declare names for the objects inside of setup.
1 import fang2.core.Game;
2 import fang2.sprites.OvalSprite;
3 import fang2.sprites.RectangleSprite;
4
24 addSprite(newton);
25 addSprite(apple);
26 }
27 }
Lines 1 through 7 should be familiar from the previous chapters: Chapter 2: the first three import three
types from FANG, line 5 is a comment, and 6 and 7 declare the name of our class (which must match the name
of the file in which it is defined) and what class this class extends. The end of line 7 is an opening curly brace,
marking the beginning of the container which is the body of our class definition. The matching closing curly
brace is on line 27.
Lines 8 through 26 are indented. Because Java is free-form, Java does not require this though we have shown
it in our Java templates. This is to aid the programmer in seeing the container relationship between the class
and the lines within it.
Contained lines (inside a pair of curly braces) are normally indented one indent level where indent level is
some number of spaces. Simple Computer Games uses two spaces for each indent level; it keeps the code from
2 Okay, blank lines and comments are ignored by javac and never produce any bytecodes when compiled. For all intents and purposes,
then, line 17 is skipped when the method is executed. Since a blank line would do nothing, it is safe (and more uniform) to talk about Java
executing every line in sequence.
102 CHAPTER 4. DECIDING WHAT HAPPENS: IF
wrapping too much in the listings. You may choose to use a slightly greater indent if formatting the code that
way makes the code structure more obvious to you.
Lines 9 and 11 are new. They look something like the declaration of a local variable in one of the programs
in the last chapter (see Listing 4.1 above, lines 13, 18, 23, 28, and 33, for example) but not identical. for example)
but not quite the same.
Each of these lines declares a field. A field is a variable that belongs to an object rather than to a single
method inside the object. The scope of an identifier is where that identifier, that name, can be used in a
program. We will spend a lot of time discussing scope but for the moment the important thing about scope is
that a variable is only in scope or able to be used inside the curly braces in which it is declared. The variable
goes out of scope or cannot be used at all when the next unmatched closing curly brace is crossed. A matched
pair of curly braces and all the code inside of them is called a block in Java. So a variable is only in scope inside
the block in which it is declared.
There is a slight difference in scope for fields than for variables; we will delay explaining it for a moment.
The following is a simplified lie.
In Target03, the local variable five is in scope from the line where it is declared, 13, down to the closing
of the block enclosing the body of setup on line 37.
In NewtonsAppleSprites, newton is in scope from line 11 where it is declared down to the closing of the
block enclosing the whole class definition on line 27.
The important thing to note is that newton is available inside of setup so line 20 refers to the field. newton
is also in scope in any other method we define in the class. That means we can use the value in the field
in multiple methods, as a way of storing game information that should last as long as the game does. Local
variables go away when the called method returns (runs off the end); fields remain “alive” and keep their
values as long as the object to which they belong is part of the Java program.
Declaring a field is a touch more complex than declaring a local variable. The Java template is:
<access-level> := public \|
protected \|
private
The field declaration takes an access level, like what we have said goes in a method header. It is one of three
keywords3 . Remember that in templates, | means “or”. The access level is public or protected or private.
The meaning of public is that any method with a reference to a NewtonsApple object can use a dot and the
name and get or set the value4 . private means that only methods defined in NewtonsApple can use the name.
protected is somewhere in the middle between the two.
Rule of thumb: all fields are to be declared private.
These lines, just like the import lines above, each end with a semicolon. Statements in Java end with
semicolons (unless they end with a curly brace). This is one of the important parts of the artificiality of Java:
the semicolons make it much easier for the compiler to take a program apart into its constituent rules for
translation to bytecode.
apple and newton are fields or parts or NewtonsApple. Each is a type of sprite, an instance of the class
OvalSprite (or RectangleSprite). These objects are part of another object: this is an example of using levels
of abstraction. The two sprite classes know all about things like setting the color and scaling; we don’t need to
think about those things except in order to use them to make our game work. An OvalSprite is an object and
has a .java file and everything. Viewed in the context of NewtonsApple, however, the OvalSprite field named
apple is just a part of the game.
3 There is an “empty” access level. We will discuss it when we discuss Java packages in Chapter 13.
4 The multiple versions of NewtonsApple defined in this chapter represent snapshots over time of the creation of the final game.
While
the complete name of the program will be used in captions of the listings, the name NewtonsApple will be used to refer to the current
incarnation generally when no confusion will result.
4.3. SEQUENCE 103
Just as each component in a chess set has a type (the board, a black knight, a white queen, etc.), every
object in Java has a type. The type of the object is the name of a type or class, either one provided by Java or
FANG or one written by you, the programmer.
Field declarations appear within a class but outside of all methods. The field name is visible everywhere
within the container of the class, including inside any and all methods. The difference between fields and
local variables is exposed: a field is in scope from the opening curly brace of the class definition until the end
of the class definition; a local variable is in scope from the line where it appears until the end of the block in
which it is declared. A field is in scope before you read it in the source code. This will not be a problem in this
book: all Java code is sorted so that fields come before anything else in the class.
Line 9 in Listing 4.2 declares the field apple, a label for an OvalSprite. In previous programs, we combined,
on the same line as the local declaration, the assignment of a newly created sprite to the name. Here we
separate them so as to put all initialization of fields into the setup method.
The declaration creates a name which can be used to label a sprite; no sprite has actually been created.
That is delayed until line 16 is executed and new is called to create a OvalSprite.
The difference between a type or class and an object or instance of the class is the difference between the
blueprints for a house and a house constructed from the blueprints.
If the blueprints are for a house in a subdivision, it might be the case that hundreds or thousands of specific
houses are built from the exact same blueprint. Creation of the blueprint (like definition of the class) does
not indicate that any houses (objects) of that type will be built. Similarly, purchasing a lot (defining a variable
as in lines 9 and 11) for a given kind of house does not build the house. Instead, having a lot (variable) and
having a blueprint (class) means that we can contract to have a house built (call to new).
Line 15 declares a new method, for NewtonsApple. The first line of a method declaration (where the method
is defined) is called the method header. A method declaration is a method header followed by a block of code
defining the body of the method. The Java template is:
<return-type> := <type-name> \|
void
<method-name> := identifier
<method-header> :=
<access-level> <return-type> <method-name>(<formal-parameter-list>)
<block> := {
<statements>
}
• <access-level> : public
This means that objects of types other than NewtonsApple (the type we are currently defining) can “call”
the method. There will be more on access levels in following chapters; for now, all methods should be
public.
• <return-type> : void
Methods can calculate and return values. This one returns nothing so we use the special type, void, to
indicate that no value of any kind is returned. This is enforced by the compiler.
104 CHAPTER 4. DECIDING WHAT HAPPENS: IF
• <method-name> : setup
The name used to call this method. Naming in a computer program is important enough to fill most of
the next chapter.
• <formal-parameter-list> : ()
The parameter list here is empty. The formal parameter list looks like a list of local variable declarations:
<type-name><variable-name>.
Compare that to the actual parameter list we saw in template in the last chapter which looks like a list
of values to assign to variables: <expression>. It looks like these could go together in a local variable
declaration as in <type-name><variable-name>= <expression>. This is no accident and we will come
back to it as we discuss parameters and methods.
The body of a method, just like the body of a class, is contained inside curly braces (line 15’s final {
matches with the } on line 26). After all that explanation, NewtonsAppleSprites draws two sprites on the
screen as in Figure 4.3 shows what the game looks like when run5 . Pressing Start has no effect because we
have not provided a non-empty advance method.
An Experiment
How does advance work? How can we animate the apple sprite without using a transformer? Though we have
set out to design the very simplest game, it will not result in the very simplest program. To get a feel for how
FANG works, we will write the simplest program that puts newton and the apple on the screen and has the
apple fall on his head. Once. No scores, no catching, no random, just a red circle moving down the screen (and
across a green square).
Building little experiment programs, to test out how things work, is a very good habit to get into. This
program will not be added to to reach the final game but seeing a very simple advance method is well worth
the effort.
1 import fang2.core.Game;
2 import fang2.sprites.OvalSprite;
3 import fang2.sprites.RectangleSprite;
4
13 /**
14 * Update the game state: only game state here is where the apple is;
15 * let it fall each frame. Velocity is 0.10 screens/second.
16 */
17 @Override
18 public void advance(double secondsSinceLastCall) {
19 apple.translateY(0.10 * secondsSinceLastCall);
20 }
5 The extra stripe of background to the right of the game space is caused by the name in the title bar being longer than will fit above
the game. This will appear in screenshots in this chapter because of the NewtonsApple prefix on all of the program names.
4.3. SEQUENCE 105
21
33 addSprite(newton);
34 addSprite(apple);
35 }
36 }
Listing 4.3 shows a program almost identical to NewtonsAppleSprites but for the insertion of lines 13-18
(and the changed line numbers below). Methods in the source code for this book will be sorted alphabetically
right after the fields (which are mostly sorted alphabetically). This makes it easy to find methods by name.
Line 15 declares a method (What is the method header? What are the parts of the method header?),
advance. Because advance overrides the version in Game, it will be called each time through the game loop.
secondsSinceLastCall is a number, somewhere close to 0.05 (one twentieth).
Line 16 calls a method on apple. Because apple is a field, the apple referred to in advance is the same one
referred to in setup. Each time through advance, the number of seconds since the last call is multiplied by
the number of screens per second the apple should fall. For the experiment 1 screen every 10 seconds or 0.1
screens/second was chosen. This is far too slow for a reasonable game but it gives us time to watch the sprite
move.
The product of the time and the velocity gives us a distance, in screens. That value is passed in to the
translateY method. All sprites have translateY which takes a distance, in screens, and translates the location
of the given sprite by that distance. translateY (and translateX and translate (which takes an x and a y
value)) differ from setLocation: the translate family moves the sprite relative to its current location while
setLocation moves the sprite to an absolute position on the screen.
Try running NewtonsAppleAdvance. See what happens. How can you slow down the apple further? How
could you make the apple move faster? Why does the apple pass in front of Newton? How could you change
that?
Because NewtonsAppleAdvance looks just like NewtonsAppleSprites, there is no screenshot (it has to be
running to really make an impression). This is the end of working with NewtonsAppleSprites; back to making
NewtonsApple.
Another Method
The apple should not always fall from the center of the screen. It would make the game too easy. Better would
be to have the computer pick a random number on the range [0.0-1.0)6 (Game has a randomDouble method that
does just that) and use that to set the x-coordinate of apple.
We could put the call to randomDouble() right in line 18 of NewtonsAppleSprites. The only problem with
doing that is we will also need to randomly position an apple whenever a new apple is dropped onto the screen.
Looking ahead, it seems we will have to repeat our code at least twice; it is a much, much better idea to have
only one copy of the code and call it from each of the places where it is needed.
The Do not Repeat Yourself Principle. (DRY) Whenever possible, you should not repeat yourself when
writing a computer program. As you learn to use iteration and delegation, you will find that when you need
to do the same thing over and over, with little changes, it is always worthwhile to define a new method that
takes the little changes as parameters. The DRY is one of the simplest rules of software engineering, the discipline
studying how to manage writing high-quality code, yet it is one of the most effective7 .
Why don’t you want to repeat yourself? What if you wanted to change how the apple is placed. If you have
the code to place the apple written out in two different places you might well miss one during the update,
creating inconsistencies which might not be easy to track down. A couple of days from now, it is unlikely you
will remember how many different copies of the code are in the program. You should always think about the
DRY principle when you find yourself duplicating code. It is still sometimes necessary to repeat yourself but
reciting DRY over and over tends to make code better.
How would we declare a helper method to randomly position the apple somewhere at the top of the screen
to begin its fall? To begin writing a method, we need to know its header; the header of a method depends on
its name, which, in turn, depends on what the method does.
6 Range notation in this book uses square brackets, [ and ]. to indicate that the range is inclusive at that end and parentheses, ( and ),
[HT99]
4.3. SEQUENCE 107
The name and the purpose are intertwined. This is an example of bottom-up design because we need to
be able to drop the apple at a different location each time the apple is dropped; the user has no meaningful
choices if the apple always appears right above Newton.
The apple is randomly positioned along the top of the screen each time a new apple is dropped. We could
call our new command dropApple. What is the method header of dropApple? It calls setLocation on a field so
it has no parameters (it always does exactly the same thing). It does not calculate anything for us so it has a
void return type. For now all methods are public and we decided on the name already.
Listing 4.4 show just the dropApple method with its header comment. The ’DropApple8 version of the game
is almost the same as ’Sprites except for the lines 13-19.
13 /**
14 * place the apple at random x-coord at top of screen.
15 */
16 public void dropApple() {
17 apple.setLocation(randomDouble(), 0.00);
18 }
19
Notice that the method does not have a @Override annotation right before the method header. Why not?
Because Game (and other, higher FANG and Java classes) does not define a method with the same method header;
in fact, no class NewtonsApple extends, either directly or indirectly, defines a method with the same name. The
@Override annotation belongs only before methods which override a definition in a higher class (higher in the
extends hierarchy).
There is one other change in ’DropApple: line 25 no longer calls apple.setLocation. Instead it reads:
25 dropApple();
It is necessary to call dropApple explicitly because FANG knows nothing about it and cannot call it automati-
cally for us. So, after creating a new apple but before setup finishes we need to call dropApple; since it sets the
apple’s location, it seems reasonable to replace the call to apple.setLocation with the call to our new method.
Numbers in Java
What is a double? It is a type, like Game or RectangleSprite. It is a built-in (to Java) numeric type. The other
built-in, numeric type we will use is int.
Two questions spring to mind seeing the type names double and int: why are there two different types of
numbers in Java and why are these the only type names we have seen that begin with lowercase letters?
In reverse order: the two types are not classes; in Java all standard class names (and all classes we write in
this book) begin with a capital letter. double and int are different in that variables referring to these types
cannot be used to call a method with a dot. There will be more on the differences between classes and plain old
data in Section 5.2.
There are two different number types in Java because computers deal with whole numbers, integers, and
numbers with a fractional part, floating-point numbers9 with different instructions. The numbers stored inside
the computer, in the bits and bytes, are integers unless we use them to encode a floating-point number.
The details are well beyond the current discussion but a variable of type int can store a whole number
between about -2 billion and +2 billion. When you type a number into a Java program and there is no decimal
8 Read ’DropApple as NewtonsAppleDropApple; the suffix serves as a shorthand reference to the many versions of NewtonsApple.
9 The decimal point in a floating-point number could be between any pair of digits; the decimal point in an integer is always to the
right of its rightmost digit.
108 CHAPTER 4. DECIDING WHAT HAPPENS: IF
point in what you type, then that is treated as an int value by Java: 0, -102, 1023, all are examples of int literals,
values literally typed into the program.
A floating-point number can hold numbers with a fractional part. A double can be typed in literally as in:
0.10, 0.50, 0.00. The trailing zeros do not change the values stored but they make the numbers stand out as
being fractions between 0.0 and 1.0. Notice that 0 (an int literal) and 0.0 (a double literal) are not the same
thing. The range for double is much larger than that for int but it cannot store every integer value in the
range. More on the range in the next chapter.
The name int makes sense: it is the first three letters of integer, the type of number it holds. What does
double mean? There is another floating-point type in Java, float (a name that makes sense!); the range of
float is larger than int but it only keeps about seven digits (both before and after the decimal place). Many
floating-point calculations need more digits of precision so the FORTRAN programming language introduced
the idea of normal or single-precision floating point numbers and higher-precision double-precision floating point
numbers which keep about sixteen digits. The name double comes from double-precision floating point and is
much easier to type and remember.
Keeping Score
The discussion of numeric types was prompted by the use of randomDouble() in the dropApple method. Accord-
ing to the documentation for Game (because there is no explicit object and dot in front of the method name, the
method is called on the NewtonsAppleDropApple object on which dropApple was called), calling randomDouble()
will “Generate a shared random double on the range [0.0, 1.0).” “Shared” means the method works across net-
worked games; not something we care about here. Since we need a random screen location, this is just the
range we want to use so it is what we pass in to apple.setLocation in dropApple.
Numeric types are also used to keep score. What is the score in NewtonsApple? The ratio between the
number of apples caught and the number of apples dropped. This means we need to count apples dropped and
apples caught. Which kind of number would we use for counting apples?
Disregarding applesauce, apples are discrete units so we would use int, adding 1 to the number dropped
whenever an apple is dropped and adding 1 to the number caught whenever an apple is caught.
Wait a minute. When is “whenever an apple is dropped”? It is exactly when dropApple is called. We
can modify dropApple so that it does two things: positions the apple along the top edge of the screen and
increments the number of apples that have been dropped.
We need a place to store the apples dropped count. Is it a local variable or a field? A local variable (inside
dropApple) is not available to any other method, in particular whatever method displays the score. This means
that a local variable has too limited a scope. applesDropped is an int field.
Adding the field to the class and updating dropApple results in Listing 4.5. The import lines and header
comment are excluded in the interest of length but because there are changes in several different spots a
complete listing makes sense.
14 public class NewtonsAppleJustSetup
15 extends Game {
16 // ----- visual representations of the apple and physicist -----
17 /** on screen representation of the apple; a small red circle */
18 private OvalSprite apple;
19
33 /**
34 * place the apple at random x-coord at top of screen.
35 */
36 public void dropApple() {
37 apple.setLocation(randomDouble(), 0.00);
38 applesDropped = applesDropped + 1;// another apple dropped
39 }
40
41 /**
42 * The method called by FANG before the game starts. Include all
43 * ”one-time” instructions in setup.
44 */
45 @Override
46 public void setup() {
47 // initialize the score
48 applesCaught = 0;
49 applesDropped = 0;
50
51 // The apple is small and red; its initial position is set randomly
52 // in the dropApple routine (called here and when apple bottoms out)
53 apple = new OvalSprite(0.10, 0.10);
54 apple.setColor(getColor(”red”));
55 dropApple();
56
72 // must add all sprites we want drawn (or moved) to the game
73 addSprite(apple);
74 addSprite(newton);
75 addSprite(displayScore);
76 }
110 CHAPTER 4. DECIDING WHAT HAPPENS: IF
The field applesDropped is declared on line 26: private means only NewtonsApple’s methods can manipu-
late it. int is the integer type. Where does applesDropped get an initial value?
It is important to think about the difference between the order of definition and the order of execution. In
the rules for Panzerblitz, for example, defines normal combat on page 5, minefield attacks on page 9, and the
standard turn sequence on page 11. The turn sequence begins:
2. German player announces which of his units are attacking which Russian units,
and what attack techniques are being used.
Though minefield attacks were defined on page 9, after the definition of normal combat. Yet game play
follows the definition given on page 11 and begins using the rule defined on page 9, then the rule defined on
page 5, and then the movement rules from page 3 (in the omitted rules). The definition of the rules is in one
order: movement, combat, obstacles (including minefields), and turn sequence. Execution begins with the
turn sequence which refers to the others in the order they are “called”.
In a FANG Game-extending class, setup is the first, automatically called, method so execution begins there.
The first three lines of setup, lines 47-49 set the value of the int fields. So applesDropped is set to 0 (the number
of apples already dropped) in line 49. The apple field is initialized (with a call to new) in line 53. Then setup
calls dropApple.
This means “going backward” in the code. Execution of setup is suspended (but remembered) and execu-
tion begins at line 37 (first line in the body of dropApple). The apple is located randomly and then the number
of apples dropped is incremented.
38 applesDropped = applesDropped + 1;
What does this line mean? If we read = as “is assigned the value of”, then the line reads “applesDropped
gets the value of applesDropped plus one”. This makes sense if we consider the <expression> (that’s what
comes after the = here as well as in a declaration) to be evaluated first. A variable refers to some thing, in this
case an integer. Using the variable with the plus sign (on the right of the = or assignment operator) means “the
value of” the variable. So we calculate the “value of applesDropped plus one” and then assign that value to
some variable (so we can use it again somewhere else).
The first time dropApple runs we know that applesDropped was assigned the value 0 (in line 49). Thus the
right-hand side (rhs) of the assignment operator is 0 + 1 or 1. This is then stored in applesDropped. Each time
line 38 is executed, the value of applesDropped goes up by one. We will examine just how Java stores different
kinds of values in the next chapter.
Lines 62-70 in Listing 4.5 construct the score showing sprite setting its color, contents, and location. Lines
65-66 use methods unique to StringSprite which move the location inside the sprite. Sprite locations are at the
center of the sprite but it is sometimes useful to have the location of a string at the left or right edge or along
the top or bottom. Together the two lines move the location to the upper-left corner of the sprite.
This makes it easier to match up strings and keep them in a given location even when their text value
changes (and might change width). By putting the location in the upper-left corner and anchoring that in the
upper-left corner of the game screen (look at the location in line 67), no matter how many digits the score
grows to, the word “Score:” will stay in the same place.
4.4. SELECTION 111
Review 4.3
(f) Name two numeric types (these are types capable of storing a number).
(g) PolygonSprite is one Sprite-extending type. Name three other Sprite-extending types (these are types
capable of holding 2D graphic and geometric information).
(j) Compile and run NewtonsAppleAdvance. Now change line 94 to apple.translateY(0.10); What happens?
Now move this line to be the last line of the setup method (advance should now have nothing between the
braces). What happens now? Explain the difference.
4.4 Selection
To complete our game, the advance method must do four things:
The code for these is longer than any method we have yet written. The steps, as comments, serve as guideposts
in the code:
36 public void advance(double secondsSinceLastCall) {
37 // (1) Move newton so his x-coordinate matches x-coordinate of mouse
42
54 // (4) Check if the apple has hit the ground (y-coordinate >= 1.0)
60 }
(Notice the line numbers are not contiguous. The comments were pulled from the finished version of
advance.)
112 CHAPTER 4. DECIDING WHAT HAPPENS: IF
No Such Object
Before filling in the code in the above template, we will take a slight detour. Consider a modified version of
checkers where we want to reward the player who last captured an opposing checker. At the beginning of
each player’s turn, if they were the last to capture a checker, they get a cookie. If we have a special square
beside the board where we put the last captured checker, then looking at it at the beginning of each turn, the
moderator can determine whether or not the current player gets a cookie.
What “value” does the “last captured checker” have at the beginning of the game? In fairness, which
player should be rewarded with cookies before either has captured a checker? We need a special value that
indicates that there is no such component, no “last captured checker.”
In Java, the special value meaning no such object is null. This is different than a method that returns void:
void means that no value is ever returned; a method that returns an object can return null to indicate that
there is no such object (right now; if you call it again the return value might change). Back from the detour.
To move newton, we need to get the player’s mouse location. Then, extract the x-coordinate from the
mouse location. Then, set the x-coordinate of newton to the new value. This is trickier than it sounds: What
if the player stared the game but has moved their mouse out of the game to answer an IM? Then there is no
location for the mouse. FANG indicates this by returning null from getMouse2D() when the mouse has no
location.
37 // (1) Move newton so his x-coordinate matches x-coordinate of mouse
38 Location2D position = getMouse2D();
39 if (position != null) {
40 newton.setX(position.getX());
41 }
If the player’s mouse is in the game, move newton. In any other case, leave newton alone. Listing 4.7 shows
the code.
The != symbol reads as “is not equal to” so, if position is not null, set newton’s x-coordinate to the
position’s x-coordinate. The if statement is how selection is written in Java.
The if Statement
Selection is choosing one rule over another. In checkers, if you jump over an opposing checker, then you get
to remove the checker. Or, in chess, if the king is under threat and there is no way to get him out of threat,
then the player whose king is under threat loses.
Each of these two examples have the rule follower either doing or not doing something. It is also possible
to select from two (or even more) different paths: if the temperature is above sixty degrees Fahrenheit, then
where shorts or else where long pants.
Where does this come into play in NewtonsApple? Three places: we must make sure the user’s mouse
location exists before we can use the dot notation with it; we need to be able to check if Newton caught the
apple and adjust the score and drop a new apple if so; we need to check if Newton missed the apple, it hit
the ground, and adjust the score and drop a new apple if so. We will examine the if statement shown in the
previous section, present the Java template for if statements, and then explore Boolean expressions.
Lines 39-41 of NewtonsApple are the if statement that checks whether the player has a mouse position.
FANG indicates that there is a valid mouse position by returning a non-null value; if the value returned from
getMouse2D() is null, there is no valid mouse position this frame.
Never use a dot with a null value. If position were null, the expression position.getX() would cause the
program to crash (halt with an error). If there is ever a chance that an object variable might have null as a
value, you must us an if statement to protect against using the dot with it.
4.4. SELECTION 113
If execution were simply sequential, then having line 40 appear in advance would mean that it must execute
every frame (a method executes from top to bottom when called unless the flow of control through the method
is modified by selection or iteration statements). The if statement on line 39 evaluates a Boolean expression,
an expression which is either true or false, and then executes the body of the if statement if and only if
the expression is true when evaluated. Whether the body is executed or not, when the if statement finishes,
execution continues with the next executable line in the method.
The Java template for the if statement is
<then-statement> := <statement>
<if-statement> := if (<boolean-expression>)
<then-statement>
The <then-statement> is any single statement; when you need to select more than one statement, put a
block there with any number of statements in it. For consistency, we will always use curly braces around even
single statements; you should be aware for reading other programmers’ code that these are not necessary
when there is a single statement.
There is an alternate template for the if statement, the if/else version of the statement:
<else-statement> := <statement>
<if-statement> := if (<boolean-expression>)
<then-statement>
else
<else-statement>
The meaning of the if/else version is that Java evaluates the expression, and if it is true, the
<then-statement> is executed and the <else-statement> is skipped over; if the Boolean expression is false
the <then-statement> is skipped over and the <else-statement is executed. Exactly one of <then-statement>
or <else-statement> is executed depending on the value of the Boolean expression.
Boolean Expressions
What is a Boolean expression? It is an expression with a truth value. One form of Boolean expression is a
comparison of two values. Consider the following snippet of code:
int x = 13;
if (10 < x) {
x = x - 1; // Line A
}
Does Line A get executed? Yes, because the < operator means “less than” and it applies to numbers just as
you would think that it would: it returns true if the left-hand side is less than the right-hand side and false
otherwise.
114 CHAPTER 4. DECIDING WHAT HAPPENS: IF
Java has six standard comparison operators: ==, !=, <, <=, >, and >=. These read “is equal to”, “is not equal
to”, “is less than”, “is less than or equal to”, “is greater than”, and “is greater than or equal to”, respectively.
Two character operators must be typed with no space between the two characters. Less than or equal to is
typed <=; the sequence <~= is interpreted as the operator less than followed by the assignment operator (and,
since that sequence of operators makes no sense, the Java compiler will complain).
Pay special attention to the “is equal to” operator, == and do not confuse it with the “is assigned the value
of” or assignment operator, =. The first compares two expressions, returning a truth value; the second assigns
the name on the left of the operator to be a name of the expression result on the right. Thus x = 13 in the
above snippet assigns 13 to the variable x while x == 13 would return true after the above assignment. Java
will flag the use of = in a Boolean expression as an error.
Another type of Boolean expression is a call to a method which returns a Boolean value. For example, the
Game class has a method called isGameOver(). This method takes no parameters and returns true if the game
is over and false otherwise. The main video game loop uses this Boolean method to determine whether to
do the loop again or not. Actually, it uses the logical inverse of this method. To invert a Boolean expression,
making true false and vice versa, place a ! in front of the expression (and read it as “not”). Thus to have an
if statement based on the game continuing, you could have
if (!isGameOver()) {
someStringSprite.setText(”Game Continues”);
} else {
someStringSprite.setText(”Game is OVER”);
}
Knowing these two ways of getting Boolean values, we know enough to finish expanding the four require-
ments of advance into actual code.
Review 4.4
(a) The statements of a method body are executed in sequential order unless specified otherwise. Suppose you
sometimes want a statement to execute and other times do not want it to execute. How could you indicate
that the order of statement execution should not be sequential and instead should execute the statement
depending upon a condition.
(b) When evaluating a Boolean expression in Java, what are the two possible outcomes?
10 Constant velocity is not how things fall in the real world; it seems that this simplification might keep Newton from discovering
gravity!
4.5. FINISHING NEWTONSAPPLE 115
Sprites can detect intersection with other sprites. This is done using a Boolean method called intersects
which takes as a parameter another sprite to test for intersection with. This method lets us check if Newton
caught the apple. If the apple is caught, we will update the number of apples caught and drop a new apple.
46 // (3) Check if newton has ”caught” the apple
47 if (apple.intersects(newton)) {
48 applesCaught = applesCaught + 1;// another apple caught
49 displayScore.setText(”Score: ” + applesCaught + ”/” +
50 applesDropped);
51 dropApple();
52 }
We know the apple was missed if the apple’s location reaches the ground. Or, when the y-coordinate of
appleis greater than or equal to 1.0 (the bottom of the screen). Listing 4.10 lists the body of the advance
method with the comments and the code filled in; this paragraph refers to lines 54-59.
36 public void advance(double secondsSinceLastCall) {
37 // (1) Move newton so his x-coordinate matches x-coordinate of mouse
38 Location2D position = getMouse2D();
39 if (position != null) {
40 newton.setX(position.getX());
41 }
42
54 // (4) Check if the apple has hit the ground (y-coordinate >= 1.0)
55 if (apple.getY() >= 1.0) {
56 displayScore.setText(”Score: ” + applesCaught + ”/” +
57 applesDropped);
58 dropApple();
59 }
60 }
That completes advance by updating the state of the game: the two movable sprites are moved (one only if
there is a viable new location) and the two scoring conditions are checked. The score is updated if necessary
and a new apple is dropped, again, if necessary.
A note on “dropping a new apple”: That phrase is potentially misleading. This game does something that
a lot of games (and other computer programs for that matter) do: it reuses an already created resource when
that resource is no longer in use by the user. That is, there is really only one apple in the game. When it is
116 CHAPTER 4. DECIDING WHAT HAPPENS: IF
caught or goes splat, that exact same apple is teleported to the top of the screen and magically becomes the
new apple. The player does not care about this (the old apple is no longer of interest) and we are not required
to call new all of the time.
Review 4.5
(a) What Sprite method is used to determine if two sprites overlap each other?
(b) Why is it okay to recycle the apple sprite? What would we have to do if we wanted to create a new apple
sprite each time it was dropped? Where would you put the code to construct the new apple?
4.6 Summary
For a player’s choices to be meaningful, a game must have different outcomes depending on the sequence of
choices the player makes. Not every choice leads to a different ending but some sequence of choices must.
This requires the rules of the game to include selection as well as sequence.
• return type the type returned by the method or void if there is no type to return
Selection
In Java, selection is done with the if statement. The if statement takes a Boolean expression, an expression
which is either true or false. If the expression is true, then the body of the if statement is executed. If the
expression is false, then the body of the if statement is skipped; if there is an else clause, the body of the else
is executed.
<ifStatement> := if (<booleanExpression>)
<thenStatement>
<ifStatment> := if (<booleanExpression>)
<thenStatement>
else
<elseStatement>
Review Exercise 4.1 What type only has values true and false?
Review Exercise 4.5 Start with NewtonsApple.java. Modify the program to drop your first name rather than
a red OvalSprite. The gameplay remains unchanged.
Review Exercise 4.6 The if statement is Java’s version of which of the problem solving techniques?
}
addSprite(rs);
}
Review Exercise 4.9 What can you not legally do with a null reference in Java?
Programming Problems
Programming Problem 4.1 It is mentioned in the chapter that NewtonsApple uses an unrealistic model of
gravity.
(a) The given game uses constant velocity. What is a more realistic model of gravity?
(b) To implement constant acceleration, you would need to replace the constant screen velocity (in
advance) with a variable.
ii. Declare a field in the class called velocity with the type double.
ii. In dropApple, initialize the value of velocity to 0 (screens/second).
ii. In advance, replace the use of the constant 0.33 with the field. (Question: What would the pro-
gram do if you compiled and ran it now? If you’re not sure, why not try it?)
ii. Also in advance, increment velocity by 0.16.
Programming Problem 4.2 Given that the Game class has a randomColor() method which returns a Color (the
type of object expected by Sprite.setColor method)
(a) Where would you change the code so that each time the apple was restarted at the top of the screen it
was set to a random color?
(b) Go ahead and add the required line to the right method.
Programming Problem 4.4 Start with NewtonsApple.java. Modify the program so that instead of limiting
newton to moving horizontally, newton moves with the mouse all over the screen. This is, in many ways, easier
than limiting the movement to a single dimension.
Programming Problem 4.5 Design a pursuit/evasion game similar to NewtonsApple. The “apple” should
begin falling above the player’s square. Then, when the player moves out of line with the falling object, it
4.6. SUMMARY 119
should correct its trajectory by moving 0.05 screens toward the player. That is, if the player’s x-coordinate is
smaller than that of the apple, the apple should subtract 0.05 from its x-coordinate this frame. The same if it
is greater. The player starts with 5 lives and loses one each time the falling item touches them.
This assignment purposely did not describe what “game” is being played. Can you provide a credible
backstory about the game to put the gameplay into an interesting context? Would the backstory be helped if
you changed the same of either the item falling or the player’s representation? See Bogost’s Persuasive Games
[Bog07] for more thoughts on meaningful play.
Chapter
5
Components: Names, Types, Expressions
We have examined what a game is: a collection of components, each with some number of attributes, and a
collection of rules determining how the components interact. The combination of the components and rules
also provide the player with meaningful choices which impact the outcome of the game.
In Chapter 4 we wrote our first game, a Java program that made extensive use of the sequence and selection
techniques of combining problem solutions. We also started to figure out how to design programs in a top-
down manner. This chapter continues using the top-down design approach, creating a game with multiple
cooperating classes; it also presents more information on declaring fields and local variables.
EasyDice Rules
EasyDice is a game with two six-sided (cube) dice. The player makes a wager on their “winning” the current
turn and then the turn begins. The value of a throw of the dice is the sum of the pips on the face of the two
dice. Thus rolling a five and a three would give the value of eight to the roll.
121
122 CHAPTER 5. COMPONENTS: NAMES, TYPES, EXPRESSIONS
The player’s initial roll of the dice either wins the turn immediately or sets the value rolled as the point for
this turn. An initial value of seven or eleven wins a turn of EasyDice on the first roll. Any other value is the
point for the remainder of the turn. 1
When a turn continues with a point, the player continues to roll until either a value of seven or a value
of the point is rolled. The player wins if the next number rolled is the point and the player loses the turn if
the value rolled is seven. Winning the turn means receiving double the wagered number of matchsticks back
while losing means losing all the wagered matchsticks.
EasyDice
P oi n t
10 matchsticks Wager: 1 ms
u e Bu tton
n
Con ti
Roll Again
In Figure 5.1 you can see a simple layout for the EasyDice: the current roll of the dice is in the middle,
the point (from the first roll) is recorded on two dice in the upper-right corner of the screen, and the player
continues to make rolls by pressing the “button” in the middle of the screen.
Note the quotes around “button”: that is really a rectangle and a string displayed one over the other on
the screen2 . To press the button, the player clicks within the box. The game waits for a button to be pressed by
checking for a mouse click within the RectangleSprite representing the button. When the button is pressed,
the state of the game is changed.
Simpler Dice
These dice (both large and small) look dauntingly complex. Each is a RectangleSprite with a collection of
OvalSprites in a contrasting color displayed above them. Depending on the number being displayed, between
one and six different OvalSprites are displayed with coordinates and sizes depending on the coordinates and
1 This is the biggest simplification in EasyDice: in craps there are a number of values (two, thee, and twelve) which will immediately
end the turn with the player losing the turn. Additionally, there are a lot of different side bets possible in a real craps game.
2 This description applies to buttons in most graphical user interfaces, too.
5.1. RANDOMNESS IN A GAME 123
sizes of the dice they are part of (and the number being displayed). We will have to leave dice with pips for a
later chapter; instead we will simplify them to show a large digit representing the number of the face showing.
Thus Figure 5.2 shows the same game state as the previous figure with the face of each die was replaced with
a digit representing its value.
EasyDice 5Poin3t
10 matchsticks Wager: 1 ms
42 C ontinue B utton
Roll Again
The simplified version is developed by the end of this chapter; the version with pips and dice faces is an
exercise in chapter 6.
The public interface is the interface between two different levels of abstraction. When we look down on
OneDie from the point of view of EasyDice, we don’t care what is inside the die or how it works. We just need
to know what we can ask it to do.
Earlier we discussed what a die can do: it displays a number and it can generate a new number. In Java,
we will also need to be able to construct one and set its color, location, rotation, and all of that.
Wait! If we extend a Sprite derived class then we will get much of the appearance methods for free. Thus
we can consider the public interface to be:
public class OneDie
extends ...Sprite {
public OneDie(...) ...
public int getFace() ...
public void setFace(int newFace) ...
public void roll() ...
}
The constructor might take parameters; that remains undecided. The getFace method returns the current
face which is up on the die and roll rolls the die, randomly changing the face which is on the top. Those three
methods provide the services ascribed to a die above.
Considering EasyDice there is one service we missed in discussing a die: you can pick it up and put it down
with a given face on top. You would do this when using a die to keep score or to indicate the point. This fourth
service is provided through the public setFace method.
These four methods, all declared to be public, are the only visible methods in our OneDie class. Looking at
the list, we have no idea how any of them work. But we don’t care because we can use OneDie without knowing
any more. Before we do that, however, we will try our hand at designing another public interface.
What is a Button?
The previous description of a button is that it is a rectangle with some text displayed above it. What is a
button’s public interface? A button can be constructed, it can do all the things sprites can do, it can change
the text displayed on it, and it can tell you whether or not it was pressed. There is not much else that you
would want a button to do. So, the public interface for EasyButton is:
public class EasyButton
extends ...Sprite {
public EasyButton(...) ...
public void setText(String message) ...
public boolean isPressed() ...
When we create a button and put it on the screen, we will call the isPressed method to see if it has been
pressed. On any time through the game loop where the user pressed the mouse button over the screen button,
that method will return true3 .
Again, we don’t need to know more than this to use an EasyButton.
1 import fang2.core.Game;
2
3 /**
4 * Demonstration game using OneDie and EasyButton classes: Roll two big
5 * dice every time the button saying ”Roll Dice” is pressed.
6 */
7 public class RollDice
8 extends Game {
9 /** the button at the bottom of the screen */
10 private EasyButton button;
11
18 /**
19 * Advance the game one frame.
20 *
21 * @param secondsSinceLastCall time since last advance
22 */
23 @Override
24 public void advance(double secondsSinceLastCall) {
25 if (button.isPressed()) {
26 leftDie.roll();
27 rightDie.roll();
28 }
29 }// advance
30
31 /**
32 * Set up the game for play. Initializes all of the sprites (either
33 * here or in other setup functions).
34 */
35 @Override
36 public void setup() {
37 button = new EasyButton();
38 button.setScale(0.5);
39 button.setLocation(0.5, 0.85);
40 button.setColor(getColor(”yellow”));
41 button.setTextColor(getColor(”navy”));
42 addSprite(button);
43
44 button.setText(”Roll Dice”);
45
50
Lines 9-16 declare three attributes of our game. Two things to notice: the names of the classes (which
we are assuming have the public interfaces described above) begin with capital letters (more on Java naming
conventions below in Section 5.3) and, if you look back at the top of the program, these two classes have no
import statements.
Java looks for named classes either in the classpath (as we saw in the examples in Chapter 2) or, if there
is no import statement, it looks in the current directory. Thus if we define OneDie and EasyButton in the same
directory as RollDice, we don’t have to specify any import statements. This feature is made possible by the
strict naming requirement that class names and file names match exactly.
Lines 36-55 are setup; setup runs before advance but sorts after advance in sorted source code. The con-
structors for our new classes, as used here, take no parameters. The rest of the lines here make sense, setting
the color, the location, and the scale of the various components on the screen. Only line 41 is odd; what does
setTextColor mean in terms of an EasyButton?
A button is composed of two different visual elements: the rectangle and the text. When designing a
composite type, if you are going to let programmers set the colors of some of the constituent parts, you want
to let them set the colors of all of them. Thus setColor will set the color of the rectangle and setTextColor
will set the text color. It is often the case that the first time you go to use a newly designed public interface
you find that there is no way to do something you want to do. It is good to notice this before implementing the
entire new class.
Finally, lines 24-29 are advance. All advance does is check if the button has been pressed. If it has, the two
dice each roll themselves. This method is short and sweet. That this is so easy to write gives confidence in the
public interfaces we have defined.
Now, how do we create an EasyButton? The problem is that we need some way to compose two sprites
together into a single sprite.
1 import fang2.core.Game;
2 import fang2.sprites.CompositeSprite;
3 import fang2.sprites.OvalSprite;
4
24 head.setLocation(0, -0.25);
25 redSnowman.addSprite(head);
26
The Snowman program is a demonstration program in the flavor of those presented in Chapter 2: light
on comments, short, and complete. Notice that in setup addSprite is called four times: three times on
blueSnowman and once on the game (with blueSnowman as the parameter). The locations of the three snow-
balls are inside the CompositeSprite.
To demonstrate that the three OvalSprites are treated as a single unit, the program rotates the snowman
about 4 times a minute. The screenshot for this program is of a slightly modified version of the program, one
which adds white lines as the x and y axes of the CompositeSprite. This is so you can easily spot the center of
the snowman.
Writing a Constructor
There are four parts to the header of a standard method: access level, return type, name, and parameter list.
Then the header is followed by a block containing the body of the method.
Until now we have not written a constructor. The header of a constructor is different than other methods in
two ways: its name must match the name of the class exactly and there is no return type. The first requirement
makes sense when you look at how the constructor is used. Listing 5.1, line 26 shows the initialization of
button:
5.2. ONE MORE SPRITE: COMPOSITESPRITE 129
After new is a call to the constructor. It names the type that is being constructed. The second requirement,
that there is no return type, is strange until you realize that a constructor is making the type that is named in
its name.
9 /** A button class. Twice as wide as high with centered text message. */
10 public class EasyButton
11 extends CompositeSprite {
12 /** the button at the bottom of the screen */
130 CHAPTER 5. COMPONENTS: NAMES, TYPES, EXPRESSIONS
18 /**
19 * Construct new button. Horizontal, FANGBlue with same color text.
20 */
21 public EasyButton() {
22 button = new RectangleSprite(1.0, 0.5);
23 addSprite(button);
24 buttonMessage = new StringSprite();
25 addSprite(buttonMessage);
26 }
This listing shows the first portion of the EasyButton definition. Notice that this class extends
CompositeSprite. This means it has all the functionality of a sprite (so setLocation, for example, works
on objects of this type). The two fields, button and buttonMessage, refer to the RectangleSprite and the
StringSprite after they are created. By keeping references to the sprites which make up the composite our
code can implement EasyButton.setText, EasyButton.setColor, and EasyButton.setTextColor. With out a
reference to the message, there is no way to set the text appearing on the button.
The constructor, lines 21-26, constructs two sprites, a rectangle and a text string. Both are positioned at
(0.0, 0.0) inside the CompositeSprite and then they are both added using addSprite. Remember the layering
order of sprites; the layering order inside a CompositeSprite is the same as it is in a regular screen so the
rectangle is added before the text.
In this constructor we used addSprite with nothing in front of it; in setup above we used
blueSnowman.addSprite. What is the difference? In setup, calling addSprite alone will add to the current
object, an instance of the Game-derived Snowman class. The sprite would be added to the screen, not the
composite. Similarly, just addSprite in EasyButton adds the sprite to the current object, an instance of
the CompositeSprite-derived EasyButton class rather than to the screen. Line 42 of Listing 5.1 adds a new
EasyButton to the game.
54 /**
55 * Set text message; resize to fit in one line
56 *
57 * @param message the new message for the button to display
58 */
59 public void setText(String message) {
60 buttonMessage.setText(message);
61 // adjust size of text message to fit on the button
62 buttonMessage.setWidth(0.75);
63 }
64
65 /**
66 * Set the color of the text on the button.
67 *
68 * @param color the color to set the text to
69 */
70 public void setTextColor(Color color) {
71 buttonMessage.setColor(color);
72 }
Lines 59-63, the setText method, makes use of the identically named method of StringSprite. The String
parameter (a type for holding sequences of characters) is passed directly to buttonMessage’s setText method.
Then the scale of the message is reset so that its width fits in the middle 75% of the displayed button.
132 CHAPTER 5. COMPONENTS: NAMES, TYPES, EXPRESSIONS
A CompositeSprite is also a Sprite so it has a setColor method. Unfortunately for us, in EasyButton, the
built-in version does not set the color of any of the elements within the composite sprite. We want setColor
to set the color of the background rectangle (the button field).
Lines 49-52 override the built-in version of setColor. A method is overridden when a more specialized class
provides a method with the same header. The @Override on line 49 is called an annotation. An annotation can
be thought of as a specialized type of comment, a comment that isprocessed by the compiler.
As a comment, line 49 tells the programmer that this is a specialized version of setColor, replacing the
version provided above in Sprite (or, if that one was overridden, the one in CompositeSprite). The compiler
reads the annotation and, for @Override, it makes sure that there is a method higher up in the class hierarchy
to be overridden. If there is no such method, the @Override annotation throws an error. There are other
annotations but they will not be discussed in this book; they are recognized by beginning with the @ symbol.
The local setColor method sets the color of the rectangle to the Color parameter passed into it. This is
similar to the setText method above as it forwards the work to the appropriate sprite within the composite.
The Color type is a Java-defined type; importing java.awt.Color is necessary so that this method compiles.
Lines 70-72 are almost identical to setColor except that they set the color of the StringSprite within the
composite. Notice that this method does not override another, identically named method. The parameter
passed to setTextColor is forwarded to the setColor method of the buttonMessage field. These two color
setting methods show why we kept references to the two sprites in the composite.
Game.getCurrentGame()
Looking back at NewtonsApple you can see how we used the getMouse2D method to determine where the mouse
was during a given frame. Since we did not define it and it is not called with a variable and a dot, the method
must be defined in Game and we get it for “free” when we extend Game.
One important thing to remember about getMouse2D is what value we get back if there is no valid mouse
position. It is the special value null. Keep that in mind as we proceed with this section.
What does the EasyButton’s isPressed method have to do. This is important because isPressed is the last
method in the public interface for EasyButton left to define.
The isPressed method returns true if the player has clicked the mouse this turn and they clicked it inside
our button; otherwise the method returns false. How can we determine whether or not the player clicked
the mouse? And how can we determine where the mouse was pressed?
33 public boolean isPressed() {
34 // The current game may have a mouse click or not.
35 Location2D mouseClick = Game.getCurrentGame().getClick2D();
36 if (mouseClick != null) {
37 if (intersects(mouseClick)) {
38 return true;
39 }
40 }
41 return false;
42 }
Section 3.3 explained how to use JavaDoc documentation to examine the classes and public methods pro-
vided by FANG and Java. The Game class supplies mouse checking methods. In Section 4.4 we used getMouse2D
to have newton follow the player’s mouse.
In addition to getMouse2D which returns the location where the mouse is every frame (if it is within the
game window), there is also getClick2D which returns the location where the mouse clicked during any frame
where it was clicked. The location is encapsulated in the Location2D type; we will use Location2D through its
public interface and leave the details until later.
5.2. ONE MORE SPRITE: COMPOSITESPRITE 133
If the player clicked any mouse button since the last call to advance, this method will return the location
of the mouse click. If the player hasn’t clicked any mouse button or the mouse is not in the window at all, this
method returns... Any guess as to what it returns if there is no location where a click took place?
It returns null to indicate that there is no mouse click available. Thus isPressed should start by calling
getClick2D and testing (using selection) whether the value is null.
Do you see the problem here? Inside a Game-derived class getClick2D is available; isPressed is inside of a
CompositeSprite-derived class. We need to get a handle on the current game. Fortunately the Game class itself
provides us with a utility method called getCurrentGame which takes no parameters and returns a reference
to the currently running game. We can call getClick2D on that reference.
That is just what line 69 does, assigning the result to the local variable mouseClick. Whenever we need
access to the current game from a sprite, be it for getting a color, getting a random number, or getting player
input, we will use Game.getCurrentGame.
We want to return a value from this method; that is what the return statements, lines 38 and 41, do. the
return statement’s template is:
Here the expression after codereturn is evaluated and that value is returned as the result of the method.
We want to return true if the mouse was clicked (mouseClick != null and, if the mouse was pressed, the mouse
click location intersects the button (the button was clicked on). In addition to being able to test for intersection
between two sprites, we can test for intersection of a location with a sprite. Thus intersects(mouseClick) in
line 71 is a Boolean expression returning true if the click is inside the CompositeSprite. Note that the click
and the sprite’s boundaries are in screen coordinates (you must change the location to internal coordinates if
you want to test intersection with subsprites).
When Java executes return, the expression is evaluated and the execution of the method halts (no other
lines are executed). Thus if line 38 executes, line 42 cannot execute unless ifPressed is called again. We
don’t need an else in this case. This makes the routine a couple of lines shorter but still clear. Only if both if
statement conditions are true does the method return true. If either is false, the method executes line 41 and
returns false.
This is because there is no expression of type void. All that happens when this return statement is run is
that the method immediately returns control to the caller. No value is ever returned from a void method.
• Objects must be instantiated (created). This is done using new or by letting the sytem construct an object
for us inside of some specified method call.
• An object variable name is a label for an underlying object in the computer memory.
– An object variable may refer to no object at all: null.
– An object variable may refer to at most one object in memory.
– What object a variable refers to can be changed through assignment.
– More than one label can refer to the same underlying object in memory.
Having these features firmly in mind, we now examine the primitive types and can see how the two types
of types differ.
134 CHAPTER 5. COMPONENTS: NAMES, TYPES, EXPRESSIONS
Primitive Variables
Primitive types can be used to declare variables using the same syntax as object types. For example, the
following line declares two int variables, p and q:
int p;
int q;
When a plain old data type is used to declare a variable, Java sets aside some space in memory to hold the
value. It then permits you to refer to that location or “memory box” by the name of the variable. Notice that
by declaring the variable’s type you have provided Java with the context necessary to interpret values stored
in that box correctly.
The types byte, short, int, and long are all types that can hold integral numeric values. They differ in the
range of numbers they can hold. byte uses eight bits (binary digits) and can hold values from 27 . . .27 1,
inclusive. short uses sixteen bits to hold values from 215 . . .215 1, inclusive. int uses thirty two bits to
hold values from 231 . . .231 1, inclusive (the numbers in the footnote on integer literals). long uses sixty
four bits to hold values from 263 . . .263 1, inclusive. We will not worry too much about these distinctions,
sticking with int values because they will encompass all of the ranges we want to use.
Assigning a value to an int variable uses the same syntax as assigning a value to an object-typed variable. It
can be done on the line when the variable is declared or later. Assuming the following code is inside a method
declaration, the following code assigns the value 99 to p and 121 to q:
int p = 99;
int q;
q = 121;
p = q;
When a new value is assigned to a primitive variable, the old value is destroyed. That is, in the last line
above, right before the assignment executes, the value in the box labeled p is 99. Right after the assignment,
the value is 121. The old value, 99, cannot be recovered from the variable p. Note also that the value 121
appears twice in memory, once in box p and once in box q.
The following code creates two integer variables and assigns a value to each of them and then changes one
of the values:
4 This is not true of all object-oriented languages. In Squeak, for example, everything is an object from the number 9 to the character
’Q’ to the mathematical constant Pi. Java is a hybrid language with both objects and primitive types. Primitive types tend to improve both
the speed and size of computer programs.
5 “Real” is in quotes because computers cannot store the infinite values of actual real numbers; as explained later, these numbers are
1 int p = 99;
2 int q;
3
4 q = p;
5 p = 1001;
At the end of this code, what is the value of q? If p and q were labels for an underlying integer object in
memory, one would expect the value to be 1001; since we were warned above that they are not labels for one
location but rather independent values, it seems likely that the value is 99.
Similar declarations and assignment (using literals) can be done with the other primitive types:
double d = 1.345;
char ch = ’A’;
boolean b = true;
The last example indicates that the keyword true (and, by implication, false) is a boolean literal, a value typed
directly into the program. The type for storing truth values is called boolean in honor of English mathemati-
cian John Boole (1815-1864) who explored mathematics with only two values.
Literal Values
Primitive type values are not created using new. They are so simple that it is possible to include the value
of a primitive type directly in the text of a computer program. For example, 100 is an example of a literal
integer value. If the three characters “100” appear in an appropriate place in a Java program, they are inter-
preted as the integer value that comes after ninety nine (and before one hundred one). This interpretation is
fundamental to Java and is always available.
An integer literal is an optional sign character followed by a sequence of digits. Examples include 100,
-1, +3200000. Notice that the last example does not use separators to group digits (that is, it is not written
3,200,000). Integer literals are, by default, of type int, a type that can store numbers from minus two billion to
plus two billion6 . When writing an integer literal, avoid using any leading zeros as this changes the meaning
of the number7 .
A floating point literal, one that represents a “real” number, consists of an optional sign, a sequence of
digits, a decimal point, and a sequence of digits. They are called “floating point” because the number of digits
before and after the decimal point can vary (the point floats within the maximum number of digits that such
a number can hold). Examples of floating point literals include 0.5, -19.876, and 100.0; notice that the part
after the decimal can have the value of 0. Floating point literals are, by default, of type double8 Note that Java
literals are typed; though 100 and 100.0 are both numeric literals and both represent the number one more
than 99, they are not the same because one has type int and the other has type double.
A character literal represents a single character and is typed using single quotes. Examples include ’$’,
’A’, and ’n’. The first represents the dollar sign character and the second the capital letter A (which is distinct
from ’a’, the character representing the lower case a). The third example is an example of a special escaped
character. The backslash (\) is not itself considered the character but instead indicates that the next character
indicates the value of the character. ’n’ represents the new line character.
6 actually from -2147483648. . .2147483647 inclusive
7 A literal that begins with 0 is considered to be in base eight (octal) notation;
each character represents a power of eight rather than
a power of ten.
8 The range of a floating point type makes little sense to the beginner but a double can store about 16 digits of precision and values
Methods are rules; they define the actions that objects of this type can perform. The actual results of
applying a given rule might depend on attribute values stored by the object, but all objects of a given type
share the same set of rules. Going back to our four Knights, consider “move up one and left two” as a rule
to apply. Each Knight, starting on a different square, will end up on a different square if this rule is applied.
Further, some of them might not be allowed to apply the rule at the moment (target square might be off the
board or occupied by a friendly piece). But the exact same set of rules applies to each component of the type
Knight (that is, any other Knight in the same position would have the same restrictions applied).
A Chess Example
The type of a component specifies what rules that component follows; it specifies a contract, the public inter-
face presented by components of that type. Taking a broader view of the game of chess, the components are a
game board of 64-alternating colored squares and 32 pieces of six different types (pawn, knight, bishop, rook,
queen, king) in two different colors. The type of a piece determines what rules apply to that piece: a knight
and a rook can both move but the knight can jump from one board square to another without regard to the
intervening squares; the rook can, under certain circumstances, participate in castling. The exact type of the
component determines the rules that it obeys; the rules a component obeys determine what it can do.
In chess, each component corresponds to a physical object. That is, each actual component with which
the game is played is a separate physical object. There are thirty two different pieces in a chess set. In a
programming language like Java, components correspond not to physical objects but rather to logical object
that are created inside the computer’s memory (more on what this means in the next section).
(The address is reversed so that it mimics a valid Java label; more on acceptable Java names follows later in
the chapter.)
A reference to a component is a name that we can use to refer to the component. To refer to the methods
of a given component, to ask that component to follow one of its named rules, we use the dot notation: a name
referring to the component followed by a dot (“.”) followed by the name of the method and a parameter list.
In the fake Java above, the parts of the method call are:
138 CHAPTER 5. COMPONENTS: NAMES, TYPES, EXPRESSIONS
reference BrockportLn123
dot .
Declaring a Name
What is in a name? Would an object by any other name not compute as fast?10 A name in Java is a label for
an object (later we will see what happens with non-object components). At any given time a name refers to at
most one component (it can refer to no component with the null value). Java is a strongly-typed language which
means that each name has an associated type and a name can only be used to label objects of appropriate types.
While this seems limiting, it permits the compiler to catch many typographic and logical errors that would be
much harder to diagnose if the program were permitted to compile and run before there was a problem.
A Java name, or identifier, is a sequence of characters that begins with a letter or an underscore and con-
tinues with zero or more letters, underscores, or digits. Thus an identifier is one or more characters long and
starts with a letter or an underscore. Examples of valid identifiers include:
• getX
• RectangleSprite
• NCC1701D
• ALEPH0
• D1999_Fall
• _someIdentifier
• smithfield
• Smithfield
• SMITHfield
Notice that the last three identifiers are different: Java is case-sensitive and upper and lowercase letters are
not the same. Examples of invalid identifiers (and why each is invalid) include:
• class — “class” is a Java keyword, an identifier reserved for the use of the language. A complete list of
Java keywords is in Appendix A
10 Apologies to the Bard
5.3. JAVA COMPONENTS 139
where the type and the name are replaced with an appropriate type and an appropriate name. For example,
inside of OneDie we will need to keep track of the current value of the die’s top face. This would be an int so
the field would be declared:
private int face;
Similarly, above in EasyButton when we declared the fields for the rectangle and string sprite the decla-
rations were
private RectangleSprite button;
private StringSprite buttonMessage;
These three RectangleSprites are one tenth, two tenths, and four tenths of a screen in size (each is a square).
Every RectangleSprite supports the same interface, that is, can do the same things; each of these three rect-
angles is an independent instance. Setting the color of the small rectangle to red will not change the color of
the other two rectangles. Setting the color of the small rectangle does bring out a particular problem: how
can we change the color of any of these rectangles? Knowing the interface that an object supports is half of
the information we need to ask the object to do something. We also need some way to refer to the specific
object we want to manipulate; we need a name for the object.
In Java all variables with a class type (typically, with a type name that begins with a capital letter; see
Section 5.3 for more on naming conventions) are references to an object of that type. They can also be thought
of as labels for such objects.
What does this mean? It means that a variable of type RectangleSprite is a label that can refer to any given
RectangleSprite or, if we wish, to no RectangleSprite at all. It also means that any given RectangleSprite
can sport any arbitrary number of labels at the same time. There is no way of knowing, given any one label,
how many labels there are on at given object.
140 CHAPTER 5. COMPONENTS: NAMES, TYPES, EXPRESSIONS
Analogy time again: Imagine a Java object is a car. Then the class definition is the blueprint for building
the car and calling new has a new car constructed and returned. As with new in Java, if we wanted to refer to
the car returned, we would need some label. One convenient label would be a license plate; you could assign
the car to a given license plate and then refer to it through the license plate from then on. Note that while
against the law, our analogy permits an arbitrary number of license plates per vehicle.
In Section 3.1 we saw that the computer’s RAM can be considered a long sequence of memory locations,
each with its own address. Further, in that section and those surrounding it, we saw that the meaning of
contents of memory depend on the type of value encoded into that memory location. This is important here
because declaring a variable associates the variable with a specific memory location and determines the type
of value encoded into a memory location.
The boxes in memory can only contain one value at a time. Whenever a value is assigned into a memory
location, whatever was in the location previously is destroyed. When a memory location holds a reference
to an object, assigning a different reference to the variable (storing the reference in the memory location
associated with the variable’s name) destroys the previous reference but it does nothing to the object referred
to.
With the license plate analogy, a license plate refers to at most one car at a time and changing the car
which the license plate labels removes any connection between the license plate and its previously associated
car.
Thus assignment of an object to an object variable is setting the variable to refer to the given object.
Memory
redThing
rect
blueThing
RectangleSprite rect =
new RectangleSprite(0.5, 0.5);
rect.setColor(getColor("red"));
rect.setLocation(0.5, 0.5);
addSprite(rect);
Figure 5.5 illustrates how object variables work. The area labeled “Memory”11 has boxes that can hold
values. The boxes are not drawn next to each other because that is not something we need to know. We just
need three boxes, one labeled for each RectangleSprite.
11 The handwriting font is used for the label to remind us that this is something like how it really works but this is a high-level, human
view of memory. The model is good enough to use until taking a computer organization or architecture course.
5.3. JAVA COMPONENTS 141
Both rect and redThing refer to the same rectangle, the one in the center of the screen. blueThing refers
to no object at all or null. The null reference is pictured as the electrical ground symbol indicating that there
is nothing referred to.
Given the situation in the figure, what would change if the following line were executed:
redThing.setLocation(0.25, 0.25);
The rectangle would be moved from the center of the screen to the upper-left corner of the screen. Note
that either of the references to the rectangle could have been used to move it.
What is the value of x after the following code snippet executes:
rect.setLocation(0.60, 0.9);
double x = redThing.getX();
The first line moves the rectangle again, down and to the right. Then the next line queries the x-coordinate
of the rectangle. Since redThing refers to the rectangle that was moved (with setLocation) to the point (0.60,
0.90), x is assigned the value of 0.60.
Declaring a local variable is declaring an attribute that is local to the method in which it appears. It is, by
definition, private so no visibility keyword is permitted.
Every time isPressed is executed, mouseClick begins with no assigned value. When Java executes the
declaration, previous calls to this method are no longer available. Thus mouseClick has no value. When this
method finishes (when execution runs off the end of the final closing bracket or a return statement is exe-
cuted), mouseClick, the name, is taken off of any object to which it was attached and the name goes away13 .
Local variable scope means that any previously assigned value (assigned during a previous call to isPressed,
for example) was disassociated from the named label at the end of the previous call. This is where we see the
12 We tend to declare everything that we can as private. This is because private data can only be touched by this class, the class where
the private field or method is declared. This means we can protect the integrity of the values since only methods in this class can change
them.
13 The object which was assigned to the variable does not, necessarily, go away. Remember that objects can have multiple labels so if
there is another label referring to the object, Java keeps the object around.
142 CHAPTER 5. COMPONENTS: NAMES, TYPES, EXPRESSIONS
“household atomic replicator” at work; when we left the scope of the variable the memory was reclaimed and
we must explicitly request more memory for an object for it to refer to.
The Java compiler is acutely aware of uninitialized local variables, so much so that any attempt to use one
will trigger a compiler error as in the following code:
70 public boolean isPressed() {
71 // The current game may have a mouse click or not.
72 Location2D mouseClick;
73 // vvvvv Compiler Error vvvvv
74 if (mouseClick.getX())
75 // ^^^^^ Compiler Error ^^^^^
76 }
Java is able to determine that between the declaration of the local variable mouseClick and getX call us-
ing mouseClick. (read as “mouseClick (dot)”), there is no way mouseClick was given a value. Thus the Java
compiler can protect you from this kind of error. Note that it cannot always do this; class-level variables and
parameters cannot be tracked like this.
Just like a class-level variable, a local variable can be assigned to as many times as you like. Assignment
uses the single equals sign (=) which should be read as “is assigned” or “is assigned the value”. Local variables
which refer to class types typically get their value from a call to new or some other method that creates a new
instance of the given type.
Consider ColorRectangle.java, a program to create a randomly sized and randomly colored rectangle
whenever and where ever the player clicks their mouse. Then the local variables in advance, mouseClick and
rectangle are initialized between their declaration and the first time their value is used with comparison or
a method call.
1 import fang2.attributes.Location2D;
2 import fang2.core.Game;
3 import fang2.sprites.RectangleSprite;
4
5 /**
6 * ColoredRectangles demonstrates how to use local variables. The class
7 * has no fields and no setup method. In advance, it checks for a mouse
8 * click and if there is one, it creates a new, randomly sized and
9 * randomly colored rectangle centered where the mouse was clicked.
10 */
11 public class ColoredRectangles
12 extends Game {
13 /**
14 * Check for a mouse click; if one is clicked, make a random
15 * rectangle.
16 */
17 @Override
18 public void advance(double secondsSinceLastCall) {
19 Location2D mouseClick;
20 mouseClick = getClick2D();
21
26 rectangle.setLocation(mouseClick);
27 addSprite(rectangle);
28 }
29 // Is rectangle in scope?
30 }
31 }
Notice the question on line 29. Is rectangle visible at that point? No, because a variable declared within a
block is only usable within the block. rectangle is declared on line 23, the block it is in begins at the end of
line 22, and the corresponding closing curly brace is on line 28. Thus rectangle is only in scope from line 23
to line 28; from where it was declared until the end of the closest enclosing block.
14 http://java.sun.com/docs/codeconv/html/CodeConventions.doc8.html
144 CHAPTER 5. COMPONENTS: NAMES, TYPES, EXPRESSIONS
These named constants represent the x-coordinates of the road, sitter, and house, respectively. When they
appear in a setLocation parameter list, it should be obvious whether or not they are being used correctly.
Consistency in code makes your code easier for a programmer to read. Any programmer, including you,
can tell at a glance whether a name names a component, a rule, or an attribute.
An expression is a piece of code which returns a value. This usage comes from the mathematical meaning
of expression as “symbol or a collection of symbols representing some quantity or a relationship between
quantities” [AHD00]. An expression, then, is a collection of code symbols that evaluates to some value. Like
attributes, expressions have types as well as values.
Components have state, the current value of all attributes that are part of the component. A Pawn, in a
chess game, has a color and a position; at a particular moment, the state of the Pawn might be {white, ”A3”};
15 mixed case with internal words capitalized is also known as camel-case because the resulting names seem to have humps in them:
CamelCaseIdentifier, DrumSet, WhisperingWind.
5.3. JAVA COMPONENTS 145
taken together, these two attribute values completely determine what the Pawn can do16 . This chapter focuses
on expressions and attributes and introduces a class which extends a class other than Game; details on how
classes are defined occupy the next two chapters.
Simple Expressions
An attribute, as we are using the term, is a value associated with and contained in a component. The
applesDropped and applesCaught variables, defined as fields of the NewtonsApple class back in Chapter 2, are
examples of attributes we have used.
When the player catches an apple, the value of applesCaught is changed by assigning the result of an
expression to applesCaught:
applesCaught = applesCaught + 1; // another apple caught
An expression is a piece of code which returns a value. This usage comes from the mathematical meaning
of expression as “symbol or a collection of symbols representing some quantity or a relationship between
quantities” [AHD00]. An expression, then, is a collection of code symbols that evaluates to some value. Like
attributes, expressions have types as well as values.
Components have state, the current value of all attributes that are part of the component. A Pawn, in a
chess game, has a color and a position; at a particular moment, the state of the Pawn might be {white, ”A3”};
taken together, these two attribute values completely determine what the Pawn can do17 . This chapter focuses
on expressions and attributes and introduces a class which extends a class other than Game; details on how
classes are defined occupy the next two chapters.
Arithmetic Expressions
One thing computers are very good at is arithmetic. A numeric expression can be built by combining simple
numeric expressions with unary and binary operators. The type of a numeric expression depends on the type
of all of the subexpressions it is composed of.
The unary arithmetic operators are + and -; either can come before an arithmetic expression. - results in
an expression with the opposite sign of the following expression; + results in the same expression it is given.
The binary arithmetic operators include + for addition, - for subtraction, * for multiplication, and / for
division. The following example expressions are followed by their type and value in a comment.
1 + 4 // (a) int, 5
12 - 100 // (b) int, -88
1.3 + 7 // (c) double, 8.3
1.0 + 4.0 // (d) double, 5.0
3 * 5 // (e) int, 15
3 + 2 * 3 // (f) int, 9
5.0 / 2.0 // (g) double, 2.5
5 / 2 // (h) int, 2
Numeric types narrower than int are widened to int before they are used with arithmetic operators. If the
two operands, the subexpressions of the operator expression, are not the same type, the narrower expression
is coerced to the type of the wider expression before the operator is applied. Thus in (c) above, the int 7 is
16 Chess, because it is thousands of years old, has some arcane rules and a Pawn needs to know its position on the previous turn as well
as the current turn to support en passant capture. To simplify discussion, we will ignore en passant capture as well as castling with the
rook and king (it is necessary to know that neither piece participating in a capture has move previously).
17 Chess, because it is thousands of years old, has some arcane rules and a Pawn needs to know its position on the previous turn as well
as the current turn to support en passant capture. To simplify discussion, we will ignore en passant capture as well as castling with the
rook and king (it is necessary to know that neither piece participating in a capture has move previously).
146 CHAPTER 5. COMPONENTS: NAMES, TYPES, EXPRESSIONS
widened to the double 7.0 before the operator is applied. This is typical of how a Java rule follower evaluates
a binary operator expression: both subexpressions are evaluated and then the operator is applied to the two
values; any required type coercion occurs just before application of the operator.
Example (f) above is 9, not 15 because of the precedence of operators, the rules that govern the order of
application of operations in a complex expression. As was the case in math class, multiplication and divi-
sion operators have higher precedence than addition operators. Thus 3 + 2 * 3 is evaluated as if it were
3 + (2 * 3), not (3 + 2) * 3. The programmer can use parentheses (as in the explanation) to change the or-
der of operators; subexpressions in parentheses are evaluated before applying the operator the parentheses
are an operand for.
Examples (g) and (h) bring up a difference between how most people define division and how Java does it.
Example (g) divides the double value for five by the double value for two and comes up with two and a half,
the value most students expect. Example (h) shows how Java defines integer division. Since both sides of the
operator are int expressions, Java returns the whole number of times the divisor goes into the dividend. Since
2 goes into 5 2 times with a remainder of 1, 5 / 2 evaluates to 2.
Java provides a related operator, %, called the mod (short for modulus) operator. The mod operator evaluates
to the remainder when the divisor divides the dividend. That is 5 % 2 evaluates to 1 and 19 % 7 evaluates to 5.
The mod operator is useful for determining whether a given value is a multiple of a given value; the remainder
of a multiple of 11, when divided by 11, is 0 by definition. It is also useful for counting over a limited range for
repeating things like the frames of an animation.
The basic assignment operator in Java is =. We have already used it when we needed to bind a label to an
underlying object or copy a primitive value into a primitive variable. We have always used it in a statement
(by placing a ; after the expression) but assignment is an expression almost like any other in this section.
“Almost” because assignment is an example of an expression that does more than evaluate to a value. In
addition to returning a value, evaluating an assignment expression also changes the value of the variable on
the left-hand side of the assignment operator. An assignment operator has a side-effect.
Assignment expressions cannot be considered in isolation just using literal expressions; the left-hand side
of the assignment operator must be a label or a primitive variable or an expression that evaluates to a label
or primitive variable (we have not yet seen any such expressions). In our examples we will define a couple of
variables and then give examples of assignment expressions using them.
int someInt;
double someDouble;
String someString;
Two things to note: these expressions return a typed value just like any other expression; the value re-
turned from an assignment expression is the value just assigned. This means that you can chain assignment
expressions together or use an assignment expression as one of the subexpressions in a comparison operator
(for example). Don’t! Using an assignment expression (or any other expression with a side-effect) as a subex-
pression in a larger expression leads to missing the side-effect when reading the code. Failing to see what is
going on in code you are reading leads to misunderstandings and errors. One easy way to make your code
more readable is to avoid side-effecting subexpressions.
In many computer programs it is useful to modify a particular variable with its new value based on its
old value. The variable would then appear on both the right-hand side of the assignment operator (where it
would be evaluated for its value) and on the left-hand side of the assignment operator (where it provides a
name where the result (or a reference to the result) should be stored). For example:
int x, y, z;
5.3. JAVA COMPONENTS 147
x = 1;
y = 2;
z = 33;
Because this operation is so common (we will see a lot of it in our loops later in this chapter) Java has
compound assignment operators. Most arithmetical operators can be combined with an equal sign to make an
operator that means “evaluate the right-hand side expression then combine it with the variable on the left-
hand side using this operator”. For example, the assignment expressions above could be rewritten as:
x += 1 // (a’) int, 2 --- x is assigned 2
y *= 19 // (b’) int, 38 --- y is assigned 38
z %= 5 // (c’) int, 3 --- z is assigned 3
someString = ”A moose”;
// (a) String, ”A moose” --- someString assigned value
someString += ” on the”;
// (b) String, ”A moose on the” --- someString assigned value
someString += ” loose.”;
// (c) String, ”A moose on the loose.” --- someString assigned value
This can be useful when building up a String, only part of which is known at any given time.
Method Invocation
Some of the simple expressions above ask an object to perform some task. That is, a method is invoked on some
object. This form of simple expression has four parts:
• The name of a method provided by the type of the object referred to.
• A parenthesized list of the parameters (if any) that the method requires. Each parameter is an expression
that is evaluated before the method is called with the result of the expression assigned to the value of
the local name of the parameter in the method body.
In the previous section’s String examples, (b) and (c) both invoked methods that were both defined in
String and returned something of type String. It is not necessary that a method return an object at all and if
it does, it can return any type of object that the author wants.
148 CHAPTER 5. COMPONENTS: NAMES, TYPES, EXPRESSIONS
Boolean Expressions
Comparison operators produce boolean, or truth values, depending on the relative values of their subex-
pressions. This is another way to get a booleanExpression as used in last chapter’s selection statements.
One comparison is for equality: is expressiona equal to (or not equal to) expressionb ? The == operator
is the “test for equality” operator18 . The != operator is the inequality comparison operator.
While it is possible to use either equality or inequality tests on object-typed expressions as well as plain-old
data expressions, it almost never means what you think it does. Thus in this book the equality and inequality
operators will only be applied to plain-old data types19 . For object types we will rely on the equals method
defined in the type.
All comparison operators have lower precedence than all arithmetic operators. This means that each of
the following expressions evaluates the arithmetic values on either side of the comparison operator before
comparing the values:
10 * 2 + 3 < 124 % 100 // (a) 23 < 24; boolean, true
4.5 / 3 >= 3.0 / 2 // (b) 1.5 >= 1.5; boolean, true
1972 * 1.1 < 2169 // (c) 2169.2 < 2169.0; boolean, false
The type of any truth value expression is boolean. In Example (c), note that the type of the right-hand subex-
pression is widened to double before the comparison takes place.
Two character operators must be typed with no space between the two characters. That is, less than or
equal to is typed <=; the sequence <~= is interpreted as the operator less than followed by the assignment
operator (and, since that sequence of operators makes no sense, the Java compiler will complain).
Logical operators combine Boolean expressions into more complex truth values. The three standard logical
operators are ! (not), && (and), and || (or). The truth value of a logical expression depends on the truth value of
the expressions these operators are applied to. ! inverts the truth value: true becomes false and vice versa20 .
When two boolean expressions are combined with &&, the expression is true if and only if both expressions are
true. When two boolean expressions are combined with ||, the expression is true if either (or both) of the
expressions are true.
(1 < 2) && (3 < 4) // (a) boolean, true
(1 > 2) && (3 < 4) // (b) boolean, false
(1 > 2) || (3 < 4) // (c) boolean, true
(1 > 2) || (3 > 4) // (d) boolean, false
!(1 > 2) // (e) boolean, true
Note that the parentheses are not necessary because of precedence. The following rewrite of Example (a)
above evaluates in just the same way:
1 < 2 && 3 < 4 // (a’) boolean, true
It should be clear that the Example (a) is more readable than Example (a’); for the cost of typing a few paren-
theses the readability is well worth it.
Object Construction
The one other type of expression is the result of using the new operator. It creates a new object (of the type
specified by the constructor after new) and returns a reference to that value.
18 Students are often confused by the assignment operator (=) and the compare equality operator (==). One way to make reading code
clearer is to read assignment as the words “is set equal” and comparison as “test equal to”.
19 Exception: All object references can be compared to the special value, null. We will use equality and inequality to check references
for null
20 Given that ! is read as not, the use of != for not equal should now make more sense.
5.3. JAVA COMPONENTS 149
Overloading Methods
Java programmers cannot overload operators; all operator definitions are specified in the language standard.
It is possible, however, for Java programmers to overload method definitions. What does that mean, how is it
done, and why would we want to?
Consider writing a method called perimeter. We will write the method inside of a Game extending class.
How can we figure out the perimeter of a Sprite?
Well, we can figure it out for a RectangleSprite fairly easily:
public double perimeter(RectangleSprite r) {
return 2 * r.getWidth() + 2 * r.getHeight();
}
This is just using the fact that an axis-aligned rectangle has a perimeter equal to double its height plus its
width. Can we redefine this so it works for OvalSprite too? For the moment we will assume the OvalSprite is
a circle (both diameters the same).
The formula given above is not particularly close to the right answer. The above would give 4 times the
diameter of the circle when the right answer is times the diameter. What if we wrote two different methods
in the same Game:
public double perimeter(RectangleSprite r) {
return 2 * r.getWidth() + 2 * r.getHeight();
}
There are now two different definitions for the same method. Java can tell them apart because they differ
in parameter list: the parameter lists are different. Java will match up the parameter types when compiling
and call the right version:
RectangleSprite first = new RectangleSprite(1.0, 0.5);
RectangleSprite second = new Rectangle(0.25, 0.25);
Each call is commented with which version actually gets called. This is useful when there are different
ways to handle different types of objects and the compiler knows that type they are at compile time. This is
different from overriding: Java will call the lowest, most specifically defined overriding method for an object;
Java will call the overloaded method which most closely matches the types of the parameters as declared in
the source code.
150 CHAPTER 5. COMPONENTS: NAMES, TYPES, EXPRESSIONS
Defining a Die
Just as we declared EasyButton above, we will declare the OneDie class here. We know the public interface of
the class; all we need to do is provide definitions for the methods.
We want the die to display as a single digit on the screen so we will extend the StringSprite class. That
gives us colors, scale, location, and all of that for free. We only need to keep track of the state of the die. What
is the state of a die? Just the value showing on the die. That is an integer on the range [1-6] so we can have an
int field.
1 import fang2.core.Game;
2 import fang2.sprites.StringSprite;
3
4 /**
5 * This class, extending StringSprite, represents one six-sided die.
6 * That is, a cube used for generating random numbers. The faces are
7 * marked from 1 to 6.
8 */
9 public class OneDie
10 extends StringSprite {
11 /** the value of the die; range is 1-6 */
12 private int face;
13
14 /**
15 * Initialize a OneDieStringSprite object with default values
16 */
17 public OneDie() {
18 setFace(1);
19 }
20
21 /**
22 * Get the current value of face.
23 *
24 * @return [1..6], the current value of the die
25 */
26 public int getFace() {
27 return face;
28 }
29
30 /**
31 * Roll this die: get currentGame(), use randomInt and setFace.
32 */
33 public void roll() {
34 setFace(Game.getCurrentGame().randomInt(1, 6));
35 }
36
37 /**
38 * Set value of face to newFace and update displayed value if newFace
39 * is legal; otherwise leave face unchanged.
40 *
5.4. FINISHING EASYDICE 151
Of course it is possible, with the current definition, to have the same effect (to violate the stated constraint
on the type, that its face value be limited to integer values from 1 to 6) by writing:
21 Notice that while we design OneDie we think of it as a component with attributes; when designing classes that use our new class, our
OneDie die;
...
die.setFace(8);
But setFace only updates the value of face if it is valid. The logical “and” applied to the two comparisons
will only evaluate to true when both subexpressions evaluate to true. The left subexpression is true only if
newFace is greater than or equal to 1; the right subexpression is true only if newFace is less than or equal to 6.
The two together are only true for the six values we consider in range.
Notice two things: (1) It was necessary to write two separate comparisons (the value being compared had
to be typed twice); this is different than the way you might write it in mathematics but it is how it must be
done in Java. (2) The way the two comparison expressions are written is done to indicate that the value of
newFace is being constrained between 1 and 6; it is possible to write either comparison with newFace on either
side of an appropriate comparison operator but this ordering was chosen to improve readability of the code.
At this point, having defined EasyButton and OneDie, the demonstration program we built using just their
public interfaces should compile and run. Each time the button is pressed, the dice will roll; remember that
sometimes they will randomly roll the same value. Also, make sure you press Start because the in-game button
only functions when advance is called.
Now on to writing EasyDice. Note that from this point forward, we do not need to talk about EasyButton
or OneDie except in terms of their public interfaces. This greatly simplifies our programming problem.
This leads to a lot of fields. Lines 14-15 declare the bank (the pile of matchsticks) and lines 18-19 declare
the wager. There are two variables for each because we track the number of matchsticks as an int and have
a pretty display sprite with the description and value showing on the screen. It is always a good idea to label
values you show to the user so they know what they mean.
The button, the four dice, and the point are all pretty much self-explanatory. The dice are named “left”
and “right” to indicate which die of each pair they are and the dice in the upper left corner showing the current
point have “point” in their name. Naming fields consistently greatly improves the readability of your code.
The last field, rolling is used to determine if we are rolling subsequent rolls in a round of easy dice (and
trying to match the point before getting seven) or waiting to place a bet and make our first roll. There will be
more on rolling when we define advance.
With the fields declared, what must setup do? Rather than try to list everything in detail, lets look at it
from a high level:
setup button
setup bank
setup bet
setup dice
This looks like it is a top-level view in the process of stepwise refinement. In fact, it is, with each statement
in the algorithm replaced with a call to a method.
64 public void setup() {
65 setBackground(getColor(”green”));
66 rolling = false;
67 buttonSetup();
68 bankSetup();
69 betSetup();
70 diceSetup();
71 }// setup
The first two lines set the color of the game field and set the initial state to not rolling. The other four calls
create, position, color, and add sprites to the game. In the interest of brevity, we will not be commenting on
them in the text as they are very similar to the setup method in RollDice. Stepwise refinement starts at the
top and works down until the described methods are easy to solve problems.
The advance method is really two methods. That is, there is work to do if we are waiting for the first roll
and different work to do if we are rolling for a point. That sounds like an application of selection and stepwise
refinement:
49 public void advance(double dT) {
50 if (!isGameOver()) {
51 if (!rolling) {
52 advanceWaiting(dT);
53 } else {
154 CHAPTER 5. COMPONENTS: NAMES, TYPES, EXPRESSIONS
54 advanceRolling(dT);
55 }
56 }
57 }// advance
The real work for either case is pushed off until we define the given method. Each method, advanceWaiting
and advanceRolling, sits and spins its wheels until the button is pressed. Thus the body of each is a big
if (button.isPressed()) statement. If the button is pressed, the dice are rolled and the state of the game is
updated accordingly.
98 private void advanceWaiting(double secondsSinceLastCall) {
99 if (button.isPressed()) {
100 // place and show wager
101 bet = 1;
102 bank = bank - bet;
103 betDisplay.setText(”Bet: ” + bet);
104 bankDisplay.setText(”Bank: ” + bank);
105
116 // set new point, set rolling flag, and change button text
117 point = roll;
118 rolling = true;
119 button.setText(”Roll for point”);
120 }
121 }
122 }// advanceWaiting
In advanceWaiting, when the button is pressed a wager is made. Then the dice are rolled. If the roll is a
winning number for a first roll, call the win method. Otherwise update the point dice to display the same faces
as the game dice (lines 113-114), point to be the sum of the dice (line 117), set rolling to true since we are
now rolling for a point, and finally, change the button text to indicate that we’re rolling for point.
79 private void advanceRolling(double dT) {
80 if (button.isPressed()) {
81 int roll = rollTheDice();
82 // game wins, loses, or keeps going. Nothing to do to keep going
83 if (roll == 7) {// lose
84 lose();
85 } else if (roll == point) {// win
5.5. SUMMARY 155
86 win();
87 }
88 }
89 }// advanceRolling
In advanceRolling, when the button is pressed we roll the dice. If roll equals 7 the player loses. Else, if the
roll equals the point, the player wins. In any other case, rolling continues. Note that win and lose are defined
to update the wager, the bank, and to set rolling false.
5.5 Summary
An expression is a piece of code which returns a value. Simple expressions are literal values, variables, new
expressions, and method calls. Simple expressions have types. Expressions are built up of simpler subexpres-
sions by combining the subexpressions with operators
Literal values, like 3.1415 or ”One Two Three” are evaluated by the compiler and compiled directly into a
program. Variables, fields and local variables alike, are names for values which are filled in (using assignment)
at run-time. The new operator allocates memory and calls a constructor to initializes the space into an object.
Method calls, where a method is named and parameters (if any) are provided, can return values.
Operators have precedence which determines the order in which they are applied. Thus 4 + 3 * 2 is 10
(4 + (3 * 2)) rather than 14 ((4 + 3) * 2). Parentheses can be used to specify order of application and to
make it clearer even when they are not required.
All expressions have types. The extends relationship between two types means that the child class is-a or
is an example of its parent class. A every running shoe is a shoe, after all.
Operators can be combined with assignment to simplify typing (and to clarify intent for a reader familiar
with the Java language). For example
vowelCounter += yCounter;
This adds the value of the yCounter variable to the current value of the vowelCounter variable.
Since almost any Java class can be extended into another class, an important design decision is what class
to extend. In this chapter we extended a sprite with additional state to play our game. More guidance on this
in the next two chapters.
<returnStmt> := return;
Review Exercise 5.1 State which of the following collection of possible identifiers are not legal in Java and
why they are not legal:
(a) oneFish
(b) ReD
156 CHAPTER 5. COMPONENTS: NAMES, TYPES, EXPRESSIONS
(c) findSmallest
(d) Bilbo Baggins
(e) NDX
(f) ingredient
(g) 2fish
(h) Go!
(i) FarFetched
(j) 123BrockportLn
(k) mouseEvent
(l) FryingPan
(m) SIZE_OF_SQUARE
(n) Alan_Turing
(o) flash cards
(p) student-grade-point-average
(q) LionTigerLeopardPumaCougarCount
(r) Orca#
(s) alpha_Bravo
(t) BrockportLn123
Review Exercise 5.2 Classify each of the following identifiers as either a type (class) name, a method, a
variable, or a constant.
(a) DirtyDozen
(b) color
(c) createCheckerKing
(d) YEAR_LINCOLN_WAS_BORN
(e) myGameProgram
(f) GolfClub
(g) findSmallest
(h) LENGTH_OF_SHIP
(i) nameOfCountry
(j) getCaptainsName
(k) MAXIMUM_NUMBER_OF_PIRATES
(l) PlayingCard
(m) currentPokerHand
(n) getHighCard
(o) ChessPiece
(p) getRasberryCount
(q) firstMove
(r) castleToKingside
(s) numberOfRasberries
(t) SmallInteger
Review Exercise 5.3 What is the scope of the variable blueLine in the following listing:
class Something
extends Game {
public void gamma() {
(a)
5.5. SUMMARY 157
LineSprite blueLine;
Review Exercise 5.4 What is the text in the label after this code is executed:
StringSprite results = new StringSprite();
addSprite(results);
int x, y, z;
String cubit = ”atom mixed replication”;
x = 10;
y = 100;
z = y;
y = 2 * y;
x = z * x;
results.setText(cubit.substring(1,7) + ” (” + x + ”, ” + y + ”, ” + z + ”)”);
Review Exercise 5.5 What color is the RectangleSprite displayed on the screen filled with?
RectangleSprite lemon = new RectangleSprite(0.2, 0.2);
lemon.setColor(getColor(”yellow”));
addSprite(lemon);
Programming Problems
(a) Make a SnowmanSprite.java file and build a constructor for the class. The constructor should instanti-
ate three OvalSprites of appropriate sizes and position them so that the center of the snowman is the
center of the middle ball and the overall height of the snowman is 1.0 screens (width is determined by
the width of the bottom ball).
You can test this sprite by building a simple Game which adds ten sprites with random locations and
random rotations. You might want to try setting random colors for each as well.
(b) Modify SnowmanSprite by overloading the setColor method. While CompositeSprite has a setColor
method, the method does not set the color value of sprites within the composite.
Physics of Bouncing
What makes a ball different from an OvalSprite? What made an apple different from an OvalSprite? The
fact that we moved it in advance. Given that we are putting animation code into our game objects, it seems
that a ball differs from an OvalSprite because it has a velocity. By tracking its own velocity, a ball can be
told to move simply by giving it the elapsed time. Further, if a ball tracks its own velocity, it can update that
velocity to bounce off of walls and other sprites. Even better, if each ball tracks its own velocity, each can have
a different velocity meaning each can bounce off of walls, each other, etc. independently.
So, how can you keep track of velocity. Our games are two-dimensional so velocity is a distance to move
in a given time in two dimensions. This could be stored as a polar coordinate or a heading and a magnitude
(which direction to move and how far to move in a unit time) or as Cartesian coordinates, an x-component and a
y-component of velocity. Either method will work but if most of our bouncing is off of vertical and horizontal
“surfaces”, it is very easy to simulate that with Cartesian coordinates.
159
160 CHAPTER 6. RULES: METHODS, PARAMETERS, AND DESIGN
soloPong
4y count
player (arrows)
e
voll
rolled
-cont
rd
keyboa
u t om atic trolled)
a c on
om p uter-
(c
What we are doing, simulating motion and collision and bouncing, is simulating physics. We are creating
rules, more complex than those in NewtonsApple, which create a believable simulation of the real world1 . Since
we are simulating physics, we will call the two components of the velocity in our PongBall deltaX and deltaY;
this is because in physics Δ, the Greek letter delta, is used to mean change so Δx and Δy are the change in
position in the x-coordinate and y-coordinate.
How does a ball bounce off of the floor? In Figure 6.2 is a ball moving from right to left and striking the
bottom edge of the screen, the floor. Notice that the angle of incidence equals the angle of reflection (or: the
angle between the floor and the movement line of the ball is the same on either side of the point of reflection).
The component velocities, deltaX and deltaY are drawn to show how they combine to create a diagonal path
for the ball; they also show how we can simulate the physics of a bouncing ball.
When a ball hits a surface, the component of the velocity perpendicular to the surface struck reverses
direction. The new velocity has the same size or magnitude, just the direction has changed2 . In FANG terms,
if the ball strikes a horizontal surface, the y-component of the velocity (perpendicular to the surface struck)
changes sign. The x-component (parallel to the surface struck) is unchanged.
Reacting to a collision with any surface takes the same form; the portion of the velocity perpendicular, or
normal, to the surface struck is reversed in direction. The two hard parts of collision reaction are determining
where two objects struck one another and what the normal of the surface at the point of collision is.
A first approximation of an arbitrarily shaped sprite is to use its bounding box or the smallest axis-aligned
rectangle that contains all of the sprite. Using a bounding box means that we only reflect off of horizontal
or vertical surfaces. Figure 6.3 shows how a bounding box would fit around a grey rectangle sprite. The two
1 for
a sufficiently loose interpretation of believable
2 This
assumes the collision is completely elastic or that a ball bounces to the height from which it was dropped. We will later see
how to simulate a “real” bouncing ball.
6.1. A SIMPLE ARCADE GAME: SOLOPONG 161
deltaY
deltaY
deltaX deltaX
balls bouncing off of it show how a ball would bounce if it hit one edge of the bounding box or if it hit two
edges at the same time. To the left, the four regions are marked with slanted lines to show where the ball
struck a vertical or a horizontal edge. In the corners, the moving ball bounces off of the corner or off of both
a horizontal and a vertical surface as shown by the cross hatching in the diagram.
Horizontal
Vertical
Vertical
Horizontal
All games so far handle all animation directly in the advance method of Game-extending class. Let’s sketch
what that one method would have to handle if we used that approach. This game is much more complex than
any we have yet written.
if (waiting to start play)
if (spacebar pressed)
start countdown timer
if (countdownTimer is counting down)
decrement timeRemaining
if (timeRemaining <= 0.0)
countdownTimer NOT counting down
else
animate based on time remaining
else
move ball according to ball velocity
move paddle according to keyboard
if (ball hit wall)
if (wall is bounce wall)
update ball velocity
if (wall is score wall)
update score
move ball to start position
wait to start play
if (ball hit paddle)
update ball velocity
6.2. TOP-DOWN DESIGN 163
This is quite complicated. As has been mentioned, one way computer scientists control complexity is
to decompose the problem into smaller, simpler problems. The solutions to the simpler problems can then
be composed together to solve the original problem. This advance method would benefit greatly from this
approach.
What if the ball, paddle, and countdown timer each had their own advance method. Then the Game’s
advance method is greatly simplified:
if (waiting to start)
if (spacebar pressed)
countdownTimer.startTimer(3)
if (!countdownTimer.isExpired())
countdownTimer.advance(deltaT)
else
ball.advance(deltaT)
paddle.advance(deltaT)
if (ball.isOutOfBounds())
wait to start
Why call the parameter of advance deltaT? For two reasons: to demonstrate that we can call parameters
anything we want to, anything that makes the code easier for the human reader to understand. Δt is the
change in the variable t or time. We will use the whole word deltaT at the moment though we might shorten
it in future chapters.
173 paddle.advance(deltaT);
174 ball.advance(deltaT, paddle);
175
176 if (ball.isOutOfBounds()) {
177 beginWaitingToCountdown();
178 }
179 }
180 }
181 }// SoloPong
There are three states the game can be in: waiting to start, counting down to start, or playing. Since we
are designing from the top down, it is not a good idea to worry too much about how, specifically, we will know
which state we are in. We can just pretend that we can write three Boolean methods: isWaitingToCountdown,
isCountingDown, and isPlaying. These methods hide the details and let us delay making a decision about how
they will work.
The problem with putting off the decision is that we don’t know how to change the state of the game. That
is, when the countdown timer finishes, we want to start playing. If we don’t know how isPlaying works, how
can we start the playing? We have to pretend that a method called beginPlaying exists that will do whatever
must be done to change to playing mode. Similarly beginWaitingToCountdown and beginCountingDown will
shift the game into each of those states.
The important thing to note here is that we have pushed the details down to another level of abstraction
and we can write the whole game control right here, right now.
Is there anything missing (from the public interface)? While in the kitchen we would go into the junk
drawer to find the kitchen timer, we probably want to be able to create a timer in Java: that implies a public
constructor (most classes have public constructors). We also need to be able to supply the countdown timer
with a “tick”. Where as a physical kitchen timer has a clock circuit built into it, we are going to have to tick
the countdown timer as part of a Game; that implies the timer needs its own advance method (the method we
know which has elapsed time as a parameter, at least in Game; we will copy the parameter list):
6.3. DELEGATION: METHODS 165
class CountdownTimer {
public CoundownTimer...
public void setTimer...
public void startCountdown...
public double getRemainingTime...
public boolean isCountdownExpired...
public void advance(double deltaT) { ... }
}
Method Headers
What do the remainder of the methods look like? When setting the time, we need to know what time to set
it to. Thus there should be a parameter indicating the remaining time to set. The other four methods don’t
seem to need any parameters (though it might be a valid design to have a constructor which took the initial
countdown time; we will separate the construction and setting in our class). Also, if we intend to have the
timer appear on the screen, displaying the countdown, we should extend a sprite class capable of displaying
a string. Thus the class’s public interface looks like this:
class CountdownTimer
extends StringSprite {
public CoundownTimer() { ... }
public void setTimer(double remainingTime) { ... }
public void startCountdown() { ... }
public double getRemainingTime() { ... }
public boolean isCountdownExpired() { ... }
public void advance(double deltaT) { ... }
}
A design rule of thumb is to give all methods and fields as restrictive an access level as possible. The
reasoning is that the fewer places a method can be called or a field be manipulated, the easier it is to find
mistakes involving that method or field. We will continue to declare all fields to be private and will begin,
with this chapter, to try to define only those methods which form the public interface of the class as public.
There might be other, implementation methods that are not specified here.
We use the public interface of CountdownTimer (and the public interface of StringSprite because we got
that for free by extending that class). It is not necessary for the user to see what happens inside the class.
JavaDoc documents are generated automatically by determining the public interface of a class and extracting
the comments from those methods.
Thus the public methods combine to advertise what the object the belong to is capable of. They do not
advertise just how it does it but they provide an indication of what the user must do in order to use the object
effectively. The advance method should make sure that it includes comments that it must be called from the
advance method of a Game with the same deltaT in order for the countdown to work.
cookbook, the author has defined how to make delicious oatmeal cookies; the definition, however, produces
no cookies. Instead, when some cook applies the appropriate ingredients (parameters) to the recipe (method),
when they use the how, they produce warm, wonderful cookies. Or whatever the recipe defined, in any case.
Calling a Method
When rules are combined sequentially, execution begins at the beginning and follows each rule in turn until
the end. With selection, some condition is evaluated and if it is true one set of rules is followed and if it is false
a different set is followed4 . How does delegation work?
Delegation has two parts. The first is defining the method. The header and the body of the method together
define a newly named rule. Note that the definition of the rule does not, by itself, do any computation when
the program containing it is run. The method only acts when it is called.
Consider living on a college campus. Over time you will internalize where you are living. You will be
able to find your way home from the bookstore, the dining hall, and even your computer science lab. That
internalized knowledge of how to get back to the room, the directions you know, is a definition of a “go home”
method in your head. Having it defined in your head does not do anything. Something happens only when
you tell yourself: “Now, go home from here.”
When you call your method, your internal directions evaluate the parameter, “here”, and you are off. It is
possible that going home was part of some larger sequence of instructions (“change clothes for volleyball”, for
example). The key is that “go home” does not know anything about that. In fact, the larger set of instructions
is suspended until you complete “go home” (which is a good thing if the next step in changing for the game is
“remove pants”).
3 Yes, a public field could also be seen and modified from any class, not just the one with the field definition. This is terrible design
Analogously, when setup calls someSprite.setColor(blue), the parameters are evaluated and then the
instructions in setup are suspended. The method stops making progress while setColor executes. The rule
follower which was performing the instructions in setup sets a bookmark so it can return to the exact spot
where that method was suspended and then execution continues with the first line of the body of setColor. By
default, the body is evaluated sequentially; using if statements, iteration, or further delegation can change the
default behavior. When the rule follower finds a return statement or run off the end of the body of setColor,
then it returns its attention to the bookmarked spot in setup.
The definition of a method constructs a new, named rule. Calling the method suspends the method con-
taining the call, marking the return address (the bookmark) to which control will return and it executes the
definition from the beginning. It is possible to have more than one copy of a given method running at the
same time though at most one of them is the current, active method.
Java can tell them apart by the list of parameters we provide when we call it. This brings up a point we have
overlooked until now: rather than one parameter list, there are really two different things that are parameter
lists: the parameter list which is part of the header of the method and the list of parameters included when
the method is called. These are known as the formal parameter list and the actual parameter list respectively.
The formal parameter list, as has been said before, is a list of zero or more <TypeName><parameterName>
pairs separated by commas. By appearing in the parameter list, the <parameterName> is just like a local vari-
able, local to the body of the method for which it appears as a formal parameter. The type of the parameter
determines the available public interface and it can be used as any other local variable. The difference is that
5 This is the biggest problem with the analogy; in the cookbook, the ingredients applies to any number of items specified in the
call. In Java you must specify exactly the number of items when defining the method.
168 CHAPTER 6. RULES: METHODS, PARAMETERS, AND DESIGN
the initial value of the parameter is not set in the definition of the method but rather when the method is
called.
The actual parameter list is the list of expressions between parentheses right after a method is called. The
getColor examples above are headers of methods, each with a formal parameter list; below is a list of calls to
those methods (assume in some method defined in a Game-extending class):
Color dummyColor;
dummyColor = getColor(”navy”);
dummyColor = getColor(”misty rose”, 64);
Color otherColor = getColor(dummyColor, 255);
The values in the actual parameter list are assigned to each of the parameters in the formal parameter list
in the order they appear from left to right. That is, in the second call to getColor, colorName is initialized with
the String value ”misty rose” and opacity is initialized with the value 64.
Passed by Value
The important thing to note is that all Java parameters are passed by value. This means that each actual
parameter expression is evaluated before the method is called and the result is assigned (copied) into the
corresponding formal parameter.
This means that all changes made to the formal parameter inside the body of the method happen to the
copy of the value. When the called method returns control to the caller, the parameter goes away. Any changes
made to the parameter go away with it.
First we will consider what happens when we pass plain-old data into a method. Assume the following is
part of a Game-derived program.
public void fiveTimes(double x) {
x = 5 * x;
}
s.setText(Double.toString(k));
addText(s);
}
Assuming no other changes are made to the text in s and s is added to the game, what value does s display on
the screen? Let’s trace the execution of init using what we know about delegation.
The StringSprite s is declared and constructed. Then the double k is declared and the value 9.0 is stored in
it. Remember, plain-old data types behave like boxes into which values are stored (rather than as references).
Then a call is made to fiveTimes.
What happens when a method is called? The parameter expressions in the actual parameter list are eval-
uated. k is a variable so it evaluates to its current value or 9.0. Then the value of the expression is used to
initialize the formal parameter in the corresponding position. First actual parameter evaluates to 9.0 which is
used to initialize the value of the first formal parameter, x. Note that initialization means initial assignment
so the value is assigned to x.
Then execution of init is suspended (with a bookmark indicating to continue just after the call to
fiveTimes (which is the semicolon at the end of the line so the line is finished and execution continues below
that) and execution of the body of fiveTimes begins.
6.3. DELEGATION: METHODS 169
The one line in the body of fiveTimes is an assignment statement. We evaluate the right-hand side of
the assignment and then assign that value to the variable on the left-hand side of the assignment operator.
The expression, 5 * x, is evaluated by getting the value of x, coercing the int 5 to a double, 5.0, and then
multiplying them together. That is, 5.0 * 9.0 is evaluated giving 45.0.
The value is assigned to x. That means that in x the 9.0 is clobbered with the new value, 45.0. Execution
runs off the end of the body of fiveTimes so control returns to the bookmark. Execution of init continues
from where it was suspended. Thus the value of k is passed as a parameter to the Double.toString method to
convert it to a string representation of the same number. If k contains 45.0, Double.toString returns ”45.0”
and if k contains 9.0 it returns ”9.0”. What value does s display on the screen?
Plain-old data is stored in a memory location. When a copy was made from k to x, both k and x contained
the value 9.0 but the were separate copies of that value. Thus when x was changed, k remained unchanged.
Thus, with pass by value parameters, it is not possible for changes within a method to directly modify any
plain old data parameters.
What about object parameters? What if we passed in a StringSprite? Could you change the value. Fun-
damentally the answer is, “No.” Yet object variables are not boxes like plain old data type variables; object
variables are references or labels. That makes things different. Consider the following modified version of the
above code:
public void fiveTimesString(StringSprite p) {
String content = p.getText();
String newContent = content + content + content +
content + content;
p.setText(newContent);
}
fiveTimesString(s);
}
What value does the StringSprite s display? The first three lines of init are straight forward: declare a sprite
variable, construct the sprite, set the text of the StringSprite, and add the sprite to the game. The last line is
just a call to a method with s as the actual parameter.
The expression, s is evaluated and the result, a reference to the given StringSprite, is used to initialize
the corresponding formal parameter. This is equivalent to p = s where p is the formal parameter variable
name and s is the actual parameter expression.
Then init is suspended, execution runs the statements in the body of fiveTimesString. The first line labels
the text in the StringSprite as content. Then the String concatenation operator is used to paste five copies
of the content together and label the result newContent. Then p.setText is called an the method finishes. init
resumes execution just after the call to fiveTimesString was called and init finishes, returning control to the
method which called init.
The question is, what does the StringSprite on the screen show?
Drawing a picture of what goes on in memory can be helpful here. When s is assigned a value from new,
the box called s refers to the StringSprite. When p is initialized from s, the contents of box s are copied into
p. Note that in the picture, p and s refer to the same sprite. This is the same situation we had in Figure 5.5
when we assigned one object variable to another. In this case, the StringSprite is set to the text ”qqqqq”.
This is somewhat confusing for beginning programmers: if the parameter is passed by value, how can
the sprite be changed. The distinction is between the content of s which cannot be changed because it was
170 CHAPTER 6. RULES: METHODS, PARAMETERS, AND DESIGN
Memory
s
qqqqq
p
copied when the method was called and the thing referred to by s, an object which is shared through the
references of both p and s. Consider what would happen if fiveTimesString assigned the results of new to its
formal parameter. That is, add as a second line in the method p = new StringSprite(); and leave the rest
unchanged. What happens.
s and p are different boxes in memory; changing p does not change s. After the assignment, s and p also
refer to two different objects (the results of two different calls to new). Thus the changes to the newly minted
StringSprite have no impact on the old StringSprite so the value on the screen in this case remains ”q”.
66 /**
67 * Has the count expired?
73 }
74
75 /**
The two methods shown above are the two functions in the CountdownTimer class. The first,
getRemainingTime is what we have been calling a getter, a method for getting the value of a private field.
6.4. EXPRESSIONS REDUX 171
The return statement takes an expression (here a single variable) of the right type, evaluates it, and sets the
return value of the method to that value. Thus, if remainingTime were 10.34 and I wrote the following in my
Game-derived class:
double d = countdown.getRemainingTime();
”p = ” + p
The expression is an application of the + operator. The type of an addition expression depends on the
types of the subexpressions being “added”. If they are both int..., no, not the case here. If one is an int and
the other is a double..., no, not that case, either. What is the type of ”p = ”? It is a sequence of characters
enclosed in double quotes. It is a String6 literal. A String followed by a plus sign followed by a variable name.
Java uses + for concatenation of strings; when the right-hand value is a variable, the variable is converted
to a String first and then tacked onto the end of the left-hand side. So, in this case, p has the value 1001 so it
is converted to the String ”1001” and the result is the String ”p = 1001”.
When concatenating a String with any plain old data type, Java uses the <Type>.toString(<type>) method
where <Type> is the name of the type with a capital letter and <type> is the name of the type. Thus, to
explicitly convert p to its String equivalent, we could call Integer.toString(p). We have seen this with
Double.toString(), too. Remember that String is an object type; that means we can use the dot notation
to call methods, too.
1 import fang2.core.Game;
2 import fang2.sprites.StringSprite;
3
4 /**
5 * Demonstrates String functions and String literals.
6 */
7 public class StringValueDemonstration
8 extends Game {
9 /**
10 * Creates local Strings and then demonstrates some String methods in
11 * various labels created on the screen.
12 */
13 @Override
14 public void setup() {
15 final double STRING_HEIGHT = 0.04;// a named constant. Used to step
16 // strings down screen
17 double yPosition = STRING_HEIGHT;// represents the y position of
18 // the next string sprite
19 String userFirstName = ”Claes”;
20 String userLastName = ”Bos-Ladd”;
21 // concatenation
22 String userFullName = userFirstName + ” ” + userLastName;
23
This example demonstrates how to declare String variables and assign literal string values to them. It also
demonstrates how to build up a String using the overloaded + operator and assign that result to a variable.
Then some methods of the String class are called. toLowerCase makes a copy of the String, converting
all uppercase letters to lowercase (non-letter characters are unchanged); toUpperCase is similar but converts
all letters to uppercase. substring takes a beginning position and one past the last postion; note that the
character indices in a String begin with 0. Thus in the String ”Claes Bos-Ladd”, substring(6,9) returns a
String containing ”Bos” (characters 6, 7, and 8 in the original sequence). The complete results of running this
program are shown in the figure below.
Every time you type a literal character string in your Java program (such as ”p = ” or
”Press Any Key to Continue”), Java implicitly creates a new String object. In addition to the special
handling of literal strings, Java defines the + operator as concatenation between String values. The last
bit of specialness permits String to behave like a primitive type because even if two labels refer to the same
underlying object, it is not possible to change the characters in the string through any reference to it. Thus
String objects behave almost like primitive data types.
Remember, though, that String is an object type. That means that String objects can be used with the dot
syntax to invoke methods on the string. What methods?
Object Expressions
Object expressions are expressions with an object type. Thus String expressions qualify. The simplest object
expression is an object variable or a call to new.
new RectangleSprite(1.0, 0.5)
This call to new calls the constructor for the type RectangleSprite. It returns a reference to the newly
constructed object. To date we have always assigned the result of this call to a variable. This is only necessary
if we want to be able to call the result by name, that is use a label for it. It is legal to use the above expression
anywhere a RectangleSprite is permitted:
addSprite(new RectangleSprite(1.0, 0.5));
This would add the new sprite to the game (or CompositeSprite). Without a label for the sprite we cannot
change its color or scale or rotate or, well, anything.
174 CHAPTER 6. RULES: METHODS, PARAMETERS, AND DESIGN
The subexpression userFullName.toLowerCase() calls the toLowerCase method (defined in the String
class) on the String referenced by userFullName. A duplicate string is returned with all upper-case letters
converted to their lower-case equivalents.
we wanted the substring to be in capital letters we could change the lines to read
59 StringSprite msg6 = new StringSprite(
60 ”userFullName.substring(6, 9) = ” +
61 userFullName.substring(6, 9).toUpperCase(),
62 STRING_HEIGHT);
The middle line chains the call to toUpperCase onto the results of the call to substring.
The constructor and setVelocity are straight forward: we need to be able to set the state of the ball, the
velocity it maintains. The advance method is where that velocity is used to move the ball. The bounce methods
are used to keep the ball bouncing off of the edges of the screen, off of the paddle (and any other sprites in
the game; see the exercises for suggestions on game variations with multiple sprites on the screen). The final
method encodes the rule of the game that the ball goes out of play when it hits the right edge of the screen.
We begin a detailed examination of the code by reading the fields (the velocity) and the constructor.
15 * and y-coordinate velocities
16 */
17 private double deltaX;
18 private double deltaY;
19
176 CHAPTER 6. RULES: METHODS, PARAMETERS, AND DESIGN
20 /**
21 * Create a new PongBall. Specify the size and the initial velocity of
22 * the ball.
23 *
24 * @param width width of the ball in screens
25 * @param height height of the ball in screens
26 * @param deltaX initial horizontal velocity in screens per second
27 * @param deltaY initial vertical velocity in screens per second
28 */
29 public PongBall(double width, double height, double deltaX,
30 double deltaY) {
31 super(width, height);
32 /*
33 * A scope hole: to which deltaX (line 9 or line 25) does the
34 * variable deltaX refer? The closest (furthest in in curly braces),
35 * so line 25 How to refer to the field? Use this and a dot.
36 */
37 this.deltaX = deltaX;// set FIELD to the parameter value
38 this.deltaY = deltaY;// set FIELD to the parameter value
39 }
40
41 /**
What is line 33 doing? It looks like a call to a method named super with two double parameters and that is
almost exactly what it is. When the keyword super appears as the first line in a constructor, it tells Java to call
the constructor of the superclass or the current class. The superclass is the class which this class extends. Thus
line 33 is a call to the OvalSprite constructor with two double parameters. The two parameters (as we have
seen in other uses of OvalSprite) are for the width and height of the sprite; we simply pass along the width
and height parameters our constructor takes as the parameters for the super constructor.
We have never had to do this before because Java defines the concept of a default constructor. The default
constructor is the constructor with an empty parameter list. If the superclass of one of our classes has a default
constructor and we do not include an explicit call to super on the first line of our constructor, then Java will
implicitly call the default constructor as if it were the first line of our constructor.
Thus EasyButton which extended CompositeSprite did not need an explicit call to super since an implicit
super() called the default constructor for CompositeSprite which was exactly what we wanted.
Scope Holes
Lines 39 and 40 look strange: the same variable name appears in the line twice, once with this. in front of it.
What does the comment, mentioning a scope hole, mean?
What is scope? Scope is where a variable or method name is usable. A field, like those declared in this
listing, is in scope inside the entire class definition (both before and after the declarations). This means, in
particular, that deltaX, the field, is in scope within the constructor beginning on line 34.
There is a problem, though. The constructor has a parameter also called deltaX. A parameter is, effec-
tively, a local variable which is declared just inside the body block of the method on which it is defined. Thus
between the curly brace at the end of line 32 and the matching close on line 41, the parameter named deltaX
is in scope.
This means that between those curly braces there are two different meanings of the name deltaX. Java
has a simple rule for resolving this: whatever definition is closer or, deepest in when counting levels of curly
6.5. FINISHING UP SOLOPONG 177
braces, wins. Thus the unqualified name deltaX, within the constructor, refers to the parameter. This creates
a scope hole, a place where the field deltaX “should” be in scope but where it is masked by another use of the
same name.
But what if we wanted to talk about the field? Remember this? The special reference to the current object?
Using the dot notation, it is possible to refer to the fields of the current object using this. That is exactly what
lines 39 and 40 do: the first reference is to the field, the second to the parameter, and the parameter values
are used to initialize the fields.
An entire subsection on what a scope hole is; why not just use different names for the parameters and the
fields? Up until now we have and it would be perfectly legitimate to do so here.
We create the scope hole to illustrate Java’s rule for handling multiple declarations of the same name at
different levels. We also note that many Java programmer’s editors use this construct, naming parameters to
the constructor for the fields they will initialize, as a standard; it is important for a Java programmer to be
comfortable with the construct and using this. to refer to the non-local declaration. Remember that programs
are written for the compiler and other programmers; being able to use standard idioms of the language will
make your code easier for others to read.
56 /**
This listing shows a second constructor for PongBall. This raises a lot of questions: Why have two construc-
tors? How does the compiler decide which one to call? What is this doing on line 55; it looks like it should be
super.
This constructor is designed to be like the constructor we use for making OvalSprite, just requiring the
user to specify the size of the sprite in screens. This seems convenient. The compiler can decide which one to
call by looking at the parameters. If there are four double parameters then it calls the first one and if there
are only two, it calls the second one. This explains how different constructors for StringSprite were used
in StringValueDemonstration above (different number of parameters) and why there are sometimes multiple
constructors in the documentation for different classes.
There are two problems for us in writing our second constructor: what value should the velocity get if the
user fails to specify one and how do we avoid repeating our selves. The first question has an arbitrary answer;
we picked 0.0 along either axis but could have picked any value we liked. The second question is harder.
Constructors exist to make sure that objects are properly initialized before they are used. Among other
things, constructors set initial values for the various fields in the class. How can we avoid copying the code
that initializes the fields, typing it into two different constructors. This is worth thinking about because if
you add another field, you only want to have to remember to initialize it in one place, the place where all the
other fields are initialized. And you want to be sure that there is no way to construct class instances without
executing that initialization.
Line 54 shows how to avoid repeating yourself in a constructor: if this appears as the first line of a con-
structor, it is a call to another constructor of the same class with the changed header. This means that this
must have a different header than the constructor in which it appears.
Here we pass all the work to the constructor which has the most parameters, specifying the default values
in line 55 when we call the constructor.
178 CHAPTER 6. RULES: METHODS, PARAMETERS, AND DESIGN
87 /**
Using stepwise refinement we can see what happens each frame: we move the ball according to its current
velocity. Then we bounce it off of the walls. Then we bounce it off of the paddle. The paddle is a parameter
passed into advance. Isn’t that wrong? According to the documentation, advance only takes a double. Wait,
that is the version of advance in Game. Since we will call advance on our PongBall, we can define any header
we like.
By passing in the paddle, the ball can handle all its own bouncing and keep track of the volley count. But
wait, why is the type of the parameter Sprite? Aren’t we defining a class, something like PongPaddle?
Yes, we are defining such a class but we are using Sprite here to make a point: If a method expects some
particular class, any class extending the expected class or any class extending the expected class and so on,
is acceptable. Thus if we expect a Sprite, then we can take an OvalSprite, a CompositeSprite, or even a
EasyButton. The only limitation when using the super class to pass in parameters is that you’re limited to the
methods defined in the super class. That is, even if we passed in a StringSprite, the formal parameter is only
a Sprite so you could not use paddle.setText because Sprite does not have such a method.
We can use the same basic formula to move the paddle except we don’t want to move the paddle on every
call to advance; instead we want to check what keys the user has pressed this frame. If they have pressed
(and not released) the left arrow key, we want to translate in the x direction by the horizontal velocity of the
paddle (deltaX because the name makes clear the similarity) times delta time times -1. The -1 is because the
x-coordinates get larger as they move from left to right so to move left we want a negative translation. Leaving
out the -1 would move the paddle to the right (so we use that with the right arrow key).
As we have seen, reading the keyboard uses the getKeyPressed() and [up,down,left,right]Pressed()
methods of the Game class. We now need that information in a different class, a non-Game derived class. That
means we need to get access to the current Game inside PongPaddle’s advance. The Game class has a special
method, a static method, called getCurrentGame which returns a reference to the current Game.
6.5. FINISHING UP SOLOPONG 179
The second consequence means that static is contagious: any method or field you want to use from a static
method is infected and becomes static. Fields, too, can be static (we saw static final fields in Section 5.3.
More discussion of static methods, including a modified Java method template, will come later when we write
our own static methods.
For right now, we will just use the getCurrentGame method to get a reference to the current game whenever
we need it. So, we can now write the advance method for the PongPaddle class:
47 translateY(-deltaY * deltaT);
48 }
49 if (Game.getCurrentGame().downPressed()) {
50 translateY(deltaY * deltaT);
51 }
52 if (Game.getCurrentGame().leftPressed()) {
53 translateX(-deltaX * deltaT);
54 }
55 if (Game.getCurrentGame().rightPressed()) {
56 translateX(deltaX * deltaT);
57 }
58 bounceOffEdges();
59 }
60
61 /**
Notice that the value returned by the expression Game.getCurrentGame() is a reference to a Game. It is valid to
apply a dot to a reference so Game.getCurrentGame().upPressed() is true when the user has pressed the up
arrow key (look at the Game documentation). Thus these four if statements work to move the paddle according
to its velocity.
What velocity components should we set to limit the paddle to vertical movement? Vertical movement
has a non-zero y-component and a 0.0 x-component. Thus if we set the x-velocity to 0.0, the paddle will only
respond to the up and down keys.
Why does the PongPaddle even have an x-component to its velocity? For two reasons: sometimes it is
easier to solve a more general problem than it is to solve a very specific problem and it almost always a good
idea to reuse code you already understand. The ball code is code we have examined and it moves the ball in
two dimensions. That code, modified to check on what keys were pressed, works to move the paddle.
The only thing that we need to do differently for the paddle is “bouncing” it off the edge. When the paddle
moves a little bit off the edge, we want to move it back immediately. The paddle never moves past the edge
so, when we detect that it has, we back it up:
71 translateY(0.0 - getMinY());
72 }
180 CHAPTER 6. RULES: METHODS, PARAMETERS, AND DESIGN
84 /**
The distance we translate is just the amount that the edge of the paddle is past the edge of the screen. We
only do the translation if the paddle is past the edge.
Laziness as a Virtue
There is a successful programming language, Perl, which is known for three things among computer program-
mers: some of the craziest combinations of punctuation as valid variable names (for example, $_ and $@ are
both regularly used system variables), having more than one way to do anything, and for having been devel-
oped out of laziness. The inventor of Perl, Larry Wall, praises laziness as a virtue because if you’re too lazy to
get right to work, you’ll think about what you need to do. Perl grew out of a report that Wall had to type up
every week with data he had to go across campus to read off of another computer. He automated the process
and added parameters so he could change the report format.
Laziness also means that you will look for general solutions that use parameters to specify them. That is,
look at the PongPaddle advance method. How hard would it be to modify the game to bounce a ball up in the
sky while the paddle moved across the bottom of the screen? The paddle is already ready (just set the shape
and the velocities). The ball would need a little work.
Of course, some part of the code is specific to the problem at hand. We will end this chapter with a look at
the six state handling methods we pretended into existence at the beginning of the chapter.
86 *
87 * @return true if we are, false otherwise
88 */
89 private boolean isPlaying() {
90 return ball.isVisible();
91 }
92
93 /**
94 * Whatever state we were in, move us to the moving ball state.
95 */
96 private void beginPlaying() {
97 ball.show();
98 countdown.hide();
99 pressSpace.hide();
100 startBall(ball);
101 setScore(0);// no volleys yet
102 }
6.5. FINISHING UP SOLOPONG 181
103
104 /**
105 * Are we in the waiting to countdown state?
106 *
107 * @return true if we are, false otherwise
108 */
109 private boolean isWaitingToCountdown() {
110 return pressSpace.isVisible();
111 }
112
113 /**
114 * Whatever state we were in, move us to the waiting to countdown
115 * state.
116 */
117 private void beginWaitingToCountdown() {
118 ball.hide();
119 countdown.hide();
120 pressSpace.show();
121 }
122
123 /**
124 * Are we in the counting down state?
125 *
126 * @return true if we are, false otherwise
127 */
128 private boolean isCountingDown() {
129 return countdown.isVisible();
130 }
131
132 /**
133 * Whatever state we were in, move us to the counting down state
134 */
135 private void beginCountingDown() {
136 ball.hide();
137 countdown.show();
138 pressSpace.hide();
139 countdown.setTimer(COUNTDOWN_SECONDS);
140 countdown.startTimer();
141 }
142
143 /**
Only one of the three sprites, ball, pressSpace, and countdownTimer is visible at any time in our game.
ball is visible when the player is playing. pressSpace is a StringSprite telling the player to press space
to start the game; when it is visible, the program is waiting to countdown. countdownTimer is only visible
when it is counting down. Note that invisible sprites will still report intersection, one with another or with
visible sprites. This can be a problem in some games but since two of the three sprites are never tested for
intersection, we are fine.
182 CHAPTER 6. RULES: METHODS, PARAMETERS, AND DESIGN
The three is<StateName> methods just return the visibility of the patron sprite of the state they name.
The first three lines of the begin<StateName> methods are the same: they set the visibility of the three patron
sprites to match the state they are beginning. After the first three lines, beginCountingDown sets the count-
down time and starts the timer. beginPlaying sets the score (volley count) to 0 and uses startBall to start
ball.
The use of a method, startBall and a parameter, the ball, is done with an eye toward having more than
one ball in play. It also takes advantage of the fact that ball is a reference to a PongBall object so that the
location of that object can be set in the method.
75 theBall.setLocation(randomDouble(0.2, 0.4), randomDouble(0.3, 0.7));
76 }
77
78 /**
The actual startBall method, shown here, starts the ball moving down and to the right at a 45 degree
angle. It then randomly places the ball in a rectangle near the middle of the left side of the screen; this makes
it unlikely that leaving the paddle in one place will keep the game going across multiple restarts of the ball.
6.6 Summary
Delegation
Delegation is the creation of new named rules. In Java this is done by defining methods. The four parts of the
header remain the access level, the return type, the name of the method, and the parameter list.
The access level can be private, protected, public, or nothing. private means that access to the method
(or field) is limited to just the class in which it appears. protected means the same as private except for
classes extending the current class; child classes of the current class can access protected fields and methods.
public means that any class which has a reference to the current class can call the method (or access the field).
Having no access level means that the method or field is accessible to the package; for the moment, package
can be considered the folder containing the source code file.
There are two different parameter lists: the formal parameter list is the one in the header, a list of types
and names; the actual parameter list appears when the method is called and is a list of expressions. The formal
parameters behave like local variables which are initialized with the values of the actual parameter list.
In Java parameters are passed by value. This means the expression’s result is copied into the formal pa-
rameter; changes made to the formal parameter inside the method do not modify the actual parameter.
An apparent contradiction is references to objects; the reference is copied into the formal parameter but
both the formal and actual parameter refer to the same value.
Delegation is how top-down design can be applied. By assuming that the next lower level of solutions
exist, a method can be written. Then, the simpler problems represented by the design of the sub-methods are
approached by defining the methods.
Scope
The scope of a variable is where the name is visible. Fields are in scope from the opening curly brace of the
class to the closing curly brace. Formal parameters are in scope throughout the block defining the body of
the method. All other variable declarations are in scope from the line where they are declared until the end
of the block in which they are declared.
6.6. SUMMARY 183
Review Exercise 6.1 Would it make sense to have a constructor for PongBall which only takes an initial ve-
locity? Why or why not? If it makes sense, can you write it?
Review Exercise 6.2 Assume you write a makeAllTheSprites method which takes no parameters.
(a) How would you call makeAllTheSprites from setup?
(b) Describe what happens to the running version of setup when makeAllTheSprites is called.
Review Exercise 6.4 cr:Rules:ScopeHole What is the type of x at each of the lettered lines?
public void someMethod(double x) {
// (a)
if (x > 100.0) {
String x = ”Hello, World!”;
{
int x = 10;
// (b)
}
// (c)
} else {
// (d)
}
// (e)
}
Review Exercise 6.5 Using the code in the previous exercise, describe where there is a scope hole for x.
int k = 13;
// (d)
alpha(k);
// (e)
int j = beta(k);
// (f)
k = j;
// (g)
}
Review Exercise 6.8 What color is the rectangle on the screen after this code executes?
public void aleph(RectangleSprite rect) {
rect.setColor(getColor(”blue”));
}
aleph(rs);
// and if we stop here?
rs = beth(rs);
}
Review Exercise 6.9 What method does Game provide to examine the player’s mouse position? What method
dose Game provide to examine the player’s keyboard?
Programming Problems
Programming Problem 6.1 Write a computer program that creates a tenth of a screen red square that
bounces around the screen. When the player clicks the mouse on the moving square, the color should change
to cyan and then, when it is clicked again, it turns back to red. Also keep track of the number of times the user
successfully clicks on the square (don’t bother keeping track of misses).
This problem should be implemented incrementally. Three levels of refinement might be:
(a) Make a program with a red bouncing square. You will need a reference to the bouncing square that
is visible to multiple routines; what should you be thinking about where to declare the label for the
bouncing ball. (Hint: Look at PongGame for some guidance on creating a bouncing object.
Why? Animating a bouncing RectangleSprite seems to have the simplest subset of the functionality
required by this program. So this seems to be the smallest program that does something along the path
to a program that fulfills the requirements described above.
6.6. SUMMARY 185
(b) Add a score. This would require a numeric component that is visible from multiple methods
(preCodeHook, some display building method, and the mouse listener (see below)). Also, add label on
the screen so the player can see their square
(c) Modify your bouncing program so that the bouncing square has a MouseListener listener for press-
ing/releasing the mouse. add a
Playtesting. Though this game is very, very simple, it offers an interesting chance to do some
playtesting to balance difficulty and enjoyment of the game. First, think about what you could do
to make the game easier.
The two ideas that come to mind are to make the target bigger or to make it move more slowly (or some
combination of both). For the purposes of this exercise we will leave the size fixed and manipulate the
speed.
Where is the velocity of the moving square set? Change the speed to one half of its current value and
play the game. It will be easier; is it more fun? It probably will be if you found the previous speed too
difficult. Try doubling the velocity from the original value. Is the resulting game more fun? Or is it
frustratingly difficult?
Now that you know the fun/challenge ratio for three different settings, you can work out the setting
that makes the game most enjoyable for you. Note that in the real-world playtesting involves a lot more
testers and an average of the results typically sets the difficulty level of the final game. Think a little
bit about how you could accommodate gamers of different abilities and keep an eye on the Playtesting
sections in other Programming Problems.
Programming Problem 6.2 Going back to OneDie.java, can you add an animation to the dice? When the dice
are rolled, they should start randomly changing the visible face once every quarter of a second and should
“roll” for two seconds (both values should be fields and should have get/set methods).
EasyDice will have to be modified so that it asks the dice if they are still rolling. If they are, ignore user
input. This implies the following changes to the public interface for OneDie:
public void setFaceTime(double secondsPerFace) ...
public double getFaceTime() ...
public void setRollTime(double secondsToRoll) ...
public double getRollTime() ...
public boolean isRolling() ...
Make sure you provide good comments for the methods (and the fields that you add).
Playtesting. Look at the two two ball variations spelled out here. Try each a few times. Which
seems to make for more interesting gameplay? Why do you think that is? Would it make the game
more interesting to have the two balls move at different velocities?
Programming Problem 6.4 Start with SoloPong.java. Modify PongBall so that after each volley it speeds up
by 5%. Does this variation make the game more fun?
186 CHAPTER 6. RULES: METHODS, PARAMETERS, AND DESIGN
Programming Problem 6.5 Add a target object to the game. Create a class which when struck by the
PongBall disappears. Disappearance is handled either by moving the object off of the screen or calling
removeSprite(<spriteToRemove>).
Notice that if you have one object that can disappear when struck, you can have an arbitrary number of
them and you could build a Breakout clone.
Chapter
7
Components Meet Rules: Classes
Abstraction, as a problem solving technique, means the creation of a new type. We have created our own
classes, by extending FANG classes such as Game and CompositeSprite. This chapter will explain, in much detail,
much of the nuance of designing your own type. It also presents the first networked game, finally exploiting the
“N” in FANG. Finally, it also introduces Java’s standard output stream, a way to print messages in the window
where Java is running. The output is more primitive than that provided by StringSprite but it is easier to set
up and can be used for simple debugging.
187
188 CHAPTER 7. COMPONENTS MEET RULES: CLASSES
ns
T ur
XO
e
Squar
in g
n at
ct
er
e-sele
Alt
X
Mo us
Figure 7.1:
O
TicTacToe game design diagram
Easier, especially given FANG’s networking capability, will be building a multi-player version of the game
where each player runs a separate copy of our TicTacToe program. Then, the two copies of the program will
communicate so that play alternates between the two players and the game will detect when one of the two
wins.
necessary.
7.2. NETWORK PROGRAMMING WITH FANG 189
If we can pass messages using the Internet protocols, what should our processes say to one another to run
our game? The answer to that depends a lot on the answer to the third question: where does the game live?
If multiple processes are cooperating to play a game, the game state could live one of three places:
1. In one process
If all of the state lived in one process that process would be the server process. All other processes would
have to provide requests to that process for doing what their player has tried to do with the input and the
server would have to decide what happened and communicate the results back to all the other processes. The
other processes in this model are called clients. If the terms client/server sound familiar, it might well be
because a Web server keeps a collection of Web pages and a Web browser (client) requests pages from the
server and the server decides what pages the client gets.
If part of the state resides in each process without a server process, then each process must communicate
its portion of the state to all other processes playing the game. Since every process is symmetric, that is
every process has the same communications needs and abilities, they are often referred to as peers and a
configuration like this is a peer-to-peer configuration. Peer-to-peer communications patters are seen in instant
messaging programs and file sharing systems such as Bittorrent.
The final configuration requires that the input fed to each process be the same. That is, if one player
presses “A”, then all of the processes must process that keystroke in the same order relative to all other input.
This means the input needs to be shared from process to process and they must all be ordered. This is most
easily accomplished using a client/server model where all input goes to the server and the server sends the
combined input stream out to all clients.
This last model, where all games simply process the same sequence of input to remain in synch has an
obvious vulnerability: if for some reason my game did something different with the input, my view of the
game would not match yours. If I rewrote my game to give me hidden knowledge (say seeing your cards in a
card game), then I would be cheating.
FANG uses this third method because the input stream to FANG games is typically fairly small so sharing
one or more such streams across the network is not too bandwidth hungry. An interesting note is that you
have been using this model with every game you have written. In order to handle multi-player games well,
even single player games create a server to handle input and then keystrokes and mouse moves are forwarded
to that server and then bounced right back to the single-player game.
When we distribute the game across multiple processes, each copy of TicTacToe runs the same game so
FANG, in each process, runs the video game loop. The only real difference from our point of view is that the
user input can come from the local player or from any other player connected to our game. So long as all
copies of the game see the same input from all players in the same order, the update action will be the same
at each game.
Sharing Information
We will define GameTile a lot like EasyButton (review Section 5.1 if you need to). Thus there will be an
isPressed method which can be called from the game’s advance method to determine whether or not the
user has clicked the mouse in a given square.
The difference this time is that rather than using the getClick2D() method of the current game (no param-
eters between the parentheses) we will use the getClick2D(playerID) where playerID is an int which tells
the game which of the multiple players we are interested in. The first player (the only player in single-player
games) is player 0; the other player in a two player game is player 1.
124 Location2D mouseClick = Game.getCurrentGame().getClick2D(playerID);
125
The method, isPressed looks almost identical to isPressed in EasyButton: it gets the current game, fetches
the mouse click location, and checks if the mouse location intersects this sprite. The differences are the pa-
rameter, int playerID on line 124 and then the use of the parameter when calling getClick2D in line 126. This
means that our game cares about which player clicked the button. In the game we will only call isPressed
with the player number of the current player. If it is X’s turn, only player number 0 will be able to click;
alternatively, if it is O’s turn, only player number 1 will be able to click.
We will see how the player numbers are assigned and how a multi-player game is started later in this
chapter. First we will look at abstraction as a problem solving technique in Java.
What is a Type?
What is a type? One answer, for beginning Java programmers, is that a type is a class. This is a tempting
equivalence because class is the way users define their own types. Type is a larger concept, however. The
plain old data types are, in fact, types while they are not classes.
1. A public interface. The collection of operators which users of the type may call along with any publicly
accessible fields and exported internal types.
2. A private implementation. Also known as a “layout in memory”. For classes this means the private fields
and methods and internal types. For non-classes the idea of a layout in memory makes more sense;
the bits inside the POD type have an interpretation defined by the language designers and that is the
implementation.
3. Interaction is limited to the public interface. No code using the type is permitted to access the private
implementation directly; all access to the value of the type is through the interface.
Java’s double type is an abstract type. The public interface includes declaring double variables, the stan-
dard arithmetic operators (+, -, *, and /) which work with double, and the cast operator, (double) which forces
Java to treat a given expression as having the type double.
What is the private implementation. We don’t know. In fact, we don’t want to know. If we learn the internal
representation of a floating point number and we decide to manipulate the representation directly (ignoring
point 3 above), we must take responsibility for any constraints that the representation has. Otherwise our
manipulation might set a double in memory to a value which cannot be used to encode a double.
Alternatively, consider a RationalNumber class. A rational number is a number of the form pq for integers
p and q . If we kept two int fields, p, and q, what constraints, if any, would there be on what values of the two
fields would represent legal rational numbers?
A rational number cannot have a denominator of 0. Thus q can never be 0. Yet, if we made the fields public
(the equivalent of letting programmers directly manipulate the bits inside a double), any idiot could put the
line rational.q = 0; in their code and change the previous value of rational to an illegal RationalNumber.
If we “know”, by examining the public interface and limiting access to the public interface, that q cannot
be 0, then we can freely divide by q whenever we want without causing a divide by zero exception.
Alternatively, we could make sure that every time, just before we ever divide by q, we check whether q
is 0. This would work so long as every single programmer who worked on the class understood and followed
this convention. Missing the check even once will cause our program to harbor a serious bug. An intermittent
bug, one which does not fire every time the program is run, is always more difficult to find because you must
determine when it happens. Then, by repeating the reproduction steps you can trigger the bug; only after you
have reliable reproduction can you begin to determine the cause of the error.
What is a static definition? Static fields and methods are fields and methods defined for the class and not
for individual objects of the class. That is, there is a single copy of the field (or method) for all instances of our
class no matter how many instances there are.
Looking at GameTile, we see two static, final fields:
22
23 /** the constant (final), class-wide (static) Color for the text */
24 public static final Color DEFAULT_CONTENT_COLOR = Palette.getColor(
25 ”lavender”);
26
The first modifier before the field type, name, and value is public. Didn’t the entire section on abstract
data types spell out that public and fields do not mix? What makes these fields special? The second modifier.
final is a keyword meaning that the value of the field, variable, or parameter is constant after it is set
for the first time. Lines 22-23 declare and assign a value to the field DEFAULT_COLOR; it is not legal to use
DEFAULT_COLOR on the left-hand side of an assignment statement anywhere else.
What use is a final field? The final keyword is a comment that the compiler can enforce. The field is
marked as constant (immutable), documenting your intent that it not change in a way that the compiler can
read. If a constant value is used a lot of different places, then it is a good idea to name it so that you do not
repeat yourself.
The DRY principle is only half of the benefit of having a final field. If you look through a moderately long
class definition and find that it always initializes its location to (0.5, 0.5), how would you change the code to
have it start at (0.0, 0.0)? You would have to look at all calls to setLocation and, in turn, at all uses of the
number 0.5. You would have to figure out whether any of those uses is one that needs to be changed. Further,
you would have to make sure you didn’t change any of the uses of 0.5 that were not used for initial location
setting.
With a named final, static field, you would document what the 0.5 is for and give someone modifying
your class a single point to modify:
public final static double INITIAL_X = 0.5;
public final static double INITIAL_Y = 0.5;
So, what is static? It means there is only one copy of the field for all instances of the class. Consider
the field that is set using setColor for a sprite. The field is called color and each sprite has its own instance
of the field. That only makes sense since if you setColor on some StringSprite you do not expect the color
of all other StringSprites on the screen (or PolygonSprites or RectangleSprites, etc.). Thus color is not
7.3. ABSTRACTION: DEFINING NEW TYPES 193
static. The default color for the text and background of the GameTile does not need to be stored more than
once. Changing the default color should change the starting color of all GameTile created after the change
(hypothetical change here; the field is also final so it doesn’t change).
This field is safely public because it is final; no one can change it. It is public so the game using GameTile
can access the default color if it is interested in using the color for something else.
How do you reference a static field (or a static method)? Regular fields/methods must appear after a
reference and a dot (or, in a method inside a class, without anything, thereby implying this. in front of them).
A static field uses the name of the class followed by a dot: GameTile.DEFAULT_COLOR.
Lines 22 and 26 show examples of using a static method: the Palette class defines several getColor meth-
ods as static methods. Since the value of the final, static is set before the program really starts running,
we cannot access getCurrentGame. Fortunately, Game just passes calls to getColor on to the Palette class in
any case so we can just use Palette.getColor directly.
36 /**
37 * Construct a new GameTile: content = ””
38 */
39 public GameTile() {
40 background = new RectangleSprite(1.0, 1.0);
41 background.setColor(DEFAULT_COLOR);
42 addSprite(background);
43 content = ””;
44 displayContent = new StringSprite();
45 displayContent.setLocation(0.00, 0.075);
46 displayContent.setColor(DEFAULT_CONTENT_COLOR);
47 addSprite(displayContent);
48 }
49
50 /**
The three fields of GameTile are shown in lines 30-37 in the above listing. They are based on those in
EasyButton: a RectangleSprite for the background of the square, a StringSprite to show the content of the
square and a String which holds the content. Note that two of the fields are marked as final. That is, after
each is initialized in the constructor they may never be assigned a different value. This is a promise to the
compiler and to programmers reading this code. The value will not change after the first assignment. Thus
final serves as compiler enforced documentation.
In the interest of simplicity we have not used final before this point for our fields. It is considered good
design practice to use final whenever possible. It simplifies reasoning about when things might change.
Key thing to notice about final: while background is a final field and the object it references cannot be
changed, the non-final fields of the object referred to by background can be changed.
102 }
103
104 /**
The setColor method calls background.setColor; thus the location referenced by background isn’t changed
but the color field, stored in the contiguous space allocated when new was called (see line 42 above), does
change.
The content field is not final because we provide a setContent method which changes the content.
68 content = content.substring(0, 1);
69 }
70 this.content = content;
71 setText(content);
72 }
73
74 /**
Notice that we use the ability to validate the value being set in setContent (the reason we don’t want to make
regular, mutable fields public is we can’t validate). We make sure if the value has more than one character
we chop it down to its first character. This makes sure the displayed value is large and fills the square.
Document Intent
Over and over you have been admonished to document your intent. This means that your comments, header
and otherwise, should talk to someone who already knows Java as well as you do. The comments should explain
why the class/method/field exists as well as why someone would want to use it.
Documentation is another level of abstraction which a competent programmer keeps in mind: dividing
solutions into multiple cooperating classes and using step-wise refinement to develop methods are language
7.4. FINISHING THE GAME 195
ways of taming complexity with levels of abstraction. The comments on a class, method, or field give you a
slightly higher level to explain the function of your class in a natural language. The documented intent serves
as a table of contents for the bit of code it comments on, helping the reader find their way around your code.
The first thing to notice is that there are a lot of lines in TicTacToe.java; this is the last method in the
class but more than 300 lines makes this the longest program we have looked at so far. We will find that there
are sections of code that are repetitious (and we are going to wish for some way to apply DRY; that is left as
an exercise in Chapter 8).
The second thing to notice is that the names of the methods called by advance serve, to an extent, as
documentation for how it works. That is, reading the code as written makes much more sense than if the
methods were just methodA through methodE (isGameOver is provided by FANG so we don’t get to pick that
name). While this author claims there is no such thing as completely self-documenting code3 , this code is
fairly simple to follow because of the names chosen.
One more thing to notice. Because advance is an entry point for our top-down design of a Game-based
program, this is at a fairly high level of abstraction. This is evident in the complete lack of all but one field
name here. The details of how the methods work was put off until they are written. The only requirement
this has is that we be able to know what symbol belongs to the current player.
that needs no comments to be understood. The earlier argument that comments serve as a table of contents or outline to the code to
which they apply seems to say that a good level of comments will always make code easier to follow. This does not excuse poor naming
conventions, it just says that good naming and good commenting compliment one another to make code readable.
196 CHAPTER 7. COMPONENTS MEET RULES: CLASSES
Naming fields becomes annoying when there are 9 variables which are pretty much the same except for
where they are in the grid.
19 private GameTile t31, t32, t33;
20
81 /**
The nine GameTile are named by their location in the board (see lines 19-21). These lines show that when
declaring fields (or local variables), the type name can be followed by a comma-separated list of names. Thus
the template for field and variable declarations should be:
<fieldDeclaration> := private <TypeName> <fieldName1> [, <fieldNamei>]* ;
This permits the declaration of an arbitrary number of fields (or local variables) of the same type in a single
statement. We will use one declaration per line unless there is a reason not to. The game tile are declared on
three lines because their layout in the declaration serves to document where each entry is on the board.
We need to initialize nine tiles; to avoid repeating ourselves, we define the makeGameTile method. It takes
the center location for the tile as parameters; the center location is the only attribute which changes from tile
to tile. The constructor, size setting, and sending the parameters to set the location, and adding the sprite to
the game are the same for every tile. makeGameTile returns the reference it gets back from new so that the nine
variables can be initialized (lines 71-79). Lines 67-70 should be clear: the three other fields of the class are a
turn counter, a String with the symbol for the current player, and a currentPlayerID, the FANG ID number
for the player whose turn it currently is.
40
41 /**
This is the first Game-derived class for which we have defined a constructor. Typically we have been able to
use the standard settings for everything in a Game or we have modified the value inside setup. Unfortunately,
by the time setup is run, the number of players in the game has already been determined. Thus we must hook
into the Game earlier in its life.
FANG always uses the default constructor when building a Game-derived class. Thus we must build our own
default constructor. Line 36 is included to remind us that any constructor must call some constructor of the
parent class. Here we are calling the default constructor of Game. This is the same constructor that would have
been called if line 36 were omitted or commented out; the line is here to make clear what happens. After the
constructor comes back we set two fields of Game: numberOfPlayers and players. Both are set to 2 to make our
game require 2 players.
When you run the program, FANG notices that it is a multi-player game before calling setup. The game
brings you into the multi-player lobby.
The lobby has three text fields and a button. From the bottom up, the Connect & Start Game button
connects to the server described by the next field up, the Name of game server. The author’s machine is
198 CHAPTER 7. COMPONENTS MEET RULES: CLASSES
named “BigRedOne” which is why this field contains that name automatically. When running an applet, the
game server is assumed to be the Web host that served the applet; when running an application, the server is
assumed to be the localhost. Number of players indicates the number of players expected to play the game.
While it can be changed from the lobby, TicTacToe cannot handle a different number of players. Finally, Name
of this game is a name you can provide for the session you are running. This is so that multiple sessions of a
given game can use the same server and still be told apart.
Since our game is TicTacToe, the session name will be “t-cubed”. If there are already sessions of this
game running and waiting for players on the server, the drop-down box will contain the name of the sessions
already running. After typing in “t-cubed” and pressing the Connect & Start Game button, the first player
sees a waiting screen.
When a second player starts a copy of the program, they will see the same lobby. They can direct their
program to any machine they wish by typing the IP address or name into the Name of game server field. The
screenshot shows the “BigRedOne” name for the game server. Having said that, the list of sessions running the
current game contains one entry, “t-cubed”. If that name is picked and the second player presses the button,
two players connect to the same session, a session which requires two players, and the game begins.
The initial game shows nine GameTile. Either player may press Start and then the game begins. The first
player who chose the name for the session is player 0; the second player to join the session is player 1 and
7.4. FINISHING THE GAME 199
so on for any other players. This permits the game to know how to allocate score and keep track of player’s
turns.
Player Turns
How do the methods called from advance work. In particular, how can we figure out if the current player
moved and whether or not they won? Moved is pretty simple: the player moved if they moved to any of the
tiles on the board.
245 isPlayersMove(t13) || isPlayersMove(t21) || isPlayersMove(t22) ||
246 isPlayersMove(t23) || isPlayersMove(t31) || isPlayersMove(t32) ||
247 isPlayersMove(t33);
248 }
249
250 /**
isPlayerMoved is an example of a very long (though not very complicated) Boolean expression. Remember
that logical or is true if either or both of the expressions it joins are true. Thus this long or is true if any of its
subexpressions are true. What is isPlayersMove?
107 gt.setContent(currentPlayerSymbol);
108 ++turnsTaken;
109 return true;
110 } else {
111 return false;
112 }
113 }
114
115 /**
isPlayersMove takes a GameTile as its parameter and it determines if the player can move there (it must
currently be empty) and whether the player did move there (was the tile clicked by the current player). If they
can and did, then put their symbol in the tile and add one to the move count.
224 col1(player) || col2(player) || col3(player) || diag1(player) ||
225 diag2(player);
226 }
227
228 /**
The isWinner method is another long or. This time, however, it calls eight different methods. There are
eight methods because there are eight different ways to win: 3 rows, 3 columns, and 2 different diagonals. We
will just look at one of the methods as the other seven are almost identical; it would be nice to have some way
to avoid repeating ourselves here.
124 t13.getContent().equals(p);
125 }
126
127 /**
row1 (and all the other individual win checkers) takes a player symbol, a String as its sole parameter.
That is possible because only the player who just made a move could possibly have won. So we don’t have to
call isWinner with both symbols. It would not be that much harder to call it twice (or once for each player).
Knowing what player we are checking simplifies the individual win checking methods.
Comparing objects. Note the use of the equals method to compare the contents of a GameTile with the
player’s symbol. This method is defined in the base class of all Java classes, Object and is overridden in objects
which need a special way to compare themselves for equality. The equals method takes an Object as its
parameter and compares the given object to this. For a String, equals returns true if the two strings have
exactly the same number of characters and both strings match in each character position.
What about ==? That is what we used to compare int and double values. Why not use it for Object and
its child classes? Java permits the comparison of Object-derived classes with double equals; comparing them
with == has a different meaning than what we normally mean.
If we create two new String objects, each with the same contents:
7.4. FINISHING THE GAME 201
What are the values of doubleEquals and equalsMethod?the first Boolean expression, using == returns
true if and only if the two references refer to the same object; it returns false otherwise.
The equals method,
on the other hand, does whatever String defines to be the comparison operation for objects of its type (this
is an example of encapsulation in a type; the meaning of “equality” is left to the implementer of the class).
Thus the two Boolean expressions return false and true, respectively. This is the circumstance on the
left-hand side of Figure 7.5.
Memory Memory
one one
two two
"Hello, World!"
On the right-hand side of the figure is the result of the following code snippet:
String one = new String(”Hello, World!”);
String two = one;
boolean doubleEquals = (one == two);
boolean equalsMethod = (one.equals(two));
The only difference is that the value assigned to two is not a newly created string with the sequence of
characters “Hello, World!”, but rather a reference to the already created string containing those characters.
Since the sequence of characters in one and two (or rather, the actual strings to which they refer) is the same,
this code sets equalsMethod to true; since the values inside the references are the same, doubleEquals is also
set to true. Now back to our regularly scheduled program.
202 CHAPTER 7. COMPONENTS MEET RULES: CLASSES
The isCatsGame method is very simple: it checks if the number of moves is 9. If 9 moves have been made,
there are no empty squares. If we already know there is no winner from the last move, the game must end in
a tie.
The handle... methods are also pretty simple: they determine a message to display and call endGame with
the text of the message to display.
250 /**
251 * Create a StringSprite with the given message and display it across
252 * the screen; also make sure that the game is over.
253 *
254 * @param announcement the end-of-game announcement (winner or
The endGame method builds a StringSprite with the message (who won or that it is a tie) and puts it on
the screen as you can see in the image above. It also calls setGameOver, a method in Game. That means advance
will do no more work (look at line 294; isGameOver will return true after it is set).
Both games in the session see the exact same sequence of input. When either player moves or clicks their
mouse, the information goes to both versions of the game. The only clicks that make a difference, though, are
those by the current player on empty squares (isPlayersMove makes sure the square is empty and isPressed
in GameTile takes a player number to make sure only that player’s clicks count). Even though two “different”
versions of the game are running, the input stream is shared so the two games unfold identically. The games
you have built in previous chapters can also be extended for multiple players.
7.5 Summary
Network Communications
A running program or process can communicate with other running processes across the network. FANG,
through Java, supports multi-player games where multiple players run the same game in multiple processes.
Network communications can be from client processes to server processes where the servers provide some
special service to the clients. This architecture is often many clients to one server. An alternative architecture
is for many peers to connect. Peers are all the same program running as separate processes.
FANG uses a client/server architecture. The server portion runs and all clients send their input to the
server. The server then sorts all the input into a particular order and sends it to all of the clients. Clients only
run the game with the input they get back from the server. This lets each program run the program from the
beginning and, since they see the exact same sequence of input, the programs remain synchronized.
Abstraction
Abstraction is the encapsulation of rules and data together in a new type, an abstract data type. An abstract
data type has three parts:
2. A private implementation. Also known as a “layout in memory”. For classes this means the private fields
and methods and internal types.
Abstraction permits a part of the solution to be wrapped up inside a class so that users can focus solely
on the interface while the implementer can ignore how the type is being used.
Review Exercise 7.1 If there were three players in a networked game, how would you check to see if the first
payer had typed ’y’?
Review Exercise 7.2 What are the advantages of sharing just the input stream in a FANG game? What risks
are there?
(a) If I told you I was running TicTacToe on my computer, nonesuch.potsdam.edu and I had started session
t-cubed, describe the steps you would take to connect to my session.
Review Exercise 7.3 Where do you specify the number of players necessary to start a multi-player game?
Review Exercise 7.4 What is a static final field for? What advantages are there to using named constants?
Review Exercise 7.5 Why do you think using final whenever possible is a good idea? Or, if you want to argue
the contrary, why is it not a good idea? Support your position with consideration of how easy/difficult your
code is to understand, how long your code is, and how much effort the code is to change.
Review Exercise 7.6 Why do we use setter methods rather than making fields public?
Review Exercise 7.7 If String (java.lang.String) is immutable, what can you tell me about all of its fields.
Review Exercise 7.8 When using a static method in RectangleSprite, what comes before the dot?
Review Exercise 7.9 What does the annotation @Override before a method mean? What checks does the
compiler perform for you when it is there? What does it tell the programmer?
Review Exercise 7.10 How would you let either player in TicTacToe pick the next square to move to? (Note:
This makes for a mildly interesting “click as fast as you can” sort of game; it is not a good game.)
Review Exercise 7.11 What is the advantage of making as many methods private as possible? These methods
are or are not part of the public interface of the class? If not, what are they a part of?
Programming Problems
Programming Problem 7.1 Start with TicTacToe.java. Modify the program to play a 4 4 game (with 4 in
a row required to win).
(a) Does it seem likely that GameTile will require changing?
(b) Where will you declare new GameTile fields?
(c) Make sure to modify makeGameTile so it sets the right sizes. Modify setup to construct all 16 tiles.
(d) How many ways are there to win in the new game? Write the required helper functions and rewrite
isWinner accordingly.
(e) Is there anything else that must be changed?
Programming Problem 7.2 Start with TicTacToe.java. How would you modify the program so that it can
be played in a single FANG instance with the players passing the mouse back and forth? What is the minimum
changing you can do?
7.5. SUMMARY 205
Programming Problem 7.3 Start with SoloPong.java. Modify SoloPong so that it plays a two player version
of the game. Add a second paddle. When constructing a paddle you will probably want to take a player number
as a parameter and keep it as a field in the paddle so that each paddle responds to just one of the players.
Playtesting. You will want to play the game some and then consider adjusting how the ball starts. You will
want to move the location so it is closer to the center and be able to vary the direction according to which
player last missed the ball. You will want to keep score for each player individually.
Programming Problem 7.4 One problem with TicTacToe is that it plays a single game and then must be
restarted to play a new game. Can you modify the game so that it advances to a new game when the current
game is over? Or better, that pauses for 10 seconds so players can see the results and then starts the game
over.
You will want to switch which player is X and which is O. What changes when player 1 is X and player 0 is
O?
Programming Problem 7.5 Look at Problem 2. Modify the two player version of EasyDice to make it a
networked, multi-player game. If you have a one-machine solution, the real multi-player solution should be
very simple (though EasyButton must be modified to make it like GameTile).
Programming Problem 7.6 Look at Problem 3 for a description of the game of Pig. Using OneDie and, perhaps,
EasyDice,you can build a multi-player game of Pig. Start with making it two player; after the next chapter
you should be able to extend it to an arbitrary number of players.
Chapter
8
Collections: ArrayLists and Iteration
When writing TicTacToe, much of the length of the program was taken up with methods to test for winning
combinations. If you look at several of them, say the row* methods, one very interesting thing to note is how
similar the three methods are.
118 * @param p player to check
119 *
120 * @return true if p won across row 1; false otherwise
121 */
129 *
130 * @param p player to check
131 *
132 * @return true if p won across row 2; false otherwise
140 * Did the given player (X, O) win across row 3?
141 *
142 * @param p player to check
143 *
Note that all three use the same form, a logical or between three calls to the equals method. equals is the
standard way of checking whether or not two objects are equal. What is interesting is the similarity of the
names of the variables at the beginning of each chained call to equals.
Notice that the three methods are actually identical except for the names of the variables: row1 uses field
names beginning t1; row2 uses field names beginning t2; row3 uses field names beginning t3. Thinking about
our desire to avoid repeating ourselves, this seems wasteful. Further, imagine the changes necessary to extend
the game to play 4 4 tic-tac-toe. More methods with more similar variables in each method.
There has to be a way to get Java to do the work, to have it calculate which variable we want to address at
run-time, saving the programmer work and headache at compile time.
This chapter is about collections, objects which contain other objects. A collection has some method of
indexing to recover individual objects in the collection. We will start with collections indexed by small integers
so that we can loop across all entries in the collection, executing the same code on each entry.
207
208 CHAPTER 8. COLLECTIONS: ARRAYLISTS AND ITERATION
Simulated Days: 19
Flu Simulator
Rec
ov
erin
g
Dea
Co
n
d
ta g
io u s
The FluSimulator will represent the village in the middle of the screen as a row of people. Each person
will change color to reflect their current state of health. The design also introduces a more complex model
of disease: an infected individual is sick but not contagious for some amount of incubation time; then, for
8.1. FLU PANDEMIC SIMULATOR 209
some amount of time, they are contagious; then, for another amount of time, they are sick but recovering.
Both healthy and dead individuals do not change their state spontaneously. This means there are five states
of health (and five colors in the final game): DEAD, HEALTHY, SICK, CONTAGIOUS, and RECOVERING.
3 da Recovering
ys
1 day
ie d
ys && d
3 da 3 d ay
s
Dead Contagious
A state diagram like that in Figure 8.2 can be used to describe the states of an object and how that object can
change states. In this diagram the circles represent a person’s state of health. Each is labeled with the name
of their health state. The arrows indicate what transitions are permitted between states. In our simple disease
model, there is no way for a HEALTHY1 person to die (go directly to DEAD). Each transition arrow is labeled with
1 We will use the named constants from the program, all capitalized and in the standard code font. The state labels in the diagram
the event which causes the person to make that transition. Thus a person must be exposed to the disease and
become infected with the disease to go from HEALTHY to SICK.
The two things to note about the state diagram are that there are two transitions out of RECOVERING and
there are no transitions out of DEAD. The decision between the two ways out of RECOVERING is made by consulting
the mortality rate or our disease. After three days pass, if our person is found to be in those killed by the disease,
they die. If not, then they become healthy again. The decision of mortality (as well as exposure and infection)
will use random numbers.
This model is very simple. At the end of each day a person can check their current health state and the
number of days they have been in the current state. With that information, the person can determine whether
or not they should transition to a new health state. If the person is leaving RECOVERING, they will also need to
determine whether the live or die.
Note that upon becoming healthy again, a person can be reexposed and reinfected. This does not match
disease spread exactly but for the simplicity of the model we will leave it (giving a person a “memory” of being
sick is discussed in Programming Problem??).
3 /**
4 * This ”game” has no sprites. In setup it just prints ”Hello, World!”
5 * to the standard output console. This is a demonstration of how
6 * System.out can be used.
7 */
8 public class HelloWorld
9 extends Game {
10 /**
11 * Print out one line. That is all the setup required
12 */
13 @Override
14 public void setup() {
15 System.out.println(”Hello, World!”);
16 }
17 }
When the program is run, it looks exactly like EmptyGame.java (except for the name in the title bar). The
interesting thing is what happens in the console window where you ran the program3 .
Figure 8.3 shows the command-line window where the program was run after being compiled. Note that
the classpath given in the screenshot is that on the author’s main machine and probably doesn’t match your
class path.
The only difference between println (as used in this program) and print is that println prints an end-of-
line character after printing the parameter. Using print it is possible to build up a single line of output over
2 The fully qualified names of classes are some collection of packages, separated by dots, and then, at the end, the class name. If no
package names are specified, it means that the class is in the current directory; classes defined in the same directory do not need to be
imported at all.
3 If you are programming in a browser using JavaWIDE or another on-line IDE, you will need to open your Java Console to see console
many calls to print; this is useful when printing while using iteration; you might only want to start a new line
at the end of the iteration. More on this below when we try our hand at iteration.
Poor-man’s Debugger
What good does console output do us? It can be used to give the user feedback that does not fit well into the
game display. Because print and println can print many different types, it is often easy to add lines that print
out the current values of a variable at several points. Consider the output of PrintASprite.java.
1 import fang2.core.Game;
2 import fang2.sprites.RectangleSprite;
3
4 /**
5 * Demonstrate how a Sprite class object prints out using
6 * System.out.println.
7 */
8 public class PrintASprite
9 extends Game {
10 /** The value to print out. */
11 private RectangleSprite someField;
12
13 /**
14 * Creates a rectangle sprite for the field; print field value several
15 * times to show toString method
16 */
17 @Override
18 public void setup() {
19 System.out.println(”someField = ” + someField);
20 someField = new RectangleSprite(0.25, 0.25);
8.2. CONSOLE I/O: THE SYSTEM OBJECT 213
21 System.out.println(”someField = ” + someField);
22 someField.setLocation(0.5, 0.5);
23 addSprite(someField);
24 System.out.println(”someField:” + someField.getX() + ”, ” +
25 someField.getY() + ” - ” + someField.getWidth() + ”, ” +
26 someField.getHeight());
27 }
28 }
The game declares a RectangleSprite field (line 13). The setup method runs sequentially. Thus the field
is printed twice, once before and once after the call to new. This book has been adamant that you not use fields
before they are initialized; if you look at the first line of output below, you will see why:
~/Chapter08% java -classpath .:/usr/lib/jvm/fang.jar PrintASprite
someField = null
someField = fang.sprites.RectangleSprite[x = 0.0, y = 0.0],
[w = 0.25, h = 0.25] color = FANG Blue
someField:0.5, 0.5 - 0.25, 0.25
The value of the uninitialized field is null; null is the reference value that means the reference refers to
nothing. The important thing is that you cannot apply the dot operator (.) to the null pointer without causing
a run-time error.
The next line in the output is the String value of the RectangleSprite right after the call to new. In Java all
object types have a toString method which is used to convert a non-null reference to an object into a string.
This means toString is part of the inherited public interface of every class; inherited because it comes from
extending an object which has the method in its public interface.
The implementation of Sprite overrides the definition of toString it inherits with the following code:
/**
* Return a string representation of the {@code Sprite}. The type,
* location, size, and color of the {@code Sprite}.
*/
\@Override
public String toString() {
return getClass().getName() + ”[x = ” + getX() + ”, y = ” + getY() +
”], ” + ”[w = ” + getWidth() + ”, h = ” + getHeight() +
”] color = ” + Palette.getColorName(getColor());
}
When an object type is passed to printf or, in this case, when it is passed as a parameter to the String
version of the + operator, Java converts the value to a String with a call to the toString method of the ob-
ject. In Java, when a method is overridden, the compiler calls the version defined farthest down the extend
hierarchy for the constructed object type. Here, we can see the call to new in line 22 and see that the object is a
RectangleSprite. Looking at the documentation page for RectangleSprite, we see the hierarchy of extend:
java.lang.Object
+--extended by fang.core.Sprite
+--extended by fang.sprites.RectangleSprite
Thus the version of any overridden method called will be: the one defined in RectangleSprite, if there
is one; or else the one defined in Sprite, if there is one; or else the one defined in Object, if there is one; or
214 CHAPTER 8. COLLECTIONS: ARRAYLISTS AND ITERATION
else throw a compiler error. So, toString is defined in Object, a class which every single object class in Java
extends. It is overridden in Sprite. It is not overridden in RectangleSprite (you can see if it is overridden in
the documentation page: if the method is listed in the Method Summary portion of the page, it is overridden
in that class; if, instead, the name of the method appears below the Method Summary in a box with a label
like Methods inherited from fang.core.Sprite, then the method was last defined in the hierarchy of extend
in the named class.
Since Sprite was the last class to define toString in the hierarchy of RectangleSprite, it follows that the
toString called to create the second output line is the one defined in Sprite. We will discuss this selection of
the lowest override in the actual type’s hierarchy in more detail later.
The final line of output is generated by the println beginning on line 26. Remember that + used with
String expressions concatenates them together. Here the first item in the println is a String literal. The
remaining pieces are calls to methods on someField and punctuation strings to make the result readable. The
final line gives the location and size of the sprite at the end of setup.
There is a program (included in most modern integrated development environments (IDE)) called a debug-
ger. A debugger lets you pause a program at an given line and examine the value of variables, look at field
values inside of objects, and even stop the program when the value of a given variable changes. There are
tutorials on the Web on how to use debuggers in various IDEs; that instruction is beyond the scope of this
book.
This use of printf is a way of doing much the same thing. At various points inside your programs, where
you wonder what is happening to the location of a given sprite or something similar, you can add a print
statement and track the value. This is useful even with an integrated debugger in that while a debugger lets
you pause the program on each line, stepping the debugger through all of the start-up code of a FANG program
(or any other framework program) can be tedious.
8.3 Iteration
How could you print the numbers 0 through 9 to the console as part of setup? Hopefully something like this
occurred to you:
public void setup() {
System.out.print(” ” + 0);
System.out.print(” ” + 1);
System.out.print(” ” + 2);
System.out.print(” ” + 3);
System.out.print(” ” + 4);
System.out.print(” ” + 5);
System.out.print(” ” + 6);
System.out.print(” ” + 7);
System.out.print(” ” + 8);
System.out.print(” ” + 9);
System.out.println(); // start new line
}
Two things to notice about the code: each line looks just the same with a single character different (seems
to violate DRY) and it would be very hard to extend to 100 numbers (and harder for 1000). We need some way
to iterate or do the same thing (or almost the same thing) over and over again.
The following code does the trick:
1 public void setup() {
2 for (int i = 0; i != 10; ++i) {
3 System.out.print(” ” + i);
8.3. ITERATION 215
4 }
5 System.out.println(); // start new line
6 }
The semantics or the meaning of this language construct is based on the three parts of the for statement
that are inside the parentheses. The three parts, separated by the semicolons, are described in the Java tem-
plate for the for statement:
<forStatement> ::= for (<init>; <continuation>; <nextStep>) {
<body>
}
The <body> of the for loop is the collection of statements which are iterated or done many times. The
<init> or initialization part of the for statement is executed once before any other parts of the statement are
evaluated or executed. In the counting loop above, the <init> is int i = 0: this declares a local variable and
sets the value of the variable to zero. Note that it is not necessary to declare a loop-control variable in the <init>
though it is often convenient.
Question: What is the scope of i? The scope is the whole for statement. That is, i is visible inside all
three parts of the for statement line and throughout the <body>. After initializing, execution of the for
loop continues by evaluating <continuation>. The <continuation> portion is a Boolean expression. When
<continuation> evaluates to true, the <body> is executed once; when it is false, execution continues after the
body of the for statement. Thus in our sample loop above, when i is not exactly equal to 10, the body of the
loop is executed. As you can see, right after initialization, i != 10 is true so the body is executed.
Each time the statement executes the <body>, upon reaching the end, execution continues by executing the
<nextStep> and then reevaluating the <continuation> condition. Again, if the Boolean expression evaluates
to true, the execution does the body of the loop and then does the <nextStep> and <continuation> again.
So, what does our example loop do, exactly? I will unroll the loop, printing each of the three parts sepa-
rately as they appear. This is not real Java in that it uses a special “statement”, EXIT to indicated that execution
should move to the statement after the loop. So if (boolean) EXIT; means to evaluate the expression and if
it is true, get out of the loop. Here is a trace of the execution of the various parts:
int i = 0; // init: i = 0
if (!(i != 10)) EXIT; // continuation
System.out.printf(” ” + i); // body: ” 0”
++i; // nextStep: i = 1
if (!(i != 10)) EXIT; // continuation
System.out.printf(” ” + i); // body: ” 1”
++i; // nextStep: i = 2
if (!(i != 10)) EXIT; // continuation
System.out.printf(” ” + i); // body: ” 2”
++i; // nextStep: i = 3
if (!(i != 10)) EXIT; // continuation
System.out.printf(” ” + i); // body: ” 3”
++i; // nextStep: i = 4
if (!(i != 10)) EXIT; // continuation
System.out.printf(” ” + i); // body: ” 4”
++i; // nextStep: i = 5
if (!(i != 10)) EXIT; // continuation
System.out.printf(” ” + i); // body: ” 5”
++i; // nextStep: i = 6
if (!(i != 10)) EXIT; // continuation
System.out.printf(” ” + i); // body: ” 6”
216 CHAPTER 8. COLLECTIONS: ARRAYLISTS AND ITERATION
++i; // nextStep: i = 7
if (!(i != 10)) EXIT; // continuation
System.out.printf(” ” + i); // body: ” 7”
++i; // nextStep: i = 8
if (!(i != 10)) EXIT; // continuation
System.out.printf(” ” + i); // body: ” 8”
++i; // nextStep: i = 9
if (!(i != 10)) EXIT; // continuation
System.out.printf(” ” + i); // body: ” 9”
++i; // nextStep: i = 10
if (!(i != 10)) EXIT; // continuation
EXIT:
System.out.println(); // After loop
Note that <init> is executed once and that <continuation> is evaluated one more time than the body of
the loop (it must evaluate to false once) and that nextStep is executed once at the end of each time the body
is executed.
This is, in many ways, similar to setup and advance: setup is run once before the main video game loop is
entered. After the game is entered, the body of the main video game loop includes a call to advance.
Practice Loops
This section will describe several sequences of numbers to print out. It will then present the for loop which
would print out the numbers. At the end of the section a couple of points the loops have in common will be
pointed out.
More Numbers
How would you change the example loop given above to print the numbers 0 through 99? We want 100 differ-
ent numbers.
for (int i = 0; i != 100; ++i) {
System.out.print(” ” + i);
}
System.out.println(); // start new line
Clearly the change was just in the <continuation>: the 10 became 100. This is right but not aesthetically
pleasing: there are too many numbers on one line. What if we want to print 7 numbers per line (with no more
than 7 on the last line since 7 does not divide 100 evenly)? We need to check, inside the loop, whether we need
to insert a new line. We will start a new line whenever i is a multiple of 7: a line before 0, 7, 14, 21,.... Note
that this means there is a new line started before the first number is printed. We will look at how to avoid that
after we get this loop working.
Also, note that the <body> of the loop is a collection of statements; it can contain calls to methods, if
statements, or even more for loops.
for (int i = 0; i != 100; ++i) {
if (i % 7 == 0) { // remainder of 0
System.out.println(); // start new line
}
System.out.print(” ” + i);
}
System.out.println(); // start new line
8.3. ITERATION 217
Here we test if i is a multiple of 7 by using the modulus operator, %. The modulus operator takes two int
expressions and divides the first by the second, returning the remainder in the division. The definition of a
number being a multiple of another number is that it have a remainder of 0 when divided by the number. Thus
the Boolean expression in the if statement is true exactly when i is a multiple of 7.
To get rid of the first newline, it suffices to test if i is not zero as well as the multiple of 7 test:
15 System.out.println();
16 }
17 System.out.print(” ” + i);
18 }
19 System.out.println();
20 }
21 }
The columns do not line up (single digit numbers print in a single space while double digit numbers use
two) but there are seven elements per line.
What if we want to do something other than print out i? What would we want to do? Perhaps add several
randomly placed RectangleSprites?
16 curr.setLocation(randomDouble(), randomDouble());
17 curr.setColor(randomColor());
18 addSprite(curr);
19 }
20 }
21 }
Figure 8.4 shows an example run of this program. This example uses iteration but not console output;
these two techniques are independent of one another.
218 CHAPTER 8. COLLECTIONS: ARRAYLISTS AND ITERATION
Not Starting at 0
How would you print (on the console) the numbers from 10 up to 99? The easiest way is to change <init>:
rather than set i to 0, set it to 10.
15 System.out.println();
16 }
17 System.out.print(” ” + i);
18 }
19 System.out.println();
20 }
21 }
49 50 51 52 53 54 55
56 57 58 59 60 61 62
63 64 65 66 67 68 69
70 71 72 73 74 75 76
77 78 79 80 81 82 83
84 85 86 87 88 89 90
91 92 93 94 95 96 97
98 99
Again, this is correct (the numbers printed are 10-99, inclusive) but it is not aesthetically pleasing. It would
look better to have 7 elements on the first line and every line after except, possibly, for the last (how many
numbers are printed? Is that number a multiple of 7? If not, how many elements will there be on the last
line?).
The trick here is that the value i in previous examples has served two purposes: it contains the value to
print next and it counts how many items have been printed. That second function must be done some other
way now. How can we determine how many elements have been printed so far? Having that number, we can
check if it is a multiple of 7 and start a new line.
When i is 10, 0 elements have been printed; when it is 11, 1 element; when it is 12, 2 elements. The pattern,
so far, looks like the expression i -10 tracks the number of numbers printed so far. Thus we can replace i in
the if statement with i -10:
15 System.out.println();
16 }
17 System.out.print(” ” + i);
18 }
19 System.out.println();
20 }
21 }
The parentheses make sure that the value of i minus ten is calculated before modulus or comparison. The
output is:
~/Chapter08% java -classpath .:/usr/lib/jvm/fang.jar NotFromOneFixed
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31 32 33 34 35 36 37
38 39 40 41 42 43 44
45 46 47 48 49 50 51
52 53 54 55 56 57 58
59 60 61 62 63 64 65
66 67 68 69 70 71 72
73 74 75 76 77 78 79
80 81 82 83 84 85 86
87 88 89 90 91 92 93
94 95 96 97 98 99
Not Counting by 1s
How would we change the original loop to print out the first ten non-negative multiples of 7? You might think
of modulus with multiples of 7 but this problem is simpler: since 0 is a multiple of seven, each larger multiple
220 CHAPTER 8. COLLECTIONS: ARRAYLISTS AND ITERATION
of seven is seven higher than the previous multiple of seven. That is, the beginning of the sequence is 0, 7, 14,
21,... How would we only print the multiples of seven? Could use the if statement from MoreNumbers but it
makes more sense to just have i only take on multiples of 7 values. Instead of counting by 1, count by 7. This
means <nextStep> is modified to i = i + 7:
15 }
16 System.out.println();
17 }
18 }
The other change is the limit on the loop: the first ten non-negative multiples of seven are less than 70
and the eleventh is 70. The output of this loop is:
~/Chapter08% java -classpath .:/usr/lib/jvm/fang.jar CountBySeven
0 7 14 21 28 35 42 49 56 63
What is the output of this snippet of code? The variable is initially 0 which is not less than 0. Thus
<continuation> is false from the start and the <body> (and <nextStep>) is never executed.The only output is
a newline.
About Iteration
In Java, for is one of the main ways of implementing iteration. Most of our examples used != in the
<continuation>; this is idiomatic in the language if you know the exact value the loop control variable will
take at the end of the iteration. When we count by 1, this is pretty simple. The instance of counting by 7 was
harder since we had to figure what value the variable would take (it had to be a multiple of 7).
It is also idiomatic to start counting with 0. Computer scientists start counting with 0 because when there
is a collection of objects (as we will see in the next section), they are indexed by integers starting with 0. The
reason is that a collection can then be stored as the computer address of the whole collection and a given
element is found by adding the index times the size of each object. This will be discussed again in the next
chapter. Also, there are multiple ways to do many things in a loop. Rather than adding 7 in the loop, we
could just count from [0-10) (the “[” means that end point is included; the “)” means that end point is not) and
multiply the value of i by 7 when we go to print it. This version is left as an exercise for the reader.
Dynamic and Static Information Attributes of programs, and, in particular, variables, which are known at
compile-time are referred to as static attributes. Attributes of programs which cannot be known until run-time
are referred to as dynamic attributes. Because static attributes are known early, they cannot change during
the life of the program.
Static attributes include the value of literals (such as 1.8, true, or ”Hello, World!”), the type of a variable,
or the name of a variable. Dynamic attributes include user input (such as the timing and location of a mouse
click), the result of a call to randomDouble(), or the value of a variable. Dynamic attributes can typically change
over time (though we make use of the final designation to indicated that the variable can only be assigned a
value once) while static attributes cannot.
The discussion of dynamic and static attributes appears here because we will be discussing two types of
collections, arrays and ArrayLists. Arrays are built-in types which can have as elements any one type of
object; arrays are created with a call to new which includes the exact number of entries which the collection
can contain. ArrayList objects are library types which can have as elements any object type; they are created
with a call to new which need not include the number of entries they are expected to hold and their size can
be grown to accommodate any arbitrary number of elements.
Java has a group of standard collection classes. We will focus on the use of the java.util.ArrayList class
because it supports adding an arbitrary number of elements to it at run-time, random access of elements based
on their insertion order, and the ability to loop across all of the elements it contains4
Declaring an ArrayList
An ArrayList is a standard class. It is defined in the java.util package. That means the required import is
import java.util.ArrayList;
To declare an ArrayList field, you specify the access level, the type, and the name just like any other field.
Except that the type includes both ArrayList and the type of objects stored in the ArrayList. For example,
we will write a program to store Integer objects in an ArrayList. As you can see, Integer is an object type
(the capital letter); from the name you can deduce that it is like the plain old data type int. That intuitive
deduction is sufficient for our purposes here. Further discussion of the Integer type will be deferred until the
end of this section.
So, to declare a field, theTable, an ArrayList holding Integer values, we use the line:
private ArrayList<Integer> theTable;
Just as we printed a RectangleSprite to the console, we will start by printing an ArrayList using this
program:
1 import fang2.core.Game;
2
3 import java.util.ArrayList;
4
5 /**
6 * Demonstrate how an ArrayList prints using System.out.println.
7 */
8 public class PrintAnArrayList
9 extends Game {
10 /**
11 * ArrayList of Integer (we will put in some numbers and do things
12 * with them)
4 There is a built-in type, the array, which is similar except for the variable size; we will discuss arrays as we need to use them.
222 CHAPTER 8. COLLECTIONS: ARRAYLISTS AND ITERATION
13 */
14 private ArrayList<Integer> theTable;
15
16 /**
17 * Create the ArrayList; print out some information about it
18 */
19 @Override
20 public void setup() {
21 System.out.println(”theTable = ” + theTable);
22 theTable = new ArrayList<Integer>();
23 System.out.println(”theTable = ” + theTable);
24 System.out.println(”theTable.size() = ” + theTable.size());
25 }
26 }
Without doing more than declaring the field, constructing a new object for the reference, and then using
one method found in the public interface described in the ArrayList, size which returns the number of entries
in the collection. The output of this program is:
~/Chapter08% java -classpath .:/usr/lib/jvm/fang.jar PrintAnArrayList
theTable = null
theTable = []
theTable.size() = 0
An ArrayList is an object so the first printf, line 23, prints null. Then the collection is instantiated:
notice that the constructor called with the new operator includes the name of the element type just like the
declaration of the field. Line 25 prints out the value of the ArrayList. Looking at the documentation for
ArrayList, the toString method is inherited from class java.util.AbstractCollection; the exact location
of the method is not as important as our ability to find the documentation for the routine that is called (it
helps us debug). According to the documentation:
toString
Overrides:
toString in class Object
Returns:
a string representation of this collection
Thus the two square brackets on the second line of output indicate that the ArrayList is empty. This is
confirmed by the last line of console output where the size of the ArrayList is 0.
8.4. COLLECTIONS: ONE AND MANY 223
Filling a Collection
So, how do we put values into the collection? The ArrayList class defines a method, add. The add method
comes in two flavors: with one parameter of the type of elements in the list, it adds a new element at the end
of the ArrayList; with two parameters, one an int index and the other an element, it inserts the element at
the given location, moving all the other elements up one index. The one parameter version is the more usual.
So, to put some multiples of 7 into the ArrayList we can use the following:
23 System.out.println(”<before> theTable = ” + theTable);
24 System.out.println(”<before> theTable.size() = ” + theTable.size());
25 theTable.add(63);
26 theTable.add(56);
27 theTable.add(49);
28 theTable.add(42);
29 System.out.println(”<after> theTable = ” + theTable);
30 System.out.println(”<after> theTable.size() = ” + theTable.size());
31 }
32 }
After adding 4 elements, the 4 elements are in the ArrayList in the order they were added. This is similar to
addSprite: with plain add, the newest item is “on top” in the sense that it is last in the ArrayList.
Now, what if we wanted the elements to be the first ten non-negative multiples of 7 and we wanted them
in ascending order? That is, 0 should be first and 63 should be last.
Yes, we could, easily, cut and paste the four lines in the current version and then go through and modify
each line and...we saw how to print out the numbers we want on the screen. Can we reuse that loop?
15 }
16 System.out.println();
17 }
18 }
The body of the loop is a call to print. We would rather call add on our ArrayList. That becomes:
23 System.out.println(”<before> theTable = ” + theTable);
24 System.out.println(”<before> theTable.size() = ” + theTable.size());
25 for (int i = 0; i != 70; i = i + 7) {
26 theTable.add(i);
27 }
28 System.out.println(”<after> theTable = ” + theTable);
29 System.out.println(”<after> theTable.size() = ” + theTable.size());
30 }
224 CHAPTER 8. COLLECTIONS: ARRAYLISTS AND ITERATION
31 }
So far we know:
• Generic means that the type name requires another type name between angle brackets to be complete
and that type must be an object type. For example, ArrayList<Integer>.
• Somewhere above ArrayList toString is overridden to print out the contents of the list.
• The add method adds elements at the end of the list or at a given location (one or two parameter ver-
sions).
The direct printing of the ArrayList has been removed and instead we use the loop in lines 29-31 to call
get 10 times.That is, once for each value 0 through theTable.size() -1. It is important to note that the first
element in the ArrayList has the index of 05 .
The console output of this program is:
~/Chapter08% java -classpath .:/usr/lib/jvm/fang.jar PrintAnArrayList4
0 7 14 21 28 35 42 49 56 63
Notice that the elements are in the same order we inserted them (and that we saw them with toString
was used to print them). If it were our desire we could call any method in the public interface of the element
type.
Updating Elements
Let’s modify IteratedRectangleSprites.java into an animated program. Let’s call it BoxParade.java and
modify it so that there is an ArrayList of RectangleSprites containing all of the sprites constructed and added
to the game. Then we will use a loop to go across all of the squares, moving them in each call to advance.
1 import fang2.core.Game;
2 import fang2.sprites.RectangleSprite;
3
4 import java.util.ArrayList;
5
6 /**
7 * Randomly place some number of rectangle sprites. Then move them
8 * upward at a fixed rate, looping them around off the top of the screen
9 * to the bottom.
10 */
11 public class BoxParade
12 extends Game {
13 /** The collection of RectangleSprites */
14 ArrayList<RectangleSprite> boxes;
15
16 /**
17 * 10 randomly colored and placed rectangles on the screen
18 */
19 @Override
20 public void setup() {
21 // Make sure you initialize the collection!
22 boxes = new ArrayList<RectangleSprite>();
23
5 This is related to programmers wanting to count from zero. It is not the reason but rather a result because ArrayList was written
by computer scientists.
226 CHAPTER 8. COLLECTIONS: ARRAYLISTS AND ITERATION
32
33 /**
34 * Move all the rectangles upward at a fixed speed.
35 */
36 @Override
37 public void advance(double dT) {
38 for (int i = 0; i != boxes.size(); ++i) {
39 boxes.get(i).translateY(-0.5 * dT);// move up
40 if (boxes.get(i).getY() < 0.0) {// loop around at top
41 boxes.get(i).setY(1.0);
42 }
43 }
44 }
45 }
The important things to notice in the listing: Line 24 calls new to create a new collection; this is im-
portant because odd errors will occur when calling .add in line 31 if it is forgotten. Line 31 is not in
IteratedRectangleSprites; it adds a reference to the newly created RectangleSprite to boxes. Lines 41-
44 move one box up at a rate of 1 screen every 2 seconds (the translateY call is similar to that seen in
NewtonsApple (see Listing 4.8); the if statement is similar to checking if the apple hit the ground but in this
case it is if the square hits the top edge of the screen and the reaction is not to randomly place it but rather
to “connect” the top and bottom edges of the screen so it looks like the squares wrap-around to the bottom
(imagine a cylinder unrolled onto the screen).
An important point here (and a lot of other places in the remainder of the book): whenever you find
yourself thinking, “I want to do blah to every element in a collection” you should think of a loop. Here, blah is
“move upward”. In the previous example, PrintAnArrayList4, blah was “print a space then the element”.
Finding an Element
What else could we do with an ArrayList? We could search for a specific element. It might be that we want
to know whether a certain value is in the list or we might want to find the minimum or maximum value.
Fundamentally this means checking each element in the ArrayList against the value being searched for (or
the best result found so far). Thus blah is “compare to the searched for value” or “compare to the best result
so far”.
To put this in action we will modify BoxParade so that the element that is closest to the top of the screen
will be colored red. When the element closest to the top of the screen changes, we will restore the color of the
old highest box and change the appearance of the newly-found highest box.
Assuming we had a way of determining the index of the highest box, we could check if that value has
changed inside of advance:
70 if (currHighest != highest) {
71 boxes.get(highest).setColor(highestColor);
72 highest = currHighest;
73 highestColor = boxes.get(highest).getColor();
74 boxes.get(highest).setColor(getColor(”red”));
75 }
76
The code calls (the as yet unwritten) indexOfHighestBox which returns the index (integer used with get)
of the box with the lowest y-coordinate. If that index is different than it was before (as stored in the field
highest), then we need to replace the color of the old highest box, change the highest field, save the current
color of the highest box (so we can restore it later) and change the color of the highest box to red. Those four
steps, in order, are what lines 73-76 accomplish. The last three steps must also be done in setup so that the
initially highest box is properly colored and has its color saved (this is not listed in the book).
How does indexOfHighestBox work? It returns an index or an int. This value is initialized to 0 because 0 is
a candidate for the highest box on the screen. It is necessary to check every box against all of the boxes. Thus
we use a for loop to go across the ArrayList, comparing the location of each box with the best one we have
seen so far.
51
60 return indexOfHighestSoFar;
61 }
62
63 /**
Notice the name of the variables. The value which is returned is the index of the best seen so far. The loop
variable is the index of the next one to check (against the best so far). This code returns the highest box seen
so far after it checks every location in the ArrayList. Thus the value returned is the index of the highest box
on the screen at the moment.
Constructs an empty list of element type E with an initial capacity of ten. The initial capacity is the num-
ber of times you can call one of the add methods before the class has to move things around in memory to
accommodate more elements. An ArrayList can contain an arbitrary number of elements.
Method Summary
boolean add(E e)
void add(int index, E element)
228 CHAPTER 8. COLLECTIONS: ARRAYLISTS AND ITERATION
The first version appends the specified element to the end of this list. The second inserts the specified
element at the specified position in this list. Either one results in an ArrayList with one more element in the
list.
void clear()
Removes all of the elements from this list. No matter how many elements were in the list before the call, there
will be 0 elements after the call (size() returns 0 or isEmpty() returns true).
boolean contains(Object o)
int indexOf(Object o)
E get(int index)
Replaces the element at the specified position in this list with the specified element. This method returns the
value previously at this location.
boolean isEmpty()
int size()
isEmpty returns true if this list contains no elements and false otherwise. size returns the number of ele-
ments in this list.
E remove(int index)
boolean remove(Object o)
The first removes the element at the specified position in this list, returning the value that used to be there
(and moving all of the elements with higher indices down one location). The second version removes the first
occurrence of the specified element from this list, if it is present. It returns true if a matching element was
found and removed; false if no match could be found. It uses equals as defined in the element type to find a
match.
These are the basic methods for use with all ArrayLists. Summarizing what we know one more time:
• An ArrayList is a generic object; the type is named with as ArrayList<ElementType> where the
ElementType is an object type.
• The constructor for an ArrayList requires the same generic type specification as a variable declaration.
• An ArrayList is an object. This means interaction uses the dot notation we use with other objects.
• add adds elements to the list, get gets individual elements in the list by index, and size returns the
current number of elements in the ArrayList.
• The indexes of an ArrayList start at 0. Thus valid indexes of ArrayList A (with any element type) are
on the range [0-A.size()). As noted previously, 0 is inclusive and the size is not.
• Whenever you think, “I need to blah every element in the list,” you should immediately think, “I need
to use a loop.”
8.6. FINISHING THE FLU SIMULATION 229
The middle 10 methods are related to the five different states that the person can be in. The predicate
returns true when the person is in the named state and false otherwise. This means we need to come up with
some way to encode the state of each person (that is an implementation detail; so long as Person is treated as
an abstract data type we don’t have to know how that is encoded to use the public interface).
The setColor method overrides the method for CompositeSprite and colors all the parts of the person
with the same color. The state of the person is expressed in the color they display in the village (we can watch
who is sick or contagious, etc.).
The three methods above setColor are important for the simulation. The first, finishRecovery is called to
determine whether or not the person got well after their recovery period. That is, in the state diagram, when
the person might die or might become healthy, this method makes the appropriate transition. The expose
method exposes this person to the disease. There is a fixed chance of catching the disease when exposed so a
healthy person has a chance of becoming sick when they are exposed. Finally, nextDay advances the person
230 CHAPTER 8. COLLECTIONS: ARRAYLISTS AND ITERATION
one day into the future. If they are sick, the illness runs one more day into its course; if they are dead or
healthy, nothing happens. nextDay operates on this person; an individual Person has no idea about finding its
neighbors; that is the simulation’s job.
Implementation
The implementation of Person begins with the definition of a large number of named constants. Recall that
named constants are static final fields which are assigned once. They can then be used in the code to make
the meaning clearer. They also serve as a single place to make changes.
The constants can be divided into three groups. The first group are the states of the Person’s health.
19 private static final int HEALTHY = 0;
20 private static final int SICK = 1;
21 private static final int CONTAGIOUS = 2;
22 private static final int RECOVERING = 3;
23
The health will be kept as an int field and each of these values will be used to indicate that the person is
in a given state. Thus the method isSick can just check if health (a field of Person) is equal to SICK.
The second set of constants are the chances of infection and death with the disease along with the number
of days the person spends in each stage of the illness. The infection and mortality rates are very high but they
match the high end estimates of the flu pandemic of 1918; one reason for clearly marking these values is that
the simulation outcome changes a great deal when they are changed.
26 private static final double MORTALITY_RATE = 0.20;
27
Finally, the last group of constants are colors. These colors are the colors the person is displayed with
when they are in each of the different states of health. The particular choices here were for shades of green
except when the person is dead (grey) or contagious (golden). That makes it easy to follow the spread of the
disease.
34 private static final Color COLOR_DEAD = Palette.getColor(”dark gray”);
35 private static final Color COLOR_HEALTHY = Palette.getColor(”green”);
36 private static final Color COLOR_SICK = Palette.getColor(
37 ”green yellow”);
38 private static final Color COLOR_CONTAGIOUS = Palette.getColor(
39 ”goldenrod”);
40 private static final Color COLOR_RECOVERING = Palette.getColor(
41 ”yellow green”);
42
Fields
The fields of a Person are the health, the count of days they have been at a given level of sickness, and the
body parts displayed for the person.
45
53 /**
Initializing a Person requires initializing the sprites that make up the body and then initializing health
and sickDays. Since a person is assumed to be healthy, we reuse makeHealthy to avoid having to worry about
any extra stuff we need to do to make the new person healthy.
59 body.setLocation(0.0, 0.25);
60 head = new OvalSprite(0.5, 0.5);
61 head.setLocation(0.0, -0.25);
62 addSprite(head);
63 addSprite(body);
64 makeHealthy();
65 }
66
67 /**
State Management
The ten middle methods, the state management methods, are all very similar. isHealthy and makeHealthy will
stand for all ten routines.
124 }
125
126 /**
127 * Set this person’s state to HEALTHY. Update health, sickDay, and
132 health = HEALTHY;
133 setColor(COLOR_HEALTHY);
134 }
135
136 /**
As mentioned above, isHealthy just checks for equality between health and the HEALTHY constant. Both
the field and the constant are plain-old data so == suffices; because == returns a Boolean value, the predicate
can just return the result of the comparison.
232 CHAPTER 8. COLLECTIONS: ARRAYLISTS AND ITERATION
makeHealthy is no more involved: the number of days at a given level of illness is set to 0, health is set to
the right constant, and the color is set to reflect the new health level.
The other eight methods are just the same with the names of the constants suitably changed.
Random Chance
The two methods finishRecovery and expose use random numbers to decide what happens to this person’s
health. When they reach the end of their recovery period, finishRecovery uses the mortality chance to de-
termine if they die; if they do not die, they recover and are healthy again.
178 makeDead();
179 } else {
180 makeHealthy();
181 }
182 }
183
184 /**
185 * This person has been exposed to the disease. Either they get it or
190 (Game.getCurrentGame().randomDouble() < INFECTION_CHANCE)) {
191 makeSick();
192 }
193 }
194
195 /**
Similarly, expose checks if this person is healthy and if they are it uses the chance of infection to determine
if they become sick. Notice that the chance of mortality and infection are numbers between 0 and 1; the lower
the number, the less chance that the event occurs. Here a random number between [0.0-1.0) is generated with
randomDouble() and that value is tested against the chance. If the random number is less than the chance, the
chance event happens; otherwise the chance event does not happen.
Day by Day
Finally, the nextDay method moves the villager’s illness forward one day. That means if they are healthy or
they are dead there is nothing to do. If they are sick, sickDay is incremented and the number of days they have
been at this sickness level is checked against the appropriate DAYS constant. If the time has elapsed, then the
method to move to the next state is called. Notices that finishRecovery is called at the end of the RECOVERING
state because we don’t know whether to make the person healthy or dead.
201 ++sickDay;
202
212 }
57 dayCount = 1;
58 dayCountDisplay = new StringSprite();
59 dayCountDisplay.setScale(0.1);
60 dayCountDisplay.leftJustify();
61 dayCountDisplay.setLocation(0.2, 0.1);
62 dayCountDisplay.setText(”Day #” + Integer.toString(dayCount));
63 addSprite(dayCountDisplay);
234 CHAPTER 8. COLLECTIONS: ARRAYLISTS AND ITERATION
64
65 infectPatientsZero();
66
67 scheduleRelative(this, SECONDS_PER_DAY);
68 }
The number of villagers is also used to determine the scale of each villager’s sprite so that they all fit
in a single line across the screen. Why is the expression for scale 1.0 / NUMBER_OF_VILLAGERS? Remem-
ber that the division operator, /, when applied between int values, returns the integer quotient. That is,
1 / NUMBER_OF_VILLAGERS where the number of villagers is greater than 1 will always return the int value
of 0. By making the first literal in the expression a double (by including the decimal point in the literal), the
whole expression is treated as a double and the number of villagers is changed into a double with the same
value (so 20 is coerced into the double 20.0) and the right value is calculated.
Line 50 uses the loop control variable to calculate the x-coordinate of the location of each villager. The
first villager is centered so its left edge touches the left edge of the screen; that mean it’s x-coordinate should
be half of its width or scale~/~2. Each following villager is one “villager width” to the right of the previous
one; since i starts at 0 and goes up by 1 each time through the loop, i~*~scale is the total width of the villagers
to the left of villager i.
With 20 villagers (number determined by the constant NUMBER_OF_VILLAGERS), the game looks like this:
Making an Action
The last line of setup is something new. It is a call to a method, scheduleRelative. The scheduleRelative
is a way of scheduling an event to take place some time in the future. FANG schedules the event either at
a fixed time after the beginning of the game (using scheduleAbsolute) or at some amount of time after the
moment the action is scheduled (using scheduleRelative). The two parameters to either schedule method
are an Action and a number of seconds. Neither timer runs before the game is started or when the game is
paused.
What is an Action? An Action is an interface defined in FANG. An interface is like a class in that it is
defined inside of a file with the same name as the interface and all the methods are declared inside of the
interface body block. An interface is different from a class in that any class can implement any number
of interfaces and an interface provides no “already working” methods or fields. The interface just defines a
public interface which a class can choose to implement by having methods with the right names and parameter
lists. Then it is possible to use a reference to the interface to call methods declared in the interface and
implemented by some class. Interfaces will be covered in more detail in the last three chapters of the book.
For the moment all we need to do is note that the Action interface declares a single method,
public void performAction(). When the scheduled time comes, FANG will call the performAction method
automatically.
We could, of course, keep track of elapsed time on our own, counting down some period of time using
the dT parameter passed to advance. In fact, that is what we did in the CountdownTimer class in Section 6.2.
This time we will let FANG do the timing for us and call our performAction method when the timer expires.
performAction will then advance the simulation one day. If the simulation is still running, it will schedule a
new event in the future; if the simulation is done running, there is no need to schedule such an event.
226 public void alarm() {
227 spreadInfection();
228 advanceSimulatedTime();
229 // schedule the next update unless the simulation is quiescent
230 if (simulationContinues()) {// schedule a new call in the future
8.6. FINISHING THE FLU SIMULATION 235
performAction is a good example of the “pretend it works” approach to top-down design. The problem
that it solves is: what to do to advance the simulation’s time clock one day into the future. There are two
things to do each day: spread the infection and advance time for the villagers. Then, having advanced time,
we can check if the simulation is quiescent; if no health values will change just through the passage of time
the simulation is over (all villagers are healthy or dead so no one can become sick, contagious, or recovering
ever again).
Leaving spreading the infection for a couple of paragraphs, what do we do to advance time one day for
every villager. We want to “advance time one day for every villager in the ArrayList village”. What should
you be thinking?
186 private void advanceSimulatedTime() {
236 CHAPTER 8. COLLECTIONS: ARRAYLISTS AND ITERATION
191 villager.nextDay();
192 }
193 // increment the day count and display it
194 ++dayCount;
195 dayCountDisplay.setText(”Day #” + Integer.toString(dayCount));
196 }
The for loop in advanceSimulatedTime is formatted across multiple lines. It is customary to break the
contents of the parentheses of a for loop at the semicolons so that the <init>, <continuation>, and <nextStep>
are not broken across lines. The body of the loop here just copies a reference to the next villager into the
villager variable. This works because the entries in village are references and the assignment just makes
two variables, one inside the ArrayList and one declared on line 189 both refer to the same Person. Then,
using the villager reference, nextDay is called for the Person. As we saw in the last section, nextDay advances
any existing illness by one day, changing the person’s state (and color) if necessary.
The last two lines in advanceSimulatedTime update the time display at the top of the screen (see Figure 8.5
to see the day number message). Advancing simulated time means traversing the entire ArrayList, calling a
method on every element. Then updating the message on the screen.
We must check each and every villager. If they are contagious, we handle a contagious villager. Handling
a contagious villager means picking some number of their neighbors and exposing them:
157 private void handleContagiousVillager(int contagiousVillagerNdx) {
158 for (int numberOfExposed = 0; numberOfExposed != EXPOSED_PER_DAY;
159 ++numberOfExposed) {
160 int exposedVillagerNdx = selectNeighbor(contagiousVillagerNdx);
161
162 if (isValidNdx(exposedVillagerNdx)) {
163 village.get(exposedVillagerNdx).expose();
164 }
8.6. FINISHING THE FLU SIMULATION 237
165 }
166 }
The loop here, rather than going over all the villagers, runs just EXPOSED_PER_DAY times (convince yourself
that this is true by looking at the code). The constant EXPOSED_PER_DAY is set near the top of the simulation to
3; this loop picks three neighbors of each contagious villager and, if the neighbor is actually a valid index into
the village, it exposes the neighbor, giving them a chance of becoming sick.
Remember, the model of the illness (the chance of getting sick, what happens when an already sick person
is exposed, etc.) is all in the Person and the model of how villagers interact (who is exposed to whom, how to
find neighbors, number of villagers, etc.) is in the FluSimulator. This is an example of using different levels
of abstraction to overcome complexity. It is also a good example of some bottom-up design in that the Person
was written before the game and it has all the right methods already defined.
Selecting a neighbor is done by taking the index of a villager and adding a random number to it. The
random number is on the range [-3,3] (where 3 is the value of the NEIGHBOR_DISTANCE constant defined at the
top of the game).
82 /**
83 * Select a neighbor of the villager at currNdx. Returned value will
84 * be within NEIGHBOR_DISTANCE of currNdx.
85 *
86 * @param currentVillagerNdx the index of the villager for whom we
87 * must select a neighbor
88 *
89 * @return an ”index” of a neighbor; index in quotes as it might be
90 * outside the legal range for village
91 */
92 private int selectNeighbor(int currentVillagerNdx) {
93 int neighborNdx;
94 neighborNdx = currentVillagerNdx +
95 randomInt(-NEIGHBOR_DISTANCE, NEIGHBOR_DISTANCE);
96
97 return neighborNdx;
98 }
This listing includes the method header comment to show you that it mentions that this method can re-
turn “index” values that are out of range. It is important that you document your intent and any limita-
tions of your methods. By including this in the header comment, users of this method know that they are
responsible for range checking. You can easily picture what the isValidNdx does (called from line 162 inside
handleContagiousVillager). If a non-valid villager is picked, it is assumed no villager was exposed that time
around.
simulationContinues is just a new name for the anySick method. Why? Because the name
simulationContinues explains what the method is being asked about, the Boolean value it returns. anySick,
in turn, explains what it does, returning true if there are any sick villagers and false if there are not. There
might well be other uses for the anySick method, other than checking if the simulationContinues. The sepa-
ration of the two methods makes reuse of anySick simpler.
How can we determine if any villager is sick? We need to check each villager and if they are sick or con-
tagious or recovering, set a flag to true. If we start with the flag set to false and test every villager, the final
value of the flag will be true if and only if there is at least one non-dead, non-healthy villager.
This could be done with an if statement inside a loop (we have to check every villager). Instead, here,
lines 111-112 use a Boolean expression to reset the value of the flag, sawSick, each time through the loop. The
new value of sawSick is true if any one of the four Boolean subexpressions is true. Once sawSick is true, it will
remain true until the end of the method. sawSick will become true on the first villager who is neither dead
nor healthy. This is a loop which checks whether any element in the ArrayList meets some criteria.
When the simulation ends, there is a report on how many villagers died. A new StringSprite is created
with the information in it and it is added to the game. The interesting part is how we figure out how many
villagers are dead. Given anySick above, consider how you would approach writing countOfTheDead.
124 private int countOfTheDead() {
125 int numberOfDead = 0;
126
131 if (villager.isDead()) {
132 ++numberOfDead;
133 }
134 }
135
We need to go through every villager and if they are dead, add one to a counter. Initialize the counter to
0 before we start and after we have checked every villager we will have the total number of the dead. Lines
131-133 check if the current villager is dead and increment the counter. The loop control and local variable
villager are done just as they are in the other loops in this section.
The only method we have not mentioned yet is infectPatientsZero. This is called from setup and provides
the initial infection. The code is almost identical to handleContagiousVillager except that the villagers are
picked at random across the whole village and rather than being exposed, each is directly made sick. That
way there will always be at least one infected person at the beginning of the simulation. The number of initial
patients is determined by the value of the NUMBER_OF_PATIENTS_ZERO constant at the top of the simulation.
8.7 Summary
Java Templates
Review Exercise 8.3 Count examples: by 1’s not at 0 start, count by other number
Programming Problems
Programming Problem 8.1 Reimplement TicTacToe with an array; row(n), column(n), index(r,c).
In the last chapter we looked at the use of the ArrayList type as a collection of objects. The ArrayList is
limited in that it must hold object types; this is not a big problem because Java provides object “versions” of
the built-in plain-old data types: Integer, Double, and Boolean to match the built-ins we have worked with. It
also supports automatically using the value of the object versions as the plain-old data types when necessary.
Thus if you put a Boolean in an if statement, Java will automatically convert the Boolean into it boolean value.
It makes the object versions behave “just like” the pod versions.
The big win with ArrayList over built-in arrays is that ArrayLists are dynamically sized; they can grow as
necessary to hold whatever number of elements are needed.
This chapter will continue working with ArrayLists: this time we will see ArrayList with ArrayList ele-
ments. That is, two-dimensional data structures where a content element is found by getting the row in which
it resides and then getting the entry at a given column.
241
242 CHAPTER 9. MULTIDIMENSIONAL DATA STRUCTURES
Rescue Mission re
Sco 6
r
S w imme
F a st
S w im in g
mers
gl
S t rug
Rin g
a rd
Lifeg u
This game is interesting for several reasons: given 4 rows of 6 swimmers each, there are almost thirty
sprites on the screen at the same time, many more than we have previously handled; all of the sprites (except
for the score) move; the swimmers (and the fast swimmer at the top of the screen) are animated in that they
change appearance while moving across the screen.
Having so many different sprites moving according to so many different rules (swimmers move left-to-
right until someone hits the wall; the rescue ring moves up from where it was launched; the rescuer moves
left-to-right, controlled by the user’s keyboard; the fast swimmer moves from side to side occasionally; the
rope is a decoration connecting the rescuer to the rescue ring), it is essential that we divide to conquer. Each
sprite class has its own advance method, encoding its own movement rules.
Consider how to call advance for each of thirty sprites. If you look back at Chapter 7 and TicTacToe, you
will see that there was a lot of repeating of code to handle just nine sprites on the screen. One argument in
favor of using ArrayList collections is that they are composed of multiple elements and we can process each
element in them within a loop. Using iteration keeps us from having to repeat ourselves.
We will use iteration but rather than having to somehow translate from a one-dimensional collection (an
ArrayList) to a two-dimensional layout (the Swimmer sprites on the screen), we will use a two-dimensional
data structure, an ArrayList of ArrayLists of sprites.
Before we get there, though, we will look at the public interfaces of the various sprites in the game and
see what we can learn about having lots and lots of moving sprites, each based on the CompositeSprite.
9.2 Inheritance
Looking at the design in Figure 9.1, there appear to be four different classes of sprite: Rescuer, RescueRing,
Swimmer, and FastSwimmer. Game implementation begins by examining the public interface of each of these
sprites.
9.2. INHERITANCE 243
Rescuer
Let’s start simple: what does the Rescuer need in its interface? Note that we want to be able to have the rescuer
move at some speed (a speed we can easily change so that we can tweak our game) according to keyboard
input. Does that sound familiar? It should, since Chapter 6’s PongPaddle class did almost exactly that. The
movement velocity was held in the paddle class which included getter and setter methods for the velocity as
well as a routine to bounce the paddle off the edges of the screen. Using PongPaddle as a pattern, Rescuer’s
public interface looks like this:
class Rescuer
extends CompositeSprite {
public Rescuer(double deltaX, double deltaY)...
public void setVelocity(double deltaX, double deltaY)...
public void setDeltaX(double deltaX)...
public void setDeltaY(double deltaY)...
public void advance(double dT)...
public void bounceOffEdges()...
public double getDeltaX()...
public double getDeltaY()...
}
Note that there are two ways to set the velocity, either in one dimension at a time or both at the same time.
In this program the y-coordinate of the velocity will always be zero but as discussed with PongPaddle, the more
general problem is sometimes easier to solve (and before this section is over, this should be abundantly clear).
The rescuer “bounces” off the edge of the screen in the same way that the PongPaddle bounced off the
edge: the user cannot move the sprite past the edge of the screen.
Swimmer
Each individual Swimmer moves at a given speed first one way and then another way across the screen. Again,
reaching back to SoloPong, this is a lot like the ball, moving in a straight line until it bounces off of the edge
of the screen. The public interface of Swimmer looks like this:
class Swimmer
extends CompositeSprite {
public static void bumpTheWall()...
public static void clearBumpTheWall()...
public static void hasBumpedTheWall()...
The individual swimmer interface is similar to that found in Rescuer; by individual we mean non-static.
The static methods are there so that all of the Swimmer objects reverse direction when any swimmer touches
the edge of the screen.
The score value of a Swimmer is how many points the swimmer is worth when rescued. It is variable so that
each row of swimmers can have a higher value than the one below it. The exact score will be determined in
the game when the swimmers are constructed.
RescueRing
The RescueRing is either waiting to be thrown or it has been thrown. If it is waiting to be thrown, it should
simply move along with the Rescuer; if it has been thrown, then it moves according to its current velocity
until it rescues a given Swimmer or it hits the edge of the screen. Thus the public interface is:
class RescueRing
extends CompositeSprite {
public RescueRing(double deltaX, double deltaY)...
public void setVelocity(double deltaX, double deltaY)...
public void setDeltaX(double deltaX)...
public void setDeltaY(double deltaY)...
public void advance(double dT)...
public void bounceOffEdges()...
public double getDeltaX()...
public double getDeltaY()...
}
FastSwimmer
The FastSwimmer is a Swimmer turned on its side. The big difference is that the FastSwimmer has two states:
swimming and waiting. When it is waiting, each frame it determines whether or not it is time to launch and
when it is swimming it just moves according to its current velocity. That means that the public interface for
FastSwimmer is the same as that of Swimmer. In fact, a FastSwimmer is a Swimmer so the public interface is:
class FastSwimmer
extends Swimmer {
}
FastSwimmer will override all of the non-static member functions of Swimmer because they perform
slightly differently (in particular, FastSwimmer has no impact on bouncing the rest of the swimmers off walls
and fast swimmers don’t bounce off the edge, really; fast swimmers go to the waiting state when they hit an
edge).
The following screenshot shows the grid of swimmers as well as the fast swimmer zipping across the top
of the screen.
Is-a
The emphasis in the sentence above, that FastSwimmer is a Swimmer, indicates a special relationship between
the two classes: every FastSwimmer is first a Swimmer and then something more. This relationship is so common
in object oriented programming that computer scientists have turned it into a single word: is-a. When one
class extends another, any child class object is-a parent class object.
What this means is that anywhere where a parent class object is expected, you can replace it with a child
class object. Consider a landscaping plan. If there is to be a tree at the north-east corner of the yard, it is
9.2. INHERITANCE 245
possible to plant an apple tree there. This is because an apple tree is-a tree. There are things you can do with
an apple tree which you cannot do with any arbitrary tree: sit under it and discover gravity, pick an apple;
there is nothing you can do with an arbitrary tree that you cannot do with an apple tree: plant it, prune it,
water it, etc.
all three classes must extend a class with those methods in the public interface. If we call the missing link in
this hierarchy SpriteWithVelocity, we split the three classes into four with the following pubic interfaces:
class SpriteWithVelocity
extends CompositeSprite {
public SpriteWithVelocity(double deltaX, double deltaY)...
public void setVelocity(double deltaX, double deltaY)...
public void setDeltaX(double deltaX)...
public void setDeltaY(double deltaY)...
public void advance(double dT)...
public void bounceOffEdges()...
public double getDeltaX()...
public double getDeltaY()...
}
class Rescuer
extends SpriteWithVelocity {
public Rescuer(double deltaX, double deltaY)...
}
class RescueRing
extends SpriteWithVelocity {
public RescueRing(double deltaX, double deltaY)...
}
class Swimmer
extends SpriteWithVelocity {
public static void bumpTheWall()...
public static void clearBumpTheWall()...
public static void hasBumpedTheWall()...
The number of classes is one higher than it was before but each of the subclasses of SpriteWithVelocity
is simpler. All the classes will override advance and bounceOffEdges but each class is simpler because of the
abstraction of the velocity out of several classes. With comments, SpriteWithVelocity.java is just under 200
lines of code; this code would have to appear in each of the three classes which now inherit it.
This is primarily an example of sharing implementation and features through inheritance. While each
Swimmer is-a SpriteWithVelocity (and the same for Rescuer and RescueRing), we are not using that fact. FANG
does use an is-a relationship in the heart of the video game loop.
Displaying Sprites
As a reminder, the main video game loop in FANG is
setup
while (not game over)
displayGameState
9.3. MULTIDIMENSIONAL COLLECTIONS 247
getUserInput
advance
The part we are interested is displayGameState. How does FANG display all the different sprites on
the screen? Inside of FANG is a class called AnimationCanvas which has a field called sprites of type
ArrayList<Sprite>1 . Each time through the video game loop, AnimationCanvas’s paintSprites method is
called. It is called with an object of type Graphics2D; a Graphics2D represents the current window on the
video screen where the program is running. The paintSprites method looks something like this:
class AnimationCanvas ... {
...
ArrayList<Sprite> sprites;
...
private void paintSprites(Graphics2D brush) {
for (int i = 0; i != sprites.size(); ++i) {
sprites.get(i).paintInternal(brush);
}
}
...
}
What is special about this? It is an example of code using the is-a relationship. The elements of sprites are
of type Sprite as far as this code knows. You know that what you called addSprite with was a RectangleSprite
or a Swimmer or a StringSprite. The Sprite class provides a method called paintInternal; each specialized
type of Sprite provides a version of paintInternal which knows how to put a rectangle or an oval or an image
or even an entire composite collection of other sprites onto the screen. That way the specialized actions of
each of the special types of Sprite takes place even while all AnimationCanvas needs to know is that all Sprite
objects know how to paintInternal.
In the landscaping example, imagine each tree knows how to makeBlooms. To have the whole garden bloom,
it is enough to cycle through all of the trees and call makeBlooms for each tree. The apple trees will make apple
blossoms, cherry trees will make cherry blossoms, and pine trees might not actually have flowers in the more
traditional sense so they might do nothing.
The is-a relationship is the power we leverage in using abstraction to tame complexity. By separating the
details of how each paintInternal method works from the details of figuring out when paintInternal is called
for each Sprite, we limit the details we must remember at any given moment.
The next section looks at how we can have collections which hold collections and the section after that
discusses animated sprites. After that we will come back to the RescueMission game and look at how to use
SpriteWithVelocity to share several useful methods among multiple classes.
1 The actual type of sprites is more complicated than ArrayList; it is much more similar to an ArrayList<ArrayList<Sprite>> where
each ArrayList<Sprite> is the collection of sprites entered with the same z-ordering or layering number. Thus what we will present as
a single loop is actually a nested pair of loops, one selecting the layer and the inner one selecting each Sprite on the layer (and even this
is a simplification).
248 CHAPTER 9. MULTIDIMENSIONAL DATA STRUCTURES
What is the result of following the above instructions? The cards are placed on the table in ascending order.
That is, the deck of numbered cards is sorted when this algorithm finishes. We will come back to this sorting
idea later. For the moment just note that the idea of iterating iteration is not as foreign to you as it might
seem.
Nested Loops
Remember that the body of a for loop can be any Java code that is valid in a method body2 . This means that it
is legal to have a loop inside of another loop.
Consider the following code. How many asterisks are printed?
for (int i = 0; i != 5; ++i) {
for (int j = 0; j != 3; ++j) {
System.out.println(”*”);
}
}
Looking at the second for line and the <init>, <continuation>, and <nextStep> parts, we can see that j
takes on all the values from [0-3) or 0, 1, and 2. When the loop control variable begins at 0, the next step
increments the loop control variable and the comparison uses != (or <) some number, n, the body of the loop
is run n times. This is the idiomatic way to write a count-controlled loop, a loop which runs a certain number
of times. This is the way a Java programmer writes such a loop; it makes it easy to read and to reason about
the loop.
How many times is the body of the outer loop executed? Reading the for line, it, too, is an idiomatic
count-controlled loop. That means it runs 5 times.
The inner loop body draws a asterisk each time it is executed. Thus each time the inner loop is executed
(in total) it draws 3 asterisks. Since the inner loop is the body of the outer loop, it is executed 5 times. Thus 5
times (once for each loop iteration) there are 3 asterisks drawn; this means there are 15 asterisks drawn, one
per line (each is drawn using println which starts a new line).
2-Loops, 2-Dimensions
The above code prints out a single, vertical line of asterisks; how would you change it to print out all of its
asterisks on a single line and only after printing all of them start a new line? That would mean changing the
println to print (so it does not start a new line) and then adding System.out.println() after the end of the
loops. All 15 asterisks in a single line.
for (int i = 0; i != 5; ++i) {
for (int j = 0; j != 3; ++j) {
System.out.print(”*”);
}
}
System.out.println();
2 Java does not support nested method or type declarations. You cannot define a method inside the body of another method nor can
you define a class inside the body of a method. The same is true of code inside a for loop; the for loop must, itself, be inside of some
method.
9.3. MULTIDIMENSIONAL COLLECTIONS 249
What code would print out 5 lines of 3 asterisks each? In this case we want to use print in the inner loop
(to keep the asterisks on a single line) and then, whenever the outer loop is about to end, start a new line with
the System.out.println().
for (int i = 0; i != 5; ++i) {
for (int j = 0; j != 3; ++j) {
System.out.print(”*”);
}
System.out.println();
}
If you use the fact that we know, because the loop is written idiomatically, the number of times the outer
loop executes is 5. That means, because println is inside the loop (and not inside of any other loop), this code
prints 5 lines. Since each line also has whatever is printed by the inner loop, each line has 3 asterisks.
Multiplication Table
How would you write a game that displayed a multiplication table on the screen? This is different than printing
asterisks because we want to position things on the screen. It is similar, though, because we need to process
something with two dimensions. We will start displaying a simple multiplication table. Then we will look at
how to put references to the displayed elements into a 2-dimensional data structure and then we will see how
to go through all the elements in the structure so we can highlight some of the elements by changing their
colors.
8 /** The number of columns of numbers in the table */
9 public static final int COLUMNS = 10;
10
MultiplicationTable begins with a collection of constant values. The number of rows and columns, the
size of each StringSprite and the size in which each sprite is positioned (the scale of the sprite is smaller so
there is space between entries), the offset of the first entries in the tables and the first entries of the label rows
and columns are calculated based on the spacing of the entries. Finally, the colors of the table entries and the
labels are specified. Notice that all of the constants are static meaning there is only one copy, no matter
how many MultiplicationTable objects are instantiated, and final meaning that they can only be assigned
to once. By assigning to the static final values at the same time they are declared makes it easier for others
to come along later and change the constant values; if we make consistent use of ROWS, for instance, changing
it from 6 to 7 should change everything so that 7 rows are drawn in the right scale.
37 public void setup() {
38 labelRows();
39 labelColumns();
40 fillProductTable();
41 }
The listing above is another example of the power of top-down design. setup just calls three routines,
labelRows and labelColumns to place the row and column labels on the screen and fillProductTable which
places all of the products.
74 private void labelRows() {
75 double yOffset = TABLE_OFFSET;
76 double xOffset = LABEL_OFFSET;
77 for (int row = 0; row != ROWS; ++row) {
78 makeOneEntry(xOffset, yOffset, row, LABEL_COLOR);
79 yOffset += SPACING;
80 }
81 }
labelRows serves to explain both label methods. There is a count-controlled for loop which runs ROWS
times for numbers in the range [0-ROWS). It creates a StringSprite each time through the loop and it changes
the yOffset by the spacing value each time through the loop. Thus each StringSprite is placed directly below
the one before it on the screen (the xOffset does not change). The text inside of each StringSprite is the
value of the loop control variable.
How do we know the value of the text? Look at the body of makeOneEntry:
96 private StringSprite makeOneEntry(double x, double y, int value,
97 Color color) {
98 StringSprite tableEntry = new StringSprite();
99 tableEntry.setScale(ACTUAL_SCALE);
100 tableEntry.setLocation(x, y);
101 tableEntry.setColor(color);
102 tableEntry.rightJustify();
103 tableEntry.setText(Integer.toString(value));
104 addSprite(tableEntry);
105 return tableEntry;
106 }
Setting the color, scale, and location are typical for creating any sprite. The rightJustify method sets the
position of a StringSprite to the right edge of the string. This means the unit digits will align vertically. What
is the text set to in line 103? The value parameter is of type int; this is not an object type. Thus there is no
way to call value.toString (you cannot apply the dot operator to non-object types). The object type, Integer,
provides a static method, toString which takes a single int as its parameter and it converts the value to a
string.
TK
Listing 9.5 shows the declaration of swimmers inside the RescueMission class (RescueMission extends Game).
The two static final ints are the number of rows and columns of sprites we will be declaring. Having named
constants and using them consistently means changing these numbers in lines 21 or 22 changes the number
of swimmers throughout the program.
Just as before, the declaration of an ArrayList type consists of the word ArrayList followed by the element
type in angle brackets. Unlike in the last chapter, here the elements of swimmers are not sprites but rather are
lists of sprites. That is, after it is properly initialized, swimmers.get(0) will return the first ArrayList<Swimmer>
in the field.
An ArrayList can have any object type as its element type and ArrayList<...> is an object type, so it
is legal to have elements which are, themselves, ArrayLists. Inside the angle brackets must be a complete
object type name; ArrayList is a generic type so to be a complete type we must provide the element type of the
collection. We want the inner collections to contain Swimmer objects. This is just what the declaration on line
65 does.3
Having declared a field which holds lists of lists, how do we initialize it? Obviously at some point we need
to call new and set the value of the field to the result. What constructor will we call? Then the question is what
do we pass to add to add new elements to the list of lists.
347 final double lowestRowY = swimmerOffset + (ROWS * objectScale);
348 swimmers = new ArrayList<ArrayList<Swimmer>>();
349 for (int row = 0; row != ROWS; ++row) {
350 ArrayList<Swimmer> currentRow = new ArrayList<Swimmer>();
351 double rowY = lowestRowY - (row * objectScale);
352 int rowScore = row + 1;// higher row = higher score
353 for (int column = 0; column != COLUMNS; ++column) {
354 Swimmer current = new Swimmer(rowScore, swimmerDX, swimmerDY);
3 In this book we will not include spaces within the angle brackets. Thus the type of swimmers is ArrayList<ArrayList<Swimmer>>.
It should be noted that spaces are permitted within and around the angle brackets and some sources recommend using
ArrayList< ArrayList< Swimmer > > as the spacing for the type name. This is mentioned here so readers are aware of it when they
see other code samples.
252 CHAPTER 9. MULTIDIMENSIONAL DATA STRUCTURES
355 current.setScale(objectScale);
356 current.setLocation(swimmerOffset + (column * objectScale),
357 rowY);
358 addSprite(current);
359 currentRow.add(current);
360 }
361 swimmers.add(currentRow);
362 }
363 }
364
365 /**
The setupSwimmers method from RescueMission sets up the list of lists and the elements in it. Line 350 calls
new to construct swimmers; the constructor is of type ArrayList<ArrayList<Swimmer>>. Ignoring lines 353-362
for a minute, we can see that line 352 constructs a new ArrayList<Swimmer> and line 363 adds that new list to
swimmers.
How many times are lines 352 and 363 executed? The loop control variable, row is initialized to 0, is incre-
mented each time through the loop, and the loop is exited when row is ROWS. That means the loop runs across
the integer range [0-ROWS). Thus the number of rows added is ROWS (set to 4 as we saw above in line 21).
What happens when we stop ignoring lines 353-362? Line 353 calculates the y-component of all sprites on
the current row. The first row is the lowest row on the screen, the second the next one up, and so on. Thus line
349 calculated the y-component of the lowest row (the highest y value because of the inverted y-axis). Each row
we subtract row number times height of a row from that value. Similarly, the score for rescuing swimmers in
the lowest row is 1, the second row up is 2, and so on for each row. Thus the score for all swimmers in a row
is calculated in line 354.
The loop in lines 355-362 is executed with column across the integer range [0, COLUMNS), or COLUMN times
(set to 6 in line 22 above). Inside the loop we create one Swimmer sprite. That sprite is scaled, located, added
to the game (so it is in the list used in AnimationCanvas) and added to the current row’s ArrayList. Thus 6
Swimmers are added to each row and 4 rows are added to swimmers: there are 24 Swimmer sprites added to the
game.
We could have added any number of sprites to the game with addSprite. This code is powerful because
we retain the ability to access each individual Swimmer sprite because we have the collection of collections of
Swimmers.
This is exactly what is done in the moveEverything method of RescueMission except that instead of having a
variable holding a reference to the current row we just use swimmers.get(row) whenever we need the current
row.
227 rescuer.advance(dT);
228 ring.advance(dT);
229 for (int row = 0; row != ROWS; ++row) {
230 for (int column = 0; column != COLUMNS; ++column) {
231 Swimmer curr = swimmers.get(row).get(column);
232 if (curr != null) {
233 curr.advance(dT);
234 }
235 }
236 }
237 }
238
239 /**
In moveEverything, the fast swimmer, rescuer, and rescue ring are moved first (lines 228-230); we will
return to these objects a little later but just consider that advance just moves each one according to its move-
ment rules outlined above. The two nested loops look just like the ones shown in the previous snippet except
for line 219 which uses two chained calls to get. The key thing to note is that get returns an element of the
ArrayList no matter what type the elements have. Thus line 233 is parsed as follows:
Thus the result of swimmers.get(row) is an ArrayList<Swimmer> (just like in the earlier snippet) and ap-
plying get to that array list yields a Swimmer just like the previous snippet.
What about the if statement inside the loop? In the previous section we saw that we added the results of
calling new Swimmer twenty four times; how can any element be null?
At the beginning of the level no entry can be null. The question is, what happens when a swimmer is
rescued? The game is played by launching the rescue ring and if the rescue ring intersects a swimmer, that
swimmer is rescued and the score for that swimmer is added to the player’s score. How is the rescue of the
Swimmer in the 2D ArrayList indicated?
There are two possible answers: We could use the hide method defined in Sprite to turn the swimmer
invisible or we could actually remove the swimmer from the array list by putting a null reference in its place.
The if statement should make it clear which design decision was made in this program. We will briefly exam-
ine the path not taken and then look at the implications of null references in our grid.
Using hide. When a Sprite is hidden, it remains on the screen and it will return true if there is an inter-
section test with any other sprite where the visual representation of the hidden sprite would have intersected
with the other, had it been visible. That is, hidden sprites still intersect with other sprites. They also have
locations and bounds so it is possible for them to intersect with the edges of the screen.
A hidden sprite is still “there” and an additional check is required with each intersection test to have our
game pretend that they are not. The advantage to using null is that the Java Virtual Machine will yell if we
dereference (use the dot operator on) a null reference while it will not yell if we forget to check whether a
given sprite is hidden. While errors, especially run-time errors, are annoying, in this case they make finding
missing reference checks easier to find. In the other case we might just see odd bouncing behavior but be hard
pressed to narrow down where to look for it.
254 CHAPTER 9. MULTIDIMENSIONAL DATA STRUCTURES
There is a set method for ArrayList objects which takes an index (just like get) and a new value to put in
the ArrayList at that location. Thus rescuing the Swimmer at the position (row, column) in the grid is done by
rescueSwimmer:
303 /**
The FANG removeSprite method is analogous to addSprite but it removes the reference sprite from the
list used inside of AnimationCanvas. Then we update the location where the sprite was to contain null. Notice
that we do not remove rows, just individual Swimmers.
Looking at the description of the game, a level ends when the player rescues all of the swimmers in the
grid and the game ends when a swimmer reaches the bottom of the water. Detecting each of these condi-
tions requires traversing4 across the swimmers collection. Each does something a little bit different with each
element.
259 /**
The remainingSwimmerCount method starts by initializing the count to 0. Then, in a pair of nested loops,
the outer looping over rows and the inner looping over the columns curr is set to refer to each Swimmer in the
structure, one after the other. The if statement (which should look very familiar) tests if curr is not null. As
long as the reference isn’t null the swimmer counts. Thus the body of the if statement increments the return
value. Then, when the loops finish, the count is returned.
Examining this method now is an example of bottom-up design: we know we will need this ability to test
for the end of a level. Now we have a command that gives us the swimmer count. We will look at how to launch
a new level later in this chapter.
4 “to travel or pass across, over, or through” [AHD00]
9.3. MULTIDIMENSIONAL COLLECTIONS 255
Now, how does getLowestSwimmer work? It should return a reference to any Swimmer in the lowest row on
the screen. Looking back at setupSwimmers, we find that row 0 is the lowest row. Unfortunately, it is possible
the player rescued all the swimmers in the bottom row (they are all set to null as discussed above; we have
not seen the rescue code yet but we know it should null out the rescued swimmer).
What we need to do is go through the list of lists and check for the first non-null value. Again, we should
be thinking of a nested pair of count-controlled loops.
214 }
215
216 /**
This pair of loops should look familiar: the loop control variable is initialized to 0 and incremented until
it reaches the end of that dimension’s size. The Boolean expression in the middle of the for loop is extended,
though: these loops also end as soon as a non-null swimmer is found. So, assuming all of the swimmers remain,
when both row and column are 0, the if statement on line 210 evaluates to true so lowest is set to curr (or
swimmers.get(0).get(0)). This is not null so the (lowest == null) expression is false and each loop ends
early (remember: false and anything is false).
Bottom-up Design
Using remainingSwimmerCount and getLowestSwimmer as building blocks lets us write two Boolean methods,
checkIsGameOver and checkIsLevelOver. Then in advance we can call these methods in if statements. Notice
that these two methods are quite simple; the reason for writing them as methods is to make the selection
statements “self-documenting”5 .
183 (getLowestSwimmer().getMaxY() >= water.getMaxY()));
184 }
185
186 /**
191 }
192
193 /**
Checking for the game being over is easy: checkIsGameOver evaluates the Boolean expression given earlier,
returning true if there is a surviving swimmer and that swimmer has reached the bottom of the water.
The Boolean expression should have you scratching your head. We know that false and any-
thing (true or false) is false but if (getLowestSwimmer() != null) is false then getLowestSwimmer()
5 The scare quotes are here because some programmers argue that properly written code is always self-documenting while others
claim that such a term is an oxymoron. As discussed in earlier chapters, the current authors believe in a balance between comments
which document the programmer’s intent and the use of good names and an appropriate level of abstraction in support of readable code.
256 CHAPTER 9. MULTIDIMENSIONAL DATA STRUCTURES
must return null. That means the expression after the && will throw a null-pointer exception. That is,
getLowestSwimmer().getMaxY() cannot be evaluated without causing the program to crash.
Java uses the fact that false and anything is false to avoid this problem. Using something called short-
circuit Boolean evaluation, Java evaluates a complex Boolean expression only until it knows the result. Expres-
sions at the same level are evaluated from left to right. If the expressions are joined by && (such an overall
expression is called a conjunction), as soon as Java evaluates one expression as false it can stop evaluating the
expression because the result is known.
Similarly, with Boolean expressions joined by the || operator (called a disjunction), as soon as Java evaluates
one expression to true it can stop evaluating as the overall expression’s value must also be true. Many modern
languages use short-circuit Boolean evaluation to permit tests to make sure expressions are safe to evaluate
(what we do in line 142) and to speed up programs (if the answer is known, why do any more processing to
evaluate the expression?).
checkIsLevelOver just counts the number of swimmers remaining and compares that number to 0; if there
are 0 swimmers, all have been rescued and the current level is over. We will get back to having multiple levels
after a slight diversion into animation and how we can animate our Swimmer class.
9.4 Animation
Animation, “the act, process, or result of imparting life, interest, spirit, motion, or activity” [AHD00], is noth-
ing new. From the falling apple in NewtonsApple to the color-changing villagers in the FluSimulator, all of the
games we have written since Chapter 2 have added interest through motion and activity. This section reviews
what we have done before and talks about how to animate Swimmer sprites in RescueMission.
• Update game film state according to current state and user input
This means that we can, already, see how we should include animation inside our objects: advance in
our various sprite classes will enforce movement rules according to the game and they will also change the
appearance of the sprite at some rate.
Lines 13-14 are included in the listing to remind us that a Swimmer is-a SpriteWithVelocity. Thus in advance
on line 99 the call to super.advance(dT) is a call to the advance method provided by that class. Whenever
possible we will use the commonly defined implementations of methods from SpriteWithVelocity to justify
our extracting the code. SpriteWithVelocity.advance(double) has just one line in the body:
9 /** The sprite’s velocity */
10 private double deltaX;
42 }
43
44 /**
Generally, if a sprite has velocity, it typically is advanced by moving it according to how long it has been
from the last frame and how fast it should be moving.
Lines 101-103 handle bumping into the wall. The bumpTheWall method is static. What does static mean
again? There is no this. The method and the field it manipulates are shared by all Swimmers. If any Swimmer
touches the wall, then all Swimmers will see the change in the shared field. More on this in a minute.
Line 100 handles animation. That is, each Swimmer has a timer. When the timer runs out, then the state of
the Swimmer is updated. This should sound a lot like how FluSimulator ran except that each sprite has its own
timer rather than using a FANG timer class.
27
44 /**
163 if (timer <= 0) {
164 timerExpired();
165 startTimer();
166 }
167 }
168
169 /**
176 }
177
178 /**
205 }
206
207 /**
212 }
213
258 CHAPTER 9. MULTIDIMENSIONAL DATA STRUCTURES
214 /**
220 }
221 }
The timer field is private; the methods for manipulating the value are all protected because it is assumed
that classes extending Swimmer will treat those as extension points, spots where the child class can modify its
behavior relative to the parent class.
The key concept is that the method getTimerCountdownTime is used to get the amount of time the timer
will take before expiring; it is called from startTimer whenever the timer needs to be restarted. Note that
getTimerCountdownTime in Swimmer just returns a constant value; it is possible to use any amount of calculation
or state information to determine the timing (we will see this in FastSwimmer where the timer determines how
long the swimmer waits off screen or, if the timer is on the screen, how long each animation frame is.
When the timer is decremented (the call to decrementTimer is inside advance for Swimmer and should be in
that method for all subclasses of Swimmer), a check is made to see if the timer has crossed 0.0 (has it expired).
If the timer expires, calls are made to timerExpired and startTimer to restart the timer. Since the state may
have changed, the call to getTimerCountdownTime may return something different.
In Swimmer, in line 213, timerExpired calls setFrame. What is the value of the parameter expression passed
to setFrame? frame and FRAME_COUNT are both int fields, one static (Which one? Notice how even capital-
ization conventions, consistently applied, improve readability of expression). So, frame + 1 is an integer one
greater than frame.
What is the % operator again? It is the modulus or remainder operator. Thus, if FRAME_COUNT is set to 4, the
result of modulus will be an integer on the range [0-4). Thus frame is cyclic, taking on the values 0, 1, 2, 3, 0,
1, ... and so on. This frame number is how we know how to change the appearance of the Swimmer sprite.
187 if (frame == 0) {
188 rotate(20);
189 } else if (frame == 1) {
190 rotate(20);
191 } else if (frame == 2) {
192 rotate(-20);
193 } else if (frame == 3) {
194 rotate(-20);
195 }
196 }
197
198 /**
The code for setFrame shows that the swimmer is rotated relative to its current facing. Assuming frame
0 is the nominal position, when advancing to frame 1 the swimmer rotates 20 degrees clockwise. With frame
2, the swimmer returns to the nominal position (rotates 20 degrees counter-clockwise). Then, advancing to
frame 3, the swimmer rotates 20 degrees counter-clockwise from the nominal position. Then, when frame
goes to 0 again, the sprite is returned to the nominal position.
This is a very simple state machine where the frame number records the current state and setFrame up-
dates the frame number and the appearance of the sprite. Note that this cyclic state is maintained “automat-
ically” by calling decrementTimer in advance.
9.5. FINISHING RESCUE MISSION 259
47 /**
It calls decrementTimer and then, if the fast swimmer is on the screen and swimming, it translates the
location according to the current velocity. This is a case of repeating ourselves: why can’t we reuse the advance
method declared in SpriteWithVelocity? Wasn’t avoiding just this sort of code in all the sprites in this game
the point of factoring out the super class?
Short answer: Yes. The point was to reuse implementation. Unfortunately there is no Java notation for
talking about the definition of a method in the class above the class above the current class. That statement
tells us what super, used with a dot, actually does: the expression following the dot is evaluated as if it had been
written in the body of the class above the current one. Typically that means that it calls a method defined in
the class the current class extends. But if the closest definition of a named method is further up the hierarchy,
super will refer to that definition. It always refers to the lowest definition that happens above the current
class.
Because we don’t want Swimmer.advance and there is no way from FastSwimmer to specify
SpriteWithVelocity.advance, we were reduced to copying the method body into our advance6 . We are
lucky in this case that there is a single line to copy; if the processing were more complex it would make
sense to move the actual movement code out to its own protected method so that it could be called from
SpriteWithVelocity.advance and any other advance where it was needed.
Launching a FastSwimmer
There is one FastSwimmer in the game; sometimes it is swimming across the screen and sometimes it is waiting.
This is an object with two states. When constructed, a FastSwimmer is waiting; it changes from swimming to
waiting when it reaches the edge of the screen and bounceOffEdges is executed.
169 super.timerExpired();
170 } else {
6 Some programming languages, such as C++, support picking and choosing which ancestor class’s implementation to call; that power
171 launch();
172 }
173 }
174 }
The timerExpired method (as mentioned above) launches the fast swimmer. When it begins waiting the
FastSwimmer sets a random waiting time on the range [0-MAX_TIME_OFF_SCREEN) seconds. When the timer
expires, since the state is waiting (isWaiting returns true), isExpired calls launch. If the fast swimmer is
already swimming, the timer is used to animate the swimmer by calling super.timerExpired and reusing the
animation code written for Swimmer.
117 if (Game.getCurrentGame().randomDouble() < 0.5) {
118 launchLeftToRight();
119 } else {
120 launchRightToLeft();
121 }
122 }
123
124 /**
131 setRotation(90);
132 setVelocity(speedX, speedY);
133 }
134
135 /**
142 setRotation(-90);
143 setVelocity(-speedX, speedY);
144 }
145
146 /**
When a fast swimmer launches the state is changed to swimming (startSwimming is called). This changes
the state, makes the sprite visible, and sets the frame number to 0. Then launch “flips a coin” or rather gets a
random number from the Game. If the number is less than half, the fast swimmer moves left-to-right; otherwise
it moves right-to-left. The two methods for actually positioning the swimmer and setting the velocity do what
you would expect.
Naming methods well is an important skill. It would be possible to save 10-25 lines by moving the bodies
of launchLeftToRight() and launchRightToLeft() directly into launch. Each call would be replaced by three
lines of code. It would greatly decrease the readability of launch and additional comments would be necessary
to explain the code.
The current structure separates the decision about what direction the swimmer should move from how it
is set to move in a given direction. The separation of functionality into small, independent units, makes life
easier for the programmer. Each method should do one thing and should have a name that reflects the one
thing that it does. If you’re having trouble coming up with a good name for a function, think carefully about
whether it really only does one thing.
In the current structure it would be easy to have newly created FastSwimmers always start moving left-to-
right. It would also be easy to change the chances that the swimmer goes in either direction without having
to worry whether any code for launching needed to change.
9.5. FINISHING RESCUE MISSION 261
59 /**
The resetting of the x-component of the velocity (lines 49 and 53) use the Math class, a class which has only
static methods. abs is the absolute value or the magnitude of the number without regard to sign. Thus it is
always a non-negative number. Since we don’t know whether the rescuer was last moving from left-to-right
or reverse that, we convert the x-component to a positive value (it must be non-zero for the rescuer to move)
and the invert it if moving to the left.
If the user presses the space bar, the rescue ring is launched. The rescue ring, like the FastSwimmer is a
sprite with two states: ready and flying. When the ring is ready, it is “attached” to the rescuer, matching
location with the rescuer each frame.
100 super.advance(dT);
101 rescueRope.setStart(rescuer.getLocation());
102 rescueRope.setEnd(getLocation());
103 } else {
104 setLocation(rescuer.getLocation());
105 }
106 }
107
108 /**
If the ring is ready it is not flying. Thus line 106 executes, moving the rescue ring to the location occupied
by the rescuer. The first branch of the if statement uses the super definition of advance to move the ring.
Then a line is drawn between the rescuer and the rescue ring (or rather, the line sprite rescueRope has
its end points set to be the location of the rescuer and the location of the rescue ring). The rope is purely
aesthetic; no other sprites ever interact with it.
Changing the state of a RescueRing takes place in startFlying and startReady: startFlying is called in
Rescuer.advance (when space bar is pressed); startReady is called from the constructor, bounceOffEdges, and
isRescuing. The rescue ring keeps flying until it rescues a swimmer (regular or fast) or it hits an edge of the
screen. With this feature the game values accuracy very highly.
262 CHAPTER 9. MULTIDIMENSIONAL DATA STRUCTURES
Rhythm in Games
How the RescueRing behaves is a central design decision for this game. How many rings can the rescuer throw
at one time? What is the “recharge time” between rings? The decision for RescueMission was that there is
only one ring and once it is thrown it travels in a straight line until it rescues a swimmer or moves off the top
of the screen.
This decision means that pressing the space bar will call startFlying but that method will do nothing
unless the ring is currently ready; if the ring is already flying, startFlying does nothing. This means there
is only one ring in flight. The ring is returned to the ready state when it rescues a swimmer or goes off the
screen so the limitation in startFlying enforces just the design described above.
This design decision forces the player to trade off their long-range accuracy (say for hitting the fast swim-
mer) and the need to throw the rescue ring often. This particular mechanic was lifted from the retro game
on which RescueMission was modeled, Space Invaders. In Taito’s arcade Space Invaders7 there was an additional
rain of missiles from the grid of invaders and a series of destructible forts between the invaders and the player.
There was no good story reason for the swimmers to want to harm the rescuer (and the game is already almost
1400 lines long) so that was left out.
114 startReady();
115 }
116 }
117
118 /**
195 readyForLaunch = false;
196 rescueRope.show();
197 rescueRope.setStart(rescuer.getLocation());
198 rescueRope.setEnd(getLocation());
199 }
200 }
201
202 /**
207 readyForLaunch = true;
208 rescueRope.hide();
209 setLocation(rescuer.getLocation());
210 }
211 }
212 }
Internally, RescueRing uses a boolean field, readyForLaunch to keep track of its state. When the field is
true, the ring is in the ready state; when the field is false, the ring is in the flying state. Lines 197 and 209
take care of setting the field according to the state the ring is changing to.
When the ring is flying, the rescue rope should be visible; when it is ready, the rope should not be visible.
Finally, each state setting method adjusts the location of either the ring or the rope, depending on what the
new state is.
Bouncing off the edges is running into a wall. When that happens, the flying ring goes from flying to ready
(line 116 above).
160 }
161
162 /**
How does a ring rescue a swimmer? The action needed to rescue the swimmer is actually beyond the scope
of RescueRing; it is something that the game is in charge of. The ring does have a boolean method, isRescuing
which takes a swimmer as a parameter and returns true if the swimmer and the ring are intersecting.
Note that this method uses short-circuit Boolean evaluation to make sure that intersects is not called
with null. The null check is necessary because of how we decided to handle rescued swimmers: they are set
to null when rescued. By putting the check here we can write a simple iteration to handle checking each and
every swimmer to see if it has been rescued.
The iteration is in RescueMission.rescueIfPossible.
273 for (int row = 0; row != ROWS; ++row) {
274 for (int column = 0; column != COLUMNS; ++column) {
275 Swimmer curr = swimmers.get(row).get(column);
276 if (ring.isRescuing(curr)) {
277 rescueSwimmer(row, column);
278 }
279 }
280 }
281 }
282 if (fastSwimmer.isSwimming() && ring.isRescuing(fastSwimmer)) {
283 rescueFastSwimmer();
284 }
285 }
286
287 /**
The nested loops look similar to those used in moveEverything; the only difference is that rather than
checking against null the if statement checks if the ring is rescuing the given swimmer. Remember that there
is a null test inside of isRescuing so this is safe. If the check returns true, the method calls rescueSwimmer.
After the loop, the fast swimmer is checked to see if it is being rescued. It is necessary to check isSwimming
on the fast swimmer because it cannot be rescued if it isn’t visible. It might be possible to intersect with it
even when it is off the screen (the reason we remove Swimmer sprites from the list of lists to rescue them).
264 fastSwimmer.startWaiting();
265 ring.startReady();
266 }
267
268 /**
297 setScore(getScore() + curr.getScore());
298 removeSprite(curr);
299 swimmers.get(row).set(column, null);
300 ring.startReady();
301 }
302
303 /**
To rescue the fast swimmer we add the fast swimmer’s score to the game score and reset the state of the
fast swimmer (to waiting) and the rescue ring (to ready). To rescue a regular swimmer we do just the same
thing but regular swimmers don’t have a waiting state. Instead we set the location where the sprite is in the
grid to null, see line 301, and remove the sprite from the FANG data structure so it is no longer painted as part
of each frame, see line 300.
Levels in FANG
The concept of Game in FANG is a little more complicated that this text has let on. Internally FANG keeps a list
of Game derived class objects. The first item in the list is the current game (and is what is returned by the call
Game.getCurrentGame). When the current game really finishes, FANG automatically calls the next game in the
list. If there is no next game, then FANG terminates the program.
Each game in the list can be considered a level. Thus when a level is over (when all swimmers in the grid
are rescued) we can create a new RescueMission, add it to the list of games FANG maintains, and then really
end the current game. That is exactly what happens in advance:
104 bounceEverything();
105 rescueIfPossible();
106 if (checkIsLevelOver()) {
107 addGame(new RescueMission(level + 1, getScore(),
108 swimmerDX * LEVEL_SPEEDUP, swimmerDY * LEVEL_SPEEDUP));
109 finishGame();
110 } else if (checkIsGameOver()) {
111 addGame(new EndOfGame(getScore()));
112 finishGame();
113 }
114 }
115
116 /**
Everything is moved, then everything is bounced off the edges of the screen and every swimmer is checked
for a rescue. Then if the level is over (there are no remaining swimmers in the grid), a new game is added by
calling addGame with a newly constructed game. The new game is constructed with a level number, the current
score, and slightly higher speeds for the swimmers.
If those four parameters are necessary for constructing a RescueMission, how does FANG know what values
to use for the first level? The short answer is that it doesn’t. FANG requires every Game derived class to have
a default constructor; the default constructor is the no parameter constructor. When creating the first level,
FANG calls the default constructor.
75 }
76
77 /**
88 this.scoreSprite = null;
89 this.level = level;
90 this.swimmerDX = swimmerDX;
91 this.swimmerDY = swimmerDY;
92 this.initialScore = initialScore;
93 }
94
95 // Methods ----------------------------------------------------
9.5. FINISHING RESCUE MISSION 265
Applying the DRY principle, we just forward the default constructor to the four parameter constructor
providing initial values: level 1, score 0, and the named constants for swimmer velocity.
The only odd thing remaining in advance is line 113. What is EndOfGame? It is used with new so it must be
a class. Is it part of FANG? Looking at the documentation the answer is no. It is a fairly simple “game”, one
which takes a score as a parameter to its constructor and displaying a StringSprite on the screen. It is like
the drawings and examples in Chapter 2: it has a setup method but no advance.
1 import fang2.core.Game;
2 import fang2.sprites.StringSprite;
3
4 /**
5 * The ”game” is really just a level of the main game. It is constructed
6 * to display a message on how well the player did (it is initialized
7 * with their score) and then the setup creates a centered end-of-game
8 * message.
9 */
10 public class EndOfGame
11 extends Game {
12 /** the score provided to the constructor */
13 private final int initialScore;
14
15 /**
16 * Construct a new EndOfGame level. Requires the score to display for
17 * the player.
18 *
19 * @param initialScore the player’s score
20 */
21 public EndOfGame(int initialScore) {
22 this.initialScore = initialScore;
23 }
24
25 /**
26 * Setup the display information on the screen.
27 */
28 @Override
29 public void setup() {
30 setScore(initialScore);
31 StringSprite announcement = new StringSprite();
32 announcement.setText(”GAME OVER\nFinal Score: ” + getScore());
33 announcement.setScale(1.0);
34 announcement.setLocation(0.5, 0.5);
35 addSprite(announcement);
36 }
37 }
This use of a level to mark the end of the game is similar to how many video games actually work. They
have modes of operation such as attract mode (show pretty video or pictures to get players interested), setup
266 CHAPTER 9. MULTIDIMENSIONAL DATA STRUCTURES
(setting up the game), the lobby (waiting to play multiplayer games), the game itself, loading screens, and
high-score or end-of-game levels. Note that it would be fairly simple to add a small advance to EndOfGame that
would start a new game of RescueMission when the player pressed the space bar.
9.6 Summary
Nested Loops
The body of a for loop can contain any valid Java code. This means that it can, by definition, include another
for loop. A collection of loops, one inside another, are nested loops. To determine how many times the body
of the inner-most loop executes, it is necessary to determine how many times the body of each loop executed
and then multiply the values together. This is simple for count-controlled loops that run a fixed number of
times; it is more difficult if termination conditions of the loops are more complex.
When working with a two-dimensional structure like a multiplication table, it is useful to think of using
two nested loops (one loop for rows, an inner loop for columns).
Collections of Collections
While nested loops permit generating two-, three-, and higher-dimensional things, keeping the data for such
a structure typically requires a list of lists (of lists of lists of ...). That is, where a loop per dimension permits
discussing a multidimensional creation like a multiplication table, a collection per dimension permits keeping
the structure around.
An ArrayList of ArrayLists of some object gives a two-dimensional data structure which can hold a “table”
or grid of the element objects. To fill such a structure requires two nested loops (one for rows and one for
columns) and rows columns + rows + 1 calls to new: one for each element in the table or rows columns
calls for those, on for each row or rows calls for those, and one call for the whole list of lists.
Traversal of a data structure means passing over all of the elements in the structure. For a list of lists,
this implies a pair of nested loops. In this chapter we saw how to count elements of a list of lists with a given
attribute (not being null in our case) and how to find the first element with a given attribute (again, not being
null though the if statement could be modified fairly simply).
Because ArrayLists can only hold references, all plain old data types have object equivalents: Integer for
int, Double for double and Boolean for boolean.
Inheritance
An oak is-a tree just as a cherry tree is-a tree. This relationship means that any time a generic tree is required,
either an oak or cherry tree would be acceptable as a tree.
In Java, an object of any class which extends another (either directly or through a chain of intermediate
classes), is-a object of the ancestor class as well. Thus a CompositeSprite is-a Sprite, a RescueMission is-a
Game, and a Rescuer is-a SpriteWithVelocity, is-a CompositeSprite, is-a Sprite, and is-a Object.
Animation
Animation, the changing of the appearance of a Sprite, is similar to regular movement of a sprite on the
screen. The only difference is if the frequency of the updates should be different than calls to advance. This
chapter demonstrated how to use a timer and how to include protected extension points, methods which can
be overridden in subclasses to extend or modify the behavior of the timer in specific subclasses.
Combining overridden methods and the is-a relationship yields the important object oriented program-
ming principle of polymorphism. Polymorphism is having many forms; the superclass provides an interface that
can be used in the general case (the timer expires) and the subclasses provide specialized implementations of
the extension point routines which are called through the superclass reference.
9.6. SUMMARY 267
Levels in FANG
The Game class in FANG has an addGame method which adds a Game-derived class to the list of games to be run by
the current game. When a running game calls finishGame, the currently running game is taken off the front
of the list of games and if there is any game remaining in the list, the first is started as the next game or next
level.
The chapter makes use of this by constructing increasingly more difficult levels of the same game when all
the swimmers are rescued. It also uses this fact to create a special end-of-game level which displays a message
for the player showing their score. This is where a level could be built to read/save high scores or the game
could be started over.
Java Templates
Programming Problems
Programming Problem 9.1 Make the game play again from the beginning
So far all of our games were self-contained and stateless. They are self-contained in the sense that they don’t
rely on any files other than their Java source code and then, after being compiled, the compiled .class files.
This is beneficial in that the games are simple: distributing the game so someone else can play it depends only
on passing along the .class file. The other side of the self-contained coin is that a game must be recompiled
to change how it works. This is not how most programs work. You do not to recompile Microsoft Word®
each time you want to edit a different file. You don’t have to recompile the javac executable each time you
want to compile a different program. This is because these programs interact with the file system on the com-
puter where they run. The only instance where we have interacted with the file system is when loading a
ImageSprite.
Our games are stateless in the sense that they do not have any sense of whether this is the first, the one
hundredth, or the one millionth time they have run. Each time the program starts it starts from exactly the
same place. There may be random numbers involved to change the play from run to run (think NewtonsApple:
starting location of the apple is random but the sprites on the screen are fixed at compile time. This is related
to being self-contained but state would mean that the running game could leave some record of what it has
done. Consider keeping track of a high score list or saving a game in progress so that it can be restored.
This chapter will address the self-contained half of the problem by introducing Files, a way to connect to
and read files on the file system. This means that the game can change its performance without being recom-
piled; consider games with multiple levels, user-designed content, and mods. We defer reading information
from disk files until Chapter 12.
269
270 CHAPTER 10. SCANNER AND STRING: CHARACTER INPUT
Enter Hangman: instead of tick marks, the chooser draws a gallows at the beginning of the game and on
each missed letter draws a new body part hanging from the gallows; when the last body part is drawn, the
guesser is “dead”. This is an example where a story (perhaps a bit morbid) can add interest to an otherwise
dry game design. This particular game has been around for more than a century[? ].
Our Hangman game will pit the human player, as the guesser, against the computer playing as chooser. The
design diagram shows that the screen has four sprites on it: the score, the gallows, the word being guessed,
and the alphabet selector.
l a y e to
u v w x y z P letters
Figure 10.1: Hangman game design diagram
Looking at the diagram, there seem to be a lot more than four sprites on the screen: the alphabet is 26
characters and the hangman/gallows has at least 6 parts. Top-down design means that we will use abstraction
to conquer the complexity in this program. We will design the interfaces of the four classes we use in the game
and then we will implement the game using those interfaces. We will discuss implementing the four sprite
classes at the end of the chapter because all of the new concepts in this chapter are part of the game proper.
Public Protocols
ScoreSprite
The ScoreSpritein the upper-left corner of the game implements a scoring model just like that found in
NewtonsApple back in Chapter 6. It is just wrapped up in a class that knows how to initialize, how to record a
win, and how to record a loss. That means the public interface for the class is:
public class ScoreSprite
extends StringSprite {
public ScoreSprite()...
10.1. DESIGNING HANGMAN 271
The sprite is added to the game, properly positioned. Then, whenever the game is won or lost, win or lose
is called and the ScoreSprite will fix up its score1 and update the displayed value. The overloaded constructors
provide flexibility: the sprite can be initialized to any arbitrary value. The number of wins cannot be greater
than the number of games played.
HangmanSprite
The HangmanSprite is a composite of the gallows and the victim. Note that the hangman takes the place of
the tick marks so it is really a second score indicator. It shows the score for the current game while the
ScoreSprite shows the scores across multiple games.
Thinking about it as a score indicator gives us the insight to build the public interface:
public class HangmanSprite
extends CompositeSprite {
public HangmanSprite()...
public void clear()...
public void incrementState()...
public void setColor(Color color)...
public boolean stillKicking()...
}
The local setColor method sets the color of the victim’s body to the new color. clear clears the state so no
body parts are on display (sets the score to 0). incrementState adds one to the state and displays a new body
part. stillKicking returns true until the victim is finished being hung. They are finished when all the body
parts are visible.
GuessableWord
The word to be guessed is displayed in the middle of the screen. It is a StringSprite with additional state. It
tracks the word to be guessed and the progress of the guess (it shows “_” characters where unguessed letters
are and the letters where they have been guessed). The public interface is:
public class GuessableWord
extends StringSprite {
public GuessableSprite(String word)...
public void expose()...
public boolean guess(char letter)...
public boolean isGuessed()...
}
A GuessableWord is initialized with the word to be guessed. As part of the implementation it will prepare
the displayed version of the word. Then, so long as !isGuessed the game goes on. Each time the user picks a
letter, the letter will be passed to guess: if the letter is in the word it is exposed and guess returns true; if the
letter is not in the word then the displayed word is unchanged and guess returns false.
1 Going back and fitting the ScoreSprite into NewtonsApple is left as an exercise for the interested reader.
272 CHAPTER 10. SCANNER AND STRING: CHARACTER INPUT
AlphabetSelector
The user must be able to pick letters to guess. This could be done with the keyboard, with the mouse, or with
both. For simplicity, we will focus on just using the mouse (extending the sprite to handle keyboard input is
presented as an exercise).
An additional problem in Hangman is that the guesser would like to keep track of letters already guessed; if
the letter is in the word it is easy to remember but if it isn’t in the word it is not clear how they are remembered.
The AlphabetSelector will contain LetterSprites, sprites which have a ready and a used state; they will be
shown in two different colors and only ready letters will be selectable by the player. Thus the appearance will
give feedback to the user that they shouldn’t reselect letters and the sprite will also keep them from making
the mistake.
class AlphabetSelector
extends CompositeSprite {
public AlphabetSelector()...
public char selectedChar()...
public void unselectAll()...
}
class LetterSprite
extends StringSprite {
public LetterSprite(char letter)...
public char getLetter()...
public Color getReadyColor()...
public Color getUsedColor()...
public boolean isReady()...
public boolean isUsed()...
public void setReadyColor(Color readyColor)...
public void setUsedColor(Color usedColor)...
public void startReady()...
public void startUsed()...
}
The public interface of LetterSprite is included to make the operation of AlphabetSelector clearer. When
an AlphabetSelector is built, it contains 26 LetterSprites, all initialized to the ready state. selectedChar
returns either a letter (if one was selected) and the null character, the char with a value of zero, otherwise. If
a letter was selected, the corresponding LetterSprite is changed from the ready to the used state.
Once a LetterSprite is in the used state, it cannot be selected again. To permit restarting with the whole
alphabet selectable again, the unselectAll method is provided.
43 doneWithGame(”Congratulations”);
44 }
45 } else {
46 hangman.incrementState();
47 if (!hangman.stillKicking()) {
48 word.expose();
49 word.setColor(getColor(”red”));
50 scoreSprite.lose();
51 doneWithGame(”You lose!”);
52 }
53 }
54 }
55 } else {
56 if (this.getKeyPressed() == ’ ’) {
57 setGameOver(false);
58 startOver();
59 }
60 }
61 }
62
63 /**
Hangman uses a single level (and level type), unlike Chapter 9’s RescueMission. Instead Hangman has two
different states: game over and not game over.
Style note. Notice that the if statement is written without using the ! symbol. Software engineering TK.
If the game is already over, the game is waiting for the user to press the space bar to restart the game. The
game was ended with a call to endGameWithMessage (definition given below) which gives displays a specific
message about how the game ended and also shows a message to press the space bar to start again. Thus the
player knows what to do and the game waits for them to do it. startOver is a FANG routine which restarts the
current Game; it is almost like adding the current game to the list of games and then calling finishGame.
While the game is going on the else clause of the if statement is executed. Line 46 checks if a letter (or the
null character) has been picked. If the character is not the null character, then the player has made a guess.
The guess is passed into the guess method of GuessableWord; as it is described above, guess returns true
if the letter was found in the hidden word. Thus if it is true it is possible that the player has won the game. If
they have, endGameWithMessage is called with a win message and the score is incremented.
If guess returns false then a new body part is added to the gallows and a check is made to see if the player
has been hung. If the player has been hung, the game is ended with an appropriate message and the score is
updated for the loss. It is also necessary to expose the word so the player knows the word that they missed.
72 ”\nPress <space> to play again.”);
73 restartMessage.setScale(0.9);
74 restartMessage.setLocation(0.5, 0.75);
75 addSprite(restartMessage);
76 alphabet.hide();
77 setGameOver(true);
78 }
79
80 /**
274 CHAPTER 10. SCANNER AND STRING: CHARACTER INPUT
The endGameWithMessage method creates a new StringSprite containing two lines: the given message and
a game restart line. The sprite is positioned in the bottom half of the screen, the AlphabetSelector is hidden,
and the game is set to over.
These two methods are the only two needed for playing the game. The rules for picking a letter, for guess-
ing a letter in a hidden word, and for keeping track of scores are abstracted away from the game program
itself; all it has to do is coordinate communication between the pieces that know how to play the game.
Stub Methods
We will take a look at one of the setup methods because it leads us to a way of applying top-down design, the
idea of stub methods. A stub method is a temporary implementation of a method, a simple implementation
which permits compiling and testing of higher level methods. A stub method is an implementation of the
design admonition, “Pretend that it works.”
132 word.bottomJustify();
133 word.setWidth(0.7);
134 word.setLocation(0.5, 0.6);
135 addSprite(word);
136 }
137
138 /**
145 return ”cats and dogs”;
146 }
147 }
At line 133 setupWord calls wordLoad2 . Lines 145-148 represent a stub implementation of wordLoad. The
method just returns a fixed word. Every time the game is played, the chooser will choose the exact same
word.
This does not make for a good Hangman game but it does make for a playable and (more importantly)
testable Hangman game. With the stub implementation for wordLoad in place we can compile HangmanWithStub
and all of the supporting classes without knowing anything about opening files for input. Thus we can debug
the mechanics of the game separately from the problems of locating a file full of possible words, reading that
file into memory, and picking a word from that list. As always, separating problems into different levels either
by delegation (as we have done here) or abstraction (as partitioning Hangman into the four specialized sprite
classes), helps us overcome complexity.
We will take a slight detour now to discuss how java runs a compiled .class file from the command-line,
how we can specify parameters to a program when it is run, and how FANG has started the program for you
automatically behind the scenes.
sorted by method name, it sorts to the end of the class. This means that through line 145 Hangman.java and HangmanWithStub.java are
identical. The reader is urged to consider what better names for this method might be.
10.2. STARTING PROGRAMS 275
rules the operating system uses when beginning the execution of a program. An executable program that
runs directly on a given CPU requires more than just the right CPU: it also requires the right operating system
so that it is properly loaded into memory and the correct entry point is chosen to begin execution.
An example of this difference lies in the executable formats for Microsoft Windows, Linux, and Apple OSX.
All three operating systems run on computers with an Intel or compatible CPU; all three operating systems
use sequences of instructions drawn from the exact same instruction set as do programs which any of the
operating systems can start.
Without special adaptation, however, programs compiled on OSX cannot be started directly on a Win-
dows computer, nor can Windows executables be started directly by Linux, and so on. The instructions in the
program depend on the CPU but how the program starts depends on the CPU and the operating system.
This is of interest to us because we are programming using the Java programming language. Rather than
compiling to machine code run directly on any given CPU (or loaded by any given operating system), the javac
compiler compiles to .class files containing bytecodes to be interpreted by the java virtual machine program.
The java interpreter is like an operating system in that it has a defined convention for how it starts a
program. When you run a given .class file, java calls a specific, static method, the method with the following
header:
public static void main(String[] args)
We will address the meaning of the two square brackets later in this section. When java is run with the
command-line:
~/Chapter10% java -classpath .:/usr/lib/jvm/fang.jar Hangman
the Java interpreter sets up all appropriate library connections, loads the Hangman class, and then calls
Hangman.main with the previous header.
The complete lifecycle of an application is:
“Wait,” you are thinking, “No where in this book have we written a main method. Looking at Hangman.java
(and HangmanWithStub.java), there is no such method.” FANG uses the standard Java inheritance model to
provide a version of main for you. In GameLoop.class, there is a static main method with the right header.
When Java runs an application, it calls main as defined by the class named on the command-line calling the
interpreter. In FANG the default implementation of main is both the simplest and the most complex method
in the library. It is simple in what it does: it determines the class which was specified on the command-line
for Java, makes sure the class extends GameLoop, calls the default constructor for that class, and then calls
runAsApplication. It is complicated in that figuring out what class was specified when the program was run
and then calling the constructor for a type that is unknown at compile time requires some deep knowledge of
Java.
We will stick with the simple definition, ignoring the convoluted details. That does leave the question:
What doe runAsApplication do?
Applets v/ Applications
One powerful feature of FANG is its ability to run exactly the same code as either an application (with a call to
java from the command-line) or as an applet inside of a Java-enabled Web browser.
What is an applet and how is it different from an application? An applet is a class which extends
java.applet.Applet, a Java library class. The lifecycle of an applet is:
276 CHAPTER 10. SCANNER AND STRING: CHARACTER INPUT
Because the applet runs inside the context of a Web browser3 , it is not started with a call to main because
the Java virtual machine has already started and is running the browser’s version of main.
What does that mean for a FANG game? A FANG game must run as either an applet or an application and
it must not require recompiling to change from one to the other. If FANG provides its own main and that main
acts like an applet browser, implementing the applet lifecycle outlined above, all started from a call to main,
then a FANG program can run like an applet no matter how it is started.
Notice that this is another separation of function, the splitting of a problem into component parts that are
solved independently. Rather than worry about “How to start and run a program as an applet or an applica-
tion”, FANG solves the problems of “How to run a program as an applet” and “How to provide a main which
starts an applet running inside of an application.”4
When a program is run from the command-line, it is possible to provide parameters. These parameters
are just like parameters passed to any method except that they are each Strings. The next section discusses
how to interpret the arguments passed in to main both in that method, when you write your own, and how
FANG saves those parameters for you so that you can have access to them without having to override main
yourself. This information is important because we want to be able to write our own programs without the
FANG library by the end of the book.
Because of the way Java handles inheritance, any class which extends GameLoop can provide its own main
method with the above header; such a method overrides the method defined in GameLoop. If you want FANG
to run as usual, your main method should end with the following line:
InitializeApplication.fangMain(args);
You will need to import fang.util.InitializeApplication in order to call this method. fangMain is the
wrapper around all of the complexity mentioned above so we will not drill down any deeper into it here.
What can you do in main? And what is the meaning of String[] args? In reverse order: String[] args is
an array of String objects and main can do anything that is built into Java.
An array is like a ArrayList in that it is a Java collection, a single object containing other objects. An array
is unlike an ArrayList in its syntax and public interface. To illustrate this and where we are going with writing
non-FANG programs, the next section will focus on writing a program which prints all of its command-line
parameters to standard output. It will not use any part of FANG.
3 or
another applet browser such as appletviewer in the JDK
4 Oneof the authors (Ladd) originally wrote a different toolkit with an inherited main method in summer, 2006, as part of an early
version of this book. The current implementation of main and its support code is based on code written for the ACM’s Java Task Force’s
Java toolkit [RBC+ 06]
10.2. STARTING PROGRAMS 277
Writing main
1 /**
2 * A program showing how to use a for-loop to iterate across the
3 * arguments passed in to the program when it is run.
4 */
5 public class PrintAllArguments {
6 /**
7 * The main program: java PrintAllArguments starts by calling this
8 * public static method. The method just uses a for-loop and
9 * System.out.println to print each argument on a line by itself.
10 *
11 * @param args The command-line arguments
12 */
13 public static void main(String[] args) {
14 System.out.println(”PrintAllArguments:”);
15 for (int i = 0; i != args.length; ++i) {
16 System.out.println(” args[” + i + ”] = \”” + args[i] + ”\””);
17 }
18 }
19 }
This program treats args just like any other collection. The following table gathers the differences between
an ArrayList and an array starting with those shown in this code and including a couple of other cases.
What does this code do? That is, given different command-lines, what is the output?
278 CHAPTER 10. SCANNER AND STRING: CHARACTER INPUT
In the first instance, there are no command-line arguments after the name of the class file for Java to load.
Thus line 16 prints the name of the program. The length of args is 0 so the for loop is never executed.
The second instance has three words appearing after the name of the program, alpha, bravo, and charlie.
The words are separated from the name of the class and each other by some number of spaces. With three
different words, args.length is 3 so the loop executes three times and the three String values are printed.
The last example shows how the command-line interpreter, provided by the operating system (bash on
Linux in this case) can group multiple words together. That is, the command-line arguments after the name
of the class are similar to those in the second case, they are just enclosed in double quotes. This causes
the command-line interpreter to treat the string enclosed in the quotes is treated as a single argument.
args.length is 1 and args[0] (remember, [0] is equivalent to .get(0) for an ArrayList), the only String
in the argument array is printed.
3 import fang2.core.Game;
4 import fang2.util.InitializeApplication;
5
6 /**
7 * Uses FANG methods to get the arguments passed into the program.
8 * @author blad
9 */
10 public class PrintAllArgumentsFANG
11 extends Game {
12 /**
13 * List all the command-line arguments on standard output.
14 */
15 @Override
10.2. STARTING PROGRAMS 279
(The \ characters are continuation characters; the lines continue onto the next line. They can be typed on
a single line in most command-line interpreters. They are necessary so the lines fit on the printed page.)
The discussion for the three different executions of the program match those given above for
PrintAllArguments. The real difference between the two programs is that FANG does the work of reading
the array and puts it all in an ArrayList for you.
1 import java.util.ArrayList;
2
3 import fang2.core.Game;
4 import fang2.util.InitializeApplication;
5
6 /**
7 * Uses FANG methods to get the arguments passed into the program.
8 * @author blad
9 */
10 public class NamedArgumentsFANG
11 extends Game {
12 /**
13 * List all the command-line arguments on standard output.
14 */
15 @Override
16 public void setup() {
17 System.out.println(”NamedArgumentsFANG:”);
18 ArrayList<String> names = InitializeApplication.getArgumentNames();
280 CHAPTER 10. SCANNER AND STRING: CHARACTER INPUT
Named arguments are similar but are grouped by name. The getArgumentNames method returns a list of
all names used for arguments; this list can be empty. Looking at the code in NamedArgumentsFANG, notice that
it is a pair of nested loops because the collection of named arguments is a list of lists (a two-dimensional data
structure).
~/Chapter10% java -classpath .:/usr/lib/jvm/fang.jar \
NamedArgumentsFANG one=alpha two=bravo one=charlie two=delta
NamedArgumentsFANG:
args[”two”][0] = ”bravo”
args[”two”][1] = ”delta”
args[”one”][0] = ”alpha”
args[”one”][1] = ”charlie”
One thing to note is that the order of the strings in the list returned by getArgumentNames is, for all intents
and purposes, random. This is because of the internal structure used to store the lists of arguments. We will
discuss how to sort collections in Chapter ??.
211 }
212 }
213 return lowest;
214 }
215
216 /**
In lines 206 and 207 the Boolean expression for <continuation> extends the standard count-control test
of having reached some bound with a test for lowest being null. The loop runs for some number of iterations
or until the body of the loop sets lowest to a non-null value. A loop which runs over and over until some
condition is met is known as a sentinel-controlled loop.
A sentinel is a guardian or, in our case, a marker. It marks the end of some phase of processing. Imagine
writing a method to generate a random multiple of 3 between 0 and some given positive value, bound5 .
If we ignore the need to have a multiple of 3, the method is fairly simple to write:
public int randomMultipleOfThree(int bound) {
int multipleOfThree = Game.getCurrentGame().randomInt(bound);
return multipleOfThree;
}
This is just a wrapper around a call to randomInt in the current game. This method could be defined in a
game or a sprite, any class which imports fang.core.Game. The value returned may or may not be an actual
multiple of 3. How can we make sure it is?
The randomInt method returns a number on the range [0-bound). What are the odds that that number is
a multiple of 3? If all numbers are equally likely, then one in three should be a multiple of 3. So one third of
the time the above method should work. Is that good enough? No, we want it to work every time.
So, if we call randomInt 3 times, if each choice has a one in three chance of being a multiple of 3, we have a
one in one chance of getting a multiple of 3, right? Wrong. With random numbers there is a chance that any
number of them in a row is not a multiple of 3. The chance gets smaller and smaller the more times we select
a random number but the number of times we must select cannot be known when the method begins. Thus this
cannot be a count-controlled loop.
We will use a new construct, the while loop. First we will show an example loop inside
randomMultipleOfThree and then we will present the template.
A while loop takes a single Boolean expression after the word while. When the while line is executed,
the expression is evaluated. If it evaluates to true then the body of the loop is executed; at the end of the
body of the loop, execution returns to the while line, reevaluating the Boolean expression. When the Boolean
expression evaluates to false, execution skips over the body of the loop and continues execution with the
following statement.
5 There are alternative approaches to this problem which do not use iteration at all. Exploring them is left as an exercise for the
interested reader.
282 CHAPTER 10. SCANNER AND STRING: CHARACTER INPUT
The while statement looks a lot like an if statement and some students confuse the two. It is important to
keep in mind that a while statement cannot have an else clause. Also, the body of an if statement is executed
zero or once (depending on the Boolean expression); the body of a while statement is executed zero or more
times with an unbounded value for more.
So, looking back at randomMultipleOfThree, if the first number chosen is a multiple of 3, then
multipleOfThree % 3 is 0. The Boolean expression ((multipleOfThree % 3) == 0) is true so the logical in-
verse (or not) of that is false. The key is we wrote an expression that is true at the sentinel value and then
applied a logical not to it. The loop will run while the sentinel check does not evaluate to true.
deMorgan’s Laws
Note that the Boolean expression in our multiple of 3 method can be simplified: if we push the logical not, !,
into the expression it modifies we can rewrite the expression according to the following:
(!((multipleOfThree \% 3) == 0)) = ((multipleOfThree \% 3) != 0)
to
while (Q) {
...
}
where P and Q are Boolean expressions and !P = Q or !P and Q have the same truth values for all possible
values of variables they reference. Studies have shown that positive logic is easier for human programmers
to understand and maintain.[? ]
August De Morgan (1806-1871) was a logician who was influenced by George Boole’s work and formally
stated a couple of simple rules, rules for simplifying expressions with logical not in them. The following two
lines use == to show that the two expressions are logically equivalent.
!(P && Q) == !P || !Q
!(P || Q) == !P && !Q
So, how can we use this? Consider a while loop to pick a random number which is either a multiple of 10
or a multiple of 11 or both. The Boolean expression can be built up:
((n \% 10) == 0) // multiple of 10
((n \% 11) == 0) // multiple of 11
((n \% 10) == 0) || ((n \% 11) == 0) // either 10 or 11
(!((n \% 10) == 0) || ((n \% 11) == 0)) // not 10/11
This expression makes sense in that it was developed by expressing the sentinel condition, the condi-
tion where the loop should stop, and then that expression was logically inverted. This is the way a sentinel-
controlled loop is typically built up.
The expression is not the simplest form to read for following programmers. It would make sense to push
the ! into the expression. That is done by applying the second of DeMorgan’s Laws:
10.4. STRING MANIPULATION 283
The expression is first rewritten as an and of the inverse of the two subexpressions. Then the not is pushed
into the subexpressions (it would work if they were && or || expressions as well). We will not spend a lot of
time working on simplifying logical expressions though this is a skill a programmer should develop.
We don’t care what value ’A’ has7 because that is an implementation detail. In fact, there are only three
important pieces of information to remember about the ASCII encoding sequence:
1. The capital letters are contiguous. ’A’ has an encoding one smaller than that of ’B’ which is one smaller
than that of ’C’ and so on.
2. The lowercase letters are also contiguous.. ’a’ is one smaller than ’b’ which is one smaller than ’c’.
3. The digit characters are contiguous. ’0’ is one smaller than ’1’, ’0’ is two smaller than ’2’, and nine smaller
than ’9’.
Remembering that a character is stored in a single byte (ignoring Unicode) and that there are three con-
tiguous ranges in the encoding, [’A’-’Z’], [’a’-’z’], and [’0’-’9’] are the only implementation details we
need to remember.
Character
The Character type is like Integer, Double, and Boolean, an object type which lets us create ArrayLists and
provides many utility functions. Among the methods provided by Character are a series of static character
classification routines. Some of these are summarized in the table below:
The value returned by Game.getKeyPressed() is a char so these methods can be applied to the values en-
tered by the user. Imagine we wanted to call setGameOver with true if the user pressed either an uppercase or
lowercase ’q’ (for ’q’uit). The following if statement makes that easy:
if (Character.toLowerCase(getKeyPressed()) == ’q’) {
setGameOver(true);
}
By forcing the case to be lower, we effectively ignore case. Notice that we use == to compare char values.
It is also possible to use less than and greater than comparisons for individual characters. For example, we
could write myIsUpperCase(char ch) as follows:
boolean myIsUpperCase(char ch) {
return ((’A’ <= ch) && (ch <= ’Z’)) ;
}
7 Though all of the authors do waste some portion of their brains remembering how ’A’, ’a’, and ’0’ are encoded.
10.4. STRING MANIPULATION 285
The Boolean expression is true when the value in ch (the encoding for the letter) is greater than or equal
to the encoding of ’A’ and when the value in ch is less than or equal to the encoding of ’Z’. Using the first rule
of ASCII, that the uppercase letters are contiguous, any capital letter must be encoded between those values
and only those 26 values encode uppercase English letters. This version of isUpperCase only handles English
letters, not extended Western European letters (e.g., ’Þ’ and ’Ê’) nor Unicode characters; the Character version
does handle those characters.
We will look at char in terms of sequences of them, the String type.
How many String objects are created by this code? Is it even legal (if a String is immutable, how does the
last line work?)?
This code is legal because both one and two are references to String objects. Thus the last assignment just
changes what String one refers to without changing the String containing ”cat”. The first line constructs
a literal String (all doubly quoted strings in Java code are converted to constructor calls by the compiler
automatically). The next line constructs a second literal string. The last line calls the + operator for String. +
takes two String operands and returns a new String containing the characters of the first operand followed
by the characters in the second operand. one ends up referring to the third String constructed by this code,
a String containing the characters ”catfish”.
A String is sequence of characters. This means we can write a loop to iterate over the characters in a
string. First, we will look at a program that takes an unnamed command-line argument and prints out the
characters in the argument, one character per line with single primes around the characters. We will do this
in FANG.
1 import fang2.core.Game;
2 import fang2.util.InitializeApplication;
3
4 import java.util.ArrayList;
5
6 /**
7 * Read unnamed arguments, print out the first argument one character
8 * per line. Demonstrates how a String is a collection of char.
9 */
10 public class ArgumentCharacters
11 extends Game {
12 /**
13 * If there are any unnamed command-line parameters, then get the
14 * first one and print out the characters, one per line. If there are
15 * no parameters, print out a message to that effect.
16 */
17 @Override
18 public void setup() {
19 System.out.println(getClass().getName());
286 CHAPTER 10. SCANNER AND STRING: CHARACTER INPUT
This program has a new feature at line 21: rather than typing in the name of the program’s class (as we
have done up until this point), the program takes advantage of Java’s runtime class identification. Java knows
what class an object is and keeps a reference to a Class object for that class; getting the class of the current
object is as easy as calling getClass. The Class class is not always easy to follow but the getName method
does just what you would think it would: it gets the name of the class, the actual class of the object on which
getClass was called.
Thus the first line of output, the name of the program, is generated by printing the String returned from
getClass().getName() in line 21.
Then, using the FANG method for getting command-line arguments, it checks to make sure there is at least
one command-line argument. If there are none, an error message is printed and setup finishes. Otherwise, the
first command-line argument is fetched and referred to with firstArgument. Each character in firstArgument
is then retrieved using the charAt method with an index. Each character is printed on its own line using
System.out.println. Sample output is given below.
str.charAt(5) = ’ ’
str.charAt(6) = ’b’
str.charAt(7) = ’r’
str.charAt(8) = ’a’
str.charAt(9) = ’v’
str.charAt(10) = ’o’
str.charAt(11) = ’ ’
str.charAt(12) = ’c’
str.charAt(13) = ’h’
str.charAt(14) = ’a’
str.charAt(15) = ’r’
str.charAt(16) = ’l’
str.charAt(17) = ’i’
str.charAt(18) = ’e’
On line 126, justMatchedIndex is initialized to the index of the left-most underscore in the field showWord;
showWord is initialized to contain an underscore and a space for each letter to be guessed (double-spaces the
word to make it easier to count the number of characters for the player). To count the number of letters as
yet unguessed, this method loops through the shown word, matching ’_’ characters. Each time a match is
288 CHAPTER 10. SCANNER AND STRING: CHARACTER INPUT
found, a counter is incremented. When no more matches are to be found, the current count is the number of
underscores and is returned.
Line 127 is a sentinel-controlled loop. For teaching purposes, to make it clear that it is a sentinel-controlled
loop, the condition on the while loop is left in a negative logical form. The sentinel is a location of -1 because
-1 is never a valid index into a String.
When designing a sentinel-controlled loop and choosing a sentinel, a good rule of thumb is to use a value
which cannot appear as a legitimate value. If the sentinel could be a legal value, how can your program tell the
difference between a legitimate and a sentinel occurrence? Once you define the context necessary to differ-
entiate between the two values you have defined your actual sentinel value, the contextualized occurrence of
the value which is never a legitimate value.
String is special in Java in that it is the only object type for which you can directly write literal values.
One reason that it is immutable is so that it behaves as much as possible like a plain-old data type. The biggest
departure in behavior between POD and String is in comparing values.
String.compareTo(String)
Consider writing a method which takes two int parameters and returns the larger of the two values. Consider
how you would write this method, myMax for a moment before you read on.
The method should return int as well as take two int parameters. Since the parameters are arbitrary
values (they are not heights or coordinates or anything), a and b make sense as names. The body of the method
should be an if statement comparing the two numbers and returning the larger value:
int myMax(int a, int b) {
if (a > b) {
return a;
} else {
return b;
}
}
Convince yourself that this works by walking through the code with a few different values. In particular,
what happens when the two integers are equal?
Looking at the code, we see a Boolean expression using the > comparison operator. If we wanted to over-
load myMax so that it worked with char or double, the plain-old data types, the body of the overloaded methods
would look identical; only the header line would change.
Since String is supposed to behave like a POD, it is tempting to try comparing two String using > (or >=
or even ==). Java will not compile the following code:
String myMax(String a, String b) {
if (a > b) {
return a;
} else {
return b;
}
}
The compiler gives the following error message (this output was edited to put the error message in the
middle of the code):
String myMax(String a, String b) {
if (a > b) {
^
10.4. STRING MANIPULATION 289
The comparison would compile with == or != in the middle but it would not mean what you probably think
that it would mean (more on that below).
Java defines a special interface called Comparable and all Comparable objects (String is one) provide a
compareTo method. The method takes a String as its parameter and compares the string on which compareTo
is called to the parameter, returning a negative number, 0, or a positive number, depending on the order of the
two strings: if called on string is before parameter, return a negative number; if called string and parameter
are equal, return 0, and if called string should come after the parameter, return a positive number. Thus the
following rewrite of myMax works for String:
String myMax(String a, String b) {
if (a.compareTo(b) > 0) { // a after b
return a;
} else {
return b;
}
}
There is also a equals method which returns true if two String objects contain the same sequence of
characters and false otherwise. Thus the following expressions are all true:
”stop”.equals(”s”+”top”)
(”abc” + ”def”).equals(”abcdef”)
”catfish”.substring(3).equals(”fish”)
It is important to note that the following expression returns false on the author’s machine:
”catfish”.substring(3) == ”fish”
This is because ==, when applied to object or reference types, compares the references, not the referents.
So, since ”fish” is a String constructed by a call to new before the program starts running and the result of
the call to substring is a newly created String copying the letters ’f’, ’i’, ’s’, and ’h’ out of a different
statically constructed literal String, they are not the exact same object so the == comparison returns false.
While == and != can be used to compare objects, the general rule is that they should not be used to compare
objects. All objects have a equals method and most types implement a meaningful comparison when they
override the version provided by Object.
What Order?
What order is used when comparing String with compareTo? The myMax method neatly sidestepped needing
to know. In Java, Strings are compared in lexicographic order which means they are compared in dictionary
order. If two strings contain only lowercase or only uppercase letters, then they are sorted into alphabetic
order according to the left-most character which differs between them. Thus ”cab” comes before ”cat” and
”capacitor” because ’b’ comes before ’t’ or ’p’. If one string is a prefix of the other (that is, the letters
in one of the strings runs out before a differing character is found), then the shorter word comes first. Thus
”cab” also comes before ”cabal” and ”cable”, too.
290 CHAPTER 10. SCANNER AND STRING: CHARACTER INPUT
The “only lowercase or only uppercase” stipulation above was to make the examples easy to include. What
actually happens is the two strings are compared, character by character, from left to right. As soon as a
position differs, the two Strings compare in the order of the differing characters. That is, for ASCII characters,
whichever of the characters has a smaller ASCII code comes first. Because uppercase letters are contiguous
and lowercase letters are contiguous, this works out to being alphabetic order as long as we don’t compare
uppercase to lowercase letters8 .
The Java String class also provides the compareToIgnoreCase method which ignored differences between
uppercase and lowercase letters.
It is worthwhile to look at the documentation for the java.lang.String class because it is a very central
class, one used for a lot of useful things. We will now look at how we can read a text file on a computer into
a String or a list of String; once we can do that, having Hangman pick a different word is as easy as reading a
list of words and picking one at random.
Specifying a Filename
Think, for a minute, about your hard drive (or, if you prefer, your USB thumb drive or a networked drive to
which your computer is connected). It is composed of a large number of sectors, units which are addressed by
numbers (the exact nature of the numbering scheme is unimportant).
The computer operating system permits allocation of the sectors for use in files and folders; a folder can also
be called a directory. Modern graphical user interfaces use a desktop metaphor where files are stored together in
folders which can, themselves, be arranged in other folders in a hierarchical storage system. Thus you might
keep your Java programs together in a CS101 folder which is inside of a SchoolWork folder.
When specifying a file name on the command-line, you will use slashes between folder and file names.
Thus, from the point of view of the SchoolWork folder the NewtonsApple source file would have the name
CS101/NewtonsApple.java9 . The concatenation of folders and finally a file name is called a file path or path
name.
8 For
those interested, all uppercase letters sort earlier than any lowercase letter. Digits sort before any uppercase letter.
9 MicrosoftWindows derivatives have a file system ported from Unix with some changes. The most noticeable difference is the use
of \ instead of / between levels in the file name hierarchy. This book will consistently use the /; feel free to translate it as you read.
10.5. READING FILES 291
A path name can begin with a slash as in /home/blad/SchoolWork in which case the hierarchy is anchored
at the root of the file system10 . A path name starting with a slash is an absolute path name; no matter where you
are inside the file system, an absolute path name will always refer to exactly the same file on the system.
If a path name does not begin with a slash, the name is a relative path name; the hierarchy starts at
the current directory and searches downward from there. The first example here specified that the path
CS101/NewtonsApple.java applies if you start in the SchoolWork folder.
We will be specifying file names using relative path names. When you run a Java program, the current
folder is the folder where you run it. Thus if I run ExistsFile.java with the following command-line:
~/Chapter10% java -classpath .:/usr/lib/jvm/fang.jar ExistsFile pets.txt
and the command-line parameters are treated as file names, the file specified is ~/Chapter10/pets.txt.
What is ExistsFile? It is a program which demonstrates how to import the File class in Java, how to call
the constructor, and how to query a File object to see if it refers to a file which exists on the system. The code
is as follows:
1 import java.io.File;
2
3 /**
4 * A program which reads its command-line arguments, treating each as
5 * the name of a file. Create an ExistsFile object with the name of the
6 * file. Then check if the file exists.
7 */
8 public class ExistsFile {
9 /** Java reference attached to file on file system */
10 File file;
11
12 /**
13 * Create a new ExistsFile with the file pointed to the named file.
14 *
15 * @param fname the name of the file to connect to; the named file
16 * need not exist (it will be checked in exists()).
17 */
18 public ExistsFile(String fname) {
19 file = new File(fname);
20 }
21
22 /**
23 * Check if the file exists, printing an appropriate message.
24 */
25 public void exists() {
26 if (file.exists()) {
27 System.out.println(”\”” + file.getName() + ”\” is a file.”);
28 } else {
29 System.out.println(”\”” + file.getName() + ”\” is NOT a file.”);
30 }
31 }
32
33 /**
10 On Microsoft systems this is also done with a letter followed by a colon as in C:. This is known as a disk name. Microsoft operating
The main method is modeled on the one used to cycle across command-line parameters. For each
command-line parameter, an ExistsFile object is constructed. On line 21 that constructor calls the File
constructor. The most common version of the File constructor takes a String containing a path name. The
constructed File object then refers to that location in the file system.
In the program, line 45 calls the exists method on the newly created ExistsFile object. All that method
does (see lines 28-32) is call the exists method of the File object. That Boolean method returns true if the
file exists and false if it does not.
Thus the output of the previously shown command-line is:
~/Chapter10% java -classpath .:/usr/lib/jvm/fang.jar ExistsFile pets.txt
”pets.txt” is a file.
This assumes that there is a file with the given name in the current folder.
26 /**
27 * Echo the file to standard output. If there is a problem with the
28 * file not existing or not being readable by the current user, an
29 * appropriate error message should be printed.
30 */
31 public void echo() {
32 if (file.exists() && file.canRead()) {
33 try {
34 Scanner echoScanner = new Scanner(file);
35 String line;
36 while (echoScanner.hasNextLine()) {
37 line = echoScanner.nextLine();
38 System.out.println(line);
39 }
10.5. READING FILES 293
40 echoScanner.close();
41 } catch (FileNotFoundException e) {
If the file exists (as in ExistsFile, file is a field which was initialized in the constructor) and the file is
readable, then we go into the body of the if statement. If either of the conditions is not met, then we print an
error message and return from the method.
We will now look at lines 25-36 at two levels of abstraction. First we will look at how a file is opened and
read line by line. Assuming all goes well, lines 26-32 run and the contents of the file are copied onto the screen.
Line 26 creates a Scanner associated with file. A Scanner is an internal Java representation of a text file. The
Scanner keeps track of a current position inside the file it is reading. As values are read from the Scanner, the
current position moves through the input file; a Scanner can read the next word or line and can be queried to
see if there is anything more to read at the current position.
Thus the while loop on lines 28-31 is a sentinel-controlled loop which runs until there are no more lines
of input to be read. Each time through the body of the loop, line is set to the contents of the next line in the
file the Scanner is scanning and then line is printed to standard output.
This loop is a special sentinel-controlled loop, an end-of-file-controlled (eof-controlled) loop. The hasNext and
hasNextLine methods return true so long as the current position in the file is not at the end of the file. Thus
the loop ends when the input file is exhausted.
Assuming pets.txt contains the names of one author’s family pets, the output of running this program
would be:
~/Chapter10% java -classpath .:/usr/lib/jvm/fang.jar EchoFile pets.txt
Explorer
Frodo
Grace Murray Hopper
What could go wrong? When associating a Scanner with a file in the file system many things could go wrong:
the file might not exist (because some folder in the path name does not exist or the file itself does not exist); the
file might be protected so only authorized users can read it (imagine a password file); even if the file exists and
is readable, something could change while the program is running which changes things (a different program
could delete or replace the file).
Imagine that every call to next had to be followed with a check to see if there were any errors that the
Scanner knew about. Then, after you see that that just about doubles the size of this loop, imagine a similar
if statement for the constructor call and another to make sure close worked.
There would be a lot of if statements. So many, in fact, that a lot of programmers would skip putting them
in: most of the time, if the file exists, reading the whole thing finishes with no problems. So if we just check
on the constructor, then we are “safe”. Of course we aren’t really safe; we just get away with ignoring errors
most of the time. When errors happen, our program crashes with ugly error messages.
Many older programming languages used a system similar to this and the errors were routinely ignored.
To avoid this and to keep the code easy to read, Java implements exceptions. An exception is a special signal
which Java can throw. In reality it is just another object type. When a method can throw an exception the Java
compiler requires the programmer to wrap the call to that method in a try...catch block. The try part is
executed. If any problem occurs, an exception is thrown and the catch block runs with the parameter set to
the exception thrown.
Thus if the file named does not exist, the Scanner constructor will throw an exception, one specifying that
the file does not exist. We specifically catch that kind of exception on line 33 and the block on lines 33-36 will
execute with e set to the exception thrown.
The printStackTrace method of exceptions will print out where the error occurred (by giving what Java
methods were executing when it occurred). Robustly dealing with exceptions is very sophisticated; this book
will catch them and, in general, end the current task or, if nothing more can be done, end the program.
294 CHAPTER 10. SCANNER AND STRING: CHARACTER INPUT
The other difference is that the TextFileByWord object has a larger public interface: the default construc-
tor, the listLoadFromFile method (which now takes the file name), a listPrint method, and a listSize
method. This is an attempt to make sure each method has a single, well-defined purpose. While the printing
could have been included directly in the loadListFromFile method, it would have made using the same code
in a program that did not print out the contents impossible (or require a new and different load method).
Single purpose methods mean we can combine them as we see fit in the order we choose to call them.
listSize and listPrint are fairly obvious, being based on code we saw when we started working with
single dimensional ArrayLists:
17 /**
57 System.out.println(strings.get(i));
58 }
59 }
60
61 /**
68 }
69
70 /**
Given that strings refers to an ArrayList<String> (which it does because one is assigned to it in the
constructor (not shown) and the value is reassigned with a file is read (just below)), the listSize method just
returns the size of the list and the listPrint method prints out the contents of the list. Again, it would have
been possible to have the marker lines (lines 88 and 90 in main) as part of listPrint; why is that a bad idea?
Finally we get to the meat of the program, listLoadFromFile. This code is based on that seen in echoFile
earlier in this chapter.
32 if (file.exists() && file.canRead()) {
33 try {
34 ArrayList<String> localStrings = new ArrayList<String>();
35 Scanner scanner = new Scanner(file);
36 String word;
37 while (scanner.hasNext()) {
38 word = scanner.next();
39 localStrings.add(word);
40 }
41 strings = localStrings;
42 scanner.close();
43 } catch (FileNotFoundException e) {
44 System.out.println(”PANIC: This should never happen!”);
45 e.printStackTrace();
46 }
47 } else {
48 System.out.println(”Unable to open \”” + fname + ”\” for input”);
49 }
50 }
51
52 /**
The method creates a File and then creates a Scanner associated with that file (if the file exists and can
be read). The try...catch construct is just as we have used it before. Assuming the loop finishes (without an
error), the scanner is closed as all files should be closed and, because there was no error, the strings field is
set to refer to the newly filled in list.
Notice that to read word-by-word, the Scanner methods hasNext and next are used to check for and read
the next token. By default, Scanner breaks its input up on whitespace; thus each value returned by next is a
string of non-whitespace characters. The output of this program, when run on pets.txt is:
~/Chapter10% java -classpath .:/usr/lib/jvm/fang.jar \
TextFileByWord pets.txt
----- listPrint -----
Explorer
Frodo
Grace
Murray
Hopper
----- listPrint -----
There were 5 words in pets.txt
Why five when there are only three pets? Because words are separated by whitespace, the last pet name,
“Grace Murray Hopper” is parsed as three separate tokens. Looking further up at the echoed content of the
file, there are five separate words, three on the third line. Thus this output matches what we would expect.
296 CHAPTER 10. SCANNER AND STRING: CHARACTER INPUT
eins 2 THREE IV
Key things to note about the contents of the file: there is a mix of “numbers” and “words”. But the Scanner
uses whitespace to mark out words. Thus there are only words, some of which have digits as characters. How
many words are there in numbers.txt?
~/Chapter10% java -classpath .:/usr/lib/jvm/fang.jar \
TextFileByWord numbers.txt
----- listPrint -----
100
101
3.1415
1000000
eins
2
THREE
IV
----- listPrint -----
There were 8 words in numbers.txt
It is worth noting that Scanner does have nextInt and nextDouble, both capable of reading text represen-
tation of a number and converting it to an int or double with the appropriate value; we will work with them
in the following chapters.
47 } else {
48 System.out.println(”Unable to open \”” + fname + ”\” for input”);
49 }
50 }
51
52 /**
eins 2 THREE IV
----- listPrint -----
There were 5 lines in numbers.txt
The key things to note are that “Grace Murray Hopper” is treated as a single line (the line was read including
the embedded whitespace) and the fourth line in numbers.txt, a blank line, is read and treated as a line. Calling
nextLine reads from the current file position to the end of the current line. The end of a line is marked by a
special sequence of characters.
Among the characters in the ASCII sequence are non-printing control characters. One is the null character,
’0’. Two others are the carriage return character, ’r’, and the new line character, ’n’. Different operating
systems use different combinations of carriage return and new line characters to mark the end of the line11 .
The Scanner uses the native version of end-of-line marker for the system on which it is running.
The combination of try...catch and the eof-controlled while loop using a Scanner is a very important
Java idiom. Many program assignments in the remainder of this book call for reading a file of a given format.
It is worth practicing with this construct until it becomes comfortable.
160 /**
If the list of strings is null (it has never been initialized) or it is empty, then we need to load the list from
a file. The listLoadFromFile method is similar to that in TextFileByLine. It fills strings. When strings is
not empty, pick a random index into it and remove the given entry. remove returns the value that is being
removed so it is exactly what we want to return from this method.
168 if (args.size() > 0) {
169 return args.get(0);
170 } else {
171 return null;
172 }
173 }
174
175 /**
185 if (fname != null) {
186 File file = new File(fname);
187 if (file.exists() && file.canRead()) {
188 try {
189 ArrayList<String> localStrings = new ArrayList<String>();
190 Scanner scanner = new Scanner(file);
191 String line;
192 while (scanner.hasNextLine()) {
193 line = scanner.nextLine();
194 localStrings.add(line.toLowerCase());
195 }
196 strings = localStrings;
10.6. FINISHING HANGMAN 299
197 scanner.close();
198 } catch (FileNotFoundException e) {
199 System.out.println(”PANIC: This should never happen!”);
200 e.printStackTrace();
201 }
202 }
203 }
204 if (strings == null) {
205 System.out.println(”There was an error reading the word file.”);
206 System.out.println(
207 ” Make sure to include the path to a word file.”);
208 System.out.println(” Make sure file exists and is readable.”);
209 System.exit(1);
210 }
211 }
212 }
The getFilename method checks for unnamed command-line arguments. If there are any, the first is
treated as the name of the word file and is the value returned by the method. If there are no unnamed
command-line arguments, then the method returns null. This is noted in the header comment; because this
value is passed directly to listLoadFromFile, that method must be able to handle a null file name.
The listLoadFromFile method checks whether the parameter is non-null. If it is, then the list loading
code matches that seen TextFileByLine except for line 196: each phrase in the word list is forced to lower
case. This is because the AlphabetSelector has no shift key.
Line 186 sets strings to null. If all goes well, it will no longer be null at line 206. If something goes wrong,
it will be null. If it is null at line 206, the game cannot be played. A message is printed to standard output
and the program terminates. System.exit is a method that ends a running application. The number passed
to it is returned to the operating system as the program’s exit code. By convention, a 0 return code means
there were no problems and non-zero return codes mean a non-standard exit. Thus code 1 means there was
an error.
The only remaining tricky code is in the AlphabetSelector.
113 /**
selectedChar returns null if the click passed in to the method is null or if the click does not intersect any
letter sprite. If the click is non-null then each sprite in the two-dimensional list is checked to see if it intersects
the mouse click and the sprite is in the ready state. A ready, intersected sprite is assigned to selected, the
value returned at the end of the method; if it were possible for more than one ready sprite to intersect the
same location, then the last one (in terms of indexes into the two lists) would be the value returned.
selectedChar is overloaded. The header of the one shown here takes a Location2D, the FANG class repre-
senting two-dimensional points (such as where a mouse has been clicked). The other overloaded version has
an empty parameter list.
75 Location2D outsideClick = Game.getCurrentGame().getClick2D();
76 if (outsideClick != null) {
77 Location2D insideClick = getFrameLocation(outsideClick);
78 LetterSprite letterSprite = selectedChar(insideClick);
79 if (letterSprite != null) {
80 letterSprite.startUsed();
81 letter = letterSprite.getLetter();
82 }
83 }
84 return letter;
85 }
86
87 /**
Line 77 calls getClick2D on the current game. If the value is non-null, then it is translated from
the game’s coordinates to the CompositeSprite’s coordinates. Line 79 uses the CompositeSprite method
getFrameLocation. The method takes a Location2D relative to the origin in the game with (0.0, 0.0) in the
upper-left corner and transforms it into the coordinate system of the sprite where (0.0, 0.0) is located at the
center of the sprite.
The transformation also takes into account rotation and scaling. This is why the parameter passed to
selectedChar(Location2D) is called insideClick, to make sure to other programmers that the location must
be in sprite coordinates.
If the value returned from selectedChar(Location2D) is non-null then the LetterSprite is set to the used
state and the letter represented on the letter sprite is returned as the value of selectedChar().
10.7 Summary
Java Templates
Programming Problems
We know half of what we need to know about standard console input and output. To date we have used
System.out to print values to standard output. This chapter addresses the missing half by introducing how we
read information from standard input. With both standard input and standard output, we can write a game
which does not use FANG at all.
303
304 CHAPTER 11. CONSOLE I/O: GAMES WITHOUT FANG
• Update the state of the game by either rolling again or holding the total for the turn
Which player has control alternates by turn. When a player holds or rolls a pig the state of the game
is updated and the next player’s turn begins. Notice that each time around all of the players the game also
displays the current standings (in the order in which players take their turns). When a player holds and puts
their score over 100, the game is over and the standings are printed one last time, this time sorted in descending
order by score.
The remainder of this section focuses on the design of the classes of Pig. Then the chapter introduces pure
console I/O and the idea of sorting a collection.
Information Hiding
Why is the Pig constructor private? Because it can be. Another principle of software engineering is the Need
to Know principle: limit the scope and visibility of all fields and methods as much as possible. If the program
will work with a method declared to be private, declare the method to be private; if it works with protected,
use protected rather than public. The point is to prefer the most restrictive visibility possible.
The Pig constructor can be private because it is only called in Pig.main (the main method declared inside
the Pig class). That is, the only call to the constructor is in a method declared in the same class. Thus a private
constructor is possible.
Limited visibility is desirable for two reasons, both related to design and, more importantly, redesign of
the class. Changing the header of a private method or the type of a private field can only impact the single
Java file in which the change is made. That is, no other file need be edited or even recompiled. Isolating change
like this makes changes much less complex.
Similarly, any special requirements of our class, say that the list of players never be null or some such,
is documented in the Java file implementing the class and private fields and methods can only be modified
by changing the source code containing them. This means the programmer should be working at the level of
abstraction represented by the class when working on the private parts. All non-static methods of Pig are
declared to be private for this reason.
What does play do? Looking at the design, there are three phases of the game: getting the players’ names,
playing the game, and finishing the game. The first and last phases are not game loops. Getting the players’
names could be wrapped up in a method of its own as could announcing the winner of the game (that is what
happens when the game ends). Playing the game is, itself, a video game loop. The extended design is:
public class Pig {
public static void main(String args[])...
private Pig()...
Five methods take a Player object reference as a parameter and a sixth returns a Player object reference.
What is a Player? We will design the class below. For the moment a Player holds all the information necessary
to identify a player and know how they are doing in the game.
The three video game loop methods take the current player so that they can tell whose turn it is. showState
shows the player’s current score and, if this is the first player, shows the standings of all players. It is possible
to show the standings before each player’s turn but it seems to make more sense to show them once per round.
The handleUserTurn method has a player take a turn. Consider that taking a turn involves showing the
player the state of their turn, letting them decide if they wish to continue, and then updating the state of their
turn. Thus this “get user input” portion of the video game loop of Pig invokes another video game loop.
306 CHAPTER 11. CONSOLE I/O: GAMES WITHOUT FANG
The continueGame method updates game state and returns true if the game should continue and false if
the game is over. Pig ends when one player wins the game. Only one player’s score can change during any
given turn (the current player), so only one player needs to be checked if they won. In some games you must
traverse the list of players in order to check if anyone has won.
The getPlayer and indexOf methods are for working with the list of Player objects. getPlayer takes an
index into the list of Players and returns the Player object associated with that location. The indexOf method
is the inverse function taking a Player in the list of Players and returning the index where that Player is. The
name indexOf is a Java standard name for methods which convert from an element to its index (we have seen
String.indexOf for finding the index of a String or char in a String, for example).
Now, what is a Player? Imagine that you were keeping track of an ongoing game of Pig with pencil and
paper. What information would you keep track of? You need to know whose turn it is (that is the turn number
passed to the various methods in Pig), the name and the score for each person playing.
When you find the need to group information together into a unit, that is when you should think of creating
a class. It would be possible to design Pig without having a Player class but keeping the score and the name
together in a single object makes life much simpler. What does the interface for the Player class look like?
public class Player {
public Player(String name)...
public String getName()...
public int getScore()...
public void takeTurn()...
public String toString()...
}
When announcing the winning player, Pig needs access to the name and the score of the winning player.
That is why the two get* methods are provided. takeTurn is the method which gets input from the user. As
the name suggests, it actually does much more: it rolls the die, tracking the player’s turn score, and, when
the player chooses to hold, updates the player’s score. It also handles the player rolling a pig and ending their
turn without adding to their score. It is, internally, another, smaller video game loop.
The toString method is a method declared in Object, the class which all Java classes extend. If a class lists
no parent classes, then they implicitly extend Object. The toString method returns a String representation
of the object on which it is called. This permits print/println methods to print out any object. When a
reference to an object is passed to print (as in System.out.print), the object’s toString method is called and
the result is printed to standard output.
This is an example of polymorphism, having many forms. The method has the header
public void print(Object obj) and internally it calls obj.toString(). Java calls the lowest overriding
implementation of a method so if obj is really a Player, then the overridden toString is called.
Player somePlayer;
...
somePlayer = new Player(”ralph”);
...
System.out.print(somePlayer);
...
In the above code we see that the dynamic type of somePlayer is the same as the static type of somePlayer. It
is not always this easy to determine the dynamic type of a variable or parameter. Consider the code for print:
public void print(Object obj) {
...
String someString = obj.toString();
...
}
What is the dynamic type of obj? It depends on what actual parameter is matched with the formal parame-
ter obj. In the previous listing, print is called with somePlayer. In that case the dynamic type of obj is Player
and the static type of obj is still Object.
Why do we care? Because overriding methods permits polymorphic behavior, behavior where the same
code (that inside of print) behaves differently depending on the dynamic type of the objects on which it oper-
ates due to overriding of methods (toString in this example).
2 The “Hello, World!” program is a short text program presented in almost every introduction to a computer programming language.
It derives from a sample in Kernighan and Richie’s 1974 book The C Programming Language[KR78]. The original actually printed “hello,
world” without capitals or the exclamation point. The C programming language was developed to implement the original Unix operating
system (which, through many twists and turns, begat the Mac OSX and Linux operating systems).
308 CHAPTER 11. CONSOLE I/O: GAMES WITHOUT FANG
Notice that the method comment for main explicitly states that the parameter, args is ignored by the
method. It is, in almost all cases, bad form to have unused parameters. According to a study TK referenced
in Code Complete, there is a positive correlation between unused parameters in the parameter list and bugs in
the method. main is the primary exception to the admonition against having unused method parameters: the
main method header must match that in the Java language definition in order for the Java interpreter to be
able to start the program.
The next program shows two new things: System.in is an object much like System.out but for standard
input (the keyboard) rather than output and Scanner has a constructor that takes an InputStream. As we first
saw in Chapter 10, a Scanner can take apart the text in an input file. In this case the values typed on the
keyboard are treated as the values found in the file.
1 import java.util.Scanner;
2
3 /**
4 * Greeting program. Standard output for a non-FANG console I/O program;
5 * user provides their name at a prompt.
6 */
7 public class YourNameHere {
8 /**
9 * The main program. Prompt user for their name and print a greeting
10 * for them.
11 *
12 * @param args command-line arguments; ignored by this program
13 */
14 public static void main(String[] args) {
15 Scanner keyboard = new Scanner(System.in);
16 System.out.print(”What is your name? ”);
17 String userName = keyboard.next();
18 System.out.println(”Hello, ” + userName + ”!”);
19 }
20 }
Line 18 prints a prompt for the user. It is good form to make sure the user knows what to do next (this is
important in text programs just as it is with games). Line 19 uses Scanner.next to read a line from the input
associated with the Scanner called keyboard. Since System.in is, by default, associated with the keyboard,
the program halts, waiting for the Scanner to finish reading the input. Then line 20 prints out a greeting
customized for the named user. An example run of the program could look like this:
~/Chapter11% java YourNameHere
What is your name? Dr. Marcus Welby
Hello, Dr.!
There is something wrong. The program paused until the user pressed the <Enter> key but it only read
one word. Why is that?
Looking back at line 19, the next method is used. By default, next parses the input stream into tokens where
a token is defined as a sequence of non-whitespace characters separated by whitespace characters.
When you call next, the Scanner goes down to the input stream to which it is attached (more on how
this corresponds to disk files below) and asks for some number of characters. It will read characters,
skipping over all whitespace (actual space characters, tabs, and end-of-line characters; anything for which
11.2. PURE CONSOLE I/O 309
Character.isWhitespace returns true). Then, once it sees a non-whitespace character, it keeps reading char-
acters until a whitespace character is found (and the whitespace character is unread so that the file read pointer
will reread the character the next time the file is read).
One thing to note about using Scanner with the keyboard: on most operating systems the standard config-
uration has characters delivered to Java for reading line by line. That is, even though the user typed the four
characters “Dr. ” as the first four characters of the line of input, Java will not see them until <Enter> is pressed
at the end of the line.
There is no universal way to change console input from line-based to character-based mode from Java.
Explaining operating system and shell specific methods is beyond the scope of this text so we will assume that
console input is in line mode for the remainder of the book.
How can we change YourNameHere so that it prints Dr. Welby’s whole name? To read a whole line with
a Scanner from a text file we use nextLine. That works with a Scanner wrapped around standard input just
as it does when reading a file. With line 19 changed (and the program renamed to YourWholeNameHere.java,
available in this chapter’s sample code) to use nextLine the earlier console session would look like this:
~/Chapter11% java YourWholeNameHere
What is your name? Dr. Marcus Welby
Hello, Dr. Marcus Welby!
3 /**
4 * Prompt user for two integers. Print the sum of the two integers.
5 * Program then halts.
6 */
7 public class AddTwoNumbers {
8 /**
9 * The main program. Prompt user for two integers and add them
10 * together. DOES NOT WORK AS EXPECTED!
11 *
12 * @param args command-line arguments; ignored by this program
13 */
14 public static void main(String[] args) {
15 System.out.println(”AddTwoNumbers:”);
16 Scanner keyboard = new Scanner(System.in);
17
18 System.out.print(”Number: ”);
19 int first = keyboard.nextInt();
20
21 System.out.print(”Number: ”);
22 int second = keyboard.nextInt();
23
What is wrong with the code? The sum of two 3 digit numbers should never be a 6 digit number. Looking at
the code, printing the program identifier and the prompts should have no effect on the final sum. The nextInt
calls also look right. In fact, looking at the output for a moment, “100121” = “100” + “121”. That is, looking at
line 27, the + operator is being treated not as integer addition but as String concatenation.
Whenever the compiler can determine that the element to the left of a + is a String, then the plus sign
means concatenation. Thus the + at the end of line 26 is a concatenation operator; first is converted to a
11.2. PURE CONSOLE I/O 311
String and tacked onto the end of the String to print. That means the + on line 27 is also interpreted as a
concatenation operator.
We need to convince Java to do integer addition of first + second before applying the concatenation op-
erator and building the output string. When we want to change the order of evaluation in an expression, we
use parentheses. Wrapping parentheses around (first + second) on line 27 yields the following:
~/Chapter11% java AddTwoNumbersRight
AddTwoNumbersRight:
Number: 100
Number: 121
Sum of 100 + 121 = 221
Again, AddTwoNumbersRight.java is included in the chapter’s sample code. It differs from the previous
listing only in the parentheses in line 27.
11 /**
12 * Get a line from the user. Prints the prompt on the console followed
13 * by a space. Then waits for user to enter a line and returns the
14 * full line of text to the calling method.
15 *
16 * @param prompt the prompt to print for the user.
17 *
18 * @return the line entered by the user (everything up to but not
19 * including the <return> key)
20 */
21 public static String getLine(String prompt) {
22 System.out.print(prompt);
23 System.out.print(” ”);
24 return keyboard.nextLine();
25 }
26
27 /**
28 * Main program. Uses getLine to prompt user and read lines in a
29 * sentinel controlled loop. User enters the sentinel value ”done”
312 CHAPTER 11. CONSOLE I/O: GAMES WITHOUT FANG
30 * when they want to quit. All other lines are converted to upper-case
31 * and echoed back.
32 *
33 * @param args command-line arguments - ignored by this program
34 */
35 public static void main(String[] args) {
36 String line = ””;
37 while (!line.equalsIgnoreCase(”done”)) {
38 line = getLine(”Line to capitalize (’done’ to finish):”);
39 if (!line.equalsIgnoreCase(”done”)) {
40 System.out.println(line.toUpperCase());
41 }
42 }
43 }
44 }
The method, getLine, defined in lines 21-25, does exactly what we want: given a prompt it shows the
prompt to the user (and even appends a space on the end) and then returns the next line of text typed by the
user.
Why is keyboard not defined inside of getLine? And what does line 9 actually mean? The keyboard Scanner
is opened on standard input and, it is hoped, it can be used to read all user input it is necessary to read. Since
we expect to call getLine over and over, it does not make sense to construct a new Scanner to read the next
line and then the next line and so on. It makes much more sense to read everything from one Scanner which
is shared by all read routines.
Since getLine is called from main and main is static, getLine must be static. As mentioned in Chapter 6,
the static qualifier is infectious: methods and fields accessed directly from static methods must be static.
The “and fields” part is new. It explains why keyboard must be declared static. Why does line 9 have
an assignment directly in the line where the field is declared? Because Java permits all fields (just like local
variables) to be initialized when they are declared. This book does not (and will not) use the declare and
initialize syntax on regular fields because it can be very confusing to understand when the initialization takes
place4 . A static field must be initialized this way if the initialization is to take place before any other code in
the class.
That means the call to new in line 9 takes place before main is called by Java. We will use the in-line initial-
ization syntax for all static fields and only for static fields.
After all of that, main is somewhat anticlimactic. The field line is initialized to the empty string and so
long as it is not the word “done” the loop reads a line from the user (using getLine) and if the line is not “done”
then the line is echoed in upper case.
What happened to the DRY principle? Looking at lines 37 and 39 the same test is done twice in very rapid
succession. Why? The problem is know in computer science as the “loop and a half” problem. The problem
is where, in the loop, to read a value into line. If we read the value in first thing, as we do here, then there
is a problem with the last time through the loop. After having read “done”, we should not execute the body
of the loop. Thus there must be an if statement and it must have the same Boolean expression as the while
statement.
Alternatively we could move the reading of the value from the keyboard from the beginning to the end
of the loop. That would mean that the last line would be read and then the loop would cycle back to the top,
4 Non-static fields have values assigned just before the constructor begins execution. It is not that hard to explain when but it is
much clearer to have all initialization of fields explicitly written in the constructor so the programmer can see the order of execution
and know all starting values.
11.3. SORTING A COLLECTION 313
the Boolean expression would return false and the body of the loop would not execute. The following code
shows the idea:
35 public static void main(String[] args) {
36 String line = getLine(”Line to capitalize (’done’ to finish):”);
37 while (!line.equalsIgnoreCase(”done”)) {
38 System.out.println(line.toUpperCase());
39 line = getLine(”Line to capitalize (’done’ to finish):”);
40 }
41 }
We have moved the problem from the last line of input to the first line of input. The first time through
the body of the loop what value should line have. That is, in line 36, what do we initialize it to? The only real
answer is to duplicate the code from line 39 in line 36 as well. So we can choose to have to handle an extra half
of a loop at the beginning or the end of the input.
Assuming the first step in the loop works, the algorithm works (each card added to the pile is the largest
remaining so the pile is built from biggest to smallest) and the algorithm must eventually halt (there are fewer
cards in the hand each time through the loop; the hand must eventually be empty).
The only problem is the first step in the algorithm: it is too complicated. We need to describe, in explicit
detail, how to find the largest card in the hand.
pick up the deck
while your hand is not empty
// ----- find the largest card in your hand
assume top card is largest
for each card below the top
if the current card is larger than the largest so far
314 CHAPTER 11. CONSOLE I/O: GAMES WITHOUT FANG
So, this algorithm is now expressed in terms of looking at a card, comparing its value to that of one other
card, keeping track of a location in the hand, and swapping a pair of cards. These are pretty much fundamental
steps in handling a deck of cards.
This algorithm is not limited to working with cards in a deck. Given an ArrayList of objects, we can get
any value by index, keep track of a location by keeping its index, and, using get and set, we can swap values
in the ArrayList. The only thing that is at all challenging is knowing how to order two objects. We will look at
how to do that by comparing Integer objects using greater than, comparing other objects just by comparing
one of their fields, and how to use compareTo with String.
1 import java.util.ArrayList;
2 import java.util.Arrays;
3
4 /**
5 * Initialize a literal ArrayList, print it, then sort it and print it
6 * again. The three method sort matches the example algorithm in SCG
7 * chapter 10.
8 */
9 public class SortIntegers {
10 /**
11 * Initialize a list of {@link Integer} values and sort them using an
12 * insertion sort (find largest remaining, put it in the right spot).
13 *
14 * @param args command-line arguments ignored by this program
15 */
16 public static void main(String[] args) {
17 System.out.println(”SortIntegers:”);
18 ArrayList<Integer> theInts =
19 new ArrayList<Integer>(Arrays.asList(9, 4, 2, 8, 3, 17, 10, 15));
20
21 System.out.println(”Before:”);
22 System.out.println(theInts);
23
24 sort(theInts);
25
26 System.out.println(”After:”);
27 System.out.println(theInts);
28 }
29
30 /**
31 * Find the index of the largest value inside of aList at or after the
32 * given starting index
33 *
11.3. SORTING A COLLECTION 315
55 /**
56 * Sort aList in descending order. Uses {@link
57 * #largestIndex(ArrayList, int)} and {@link
58 * #swap(ArrayList, int, int)} to do much of the work.
59 *
60 * @param aList the list to sort
61 */
62 private static void sort(ArrayList<Integer> aList) {
63 for (int firstUnsortedIndex = 0;
64 firstUnsortedIndex != aList.size();
65 ++firstUnsortedIndex) {
66 int largestIndex = largestIndex(aList, firstUnsortedIndex);
67 swap(aList, firstUnsortedIndex, largestIndex);
68 }
69 }
70
71 /**
72 * Swap elements aList[a] and aList[b] (using array notation). Works
73 * for all valid index value for a and b (even if they are equal).
74 * Does not do anything crafty when they are equal.
75 *
76 * @param aList list in which elements should be changed
77 * @param a an index into aList
78 * @param b an index into aList
79 */
80 private static void swap(ArrayList<Integer> aList, int a,
81 int b) {
82 Integer temp = aList.get(a);
83 aList.set(a, aList.get(b));
316 CHAPTER 11. CONSOLE I/O: GAMES WITHOUT FANG
84 aList.set(b, temp);
85 }
86 }
In SortIntegers, lines 19-20 initialize the variable theInts with a literal list. A literal value is one typed
into the source code. The java.utils.Arrays package contains static methods for working with arrays. The
one used here takes an arbitrary number of arguments and returns a List containing the right element type.
The ArrayList constructor can take a list and copy all the elements into the newly created ArrayList.
Line 24 (and line 29) print out the contents of theInts. Because ArrayList overrides toString in a sensible
way (it calls toString on all the elements in the list, wrapping them in square brackets and separating them
with commas), we can just pass the object to println. The output of this program is:
~/Chapter11% java SortIntegers
SortIntegers:
Before:
[9, 4, 2, 8, 3, 17, 10, 15]
After:
[17, 15, 10, 9, 8, 4, 3, 2]
The sort method, lines 64-71 follows the algorithm given above except that rather than placing numbers
on the table all numbers are kept inside the array. The front part of the array is sorted (from largest to
smallest) and the back part of the array remains to be sorted. Each time through the loop in sort, one more
element from the unsorted part is moved to its right position and the sorted part of the array is extended by
one. Before the loop runs at all, 0 elements are known to be in sorted order; after performing the loop n times
(where n is the number of elements in the array), each iteration adding one element to the sorted region, all
n elements are in sorted order.
The sort method uses firstUnsortedIndex to keep track of the index (in the list) of the first element in
the unsorted region. Thus at the beginning of the method firstUnsortedIndex is set to 0 (all elements are un-
sorted). The body of the loop finds the index of largest element in the unsorted region by calling largestIndex.
The second parameter to largestIndex tells the method where to start looking for the largest element; by
starting at firstUnsortedIndex it will find the largest element in the unsorted region. Knowing the index of
the largest element in the unsorted region, we just swap that value with the value first value in the unsorted
region; the largest element is now in the right place and we can move firstUnsortedIndex forward by one.
Figure 11.2 shows the sorting of this particular list visually. The left column shows the situation just before
the call to swap at line 69 in the sort method. The pink region of the list is unsorted, the triangle atop the list
is the index of the first unsorted value, and the red triangle below the list is the index of the largest element
in the unsorted region.
Looking from the first list to the second list, you can see that the element indexed by the red triangle was
swapped with that indexed by the white triangle in the first list diagram. Thus the values 17 and 9 were swapped.
That put 17 in its sorted place in the list; the first location is sorted, the rest of the list remaining unsorted.
The right column shows what happens inside of largestIndex the first time through the loop. The white
triangle is the startIndex parameter. The red triangle represents the largestNdx variable; on line 46 it is
initialized to be the startIndex. The loop control variable, contenderNdx, represented in the drawing by the
red question mark, cycles through all the values from one more than the startIndex through the last valid
entry. Each time through the loop, the value of the element indexed by contenderNdx and the value of the
element indexed by largestNdx are compared.
If the contender is larger than the largest, largest must be updated. You can see that the red triangle
(largestNdx) moves when 17 is compared with 9. Finally, after contenderNdx is past the end of the list, we
11.3. SORTING A COLLECTION 317
Largest In ner ?
Lo o p 9 4 2 8 3 17 10 5
17 4 2 8 3 9 10 5 ?
9 4 2 8 3 17 10 5
?
17 10 2 8 3 9 4 5 9 4 2 8 3 17 10 5
?
9 4 2 8 3 17 10 5
17 10 9 8 3 2 4 5
?
9 4 2 8 3 17 10 5
17 10 9 8 3 2 4 5 ?
9 4 2 8 3 17 10 5
17 10 9 8 5 2 4 3
17 10 9 8 5 4 2 3
17 10 9 8 5 4 3 2
know that largestNdx really is the index of the largest value at or to the right of startIndex so it is the value
returned by the method (line 54).5
Note that in line 50 we use > between two Integer objects. Earlier it was mentioned that direct comparison
between objects was not supported (and even equality tests only test for exact reference identity). Why does
this compile and run? Since version Java version 1.5, Integer and the other object wrappers for plain-old
data types automatically convert themselves to plain-old data types with the right values when Java requires
plain-old data types. This is called autounboxing where the Integer object is considered a box around the int.
Java also supports autoboxing, the conversion of a POD type to its corresponding box type when necessary.
5 The drawings and approach were inspired by Chapter 11, “Sorting” of John Bentley’s Programming Pearls, 2E. [Ben00]. The book is
a collection of columns originally written for the Association of Computing Machinery’s Communications magazine. They are insightful,
readable software engineering case studies that most computer science students can understand.
318 CHAPTER 11. CONSOLE I/O: GAMES WITHOUT FANG
It depends on how we want to sort Players. If we sort in descending order, what makes one Player
larger than another? At the end of a game of Pig we want to sort the objects by their score. This means
in largestIndex we will compare the result of getScore() called on the two elements in the list rather than
directly comparing the objects. The following code is from Pig; the names of the methods were prefixed with
sp_ and slightly shortened so they would group together in the source code file and print more clearly. The
following three methods are the last three definitions in the Pig class (the comments have been elided in this
listing).
255 contenderNdx != standings.size();
256 ++contenderNdx) {
257 if (standings.get(contenderNdx).getScore() >
258 standings.get(largestNdx).getScore()) {
259 largestNdx = contenderNdx;
260 }
261 }
262 return largestNdx;
263 }
264
265 /**
266 * Sort the standings in descending order by score. firstUnsortedIndex
267 * indexes the first entry in the standings which is not yet sorted.
276 int largestUnsortedIndex =
277 sp_LargestIndex(standings, firstUnsortedIndex);
278 sp_Swap(standings, firstUnsortedIndex, largestUnsortedIndex);
279 }
280 }
281
282 /**
283 * Swap elements standings[a] and standings[b] (using array notation).
284 * Works for all valid index value for a and b (even if they are
295 }
296 }
Except for the change in the types and calling aList standings, sp_Sort and sp_Swap correspond exactly to
sort and swap in SortIntegers.java. The important thing to take away from the swap method is that swapping
two values (in a collection or not) requires a temporary variable to hold one of the values because as soon as a
new value is assigned to a variable the old value is gone. The type of the temporary variable must match the
type of the two values being swapped.
Even sp_LargestIndex is very similar to largestIndex in the integer program. Lines 264-265, where the
comparison between two scores is made, is different than line 50 (in part because it is longer and therefore
prints on two lines). In one case the two values returned by get are compared directly; in the other the values
returned by get are used to get a particular field on which the list is to be sorted.
11.3. SORTING A COLLECTION 319
22 System.out.println(”Before:”);
23 System.out.println(someAnimals);
24
25 sort(someAnimals);
26
27 System.out.println(”After:”);
28 System.out.println(someAnimals);
29 }
44 int startIndex) {
45 int largestNdx = startIndex;
46 for (int contenderNdx = startIndex + 1;
47 contenderNdx != aList.size(); ++contenderNdx) {
48 if (aList.get(contenderNdx).compareTo(aList.get(largestNdx)) > 0) {
49 largestNdx = contenderNdx;
50 }
51 }
52 return largestNdx;
53 }
54
55 /**
The type of the list, someAnimals, is an ArrayList of String. That means that the swap method (not shown)
has a temporary variable of type String. It also means that the > comparison we used in SortIntegers is not
available; autounboxing only works for classes that wrap POD types and a String does not have a correspond-
ing POD type.
Looking back to Section10.4, the compareTo method is how objects (other than POD wrappers) should be
compared. a.compareTo(b) returns a negative integer if String a comes before b in an ascending dictionary
sort order, 0 if they are equal String values, and a positive integer if a comes after b. We are sorting in
descending or reverse order so we want the largest or last in dictionary order. So if the contender compared
to the largest is positive, the contender comes after the largest or, in other words, is larger than the largest.
That is what line 50 does. A run of SortStrings looks like this:
~/Chapter11% java SortIntegers
SortSrrings:
Before:
[dog, cat, hamster, catfish, pig, aardvark, zebra]
After:
[zebra, pig, hamster, dog, catfish, cat, aardvark]
320 CHAPTER 11. CONSOLE I/O: GAMES WITHOUT FANG
51 /**
61 System.out.print(” ”);
62 return keyboard.nextLine();
63 }
64
65 /**
These methods are defined right before main in the body of the class. getLine is just as it was defined
earlier in the chapter.
The answersYes method is a Boolean method which returns true if the user answers yes to the prompted
question. The body of the method is a sentinel-controlled loop. It ignores all of the non-sentinel lines and
processes the final line outside the loop so we don’t have a loop-and-a-half problem.
The sentinel condition is userAnswer is equal to “yes” or “no” or “y” or “n” without regard to case. It is
possible to write all the possible upper/lowercase mixes for “yes” and “no” (8 and 4 possibilities, respectively;
generating them is left as an exercise for the interested reader) but Java provides a simpler solution. In ad-
dition to equals, the String class provides a equalsIgnoreCase method. It does what its name suggests: it
compares two strings permitting upper and lower case versions of the same letter to evaluate as the same.
To express the sentinel condition, we need to call equalsIgnoreCase four times and or together the results.
The sentinel-controlled loop is finished when:
userAnswer.equalsIgnoreCase(”y”) ||
userAnswer.equalsIgnoreCase(”yes”) ||
11.4. FINISHING PIG 321
userAnswer.equalsIgnoreCase(”n”) ||
userAnswer.equalsIgnoreCase(”no”)
To use the Boolean expression of the sentinel condition in the while loop, we need its logical inverse. We
need while (!<sentinel>) .... Or:
!(userAnswer.equalsIgnoreCase(”y”) ||
userAnswer.equalsIgnoreCase(”yes”) ||
userAnswer.equalsIgnoreCase(”n”) ||
userAnswer.equalsIgnoreCase(”no”))
The expression makes sense: it is the inverse of the sentinel condition. What if having ! in front of such a
long Boolean expression makes you uncomfortable. Would it be possible to use DeMorgan’s rules to distribute
the ! into the expression? Yes. Remember that the rules say || goes to && and each subexpression is inverted:
!userAnswer.equalsIgnoreCase(”y”) &&
!userAnswer.equalsIgnoreCase(”yes”) &&
!userAnswer.equalsIgnoreCase(”n”) &&
!userAnswer.equalsIgnoreCase(”no”)
This is exactly the expression in the while loop’s Boolean expression on lines 43-46. Breaking it up on the
operators (and keeping the parts on each line at the same level) makes it easier for a programmer to follow.
It also keeps the lines short enough to print in the book.
The body of the loop prompts the user for a line of input and reads a line from keyboard. Rather than
repeat that code from the body of getLine, the loop just calls getLine.
Line 50 is only reached if the sentinel condition is true (the inverse of the sentinel is false). That means
userAnswer is one of the four values. The user answered yes if it is equal to ”y” or ”yes”. We could test for
either of these with an || in a Boolean expression. It is also possible to note that the first character of each of
the affirmative answers is ”y”, extract the first character, and test if it is ”y”. There is an argument to be made
that this code is too clever (especially if it needs a comment to say what is happening) but it is clear enough
that it stayed in the game.
82 /**
As described earlier, this method borrows from FANG in that it constructs a new object of the type Pig
and then calls specific methods to have that object play the game. The getPlayersNames() method fills the
collection of Player objects (it will be detailed in the next section). isPlayable is a Boolean method which
checks to make sure there are enough players to play the game; at present it checks for at least 2 but it could
be changed to check for at least 1 if the game should support solitaire play. Finally, if there are enough players,
the play method is called; play is the game loop for Pig and will be detailed in the second following section.
322 CHAPTER 11. CONSOLE I/O: GAMES WITHOUT FANG
30 /**
121 /**
122 * Get the index of the {@link Player} p in the {@link #standings}
123 * list.
134 ndx = i;
135 }
136 }
137 return ndx;
138 }
139
140 /**
141 * Get player’s names from the user. A sentinel-controlled loop
142 * (sentinel: when user enters ”done” as a player’s name) uses {@link
151 if (!playerName.equalsIgnoreCase(”done”)) {
152 standings.add(new Player(playerName));
153 }
154 }
155 }
156
157 /**
158 * Get input from the user to complete their turn
159 *
175 }
176
177 /**
178 * Increment the turn variable; return the next turn number.
179 *
109 /**
110 * Get the n’th player in the list of players.
111 *
166 /**
167 * Is this game playable? Are there enough players?
168 *
202 while (playing) {
203 curr = getPlayer(turn);
204 showState(curr);
205 handlePlayerTurn(curr);
206 playing = continueGame(curr);
207 turn = nextTurn(turn);
208 }
209 announceWinner(curr);
210 }
211
212 /**
213 * Print the standings (player’s names and scores) in the order they
214 * are stored in {@link #standings}.
232 System.out.println(”Standings:”);
233 showStandings();
234 }
324 CHAPTER 11. CONSOLE I/O: GAMES WITHOUT FANG
239 /**
240 * Find the index of the largest score inside of standings at or after
241 * the given starting index
play and the three main game loop methods are shown together in Listing 11.12. Again, the methods are in
alphabetical order so play is near the middle. Each time through the loop, curr is set to the current Player by
calling getPlayer with the turn number. That Player is then passed to each of the three methods to show, get
input for, and update the state of the game. showState prints out the current player (the last two lines of the
method, lines 239-240). If it is the first player’s turn, it also prints out the complete standings. This is where
the indexOf method is called; notice again the use of a name for the boolean expression. Thus the reason for
the expression indexOf(curr) == 0 is documented right on line 233 and the reason for the if statement is
documented right in the next line.
The handlePlayerTurn method, lines 166-168, forwards the real work of getting input from the user to the
Player.takeTurn method.
99 // Could have gotten here for two different reasons; only update
100 // score if player held the points.
101 if (heldPoints) {
102 incrementScore(turnTotal);
103 }
104
108 /**
11.4. FINISHING PIG 325
The takeTurn method looks like a game loop itself. The loop is slightly skewed in that updating the state
happens at the beginning; if you rewrite it so that it is at the end you’ll find this loop has a loop-and-a-half
quality to it.
In the loop we roll one die and check if the player rolled a pig. Then the state of the turn is printed with
the number rolled shown to the player. If they didn’t roll pig then they are asked if they wish to hold. Note
that lines 98-99 are a call to Pig.answersYes. That public static method isolates interaction with standard
input which is why it has public visibility.
The loop finishes when the sentinel condition is reached: rolledPig || heldPoints. The inverse of the
sentinel condition is the Boolean expression in the while loop.
The sentinel combines two different reasons for exiting the loop. Line 103 makes sure the score is recorded
only if the player held their points. Finally, upon leaving the method it prints a short message letting the player
know how their turn went.
117 }
118
119 /**
When Pig needs to print information about a Player, it passes the object to println. As discussed above,
println calls the object’s toString method. Player overrides toString to return the name and the score of
the player.
After calling handlePlayerTurn in play, Pig calls continueGame with the current player. How doe a game
of Pig end? When someone wins by having a score higher than the required score. When do player’s scores
change? Only at the end of their own turns; it is not possible for a player to change the score of any other
player. Thus we continue to play so long as the current player has not won the game. Lines 109-110 determine
if the current player has won and return the inverse of that for continuing the game.
The only two things that Pig does that we have not looked at in detail are to print the whole standings
table and to announce the winner. Printing the standings table is just looping over the contents of standings,
passing each entry to println (using the toString override again). After the game loop ends (because someone
won), the winner is announced.
90 System.out.println(”Final Standings:”);
91 showStandings();
92 System.out.println(winner.getName() + ” wins with ” +
93 winner.getScore() + ” points.”);
94 }
95
96 /**
To announce the winner standings is sorted in descending order by score (sp_Sort was examined in the
previous section). Then the standings table is printed and a line identifying the winner is printed. The final
line is redundant but it is important for winners to feel recognized; it improves the feel of the game for most
players.
326 CHAPTER 11. CONSOLE I/O: GAMES WITHOUT FANG
11.5 Summary
Java Templates
Programming Problems
In previous chapters we have seen how to write console programs where we can treat standard input and
standard output as input and output files. We have also seen how to read information from a file so that a
single game can change its behavior without being recompiled. This chapter adds the finishing touches to
input and output with text files: we will see how to open output files on the disk and save information to the
file, information which can be read by the same or another program.
This chapter uses the same console-based input approach found in the previous chapter so that the pro-
grams written in this chapter do not use FANG. The game in this chapter is 20 Questions; we will build a
computer opponent which gets smarter and smarter the more it plays.
327
328 CHAPTER 12. MORE STREAMS: SEPARATING PROGRAMS AND DATA
Billy: “No.”
Anne: “Is it a mammal?”
Billy: “Yes.”
Anne: “Does it swim?”
Billy: “Yes.”
Anne: “Is it a whale?”
Billy: “No.”
Anne: “Is it a dolphin?”
Billy: “Yes.”
In nine questions Anne zeros in on the answer. Somewhat surprisingly, with twenty questions, if each
question divides the remaining elements of the domain in half, the questioner can differentiate between 220 =
1048576 different objects. It is literally possible to find one in a million.
How can we convert Twenty Questions into a computer game? Two possibilities jump out: make a two-
player game where the computer just provides a communications medium, have the computer play one of the
two roles in the game. A two-player version of Twenty Questions would have the “game” acting more like an
instant messaging program than an actual game; none of the rules of Twenty Questions would really end up
inside of the program.
What would the program need to do to play each of the roles? Looking back at Hangman, reading a data file
containing a collection of animals (or other domain objects) and selecting one at random is easy. Breaking
arbitrary yes-or-no questions down and interpreting them and then answering them correctly is far beyond
the scope of our current programming skills.
Alternatively, the file contains a collection of yes-or-no questions and guesses of domain objects (animals)
related to each sequence of “Yes” or “No” answers provided by the user. The structure of the file is more
complex than the data file we used in Hangman. Rather than a single line containing a single phrase, each item
stored in the file will cover multiple values stored across multiple lines. The exact format of the data file will
depend on the structure of the question and answer classes; there will be one line per field in the class. We
now digress in the process of designing the question and answer classes to examine an interesting variation
on how to read a book.
You are a small, hungry hobbit. You Unfortunately your cupboard is bare The roast goose is wonderful, crisp
can stay home or go off to Bree for and hobbits need their dinner on but still juicy. Your waiter is
roast goose. time. also quite good, and cute, too.
If you stay home goto page 5 You are dead. If you flirt goto page 12
If you sup at Bree goto page 8 else goto page 31
1 5 8
The two of you fall in love, are You grow old alone and become more
married in the hobbit fashion, and and more bitter. And you never
grow old together, raising a happy enjoy roast goose ever again.
family. And eating roast goose.
12 31
turning one or two pages ahead to see if the outcome of some branch of the adventure is good or bad for the
reader.
The story in the figure, such as it is, has three possible outcomes: you die of starvation, you grow old and
bitter alone, or you meet the love of your life and have a happy ending. Seeing all of the pages for all of the
paths through the book makes it obvious that this book is not “interactive” in the sense that it reacts to the
reader by changing due to previous actions. It is equally clear, however, that the story, from the point of view
of a reader an any given page, is interactive. If the reader is making meaningful choices, then this is a form of
a game by our working definition.
Why are we interested in a fifty-year old literary devise1 widely used in children’s books?
We are looking for a structure where we can store questions and answers in an easy to read manner (so
we can load it from a file). The structure must also encode the relationship between the questions and the
answers. That is, once the player tells us that their animal has four legs, the game should no longer guess that
the animal is a dog.
The choose your own adventure book provides us with such a structure: each page is either a decision point
for the user (a question) or an ending (an answer). The structure of the book is linear (the pages are printed
in a fixed order in the book or stored in a fixed order in an input file) so it is easy to read. The structure of the
story is encoded in the page numbers (indexes into an ArrayList) associated with each choice on a decision
page.
1 Raymond Queneau, a French novelist/poet pioneered the form with his 1959 Story As You Like It and Julio Cortázar, an Argentine au-
330 CHAPTER 12. MORE STREAMS: SEPARATING PROGRAMS AND DATA
Does the animal have 4 legs? bunny rabbit Does it have wings?
1 5 8
turkey dolphin
12 31
Figure 12.2 is identical to the previous figure except that the text in each page has been changed. Instead
of telling a story about a hobbit, the book now plays a game of twenty (or is it two?) questions. Notice that on
the white pages, let’s now refer to them as Question pages, the two choices are now universally labeled “Yes”
and “No”. Each label is then followed by a “goto page #” just as before. The pink pages, let’s refer to them as
Answer pages, now each hold just the name of a domain object. When processing an Answer page, we read the
text to be “Is it a(n) ...” where “...” is the name of a domain object.
To demonstrate playing the game we will pick an animal, begin at page 1, answer the questions and turn
to the indicated pages. First we pick one of the animals the book knows, a turkey.
The game progresses as follows: On page 1, “Does the animal have 4 legs?”; our answer is “No” so we turn
to page 8. On page 8, “Does it have wings?”; our answer is “Yes” so we turn to page 12. On page 12, “Is it a(n)
turkey?”; our answer is “Yes” so the game was won by the book.
Suppose we had selected a penguin instead of a turkey. The first two questions would go exactly as they
did before. It is only on page 12, when we are asked if we had chosen a turkey, would our answer change from
“Yes” to “No” and the book would be stumped.
If the book is immutable, that is its contents cannot be changed (as most printed books in the real world
are), we could win every time we played with the book by picking penguin. We won once and now know we
will win every time. In fact, this book is very limited and only knows three animals; all other animals stump
it.
thor, used it in his 1964 Hopscotch. Both of these experimental novels had great influence on the development of hypertext and interactive
fiction. See Chapter cha:TextAdventure for more.
12.1. OUTSMARTING THE PLAYER: 20 QUESTIONS 331
If the book is, instead, mutable, that is we can add new pages and modify existing pages, then the book can
be extended, made smarter. What if when we stumped it we were asked for two things: what we had chosen
and a question to tell the difference between the new and the wrong animal.
On page 12, “Is it a turkey?”; our answer is “No” so the book is stumped. “What is your object?”; “penguin”.
“What yes/no question would differentiate between ’turkey’ and ’penguin’?”; “Does it eat fish?”. “Which
answer means ’penguin’?”; “Yes”.
Now we add two pages to the end of the book. Assuming the book had 100 pages in it before, the new pages
are numbered 101 and 102. One is a question, “Does it eat fish?”, and the other is an answer, “penguin”. The
“Yes” direction from the new question goes to the new answer (remember that we asked) and “No” goes to the
wrong answer (page 12, remember). Finally, the question on page 8 cannot guess “turkey” when the answer
is “Yes”; instead it must ask the new question.
Does the animal have 4 legs? bunny rabbit Does it have wings?
1 5 8
12 31 101
penguin
102
The smarter book now knows four animals. In Figure 12.3, the updated information is written in red: the
text on the new pages, the page numbers on the new pages, and the fixed up page number on the question
before the wrong answer.
Another way to look at the data in the pages of our Twenty Questions books is by moving the pages around
and drawing connections between the pages:
This drawing shows what computer scientists call a tree. A tree is a collection of nodes where one node
is designated the root of the tree. All nodes but the root have exactly one node referring to them: the node
referred to is the child of the referrer; the referrer is the parent of the referred to node. Thus page 1 is a parent
of page 8. A consequence of having only one parent and a single root is that it is possible to traverse the tree
332 CHAPTER 12. MORE STREAMS: SEPARATING PROGRAMS AND DATA
Does the 1
animal have
4 legs?
yesNdx noNdx
5 8
yesNdx noNdx
12 31
turkey 12 dolphin 31
from the root to any other node in the tree only following references from parent to child. Nodes with no
children are leaf nodes.
Looking at Figure 12.4 you will see that computer scientists have little experience with nature: the root of
the tree is at the top of the figure and the leaves are all drawn below the root.
Playing Twenty Questions with the book is really a matter of starting at the root of the tree, reading the text
on the current page, and then, depending on whether the answer to the question is “Yes” or “No”, continue
down the tree to the yes-child or the no-child. This is just another way of visualizing the reading of the book.
When the book loses the game, the current node is the wrong answer node. To extend the tree we need
to build a small tree consisting of the new question, the wrong answer leaf and the new answer leaf, and then
replace the reference to the wrong answer from the question that was its parent with a reference to the new
question.
It is useful to notice that we can still get from the root to all of the nodes in the tree: since we could get to
the last question in the game and we didn’t change its parent, we can still get there. Further, the last question
now refers to the new question, so we can get to the new question. The new question refers to both the wrong
and the new answer. Thus it is still possible to follow a path of yes and no references to get from the root of
the tree to any node. The wrong answer page, in particular, has the same page number and is still reachable.
The path from the root to the wrong answer goes through one more question than it did before.
12.1. OUTSMARTING THE PLAYER: 20 QUESTIONS 333
Does the 1
animal have
4 legs?
yesNdx noNdx
5 8
yesNdx noNdx
101 31
yesNdx noNdx
102 12
Twenty Questions
Does it swim? y What was the right answer? seal
Does it breath under water? no What yes/no question would tell the
Is it larger than a pickup truck? no difference between a dolphin and a seal?
Is it a dolphin? yes Does it nest on shore?
I win! I guessed right! Which answer means it is a seal? y
Would you like to play again? yes Would you like to play again?
.
red r n o.
Does it swim? yes e rs in r n o s.
Does it breathe under water? n r ans yes o item
w
lay e or s new
Is it larger than a pickup truck? n P dle y n
Ha n m lear
Is it a dolphin? n ra
P rog
Darn! You stumped me.
Figure 12.6: TwentyQuestions Program Design
as long as the player wants to, and, optionally, save which will save the information known by the program
back into the question file.
The call to save is necessary so that any new domain objects added by the player are “remembered” by
the game the next time it is played. Remember the discussion of RAM and disk drives in Section ??. The RAM
is the working memory of the computer. It is where the Java program, local variables, and objects constructed
with new reside. It is volatile meaning its contents only last as long as the computer is running. It is actually
worse than that in a sense: the contents of the RAM memory belonging to a Java program only last as long as
the program is running.
The information about telling the difference between a turkey and a penguin must be saved onto the hard
drive. The contents of the file on the hard drive is nonvolatile. It will remain the same until the file is deleted
or overwritten. In particular, if the saved information in the file is of the right format (one we have not yet
discussed), then the load method can be used to reload the smarter twenty questions information when the
game is next played. In turn, this means that the more people play the game, the more elements are added to
the collection of domain objects and the better the game becomes.
The main class’s public interface looks something like this:
public class TwentyQuestions {
public static boolean answersYes(String prompt)...
public static String getLine(String prompt)...
public static void main(String[] args)...
private TwentyQuestions()...
private void load(String fname)...
private void play()...
12.1. OUTSMARTING THE PLAYER: 20 QUESTIONS 335
The constructor and the playing methods are private just like those in Pig because they can be. They are
only called from the main method in the same class definition. Thus they can be private.
63 String fname = args[0];
64 TwentyQuestions game = new TwentyQuestions();
65 game.load(fname);
66 game.play();
67 if (TwentyQuestions.answersYes(”Save guessing data?”)) {
68 game.save(fname);
69 }
70 } else {
71 System.out.println(”Usage: java TwentyQuestions <fname>”);
72 System.out.println(” where <fname> is the data file name”);
73 }
74 }
75
Given the TwentyQuestions interface, the main method almost writes itself. The main things it does is get
a file name from the user, construct a new TwentyQuestions object, and then use the load method to load the
data file for the game. The play method plays the game with the user. Then, finally, if the user wants to save
the changed game data, the save method is called.
The AdventureBook needs to support the same load, play, and save sort of methods. The AdventurePage
class uses indexes into the book to keep track of the parent and any child nodes (in the tree), so the
AdventureBook has to behave like an ArrayList and support getting an entry by index, finding the index given
a page, adding a new entry, and being able to determine the size of the book overall. These requirements lead
to a public interface of the form:
public class AdventureBook {
public static AdventureBook readBookFromFile(String fname)...
public static void writeBookToFile(String fname, AdventureBook book)...
Because an AdventureBook is treated like a collection, we will need to support some of the standard col-
lection methods such as size and get as well. We will want to be able to find the page number of a given page
inside the book.
Loading is done through a static method which loads a whole book from a named file, returning a
reference to the new book. A load method like this must be static because it must be callable before any
AdventureBook object has been constructed.
A static load method like this is sometimes known as a factory, a method which wraps up calls to new so
that the implementation details of the constructor don’t need to be known by clients of the class. A client is any
class using objects of a given class. TwentyQuestions is a client of AdventureBook. If we consider the parameter
list for a AdventureBook to be an implementation detail, wrapping the call to new inside of a factory hide the
detail from TwentyQuestions and the programmer writing it.
336 CHAPTER 12. MORE STREAMS: SEPARATING PROGRAMS AND DATA
The save method is also static to remain parallel with the loading method. It is very good practice to make
sure that the parameters to methods which go together conceptually are at the same level of abstraction; it
would be possible to write writeBookToFile to take an open file rather than a file name. That would make it
hard for programmers using the class to remember which takes a file name and which takes an open file. This
way the related methods are “the same” in their interface.
The two node types found in the book. Question and Answer have a lot in common. Looking at the picture
of the tree, both contain text, both have a parent, and both have a page number. It is also possible to play both
kinds of pages. This gives us a feeling for the public interface for AdventurePage:
public abstract class AdventurePage {
public static AdventurePage readPageFromFile(AdventuerBook book,
Scanner scanner)...
public static void writePageToFile(AdventurePage page,
PrintWriter out)...
public int getNdx()...
public String getText()...
public abstract boolean play()...
The readPageFromFile is another example of a factory, a method which constructs either a Answer or a
Question depending on what type of page is specified in the input file (the formatting described below). This
factory takes the place of a polymorphic constructor. Remember that a polymorphic method is a method which
is overridden in subclasses. The code calls the method on an object of some static type but the version of the
method that executes depends on the dynamic type of the object referred to.
Since the dynamic type of an object depends on the constructor which is called, an actual polymorphic
constructor makes no sense in Java. This factory method is the next best thing. The input file connected to
the Scanner will encode the desired dynamic type and readPageFromFile reads the encoding and uses an if
statement to call new Answer(...) or new Question(...). The factory returns a reference to the superclass
AdventurePage but inside the method it calls the constructor of one of the subclasses of that class. More on
why this is a good idea later when we discuss the file format.
What does abstract mean? It appears twice, once modifying the class AdventurePage and once modifying
the method play. An abstract class is a class which contains one or more abstract methods. An abstract
method is a method which provides only a header; an abstract method has no method body. As a consequence
of having “empty” methods, Java does not permit programmers to call constructors of abstract classes di-
rectly. An abstract class can only be instantiated by some subclass which extends it and overrides all abstract
methods.
Another way to think of a abstract method is as one that provides only the interface. The header of the
method provides the return type, name, and parameter list which must be overridden so it describes one
aspect of the class’s interface. Since no implementation is provided, all non-abstract subclasses must provide
an implementation. Thus AdventurePage references can be used to call play but it will always be the play
method defined in a subclass.
A Question is a page which has two additional indexes, one to turn to if the answer to the question on
the page is “Yes” and another to turn to if the answer is “No”. Turning to the next page after asking the
question is embedded in the play method. The only other operation which needs access to the index values
is userProvidesNewAnswer, the method which extends the tree with a new question and a new answer. The
extension method is part of the interface because it is called from play in an Answer node (that is where the
game will find out that it has guessed wrong). The interface for Question is thus:
public class Question
extends AdventurePage {
public Question(...)... // don’t know header yet
public boolean play()...
12.2. READING AND WRITING FILES 337
An Answer is even simpler. It has to implement play, constructor(s) and toString. The toString method
is in both Question and Answer so that we can print out the content of a tree and see if it is put together the
way we think it is. Remember that toString is provided by Object and that it is used by System.out.println
to convert object instances for printing. The whole Answer interface is thus:
public class Answer
extends AdventurePage {
public Answer(...)... // don’t know header yet
public boolean play()...
public String toString()...
}
Before we delve further into abstract classes and how to encode domain knowledge for a smart computer
program, we will take time to extend our knowledge of file I/O to include file output. That way we can write
text files as well as read them.
Two important caveats. When a PrintWriter is constructed with the name of an existing file and you, the
user who ran Java, have permission to overwrite the file’s contents, any existing contents are lost. In most
graphical user interfaces, when saving over an existing file, programs ask if you are sure you want to overwrite
the existing file contents. You can write a program which does that but PrintWriter is of low enough level that
when you open the file (call the object constructor), the file is reset to have a length of 0 characters waiting for
you to write new contents. You must also always close your output files. There are file systems which will lose
the contents of a file if the program terminates and an output file was not properly closed. Very few modern
file systems will do this but it is important to make it a habit to close all files (but especially output files).
For practice writing a file, let’s modify AddTwoNumbersRight.java so that instead of writing the sum to
standard output the program instead writes it to a file. We will prompt the user for the name of a file in which
to save the results and then we will prompt the user for the two numbers to add and then we will print the
results in the output file.
2 Just a reminder: Good object-oriented design abhors public access for fields.
338 CHAPTER 12. MORE STREAMS: SEPARATING PROGRAMS AND DATA
27 System.out.print(”Number: ”);
28 int first = keyboard.nextInt();
29
30 System.out.print(”Number: ”);
31 int second = keyboard.nextInt();
32
38 } catch (FileNotFoundException e) {
39 e.printStackTrace();
40 System.err.println(
41 ”Program cannot make useful progress: terminating.”);
42 System.exit(1);
43 }
44 }
45 }
Most of main is familiar. Even the try...catch block is familiar; we have just not used it for PrintWriter
objects before. Just like when creating a Scanner, it is possible that the file represented by the File object
cannot be found. Typically PrintWriter, because it is opening the file for output, will just create a file. But it
is possible that the user does not have permission to create the named file for some reason. If that happens,
then an exception is raised by the PrintWriter constructor. This program moved all the useful code inside
the try block because a failure to open the output file moots any values calculated by the program. Thus the
exception is handled by reporting it and terminating the program.
Let’s run the program:
~/Chapter12% java FileTwoNumbersRight
FileTwoNumbersRight:
Results file name: sum.txt
Number: 100
Number: 121
Notice that there was no output from the program after the second number was entered. The program
simply terminated. When EchoFile (see Listing 10.11) was run (at the next command-line prompt), it echoed
the contents of the newly created output file. Java does not add any extension to a file name. The full name,
sum.txt was provided to the File constructor.
12.3. DATA-DRIVEN PROGRAMS 339
When a PrintWriter is constructed with the name of an existing file, the contents of the file are truncated
(the size is set to 0) and then anything printed to the output file are appended to the file pointer which is then
moved to the end of the input.
So, if we run the program a second time with the same file name but different input, the following console
session results:
~/Chapter12% java EchoFile sum.txt
Sum of 100 + 121 = 221
The first run of EchoFile shows that the contents of sum.txt are just what they were at the end of the
last session. Then we run the sum program and finally look at the new contents. One reason for choosing the
numbers that we did are because the results line is shorter (in characters) than the previous results line. Thus
if any characters were left from the previous file contents we should see them at the end of the line after “=
82”.
examples barely scratch the surface, focusing only on mods which became separately distributed games and only on first-person shooter
games. The range of maps, new characters, weapons, gameplay modes, and the like is much, much larger.
4 This syntax is fictional but similar to that found in several commercial games.
340 CHAPTER 12. MORE STREAMS: SEPARATING PROGRAMS AND DATA
The important thing to notice is that the program, the game itself, is not changed. The modder does not
need access to the source code of the game, nor do they need to compile source code into executable code.
The format saved by the level editor is the same format used by the game’s own levels.
Separating the code (the compiled game) from the data (the level files), the game has increased flexibility.
During development the game designers can make changes without needing to compile the whole program;
this is a major time savings since compiling a moderately complicated game on a single computer can take
upwards of two hours. Since changes are inexpensive (in terms of time), game designers can try many different
things to find the ones that work. The separation also makes it possible to sell downloadable content to extend
the life of the game and provide additional revenue to the game company. And, as discussed above, separating
code and data permits the user community to mod the game and even develop new games atop the existing
game.
Let’s start by agreeing to label each page in the text file with the type of the page. Thus if we have a single
question with two answers on the following pages, the text file would look something like this:
Question
...
Answer
...
Answer
...
The ellipses stand for whatever information, in whatever format, is required for defining a Question or an
Answer. Looking back at Figures 12.2-12.5, it is clear that every page has a page number, contains text , every
page refers to a parent page, and every page belongs to some particular book.
How should these common features be written in the text file representing a book? Since the text file
represents a book we can assume that the book a page belongs to is indicated, implicitly, by the file the infor-
mation is in. Further, each page in the book will be represented in the file so the pages are numbered, again
implicitly, from the beginning of the file. Thus the file shown above corresponds to the tree in Figure 12.7.
Does the 0
animal have
4 legs?
yesNdx noNdx
1 2
The tree shows the page number assigned to each of the pages when they were read in from the file: since
they are stored in a Java collection it is convenient to use 0-based numbering. The layout of the page indicates
the parent/child relationship between the pages but we have not yet specified the formatting for all of the
fields.
342 CHAPTER 12. MORE STREAMS: SEPARATING PROGRAMS AND DATA
In Hangman we used one line per item. Here we have committed to multiple lines. An Answer needs text and
the page number of its parent. That sounds like two additional lines. A Question has the same information,
text and the parent page number, as well as the page numbers for its yes and no following pages. Given a line
per page number, the data file for a very simple game tree would look like this:
Question
Does the animal have 4 legs?
-1
1
2
Answer
bunny rabbit
0
Answer
turkey
0
Does the 0
animal have
4 legs? parent
-1
yesNdx noNdx
1 2
parent parent
0 0
AdventurePage
The common information is stored in AdventurePage and the specialized information for Question is stored in
that subclass. We will look at the constructors for the three page classes. The one thing to note is that a Answer
or Question constructor is called right after the label is read. So the read marker is at the beginning of the text
line. We will see how to arrange that when we discuss the factory method’s implementation.
50 */
51 public static AdventurePage readPageFromFile(AdventureBook book,
52 Scanner scanner) {
53 String questionOrAnswer = scanner.nextLine();
54 if (questionOrAnswer.equals(”Question”)) {
55 return new Question(book, scanner);
56 } else {
57 return new Answer(book, scanner);
68 */
69 public static void writePageToFile(AdventurePage page,
70 PrintWriter out) {
71 page.save(out);
72 }
73
74 /**
The AdventurePage constructor copies the three parameters into the class’s fields. The only interesting bit is
where the page is added to the book. This makes sure that every new page is actually in the book it references
as its containing book. Notice that there is no field for the page number of this page. That is because we can
always use the book’s indexOf method to find our index in the collection (which is the page number of the
page). Another application of the DRY principle.
21 }
22
23 /**
24 * Construct an answer with the given text
35
36 /**
37 * Play this {@link Answer}: generate a guess question and ask the
Answer has two constructors and no fields (beyond those inherited from AdventurePage). The second
constructor passes its parameters to the AdventurePage constructor. The first constructor is called with a
AdventureBook and a Scanner with the read pointer at the beginning of the text line. Line 22 reads the next
line from the input as the text and then reads an integer (on the next line) as the index of the parent page.
These values are passed directly into the other constructor.
There is a problem at the end of line 22: nextInteger leaves the read pointer at the character following
the integer’s characters. That is, the read pointer is at the end of the line with the integer rather than at the
beginning of the next line. This is a problem because if a call were made to nextLine, it would read from the
read pointer to the end of the line with the integer.
We want to skip over everything up to and including an end of line character. The Scanner class has a skip
method which takes a String describing the pattern to skip over. In the pattern, “.” means any character,
“*” means 0-or-more of what comes before, and the slash n is the end of line marker. Thus line 23 skips over
anything up to and including an end of line marker. Just what we want.
344 CHAPTER 12. MORE STREAMS: SEPARATING PROGRAMS AND DATA
18
19 /**
20 * Construct a new {@link Question} from values read from the file to
21 * which the given {@link Scanner} is attached.
22 *
35
36 /**
37 * The primary constructor for a {@link Question}. Takes the book in
38 * which the question appears, the text of the question, and the
39 * indexes of the parent, yes, and no pages for this question.
59 }
60
61 /**
62 * Play the game by asking this question. Based on user’s response to
63 * the question, continue play by ”turning” to the yes page or the no
64 * page.
The Question constructors look a lot like the Answer constructors: the one passed a AdventureBook and a
Scanner reads all the values from the input file and passes them to the other constructor; the other constructor
takes five parameters, passing three up to AdventurePage and setting the two local fields to the appropriate
values. The two local fields are yesNdx and noNdx, the page numbers that follow this question if the answer is
“Yes” or “No”.
The Factory
So, where do the Answer and Question lines in the data file come in? The factory method (the polymorphic
constructor) can construct either a Answer or a Question from the contents at the read pointer of the Scanner
passed into the method. The decision of which type of page to construct is made by reading the next line.
25 */
26 protected AdventurePage(AdventureBook book, String text,
27 int parentNdx) {
28 this.book = book;
29 book.add(this);
30 this.text = text;
31 setParentNdx(parentNdx);
32 }
33
The readPageFromFile method is static so that it can be called before any pages are actually constructed.
It is passed an AdventureBook, to which the next page read will be added, and a Scanner. The Scanner provides
the next line from the file. Since we assume the input stream is position at the beginning of a page record
(either 3 or 5 lines long, beginning with the name of the class), the method reads the next line and compares
it to ”Question”: if it is equal, a Question constructor is called; otherwise an Answer constructor is called.
Remember that the AdventurePage constructor (called from the subclass constructor) adds the page to the
book. This makes sure that the read page is given an index (its own page number) as well as having a reference
from the book.
12.4. ENCODING OBJECTS TO READ OR WRITE THEM 345
If there were a third type of page we would have to write a new AdventurePage subclass and update the
factory so that it could handle the new kind of page. When reading from a file it is common to use database
terms: each page in the text file is a record composed of a type identifier and some number of fields.
AdventureBook
The factory can, given an open input text file, read the next page record. The constructors for the pages, as we
saw above, leave the input read marker just past the end of the last line of the record. How can such a method
be used to read a file?
AdventurePage.readPageFromFile is similar in function to nextLine in Scanner: nextLine reads from
where the input marker is, constructing a String until it reaches the end of the current line; the input marker
is left just past the end of line marker and the newly constructed object is returned. Thus a sentinel-controlled
loop that looks much like our other file reading loops should be used to read in all of the pages for a book.
32
33 while (scanner.hasNextLine()) {
34 AdventurePage.readPageFromFile(this, scanner);
35 }
36 }
37
38 /**
50 AdventureBook book = null;
51 try {
52 Scanner scanner = new Scanner(file);
53 book = new AdventureBook(scanner);
54 scanner.close();
55 } catch (FileNotFoundException e) {
56 book = null;// return an error value
57 }
58 return book;
59 }
60
61 /**
The static readBookFromFile method takes a file name and opens the file for input, if possible. If it is
possible, then a new AdventureBook is constructed using the Scanner attached to the input stream; if there is
a problem, null is returned.
The important lines in the constructor are 80-82: it is an eof-controlled loop. If there is another line in
the file, then there is another page. Thus the loop reads until there are no more lines; each call to the page
factory consumes one page record from the input file, leaving the read marker just past the last line of the
record. When the last record is read, the last line of the record is also the last line of the file so there are no
more lines available. Thus the loop ends and the book is full of pages.
85 /**
151 AdventurePage.writePageToFile(pages.get(i), out);
152 }
153 }
154 }
When writeBookToFile is called, it is passed a file name and a AdventureBook. The method attempts to
open the named file for output. If all goes well the file content is truncated and then AdventureBook.save
is called. save is a count-controlled that traverses the pages list, calling writePageToFile with each page in
pages in turn along with the open output file. When save finishes, writeBookToFile closes the output file and
returns.
If there was a problem opening the named file, a message is printed and the method returns. Notice that
it returns without having saved anything. It prints an error message giving that information to the user.
Lines 79-80 use println but not with System.out. The System class has three file handles, one for stan-
dard input, in, one for standard output, out, and one for standard error (or, in better English, “the standard
error channel”), err. Everything we have seen about using the PrintWriter System.out applies to using the
PrintWriter System.err.
When running from the command-line, out and err are typically both attached to the screen for output.
In some operating systems it is possible to split the output so that each goes to a different place5 . It is good
programming practice to split “regular” output from error messages so we will make sure to print output to
out and error messages to err.
The AdventurePage writePageToFile method is also static to parallel the read method. It is passed a
PrintWriter where the record should be written and the page to be written. It calls save on the page.
45 * belongs
46 * @param scanner input {@link Scanner} attached to a proper input
47 * stream
48 *
153 out.println(getText());
154 out.println(getParentNdx());
155 }
156
157 /**
158 * Set the value of the parent of the current {@link AdventureBook}.
5 The details on doing this are operating system and command-line processor specific and are beyond the scope of this book.
12.5. FINISHING THE GAME 347
It is common for overriding methods in subclasses to literally extend the function of the method defined
in the superclass. That is, Question.save does whatever AdventurePage.save does and then does whatever
special stuff it does to handle Question objects.
AdventurePage.save must be called by all overriding definitions before they write anything to the output
text file. This is why it is protected (subclasses must be able to call it). An AdventurePage is written by writing
the name of the class of the page (getClass, defined in Object is polymorphic: it returns the dynamic class of
the object) on a line, then the text on the next line, and the parent page index on the next line. Any subclass
which must save more information, say Question, writes additional information following the base informa-
tion. Answer has no additional information; no need to override the method in Answer.
250 }
251 }
Question.save overrides AdventurePage.save; in line 249 it calls the superclass definition. After the 3 lines
written by AdventurePage, Question adds the yes and no page indexes, each on their own line. This matches
the input format expected by the input factory described earlier.
Does the animal have 4 legs? bunny rabbit Does it have wings?
1 5 8
12 31 101
penguin
102
101 /**
The play method in TwentyQuestions has the book play one game and then asks the user if they want to
play again. This is a sentinel-controlled loop with an ending condition when the user says no more. It is also
an example of reusing code we already wrote: since we have answersYes, we call it to ask if the user wants to
play again, just as we call it to ask the various questions in the game.
19
132 /**
AdventureBook.play forwards the call to play in the root page. The root page is the page with the rootNdx
as its index. As you can see in the listing, rootNdx is a constant set to 0. Why name it if it is only used in one
place? It cannot be a DRY consideration since typing “0” on line 131 would actually be shorter.
One reason is documentation. Line 131 with rootNdx as the value passed to get indicates why we want that
particular page. The other reason is that it would be possible to modify the file format to include not just a list
12.5. FINISHING THE GAME 349
of pages but also to include the root page index. We could then load the root page index from the file (it would
no longer be a constant value) and the code would still work. It might be convenient to be able to rearrange
the book without worrying about moving the root.
Finally we look at the play method in the two page classes. Remember, play is abstract in AdventurePage;
it has no definition in that class so there must be overrides in all concrete (non-abstract) subclasses of
AdventurePage.
69 if (TwentyQuestions.answersYes(getText())) {
70 nextPageNdx = getYesNdx();
71 } else {
72 nextPageNdx = getNoNdx();
73 }
74 AdventurePage nextPage = getBook().get(nextPageNdx);
75 return nextPage.play();
76 }
77
78 /**
In Question we see the complete game loop for TwentyQuestions: the call to answersYes displays the ques-
tion in the text (shows the state) and waits for an answer from the user (gets user input). Based on the input
(was it “Yes” or “No”) a next page is selected and play is called on that page (state is updated and the loop
starts over).
Two related things to notice: there is no explicit loop construct here, instead play calls play to start the
loop over again. Each iteration of the loop is one call to play and the method will call itself if another iteration
is needed.
A method which calls itself (same name, same header, same class of object) is a recursive method. Recursion
is an alternative way to implement iteration. For some data structures, in particular trees and other linked-
together structures (graphs), recursion can be conceptually simpler than writing a loop. Here we recur to play
at the next question after the current question (think about moving the coin and asking the question). Then,
when the user reaches an Answer, the play method on Answer does not recur so it returns true or false so the
Question.play that calls it returns that value to the Question.play that called it which returns that value to
the Question.play that called it which.... You get the idea.
48 if (TwentyQuestions.answersYes(guess)) {
49 System.out.println(”I am so smart! I win!!!”);
50 return true;
51 } else {
52 System.out.println(”Darn, you stumped me.”);
53 getParent().userProvidesNewAnswer(this);
54 return false;
55 }
56 }
57
58 /**
Answer.play builds a question out of the item to be guessed and ask it of the user (again, show state, get
input from user). If the player answers “Yes” we can just return true because we won.
If the player answers “No”, then we don’t know enough domain objects. The player has one we don’t know.
So, lets get the parent of the wrong answer (it is a Question) and have it fix up the contents of the tree. We
then return false because we lost the game.
350 CHAPTER 12. MORE STREAMS: SEPARATING PROGRAMS AND DATA
A Learning System
Section 12.1 described how to fix up the decision tree/book when it failed to guess a domain object. That
description is reflected in the userProvidesNewAnswer method. The method is longer than any other we have
seen and it has more internal comments than we have seen since NewtonsApple. It is not difficult to follow but
it is a little bit complicated.
109 String correctAnswerText = TwentyQuestions.getLine(
110 ”What were you thinking of?”);
111
123 // Make a new answer and a new question. Attach the two answers,
124 // right and wrong, to the yes/no or no/yes positions of the new
125 // question (according to what the player told us)
126 Answer correctAnswer = new Answer(getBook(), correctAnswerText,
127 NO_SUCH_PAGE);
128
153 /**
The method asks the user for the name of their new domain object, a question to differentiate between
the new and the wrong objects, and how to answer the question to mean the new object.
It then builds a new Answer object without a parent (the parent will be fixed up when the new question is
constructed). Then the answers, new and wrong, are matched with yes and no answers so that the differenti-
ating question can be constructed. Lines 141-143 make the new question, adding it to the book, and fixing up
the parent fields of the two Answer objects referenced. setYesNdx is listed here; setNoNdx is parallel.
231 if ((yesNdx != NO_SUCH_PAGE) && (yesNdx < getBook().size())) {
232 getBook().get(yesNdx).setParentNdx(getNdx());
233 }
234 }
235
236 /**
When the yesNdx in the differentiatingQuestion is set, the parentNdx of the page (since both Answer
objects are already in the book so they already have valid indexes) referenced is set to the current page.
Finally, the current Question must be updated. Why? Because this Question refers to the wrong an-
swer (we called the method on the wrong answer’s parent, remember?). It must refer, instead, to the
differentiatingQuestion. Only difficulty: do we fix up the yesNdx or the noNdx? Depends on whether the
wrong answer’s index is in the yesNdx or not. isYesChild returns true if the given index is the yesNdx (again,
a named method to explain why we compare the two indexes).
We will look at one more method, the getParent method in AdventurePage. The method is fairly simple
in concept: go to the book, get the object with the parent’s index and return that object. The problem? The
pages in the book are AdventurePages and all parents must be Questions. We could just return AdventurePage
objects and leave it to the routine that got it to figure out that it is a Question and figure out how to change the
reference. That is not a good choice because it requires a lot of error checking all through the code. It makes
more sense to make the change in one place, especially since we know that in a properly constructed decision
tree any parent must, of necessity, be a Question page. We can avoid any error checking since we know what
type of AdventurePage it must be.
122 protected Question getParent() {
123 if (getParentNdx() != NO_SUCH_PAGE) {
124 return (Question) getBook().get(getParentNdx());
125 } else {
126 return null;
127 }
128 }
Line 124 uses get on the book; that returns a AdventurePage. The (Question) in front of the expression
is a cast operator. We saw type casting briefly in Chapter 7. The form of a cast operator is the name of a class
inside a pair of parentheses. If the expression following isa Question, then the whole expression is a reference
to that Question. If, perchance, the expression is not actually a Question, then the cast fails and the result is
null.
352 CHAPTER 12. MORE STREAMS: SEPARATING PROGRAMS AND DATA
Most object-oriented programs should not need casts. In fact, this program needs casts because we use
numeric references into the pages list rather than Java references to hook questions to answers7 . This one
cast is unfortunate but it does not completely invalidate the class structure of the program.
There are a handful of helper methods which were not described in detail in the chapter. This is because
simple getter and setter methods should be easy to understand. The choice of private or some other access
should also be understandable: choose private unless, for some reason, you cannot.
12.6 Summary
Java Templates
Programming Problems
7 That leads to recursive data structures where a Question contains a reference to another Question and requires recursive methods
for reading and writing the structures to text files. For the cost of one cast the read and write routines are must easier to understand.
Chapter
13
Lists of Lists and Collision Detection
We have spent some time working with non-FANG games, looking at running our own game loops and ma-
nipulating files. In this chapter we will go back and write a more complex FANG program. The game takes
advantage of the animation capabilities of FANG and explores different ways of modeling the internal struc-
ture of the game world at the same time. Since this program is longer than previous programs, this program
takes advantage of a Java structuring mechanism, packages to permit us, as programmers, to focus on one
portion of the program at a time. BlockDrop also uses the factory pattern, multi-dimensional ArrayLists, and
file input/output.
referred to as a port. Tetris has been widely ported to different operating systems and computers since it was first developed.
353
354 CHAPTER 13. LISTS OF LISTS AND COLLISION DETECTION
Think about your favorite arcade or console game. What does the game do when no one is playing it? It
shows a gameplay video or it plays itself in a level of the game or it shows the high scores. This is known as an
attract screen, something which attracts the player to the game so they press the appropriate start button.
The attract screen for BlockDrop will be a scrolling display of the ten highest scores earned in the game,
challenging players to try their luck at getting on the leader board. In order to keep the high scores from one
session of the game to the next we will need to have a high score file. The HighScoreLevel and BlockDrop will
alternate, as one ends it creates and starts the other.
With more than ten new classes (the two Game-extending classes discussed above and at least nine Sprite-
extending classes), this is the longest game with the most files we have yet examined. How can we tame the
complexity? The next section will take a time out from building the game to discuss software engineering, a
computer science discipline focused on efficiently writing correct, effective code.
I J L
S T Z
Figure 13.2: BlockDrop Tetraminoes
Object-oriented programming developed out of the study of abstract data types [ADTs]. The creation of
languages with programmer-defined classes, inheritance, polymorphism, and encapsulation created the object-
oriented programming paradigm.
As we saw in Section 7.3, an abstract data type has three features: a public interface, a private implemen-
tation, and interaction limited to the interface. The last feature means that clients of the abstract data type
cannot make use of any aspects of the private implementation. The separation of interface from implementa-
tion means that the implementation can be changed without having to change client classes.
Notice that encapsulation, the ability to hide data and other details inside a class, done in Java with private
members and methods, comes directly from the last feature of ADTs2 . Java access modifiers in general are used
to separate the public interface from the private implementation. protected elements kind of cross the border
between implementation and interface: subclasses of a class with protected elements have interaction with
the implementation of the base class. This is sometimes necessary for efficiency but it is, strictly speaking, a
sharing of the implementation between the super and subclasses.
Inheritance is the ability to extend an existing class, providing only that state and those behaviors which
the new class needs. The ability to override existing behavior and have the overriding definitions used through
references to the original class is polymorphism. Our use of classes, inheritance, and single-purpose methods
fits very well into using object-oriented programming techniques well.
The other reason for the approach used in this book is to overcome complexity. Computer programs are
complex structures. Because they have no physical manifestation, there is no natural limit on how complex.
A steel bridge can only be so complicated before it literally falls under its own weight; a computer program is
only limited by the memory space within the computer.
2 ADTs predate object-oriented programming; thus we talk about how ADT concepts drove the development of object-oriented pro-
gramming.
356 CHAPTER 13. LISTS OF LISTS AND COLLISION DETECTION
A disciplined approach to building software is important to overcome the complexity and produce cor-
rect software in an acceptable amount of time. The study of how to do this is software engineering. Software
engineering, generally considered a sub-discipline of computer science, is focused on quantifying software
development costs, risks, and quality. Then, armed with quantitative analysis of existing projects, different
approaches can be applied in an attempt to improve the metrics.
Among the practices studied in software engineering are program design patterns like our factory, the
separation of interfaces and class implementations, grouping classes together into larger modules, and the
reuse of existing code. This section will present each of these ideas and discuss how it applies to our current
project.
Java interfaces
Every time we needed a new kind of “thing” in a program we have created a new class in a new file. In
defining a class we provide the public interface and the private implementation. What if we relaxed the two
requirements: that we must provide an implementation whenever we define an interface and that we must
start a new file for each new class. This section examine interfaces without implementations. The next will
look at packing more than one class in a single file.
Consider building a generic sorting class. The class should take an ArrayList of sortable objects, objects
which implement the compareTo method, and, using compareTo and something like the sorting code we saw
previously, it could polymorphically compare whatever actual objects were in the list, sorting the list.
We need a “superclass” that collects together all objects defining compareTo. Then all we need to do is
make sure any new “sortable object” just extends the “superclass”. You can guess from the liberal use of scare
quotes that there is a problem in declaring such a “superclass”. There is.
Java has single inheritance: a given class can extend one other class. Every class, except for
java.lang.Object, must extend exactly one other class. This means that the inheritance graph3 is a tree, sim-
ilar to the decision tree we saw in Chapter 12. While each node can have an arbitrary number of children, no
node can have more than one parent.
If we defined a superclass for all sortable objects, then all such objects would be in the tree below that
superclass. What if we wanted a Sprite-derived class that was also sortable? If our SortableSprite class
extends Sprite and the sortable superclass; since the inheritance graph is a tree, either Sprite is a superclass
of all sortable objects or sortable objects is a superclass of Sprite.
The first outcome would require all Java objects that are sortable to extend a class from our third-party
framework4 . This means Sun has to include FANG with every Java installation which makes no sense. Thus
Sprite must extend sortable.
But wait, that means all Sprite-derived classes are sortable, even if that makes no sense for many Sprite
classes. How should LineSprite objects compare, one to another?
What we need is either multiple inheritance or some way to define interfaces. Multiple inheritance is the
solution in some languages such as C++ and Eiffel. It is not without cost, particularly in changing a reference
to a subclass back into a reference to one of its superclasses5 .
Java’s solution is the use of interfaces. An interface is a totally abstract class. That is, where an abstract
class can have methods, declared abstract, which have no implementation, an interface has only methods
which have no implementation. An interface is declared just like a class: it is declared to be public so that
it is visible to other classes; it is declared in a file with the same name as the interface; the definition of the
interface goes in a block after the name of the interface.
3 A mathematical graph is a set of nodes which are joined into ordered pairs by a set of edges. The inheritance graph has classes as nodes
and there is an edge between two classes if one directly extends the other.
4 In a contract for software, let’s say Java, the provider, Sun Microsystems, is the first-party. The end-user of the software, you, the
programmer of the language, is the second-party. FANG, provided to enhance Java but created by neither Sun nor you, is a third-party
product.
5 The details are beyond the scope of this book.
13.2. SOFTWARE ENGINEERING: MANAGING COMPLEXITY 357
When writing a class which implements the methods named in the interface, the keyword implements goes
between the name of the implementing class and the beginning of the class definition block. A class extends
exactly one class so only one class name appears after extends; any class implements an arbitrary number of
interfaces so a comma-separated list with all the interfaces it implements can appear.
We will not go into detail here but there are a large number of interfaces provided by Sun’s Swing graph-
ical user interface code. FANG defines a class called a GameWindow:
public abstract class GameWindow
extends JApplet
implements ActionListener,
WindowStateListener {
The above code, taken from GameWindow.java shows how the GameWindow class extends something called a
JApplet6 The GameWindow also implements two interfaces, ActionListener and WindowStateListener. Looking
at the import statements and Java documentation (Swing is a standard, first-party library for Java), both of
these interfaces are defined in the javax.swing package. We will stop here with GameWindow and change our
view to a class for BlockDrop, HighScore. We take a slight diversion from interface to motivate the idea of
nested classes; the code for HighScore implements an interface, giving us an example of how to do that.
Nested Classes
Between games of BlockDrop we will display the high scores so far achieved. That means that there is an end-
of-game level, HighScoreLevel. The main game, BlockDrop needs to be able to communicate the name and
score for a new high scoring player but otherwise it knows nothing about high scores.
None of the visible elements discussed above in the game design know anything about high scores either.
The HighScoreLevel must be able to keep a list of some number (say 10) high scores which group a name and a
score, save the list to a file, load the list from a file, and sort the list. Note that the methods to do these things
are implementation details. So, it turns out, is the existence of a class to group a name with a score. That is, the
HighScore class, the element type in the list of high scores, is an implementation detail of the HighScoreLevel.
All the fields and methods that are implementation details are encapsulated, hidden, private inside the class.
HighScore should be, too.
Java supports nested classes, the inclusion of a class within the body of another class. A nested class can
be given any Java access modifier so it is possible to declare nested classes to be public to include them in the
public interface of the class or private to use them solely in the implementation7 . The following listing shows
the skeleton of the declaration of HighScoreLevel.HighScore. The class lines and beginning of the definition
block and the closing curly braces of the definition block have been included.
23 /** the default score file */
24 private static final String DEFAULT_SCORE_FILENAME =
286 /** number of characters used in the full-width name */
287 private static final int SIGNIFICANT_CHARACTERS = 10;
Notice that the name of the nested class is HighScoreLevel.HighScore. This is similar to the naming of
static methods and fields. We will tend to use the short name, HighScore when no confusion will result.
The other thing to notice in this listing is that HighScore implements an interface. Looking at the docu-
mentation and the code provided by Java, the definition of the interface is:
6 A JApplet extends Applet and that is why FANG games can be run as applets inside of properly configured browsers. When
they run as applications, FANG arranges to start the applet by calling runAsApplication (might be better to call that method
startSuperSimpleAppletBrowser).
7 Or protected, again so that they are mostly private but available to subclasses.
358 CHAPTER 13. LISTS OF LISTS AND COLLISION DETECTION
The first thing to do is ignore T for the moment. If we just let T be some type, then we see that this
looks like a class but with the word interface in place of class. It has a method in it, compareTo. Just like
when a concrete class, one on which new can be called, extends an abstract class, any class which implements
Comparable must have a method with the given header. An interface defines one or more headers for methods
which implementing classes must define.
The <T> is an example of Java generics. We have been using Java generics with ArrayList for several chap-
ters now. The definition of the interface simply says that the interface is generic, taking the name of a type
between angle brackets. It also says that the parameter type of compareTo must be the same type as that in
the generic angle brackets. Defining generic classes is beyond the scope of this book so we will now return to
implementing Comparable<HighScore> (because HighScore values can be compared to other HighScore values).
In order for HighScoreLevel.HighScore to implement the Comparable interface, it must override the re-
quired method with one having the right header and a method definition.
340 return 1;
341 }
342 if (name.isEmpty()) {
343 return -1;
344 }
345 if (score == rhs.score) {
346 return name.compareTo(rhs.name);
347 }
348 return score - rhs.score;
349 }
350
351 /**
Tracing the code in compareTo we see that empty names always come after non-empty names. If neither
name field is empty, then the score values are compared. If the score values are the same, then the HighScore
objects compare in order by their name values (which means if they have the same score and name, they com-
pare as equal which is what we would expect). If the scores are different, they sort in order by the score.
Remember that compareTo only guarantees that the sign of the return value will reflect if this or rhs sorts
first (negative means this is first; positive means rhs first). Thus we don’t have to use an if here, we just
subtract the rhs.score from the this.score; if rhs is bigger, then the result is negative (this < rhs) and if
this is bigger, result is positive (this > rhs).
Note that the header of a method does not include the names of the formal parameters in the parameter
list. While the interface calls the parameter o, when overriding the method any valid Java variable name can
be used. rhs, standing for “right-hand side” was used to bring to mind the comparison operators shown in
parentheses in the previous paragraph. this is on the left-hand side and the parameter is on the right-hand
side. Headers match up on the number and type order of parameters. Notice that the @Override annotation
is used here because we are overriding the empty definition of the interface.
One other thing to note: just as any class which directly or indirectly extends a superclass is-a super-
class object, any class which directly or indirectly implements an interface is-a object of the interface type.
HighScore is-a Comparable<HighScore>. We can use methods which depend solely on the methods described
in an interface and we can even write our own methods that do. The next section will show how we can
13.2. SOFTWARE ENGINEERING: MANAGING COMPLEXITY 359
use Java defined sorting methods to sort a collection of Comparable objects; an ArrayList<HighScore> is a
collection of Comparable objects8 .
42 /** game state variable: are we waiting for user to enter name? */
The ArrayList containing the scores is a field called highScores. When a HighScoreLevel object is con-
structed, the high scores are all loaded from a file; when a new high score is recorded, the high scores are
saved back to the file from which they were loaded:
216 File highScoreFile = new File(fname);
217 try {
218 Scanner highScoreInput = new Scanner(highScoreFile);
219 while (highScoreInput.hasNextInt()) {
220 hs.add(new HighScore(highScoreInput));
221 }
222 highScoreInput.close();
223 } catch (FileNotFoundException e) {
224 // It is okay if there is no such file; we will just ignore this
225 }
226 return hs;
227 }
228
229 /**
258 PrintWriter saveFileWriter;
259 try {
260 saveFileWriter = new PrintWriter(saveFile);
261 for (int i = 0; i != highScores.size(); ++i) {
262 if (!highScores.get(i).isEmpty()) {
263 saveFileWriter.println(highScores.get(i));
264 }
265 }
266 saveFileWriter.close();
267 } catch (FileNotFoundException e) {
268 System.err.println(”Unable to open ” + fname +
269 ” for output; high score file not saved”);
270 }
271 }
272 // ----- NESTED PRIVATE CLASSES -----
273
8 Comparable<T> and Comparable are basically the same thing; the book will use Comparable when talking about the interface in general
(any possible instance of T) and Comparable<HighScore> (or similar) when the type would make a difference. The sort routines are generic,
handling any type which can be compared to itself.
360 CHAPTER 13. LISTS OF LISTS AND COLLISION DETECTION
These two methods should look familiar: loadHighScores uses an eof-controlled loop to read HighScore
records (format actually defined inside of HighScore so it is not of interest to us right now) and saveHighScores
uses a count-controlled loop to go through the list of high scores writing them out to the output file (again, the
format of the writing is determined by the toString method of HighScore so inside of that class it is necessary
to make sure the formats match; out here in the level, we just let HighScore do the work).
We will look back at the level constructor which calls loadHighScores:
77 this.currentScore = currentScore;
78 highScores = loadHighScores(fname);
79 while (highScores.size() < MAX_HIGHSCORE_COUNT) {
80 highScores.add(new HighScore(0, ””));
81 }
82 Collections.sort(highScores, Collections.reverseOrder());
83 }
84
85 /**
The level is constructed with a current score, the score the player made in the last game of BlockDrop, and
the name of the high score file. loadHighScores is used in line 80 to load the high scores from the file. If there
were fewer high scores than expected, then highScores is filled with unnamed 0 scores. This means that the
list will never be empty so we don’t have to check for that.
Finally, line 84 is executed. The Collections class, in the java.util package, is a class with a number of
static methods that provide useful algorithms for working with collections. One such algorithm is sorting.
Collections.sort comes in two flavors. The first takes just a List of Comparable objects. Note that List is an
interface and an ArrayList is-a List. When provided with just a List, the sort is done in ascending order
according to compareTo; the list has to be of Comparable so that they all have compareTo.
The second overload of Collections.sort takes a List of Comparable as before and a Comparator. We’re
not going to go into what a Comparator is except to say that it modifies the results of calling compareTo. In the
case of the Collections.reverseOrder() Comparator, it just reverses the sign of the result, thereby reversing
the sorting order. Thus the list of high scores will be sorted but from highest down to lowest.
181 Collections.sort(highScores, Collections.reverseOrder());
182 saveHighScores();
183 if (showScores != null) {
184 removeSprite(showScores);
185 }
186 showScores = makeShowScoresSprite();
187 addSprite(showScores);
188 }
189
190 /**
The other place we need to sort the list is when we add a new high score. In Listing 13.6, you can see that
the last score in the list is set to a new HighScore object with the new score and name. Then highScore is
sorted (in reverse order). Then the high scores are saved and the screen is fixed up with a new showScores
sprite. We will examine when this code is called in the last section of this chapter when we describe how the
BlockDrop and HighScoreLevel games communicate.
13.2. SOFTWARE ENGINEERING: MANAGING COMPLEXITY 361
Java packages
When do we break a method down into several smaller methods? One answer is when we are doing stepwise
refinement and working our way down from the main problem. We define a solution in terms of simpler
problems which we pretend are working, putting off defining the additional methods until we think about the
next lower layer of abstraction.
There is one other time: when we find a method growing too long or taking care of too much. Think back
to Chapter 5 and the EasyDice program (see Listings 5.12-5.14). The game had two states: rolling the dice
or waiting for the user to place a bet. Each one did something different when advance was called. When the
author first wrote the program, he put all of the processing for both states directly in the advance method.
The method was over 40 lines long and handled checking what state the game was in and then, in both the if
and else branches it checked if the button was pressed, and then it... Needless to say, breaking the method
into three pieces, advance to determine game state, advanceWaiting to handle advancing one frame in the
waiting state, and advanceRolling to handle advancing one frame in the rolling state, greatly improved the
readability of the code. It limited the amount of context necessary to read any one of the three methods.
A similar problem occurs when writing a program with many classes. BlockDrop has fifteen different
classes. When looking at a listing of all of the class names, it can be overwhelming. The context a programmer
must comb through to find one desired class is too much. Java supports limiting the context by grouping
classes together into packages. With good package naming it can be simpler to find a given class and, by
keeping related classes together, it can be easier to reuse code in other projects.
The BlockDrop project has fifteen different class files. More than half of them are Piece and its subclasses.
It would be nice if those files could be segregated off by them selves. Then there is HighScoreLevel and
BlockDrop: they are separate levels and should probably be separated in some way (they don’t belong in the
same context). In fact, the files in this project can be divided into four different groups:
core + classes for main game level
BlockDrop.java - main Game
Board.java - where the pieces fall
NextPieceBox.java - generates and shows new pieces
ScoreSprite.java - show the current score
highscore + classes for high score level
HighScoreLevel.java - Game handling high scores
ScrollingMessageBox.java - looping, scrolling string table
pieces + piece classes
I_Piece.java
J_Piece.java
L_Piece.java
O_Piece.java
Piece.java - superclass of all pieces
S_Piece.java
T_Piece.java
Z_Piece.java
util + general utility classes (core & piece)
Position.java - a position on the Board: row, column
Our source files are stored in a directory named for the current chapter, Chapter13. Splitting the files into
the four named packages requires four steps:
A package subdirectory is just a subdirectory with the same name as the package it contains. Again, Java
simplifies its job of searching for needed code by making the file system and Java name the same (just like
class names and .java file names). Using our operating system we create the four subdirectories and move the
source files down into them. The exact commands to create the directories depend on the operating system.
We will just take a look at the output of listing the contents of the Chapter13 directory and its subdirectories.
ls * lists the directories and their contents:
~/Chapter13% ls *
core:
BlockDrop.java Board.java NextPieceBox.java ScoreSprite.java
highscore:
HighScoreLevel.java ScrollingMessageBox.java
pieces:
I_Piece.java L_Piece.java Piece.java T_Piece.java
J_Piece.java O_Piece.java S_Piece.java Z_Piece.java
util:
Position.java
The package line is the key word package followed by the name of the package (the directory name) and a
semicolon. The package line must be the first (non-commment) line in the file. It must be before any import
or class or interface lines. Thus the first line of HighScoreLevel.java is:
1 package highscore;
When the HighScoreLevel is showing, the player is expected to press the space bar to start the game (or,
alternatively, to start at a higher level, press a digit for the starting level). When the key is pressed, the
HighScoreLevel creates a new BlockDrop game with the appropriate level and then finishes:
159 if (keyPressed()) {
160 char key = getKeyPressed();
161 if ((key == ’ ’) || ((’1’ <= key) && (key <= ’9’))) {
162 int level = 1;
163 if ((’2’ <= key) && (key <= ’9’)) {
164 level = key - ’0’;
165 }
166 addGame(new BlockDrop(level));
167 finishGame();
168 }
169 }
170 }
171
13.3. WHEN IT’S SAFE TO MOVE: COLLISION DETECTION 363
172 /**
There is a problem for Java now. BlockDrop.java is not in the current directory when
HighScoreLevel.java is compiled. You never had to think about import for any classes you wrote because
they were always in the same directory as every other class you wrote (at least for any one project). Java just
looked for the matching .java file in the current directory and all was well. Now, with multiple packages, you
must explicitly tell Java where to find any classes not in the same package as the current class.
Given that the BlockDrop class is in the core package, HighScoreLevel needs the following include (so that
line 168 can compile):
14 import core.BlockDrop;
(One other thing to note in advanceScrollingScores: we take advantage of the fact that the digit character
codes are contiguous. In line 166 we convert from the code of a digit, say ’4’, to the number 4 by subtracting
the code for ’0’ from the value of key. If we pretend that the digits begin at code 150, then, because they are
contiguous, ’0’ = 150, ’1’ = 151, ’2’ = 152, etc. Thus ’4’ = 154. ’4’~-~’0’ = 154 - 150 = 4. This calculation does
not depend on the particular value for the code of ’0’; in fact, 150 is not the code of ’0’ in ASCII code. What
value it is has is not important. The contiguous nature of digits (and lowercase letters, and uppercase letters)
is the important thing to remember.)
We run javac and java in the chapter root directory. That used to be the same directory where the source
code was; now the source code is below the root directory. We need to tell javac and java how to find the files.
~/Chapter13% javac -classpath .:/usr/lib/jvm/fang.jar core/BlockDrop.java
Notice here that the name of the file to compile is the name of the directory followed by a slash (some
operating systems use a different character) and then the full name of the file. The compiler is run in the
directory above any package directory.
~/Chapter13% java -classpath .:/usr/lib/jvm/fang.jar core.BlockDrop
Notice in the java execution line that the name of the class to run is the name of the package followed by
a dot followed by the name of the class. We never noticed that javac works with file names and java works
with class names before (except for leaving off .java) before.
Moving files to packages lowers the cognitive overhead in keeping track of context when looking at them.
Grouping files in well-named packages documents what they have in common and guides the reader of the
code to find what they need. Using packages requires moving the files, adding pacakge and import lines, and
changing the way you compile and run the program. The payoff, in ease of programming, is well worth a little
bit of effort.
This section introduced some basic concepts of software engineering: Java interfaces, nested classes, the
use of built-in algorithms (Collections is your friend), and using packages to group your classes and make
reading your code simpler. All of these were presented in terms of making code easier to understand, one of
the driving forces in writing code for this book. When you go on to study software engineering in more depth
you will find that most of them have even deeper benefits to your code and the programmers who write and
use it.
the screen one block height (where each tetramino is composed of four blocks) at a time until it comes time
to move and it cannot move down. Then the blocks freeze on the screen in their current position.
We will create each Piece as a FANG CompositeSprite. That way we can put the blocks into the Piece
simply by setting their positions in the internal coordinate system.
Moving a Piece a discrete amount is actually quite simple. Rather than calling translate every frame
with the product of the velocity and the frame time, we will use a countdown timer to wait for some number
of seconds and then translate the Piece by a full block size. This does mean that the block size (the apparent
size of the blocks in the Board’s coordinate system needs to be known but that can be calculated when scaling
the Piece. Actually, if all the Piece classes were the same size, then it would be easy to calculate. We will come
back to this same size idea below.
The idea of a countdown timer was used in Chapter 6 (see Listings 6.2 and 6.1). We set a timer to the
interval between moves, decrement the timer value by dT each time through advance, and when it crosses 0
the timer has expired.
76
77 moveClock -= dT;
78 if (moveClock <= 0.0) {
79 updateBoard();
80 moveClock += moveDelay;
81 }
82
83 checkKeyPressed();
84
89 /**
We move the Piece and reset the timer and do it all over again as shown in Listing 13.10. Each time
moveClock expires, updateBoard is called (which moves the block down) and the timer is reset. The use of +=
is to keep the time between movements as regular as possible; if one movement runs a little over, the next
movement comes a little bit earlier.
182 return;
183 }
184 board.add(nextPiece.getPiece());
185 nextPiece.nextPiece();
186 }
187 }
188
189 /**
190 * Update the displayed level on the screen. The level is the number
191 * of times {@link #LINES_PER_LEVEL} rows have been cleared since the
The updateBoard method uses the moveDown method defined in Board to determine whether the current
piece moved down or it was frozen. If it was frozen, then the game might be over. If not, move the Piece from
the NextPieceBox (called nextPiece) and have it generate a new, random Piece.
13.3. WHEN IT’S SAFE TO MOVE: COLLISION DETECTION 365
How can moveDown determine whether or not it is safe to move the current piece down one row? And what,
exactly, is a row and a column? What are the implementation details of Piece and Board and how do they work
together?
A Square Grid
Any Tetris-clone has to deal with making sure that pieces are only moved or rotated when doing so does not
move the piece (or some of its blocks) into an illegal position. An illegal configuration would have two blocks
super-imposed on the board or one or more block off of the board. This is an instance of collision detection:
would moving the piece to the proposed new position collide with any of the frozen blocks on the board (or
the edge).
The collision detection needs to be done before moving the piece (or, alternatively, the piece has to be able
to “back up” to where it was before the move) so that the piece never ends up in an illegal position. This means
that our standard FANG intersects reaction after collision will not work.
How is a Piece stored? To make scaling pieces simple (all blocks in all pieces must have the same visual
dimension and must be the same size as frozen blocks), all Piece objects should be square. Javier López, in his
on-line Tetris-cloning tutorial[Ló08], simplifies things still further by placing all tetraminoes inside of a 5x5
grid.
0 1 2 3
Figure 13.3 shows three different pieces as they appear inside the grid in each of their four rotations. Using
a 5x5 grid makes sure that all rotations of every piece fit completely within the grid; this is one of López’s most
effective simplifying contributions. The numbers along the bottom of the figure are the facing value (facing
is a field of the Piece class). Rather than being able to rotate freely, the Piece is constrained to be in one of four
facings. We number them [0-3] so that we can use modular arithmetic to cycle around the different facings.
366 CHAPTER 13. LISTS OF LISTS AND COLLISION DETECTION
The darker square marks the pivot square. It is the center of the grid. The piece rotates around that square.
It is also the center of the CompositeSprite, the point around which it will rotate. This makes showing the
Piece in different orientations very easy.
There are two different representations for the same thing: the blocks which make up a Piece are repre-
sented by actual RectangleSprite objects with given positions inside the coordinate system of the sprite and
also inside a 5x5 grid of references to RectangleSprites. Any given location in the grid is null if that position
is empty in the piece and refers to one of the blocks making up the Piece otherwise.
This same dual view is used in the Board class. The Board is a CompositeSprite but it also maintains a grid
of blocks where null means the position is free and non-null means that the position is occupied by the given
RectangleSprite. The number of rows and columns in the Board grid is set when the constructor is called.
62
109 /**
Both Board and Pieces have ArrayLists named blocks (or blocksByFacing; explanation below). These are
the grids stored as lists of rows, each row being a list of RectangleSprites. The initBlocks method creates
a 2-dimensional list of lists of blocks, initializing all values to null. In the Board, this method is called once
and the grid is then used to play the game. When a Piece is constructed, it is called four times, once for each
facing the piece can have.
31 * description of the piece in that facing. Blocks are either null
32 * (empty) or non-null (full), referring to a {@link RectangleSprite}
33 * that goes in that {@link Position}.
34 */
35 private final ArrayList<ArrayList<ArrayList<RectangleSprite>>> blocksByFacing;
36
71 this.columns = 5;
72 setColor(color);
73 position = new Position(0, 0);
74 this.facing = facing % 4;// make sure its safe
75 setRotationDegrees(90 * this.facing);
76 blocksByFacing =
77 new ArrayList<ArrayList<ArrayList<RectangleSprite>>>();
78 // for each facing, insert an empty blocks grid
79 for (int f = 0; f != 4; ++f) {
80 blocksByFacing.add(initBlocks());
81 }
82 }
83
The blocksByFacing is, conceptually, four copies of the blocks field in the board. Just as Figure 13.3 shows
four different facings for each of the pieces in it, so, too, does each Piece have a list of block grids, one for
each facing. Rather than create each RectangleSprite in each of the facings, the four blocks grids each refer
to the same four blocks. We will defer talking about that until after discussing collision detection. For now
just accept that the drawings in the figure represent the facing 0-3 blocks grids. Empty squares are null and
filled squares are references to RectangleSprites. Thus there are 25 references and 4 are to actual sprites.
How can we keep track of a Piece on the Board as it falls? It is enough to know the board position of some
reference square in piece. Because we use position a lot, it makes a useful class. A Position has a row and a
column field, both int and both with a getter and a setter. This lets us track the Position of a Piece by having
a field, position:
43
47 /**
Along with the position, we also keep track of the current facing. Each facing is a quarter of a turn off of
the previous facing. This means that there are at most four different facings since four quarter revolutions is
one full revolution, returning the spinning object back to its original orientation. Thus we track facing as an
int modulus 4.
The different facings have blocks in different locations. When a Piece is constructed, each different type
of piece fills in a list of lists of Positions: it is a list with four lists in it, one for each block in the piece; each list
has four Positions in it, one for each facing:
49 * facings so 4 different positions for each block. These are the only
50 * non-empty locations in the cells for each facing. Note that the
51 * every block appears, by reference, in each facing of cells.
52 * Protected so that subclasses can set the value after calling the
53 * {@link Piece} constructor.
54 */
55 protected ArrayList<ArrayList<Position>> blockPositionByFacing;
56
57 /**
The blocks in a piece can be numbered, 0-3 (there are 4 blocks in every tetramino). Each block has a
position inside the grid for each facing. Figure 13.4 repeats the figure of the blocks within a grid but this time
each block in facing 0 is given a number.
0 2 3 2
1 3 1 0 1 0 1 3
2 3 0 2
0 3 0 2
1 2 3 1 2 1 1 3
3 2 0 0
0 3 2
1 2 3 1 0 2 1 0 1 3
3 2 0
0 1 2 3
The numbers are assigned, in facing 0, from the top row to the bottom row and, in any given row, from the
left to the right. Then, as the piece is rotated in each facing, each number follows the block to which it was
assigned. That is, in the J piece in the top row, block 0 begins in position row 1, column 2 or (1, 2). When the
piece is rotated one quarter turn clockwise, the block moves to position (2, 3) in facing 1. Then, subsequently,
it moves to (3, 2) and (2, 1) in the next two facings.
The list of Position objects with values (1, 2), (2, 3), (3, 2), (2, 1) is the value of entry 09 of
blockPositionByFacing in J_Piece:
17 blockPositionByFacing =
18 new ArrayList<ArrayList<Position>>(Arrays.asList(
19 new ArrayList<Position>(
20 Arrays.asList(new Position(1, 2), new Position(2, 3),
21 new Position(3, 2), new Position(2, 1))),
22 new ArrayList<Position>(
23 Arrays.asList(new Position(2, 2), new Position(2, 2),
24 new Position(2, 2), new Position(2, 2))),
25 new ArrayList<Position>(
26 Arrays.asList(new Position(3, 2), new Position(2, 1),
27 new Position(1, 2), new Position(2, 3))),
9 Tracking block 0.
13.3. WHEN IT’S SAFE TO MOVE: COLLISION DETECTION 369
28 new ArrayList<Position>(
29 Arrays.asList(new Position(3, 1), new Position(1, 1),
30 new Position(1, 3), new Position(3, 3)))));
31
36 generateBlocks();
37 }
38 }
Lines 22-23 contain the value for block 0’s positions in the four facings. Lines 25-26, 28-29, and 31-32
contain the positions for each of the other labeled blocks in the J piece.
The code, lines 20-32, is a more complex application of the Arrays.asList method first presented in Sec-
tion 11.3. The method takes an arbitrary number of objects and returns something implementing the List
interface in the java.util package10 .
One use of Arrays.asList is to provide literal values for an ArrayList (or any other kind of list); the
ArrayList constructor can take a List and copy its contents into the new ArrayList.
Reading a complex structure like this, it is good to understand the type of the variable to which it is being
assigned and then read from the inside out. This is an ArrayList<ArrayList<Position>>. Lines 21-23 are as
far in as we can go. Line 21 calls new on an ArrayList<Position>. The List passed into the constructor is a
list of four Position objects. Line 23 ends with a comma because the whole ArrayList<Position> is just one
object being bundled into a List by the call to Arrays.asList on line 20. The four new ArrayList<Position>
lines (21, 24, 27, and 30) are where the four lists that will be elements in blockPositionByFacing and they will
be inserted in the order they appear in the source.
Thus blockPositionByFacing.get(2) traces the location of the block labeled 2. Notice that block 1 does
not move (as you would expect from Figure 13.4).
381 }
382 }
383
384 /**
385 * Get the blocks grid corresponding to the current facing of the
386 * {@link Piece}.
387 *
388 * @return the blocks grid (2-d list of list of {@link
389 * RectangleSprite} references)
390 */
391 private ArrayList<ArrayList<RectangleSprite>> getBlocks() {
392 return getBlocks(this.facing);
393 }
394
395 /**
396 * Get the blocks grid corresponding to a given facing.
397 *
10 A package can be defined inside of another package. Thus there is a java package containing a util package. Objects defined in that
package have package java.util as their package line. Further, there is, somewhere in the code, a directory called java which contains
a directory called util.
370 CHAPTER 13. LISTS OF LISTS AND COLLISION DETECTION
405 /**
406 * Set the facing of the {@link Piece}; protected because it is not
407 * part of the interface, just the {@link #rotateCW()} and {@link
408 * #rotateCCW()} methods are exposed.
409 *
410 * @param facing the new facing value; well be reduced modulus 4.
411 */
412 protected void setFacing(int facing) {
413 this.facing = facing % 4;
414 }
415 }
The last line in J_Piece is a call to generateBlocks. The method is long because generating one block
requires setting up both models of the Piece, both the grid which is described above and the CompositeSprite
which displays the Piece on the Board.
The outer for loop loops over all of the blocks that make up the piece; the number of blocks is the number
of entries in the blockPositionByFacing ArrayList. For all tetraminoes, this will be 4.
The block will be located (inside the CompositeSprite) in its facing 0 location. That way we can use the
regular rotateDegrees method to rotate the appearance when changing the facing. Lines 390-391 get the
facing 0 row and column for our block. Lines 394-395 calculate the location corresponding to the position.
Remember that the center of a CompositeSprite is (0.0, 0.0). Each block, in internal screens, is 0.20
wide/high. This is so the whole grid is 1.0 screens in each dimension. That makes the scaling on the board
work well.
Lines 398-408 create the new block and add it to the CompositeSprite. The color of the block is the color of
the Piece unless the block is in the pivot position. The pivot block is colored darker. This adds some interest
to the colors of the pieces and makes it easier to see how rotation is working.
The final loop, lines 411-419, processes each of the four facings. For each facing the row and column
where block belongs in the blocks grid is determined. Then the blocks grid, facingBlocks, is retrieved from
blocksByFacing, the blocksRow is retrieved from facingBlocks, and, finally, the column position in blocksRow
is set to refer to block. This last loop takes the blank grids built in the Piece constructor and populates them
with the displayed blocks. Block 0 is put in at the appropriate positions according to Figure 13.4 (or rather,
according to blockPositionByFacing.get(0)).
0 1 2 3 4
(-1, 3) (8, -1) (17, 6)
0 X
1 0 0 0
2 1 1 1
(a) 3 2 (b) 2 (c) 2
4 3 3 3
4 4 4
5 5 5
6 6 6
7 0 1 2 3 4 7 7
8 0 X 8 8
9 1 9 9
10 2 10 10
11 3 11 11
12 4 12 12
13 13 13
14 14 14
15 15 15
16 16 0 1 2 3 16
17 17 0 X 17
18 18 1 18
19 19 2 19
20 20 3 20
21 21 4 21
22 22 22
23 23 23
24 24 24
25 25 25
26 26 26
27 27 27
28 28 28
29 29 29
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
Figure 13.5 shows a J-piece at several points in its journey down from the top of the screen. Figure 13.5.a
shows the piece with a facing of 0 positioned at the top of the board. The position of the piece is (-1, 3) (shown
at the top of the figure). That is the Position of the upper-left grid square in the Piece grid (the one marked
with the X) in the grid coordinates of the Board.
That last bit is important: the Position of the Piece is expressed in the grid coordinates of the Board. Since
we are tracking the upper-left corner of the Piece grid, the Position of that grid square is always (0, 0) inside
the Piece. It is important to keep in mind the difference of the two coordinate systems.
Figure 13.5.b shows the same J-piece later in its descent. It has been rotated to facing 3 (according to
Figure 13.3) and moved to the left edge of the Board. The Piece is positioned at (8, -1). The piece cannot
be moved further to the left in its current facing because some of its blocks (to be differentiated from its grid
squares) will be off of the board. The limitation to a column coordinate of -1 depends on the facing of 3. In
facing 2, there are two blank columns on the left edge of the piece grid so it could move further over in that
facing.
The core collision detection method is in Board. It is called canMoveTo and it takes a Piece and a board
position (row, column, and facing). The board position is provided in parameters because that way we can test
a piece in different positions before we decide to actually move it.
31 /** the default color of the main board */
32 public static final Color DEFAULT_BACKGROUND_COLOR = Game.getColor(
33 ”misty rose”);
34 /** the default color of the border around the board */
35 public static final Color DEFAULT_EDGE_COLOR = Game.getColor(
36 ”dark slate blue”);
37 public static final int HIGH = 2;
372 CHAPTER 13. LISTS OF LISTS AND COLLISION DETECTION
canMoveTo returns an int; the list of constants in the listing (from the beginning of the .java file), lists
the values it might return. CLEAR means that there were no problems: all piece blocks on the board, no piece
blocks overlapping board blocks. The other 5 values indicate what kind of problem there was. LOW, for example,
means that at least one piece block is past the bottom of the board.
Back to canMoveTo. It goes through each row in the piece (the for loop on lines 144-159). Note that the
loop is both count-controlled and sentinel-controlled. The loop will exit as soon as a block in piece is found to
not be CLEAR. The inner loop (lines 148-158) loops through each column in the piece. Inside the loop a check
is made whether there is a block at position (r, c). If not, then the empty grid cannot effect whether or not
the Piece can go in the given position so we jump over the rest of the body of the inner loop.
If position (r, c) has a block, then we need to check if the corresponding board position,
(boardRow, boardCol) is on the board. positionOnBoard simply checks whether the (row, column) posi-
tion it is passed is on the board, or in the range [0-rows) for row and [0-columns) for column. Depending on
which limit is violated, the method returns an appropriate non-CLEAR value11 .
If the J-piece in Figure 13.5.b were tested in the same facing and one column to the left, then the block at
(2, 1) would be at board location (10, -1): if the board row or board column of a Piece block is off the board, then
canMove is set to something other than CLEAR by positionOnBoard, the if statement at line 153 is false so we
go back to the top of the inner loop, then back to the top of the outer loop, and canMoveTo returns a non-CLEAR
value.
If the board position of the block is on the board, it still might be on top of a block already in the game.
Check that using the hasBlock method of the Board. One advantage of using the same model of the grid for both
Piece and Board is the reuse of method names which do the same thing. Once you understand that hasBlock
will return true if a position is occupied by a block (either in Board or in Piece), then it is easy to logically use
the hasBlock method.
Figure 13.5.c shows the same piece further down, now on the right side of the board. Now the piece cannot
move to the right because it will have two blocks off of the board. How do we actually move a piece down?
11 The method is not listed here for brevity.
13.3. WHEN IT’S SAFE TO MOVE: COLLISION DETECTION 373
205 currentPiece.moveDown();
206 return true;
207 } else {
208 freeze(currentPiece);
209 int moveScore = deleteAllFilledRows();
210 if (score != null) {
211 score.increment(moveScore);
212 }
213 return false;
214 }
215 }
216
217 /**
The moveDownIfPossible method asks the piece if it can move down. canMoveDown is a wrapper method
which just fills in the position and facing of the Piece and calls canMoveTo, returning the value it gets back:
131
132 /**
133 * Can the piece move right without colliding?
134 *
277 rotateDegrees(-90);
278 }
279
The canMoveDown method exists so that we don’t mix the logic of making the current piece move
(Board.moveDownIfPossible) with the arithmetic of figuring out the row number (Piece.canMoveDown). The
Piece then uses its reference to the Board it is on to call canMoveTo with the right parameters.
If the piece can move down, moveDownIfPossible moves the piece, again by telling the piece to moveDown. It
should be noted that moveDown does no error checking. If the piece is ordered down when canMoveDown returns
false, the consistency of the game world is broken. It is safe to call in line 207 (because of the surrounding if
statement). Then moveDownIfPossible returns true (the piece moved).
If the piece could not move down, then the method returns false but not until it freezes the piece, removes
all full rows, and increments the score.
391 int boardRow = piece.getRow() + r;
392 for (int c = 0; c != piece.getColumnSize(); ++c) {
393 int boardCol = piece.getColumn() + c;
394 if ((piece.hasBlock(r, c)) &&
395 (positionOnBoard(boardRow, boardCol) == CLEAR)) {
396 RectangleSprite block
397 = acquireBlock(piece.blockAt(r, c), boardRow, boardCol);
398 blocks.get(boardRow).set(boardCol, block);
399 addSprite(block);
400 }
401 }
402 }
403 removeSprite(piece); // remove the CompositeSprite
374 CHAPTER 13. LISTS OF LISTS AND COLLISION DETECTION
404 }
405
406 /**
The freeze method has a structure very similar to that found in canMoveTo. It has two nested loops that
go over the grid of the piece passed in as a parameter. here it uses the current position and facing of the
block rather than providing one as a parameter. For each location in the piece grid, if it has a block and the
corresponding board position is actually on the board12 then the Board steals the block from the Piece. The
RectangleSprite representing the block is returned with the call piece.blockAt. The block is rescaled and
relocated according to the board’s size and the boardRow and boardCol where the block is going. Then it is
inserted into blocks and added to the Board’s list of sprites to display.
Look at lines 380-383 again. The block is retrieved from the Piece and the reference to it is used (in
acquireBlock) to recolor it and set its location. Then the grid model of the Board is updated with the addi-
tion of the block at the correct position. Finally, the visual model (the CompositeSprite model) of the Board is
updated with a call to addSprite.
Hopefully you are a little bit concerned right now. Earlier we had four different grids, one for each facing,
referring to each block in the Piece. Now we have not only the Piece but also the Board referring to same
RectangleSprite. Is this legal? Is it sound?
It is legal: recall that any number of references can refer to a single object so references from different
underlying objects is perfectly legal as far as Java is concerned. It is also sound in this case: line 405 removes
the Piece from the Board so there will be no attempt to draw the block inside of the Piece ever again. It would
still be sound, even if the Piece remained on the Board except for the fact that we rescaled and relocated the
block. Comment out line 405 and see what happens13 .
So, we transfer ownership of the blocks from the Piece and then stop paying attention to the Piece. Out
in moveDownIfPossible, after freezing, the board clears out any filled rows. A row is filled if all positions in
the row have a block in them; alternatively stated, a row is filled if it has no null references.
449 /**
450 * Is the given row full of blocks?
451 * @param rowNdx the index into {@link #blocks} of the row to check
452 * @return true if row is filled with blocks; false otherwise
453 */
454 private boolean isRowFilled(int rowNdx) {
455 ArrayList<RectangleSprite> currRow = blocks.get(rowNdx);
456 int c = 0;
457 for (c = 0; c != columns; ++c) {
458 if (currRow.get(c) == null) {
Given a rowNdx, isRowFilled traverses across the row using a count-controlled/sentinel-controlled loop.
This time, for variety (and ease of understanding the purpose of the code) the sentinel appears inside the
loop. The if statement on line 453 is the sentinel condition stated positively. The break statement on line
12 The move...IfPossible and rotate...IfPossible methods constrain movement; if they are the only ways the current piece was
moved, this check is superfluous. The check for a position being on the board is not expensive either in running time for the program,
nor for programmers understanding why it is called, so it remains here.
13 Little copies of the pieces begin to appear. They are scaled down because the Board has more blocks in its grid so each block is smaller.
They are offset from the piece on the screen because the position depends on where the piece last was on the Board. Commenting out a
line of code is one way of trying to figure out what it does; you have to be careful not to randomly change things, though. Try to predict
what the result will be to check if you really understand the code.
13.3. WHEN IT’S SAFE TO MOVE: COLLISION DETECTION 375
454 “breaks out” of the inner most loop construct in the current scope. Thus break stops execution of the for
loop, continuing execution with the first line after the loop. This means that we can tell if the loop terminated
by the count or by the sentinel by looking at the count-control variable. Since we want to check the value of
c after leaving the loop, the variable must be declared before the loop. The row is filled if the for loop ended
because c == columns.
To delete all filled rows, loop across all rows in the board and use isRowFilled to determine if it should be
deleted.
345 for (int r = 0; r != rows; ++r) {
346 if (isRowFilled(r)) {
347 deleteRow(r);
348 ++rowsDeleted;
349 }
350 }
351 return rowsDeleted;
352 }
353
354 /**
The check of rows has to go from top to bottom. If it went the other way, deleteRow on row r would move the
unchecked row r -1 into the r row (all rows above r have to move down when the row is deleted; more just
below). Then the increment in line 347 would move the index above the unchecked line. Moving down this
will not happen (the already checked line above the full line falls and then the loop moves to the next lower
line, the next unchecked line).
361 for (int r = deadRowNdx; r != 0; --r) {// counts backwards!
362 blocks.set(r, blocks.get(r - 1));// (r - 1) >= 0
363 ArrayList<RectangleSprite> currRow = blocks.get(r);
364 for (int c = 0; c != columns; ++c) {
365 if (currRow.get(c) != null) {
366 currRow.get(c).setLocation(locationFromRowColumn(r, c));
367 }
368 }
369 }
370 // add a new, empty row at the top.
371 blocks.set(0, initRow());
372 }
373
374 /**
375 * Remove the sprites in the deadRow from the display of the {@link Board}
376 * @param deadRow the row of sprites (and, perhaps, nulls) to remove
377 */
378 private void removeRowOfSprites(ArrayList<RectangleSprite> deadRow) {
379 for (int c = 0; c != columns; ++c)
380 if (deadRow.get(c) != null)
381 this.removeSprite(deadRow.get(c));
382 }
383
384 /**
In deleteRow, first all of the blocks in the dead row are removed from the Board (the CompositeSprite
model) using removeRowOfSprites. While it is assumed that deleteRow is called only on filled rows, it is a good
idea to check the blocks to make sure you don’t try to remove null values. removeRowOfSprites shows how
the curly braces after a for or if statement are optional: the if controls the execution of the next statement;
if there is only one statement, the curly braces are optional and if there are more than one, the curly braces
make the multiple statements into a block which can be treated as a single statement.
At line 363 the rows are taken from the dead row’s index up to 0. This for loop counts backwards. Each
time through the loop the row above (at index r -1) is moved down in blocks (line 367). That fixes up the
grid model. The blocks in the row that moved must be relocated, too. That is what the loop in lines 369-372
does. Finally, after row 0 was moved down to row 1, make a new row 0. initRow was used in building the new,
empty board grid and returns a new, empty row, just what we need at index 0.
deleteAllFilledRows returns the number of rows which are deleted; the Board adds that value to the score.
This section examined using two different models for a game simultaneously. While it might seem contrary
to the DRY principle, two different models are not repeating one another but serving different purposes. In
BlockDrop the two models are the grid and the CompositeSprite. Blocks (RectangleSprites) are kept in both
models.
The simple, grid-based model is used to check for collisions and to keep track of the blocks. By having all
facings of a given Piece take up the same space, rotating a piece does not require any fixing up of the position.
The CompositeSprite model, the screen model used by FANG since the beginning of the book, uses double
values to keep track of locations. Blocks must be scaled so they appear properly within the visual representa-
tion.
The next section looks at how another factory is used as a polymorphic constructor, how the high score list
is implemented using a CompositeSprite with its own advance method, and a few other small finishing details
for BlockDrop.
Another Factory
Part of the gameplay of BlockDrop is being able to see into the future. While maneuvering a piece as it falls, the
play can see the next piece which will be served up. In FANG terms, this means the next Piece, a Sprite, must
be displayed on the screen. To keep the scale of the Piece consistent with the size it will have on the Board, the
new Piece is generated by a CompositeSprite-derived class called the NextPieceBox. The NextPieceBox is a
square scaled to match the grid of one Piece. The scaling is accomplished by passing the blockSize determined
by the Board into the NextPieceBox constructor.
13.4. FINISHING BLOCKDROP 377
The piece inside the box can be accessed through a getter method; that is necessary so that when the
currentPiece is frozen in the Board, the piece in the NextPieceBox can be added to the game. The one other
public method of NextPieceBox is nextPiece. Calling that method has the box create a random new Piece.
Since there are seven different kinds of pieces, the code to do this is a factory.
70 public void nextPiece() {
71 // remove old piece from display
72 if (piece != null) {
73 removeSprite(piece);
74 }
75
The nextPiece method is broken into three parts: get rid of the old piece (if there was one), generate
random piece number, facing, and contrasting color, and finally call the right constructor to create the piece
that was randomly selected.
Lines 71-75 are straightforward. The old Piece has probably been added to the Board so it should no longer
be displayed.
Lines 76-80 are also understandable, so long as we accept that getRandomContrastingColor does what its
name claims.
In TwentyQuestions the factory code used a String value found in a text file to determine what kind of
AdventurePage to create (see Section 12.1). Here, instead of encoding the class of the Piece as a String, we
encode it as an int. Since there are 7 possible pieces (the number is stored in the Piece.PIECE_COUNT constant),
we generate a non-negative int less than 7 and then use a multi-way if to call one of 7 constructors. The result
is stored in a reference to a Piece and the Piece is added to the NextPieceBox’s display.
378 CHAPTER 13. LISTS OF LISTS AND COLLISION DETECTION
Contrasting Colors
How can getRandomContrastingColor work? First, what does it mean for two colors to be contrasting? For
the our purposes here, all colors are either light or dark and opposites contrast. How can we tell a light from
a dark color? Take the three channels of color information, red, green, and blue. Each is an int on the range
[0-256). Average the three channels together. If the average is greater than or equal to half of the range (128)
then the color is light; less than half and it is dark.
134 private Color getRandomContrastingColor(Color bgColor) {
135 int low = 128;// assume we want a light color
136 int high = 256;
137 int averageBrightness =
138 (bgColor.getRed() + bgColor.getGreen() + bgColor.getBlue()) / 3;
139
If the parameter color, bgColor, is dark, we will generate numbers in the upper half of the range for each
color channel of the random color and if bgColor is light, the lower half. Lines 135-136 assume we want a light
color. If the average brightness of bgColor is in the light range, change the high and low limits for the random
numbers. Then, finally, just generate a color from three random integers.
Scrolling List
When we show the high scores list, how can we make it interesting? Further, how can we show scores than
would comfortably fit on the screen at one time? The answer is that we could scroll the names on the screen.
That is, have a CompositeSprite positioned so that everything below a certain point is not visible on the
screen and move items up the screen, wrapping them to the bottom of the list when they go off the top of
the CompositeSprite.
Everything is stuff we have done before: the StringSprites in the list are sprites with velocity, moving a
sprite until it hits some horizontal line is dealing with falling apples, even relocating the sprite as it crosses
one horizontal line back at the beginning horizontal line is from NewtonsApple. Doing all of this with several
sprites at the same time in a ArrayList extends what was done in previous games but we have moved groups
of sprites before as in RescueMission.
The public interface (not to be confused with a Java interface) for ScrollingMessagBox is a little like what
we would have for a game because it contains other sprites and advances them.
class ScrollingMessageBox
extends CompositeSprite {
ScrollingMessageBox()...
ScrollingMessageBox(ArrayList<String> msg, double velocity, int gap)...
13.4. FINISHING BLOCKDROP 379
Hopefully it is clear that the get/set comment expands into six methods. Further, it is hopefully obvious
how to write the six methods (or will be once you know the types of the values). setColor is necessary so that
the color set to the sprite is propagated to the lines. The ScrollingMessageBox has no background or edges;
those must be provided by the client code if they are required.
The primary ScrollingMessageBox constructor takes three parameters: the list of messages to scroll, the
velocity to scroll the messages, and the gap between the last message and the repeat of the first message.
The gap is expressed in blank lines between the last message and the starting over of the message list. It
increases the length of the cycle as if there were gap blank messages at the end of the list. The default con-
structor passes in an empty list of String and default values set in the public static final constants of
ScrollingMessageBox. The line height, in internal screen coordinates, is set to a default in the constructor. It
can be changed with the appropriate get/set methods.
91 public void advance(double dT) {
92 scrollMessages(dT);
93 wrapIfNecessary();
94 }
189 private void scrollMessages(double dT) {
200 private void wrapIfNecessary() {
201 StringSprite top = messages.get(indexOfTopLineOnScreen);
202 if (top.getMinY() < TOP_EDGE) {
203 int indexOfBottomLineOnScreen = (indexOfTopLineOnScreen +
204 (messages.size() - 1)) % messages.size();
205 StringSprite bottom = messages.get(indexOfBottomLineOnScreen);
206 top.setY(bottom.getY() + lineHeight);// top just below bottom
207
The primary public method is advance which calls two implementation methods, one to move all the lines
according to the velocity and one to wrap. The movement is done in ScrollingMessageBox.scrollMessages
rather than inside a StringSprite-extending sprite because all of the sprites share the same velocity.
The wrapIfNecessary method gets the StringSprite with the highest y-coordinate and checks if it is passed
the top edge of the “screen”. If it is, the StringSprite is relocated to one line height below the line with the
lowest y-coordinate. The scrolling is only designed to work going up the screen. If the line moved had index
0, then the gap is opened up between the last and first lines.
380 CHAPTER 13. LISTS OF LISTS AND COLLISION DETECTION
When we checked whether an apple hit the ground or if swimmers hit the edge of the screen, we checked
all of the sprites. How does wrapIfNecessary find the StringSprite with the highest y-coordinate? Or the one
with the lowest y-coordinate for that matter.
One way it could work is to search the ArrayList<StringSprite> messages for the highest/lowest
y-coordinate (just call getY() on each element). This sprite uses an alternative: it has a field called
indexOfTopLineOnScreen. The field is initialized to 0 because lines are located from top to bottom of the
sprite. Line 212 in wrapIfNecessary advances the field to the next highest line on the screen after the old
top is wrapped.
Finding the other extreme, the lowest y-coordinate, just means finding the one before. Lines 203-204 do
the arithmetic. The expression in those two lines should be read as
(indexOfTopLineOnScreen -1) % messages.size()
The problem with using that simple statement is that computer modular numbers are not the same as mathe-
matical modular numbers. In mathematics, modulo 17 (just to pick a modulus), (0 -1) % 17 is 16; this makes
sense because (16 + 1) % 17 is 0 and mod 17 arithmetic limits the values to [0-17).
In Java and most computer languages, modular arithmetic limits the values for mod 17 to (-17-17). That
is, -1 is a valid value and makes sense to be the value of the expression (0 -1) % 17. Unfortunately, -1 is not
a good value to use as an index into an ArrayList. So, instead of subtracting one, in Java we add the modulus
minus one. That is, we evaluate (0 + (17 -1)) % 17. This expression evaluates to 16 in Java and to 16 in
mathematics.
Since the modulus for the indexes of the messages list is the size of the list, on line 204 the size
minus one is added to the index and then the modulus of that sum is taken. This makes sure that
the value of indexOfBottomLineOnScreen is the index right before (in a cycle modulus messages.size())
indexOfTopLineOnScreen.
13.5 Summary
Java Templates
Programming Problems
This chapter brings together all of the topics presented so far while examining a text adventure game. A text
adventure game is a game where the player explores a virtual world; the exact goal of the game depends on
the contents. It is called a text adventure game because the world and everything in it is described in text and
the user’s interaction with the game is by typing commands.
Creating the game engine is only half of the effort in creating a text adventure game: every object in the
world must be described in a data file. To support game authors, this chapter examines a flexible data file
formatting, what Jon Bentley calls a “self-describing data file”[Ben88].
Text adventure games require more sophisticated string processing than we have yet seen. User commands
are short declarative sentences like “get ticket” or “talk Dean of Students” or “move north”. The last one can
be shortened to “north” in most games. Taking apart a sentence like this involves parsing the string, breaking
the whole thing up into syntactic units. Remember that syntax, when applied to programming languages,
talks about structure. The same is true when writing a simple parser: the parser breaks the string into tokens,
words and symbols defined as being significant. Then the sequence of tokens is processed to figure out the
meaning, the semantics, of the user command.
Similar parsing tasks are necessary when processing a self-describing file. Rather than having each record
in a text file have exactly the same ordering, each field is identified by name. The value of a field might be just
one line or multiple lines long so the syntax of the data file must indicate where the field values begin and end
and the parser must be able to handle them. Finally, printing descriptions for the user requires wrapping the
text so no lines are too long to display. Wrapping is frequently used in word processors and other text editors.
The scale of the text adventure game program lends itself to incremental development, the creation of in-
creasingly complete and complex “versions” of the program. Incremental development permits the program-
mer to focus on one piece at a time and is one component in the currently popular development methodology
known as agile development.
First a quick introduction to what a text adventure game is and a high-level design of the game. Then the
project is broken into phases for incremental development starting with reading the data files then working
with the commands in the game.
user with text on the screen. This section will give a broad overview of how a text adventure game works
and discuss the design of a game engine (it will also define the difference between a game and a game engine).
Limited to text input and output, interactive fiction is well-suited to less CPU powerful computing devices such
as smart phones and MP3 players. They are also well-suited to our abilities to read and write from standard
input and standard output.
Interactive Fiction
Text adventure games or, in the more mod-
ern parlance, interactive fiction, is a medium for
gameplay and storytelling which came into be-
ing before there were computers to play the
stories. It is related to hypertext and experi-
mental fictions such as the French Oulipo move-
ment. The genre came into full flower dur-
ing the 1980s because the personal comput-
ers of the time were well suited to displaying
text, perhaps in different colors, but they were
not yet powerful enough to display bit-mapped
graphics1 .
Experimental novels and poems from the
Figure 14.1: zoom running a z-machine port of Advent late 1950s and early 1960s were sometimes
printed in interesting, mixable ways permitting
the reader to interact with the ordering of the
story as well as with the story itself. Raymond
Queneau, a French poet, wrote Hundred Thousand Billion Poems, a book of ten sonnets. Each sonnet, having
fourteen lines, was cut into fourteen strips of paper. Thus there were 1014 different poems in the book
because each strip could display the line from any of the ten sonnets. This same mechanism has been
used by Ray Kurtzweil to have a computer generate poetry.
In Section 12.1 we saw that Choose Your Own Adventure books are also interactive fiction printed on
paper. A paragraph or two atop each page offers the reader a choice. At the bottom of the page each
choice planned by the author is listed along with a page number. The reader turns to the page number
corresponding to their choice of action.
Computerized interactive fiction traces its history back to William Crowther’s Advent, also known as
Colossal Cave Adventure (file names had a limit of six characters in their name). Crowther was an ama-
teur spelunker as well as a systems programmer on the early ARPANet (predecessor to the Internet). He
wanted to share his passion for exploring caves with his daughters so, in 1974, he wrote Advent. The orig-
inal Advent was written in the FORTRAN programming language, one of the oldest compiled languages
which focused on scientific/mathematical programming (the name is a contraction of FORmula TRANSla-
tor).
Because he was a network programmer, he put his program source code out where other ARPANet
programmers could find it. Many of them compiled it and passed it on. Others modified or extended it.
In 1976 Don Woods expanded Adventure, adding many fantasy elements influence by J.R.R. Tolkein. Then,
in 1978 Scott Adams wrote Adventureland which he advertised and sold. This was the first commercial
text adventure game. If you want more information on Adventure, visit http://www.rickadams.org/
adventure/ (that is where the version shown in the figure was obtained).
14.1. BACK TO THE FUTURE: INTERACTIVE FICTION 383
The World
The world of a text adventure game is a collection of locations which are connected, one to another. In these
locations there are critters, characters inhabiting the virtual world. Locations and critters can have items in
their possession which can be picked up by, stolen by, or perhaps traded for by the player. The player is also
a critter because they have an inventory, a collection of items.
This description is intentionally very general. Exactly how locations are connected one to another depends
on the world being modeled. If the game world is on the surface of the Earth, it would make sense to discuss
locations being laid out in a grid with locations laying to the “north”, “south”, “east”, and “west” of a given
location; it should be possible that a location have no location in a given direction so that the location is at
the edge of the map in that direction. If the game world represents the interior of a submarine, “fore”, “aft”,
“port”, “starboard”, “up”, and “down” might all make sense. A space station, a system of deep caves, or a
collection of airports might each have a connection scheme different than either above.
Critters and items differ primarily in how the player can interact with them. A critter is something to
which the player can speak or with which the player can fight or barter. Note that any given game might have
none, some, or all of these options. A critter might represent a talking garden gnome, a superhero in blue
spandex, or a clanking robot. A critter might even be a vending machine if the interaction with the vending
machine is more like that of interacting with other actors in the drama rather than in interacting with things.
Items can be picked up or dropped. Some might be able to be eaten or drunken, read or, perhaps, even combined
to make some new item. It is possible that the connections from one location to another might require the
player to possess some particular item (generically a key) to pass or require the player to have met some
particular critter.
Note that the last several examples show that text adventure games on a computer go well beyond what
is possible in a Choose-Your-Own-Adventure-Book. In the book, no matter how you turned to page 57 the
choices presented to you are the same. So if you got there after the second page you read or after bouncing
384 CHAPTER 14. STRING PROCESSING: INTERACTIVE FICTION
through more than a hundred pages of epic battles, whatever happens on and after page 57 is the same. The
player critter has state which can be checked at any time. If the player must have visited the great elf before
leaving the forest, this can be enforced. If killing the dragon precludes any happy ending, then lock the happy
ending choices after the dragon dies. The key here is that the computerized game has state which can be used
to permit choices at one point in the game to have consequences in another part.
GameObject
uuid:UUID
name:String
description:String
inventory:ArrayList<Item>
extends extends
Location Item
visitors:ArrayList<Critter> ownerID:UUID
north,south,east,west:UUID owner:GameObject
nKey,sKey,eKey,wKey:UUID ownedDescription:String
winningLocation:boolean attackStrenght:int
extends
Critter
health:int
locationID:UUID
location:Location
hurtBy:ArrayList<UUID>
tradesFor:
ArrayList<UUID,UUID>
What fields define a location, critter, or item? Figure 14.2 shows an inheritance diagram indicating the
fields in each object.
Each game item has a universally unique identifier (UUID). A UUID is unique within the collection of all objects
in a given game. Each field is followed by its type; the type UUID means whatever type we choose for UUIDs.
The UUID, UUID is just a placeholder for a type to be defined later: it indicates that a critter will trade one item
for another item.
Unique identifiers permit any object to refer to another by its UUID. This should sound familiar: the page
number in TwentyQuestion served as a UUID for the AdventurePages. We could, again, use a serial number to
identify each item; the UUID of an object would be implicit in its position in the data file, a record number
counting from the beginning of the file.
Consider how difficult it was to write and read (as a human) the short data files for TwentyQuestions. With
just 8 entries, keeping track of the index number of each and having a given question refer to its yes and no
children was not easy. Explicit UUIDs, included in each object’s entry in the data file, make it much easier
to refer from one object to another. What data type should the UUIDs have? The choices seem to be int or
14.1. BACK TO THE FUTURE: INTERACTIVE FICTION 385
String. The int would make it possible to use the UUID as an index into an ArrayList to make looking things
up easier.
Hopefully you read that last sentence with some discomfort. It mixes different levels of abstraction in a
very dangerous way. When designing a class to hold associated information, it is not appropriate to worry
about how a collection full of the class will be structured. It is enough to assume that we can look up a game
object in a collection of game objects and, based on the UUID, find the one we want.
So, one option for UUID remains int. Does String have any advantages over int? For the game author the
answer is, “Yes!”
Imagine that an item has a field, call it owner. The owner of an item is the critter or location where that
item currently resides. The data file will contain something that says the owner of the Dagger of Ensidor is
either 1003452 or GrummTheBarbarian. Which would make the data file easier for the human being to read,
write, and debug?
An item, a location, and a critter each have a UUID. They also each have a name (the Dagger of Ensidor or
Grimm the Barbarian), and a description (the text displayed on the screen when the player enters the location or
sees the critter or item). Working on designing the hierarchy showed that critter and location both needed an
inventory, a list of items held by that object. Factoring the inventory and its routines out of those two classes
and putting it in the game object shortened the code. It also made it possible to implement container objects
(like a suitcase); the code to actually use container objects is not part of the engine.
A location has some number of links to other locations, the connections that let players move around. For
our game engine the fields are the four cardinal directions and each records a UUID. There are also corre-
sponding “key” fields. A key is some item or critter which must be present (or must not be present) to permit
traveling to a neighboring location. Explicitly naming the directions in the data file as in north = infirmary
makes writing a game data file much easier; there is no requirement to list all the possible out directions,
most saying “no such place”. Labeling also removes the need to remember the exact order of the outgoing
connections (does “nsew”, “news”, or “ensw” make more sense in ordering them?).
Critters have health, a list of items that hurt them, and a list of item pairs that they are willing to trade for.
These exact features fit the “game” designed for this chapter. More importantly they provide an opportunity
to look at how to read values from data files with different types: health is an integer, the hurt by is a list of
strings, and the trade list is a list but the values are, themselves, structured (each contains two UUIDs).
Items have an owner (stored as an owner UUID in the data file). Each has an optional “owned description”.
The owned description is description text added to the description of any critter or location which owns the
object. For example, the CheddarCarp, which are originally owned by the VendingMachine adding the sentences
“There is a single package of Ridge River brand Cheddar Cheese Carp, ”The cheese cracker that tastes like a
fish.” It is marked $0.25.” to the description of the vending machine when it is examined. The attack strength
of an item is the amount of damage it does to critters hurt by it. The game engine is not designed around
combat but there is a need, at times, to be able to remove a critter which is in the way.
These lists are minimal and focused on the game designed for this chapter, Escape from T-Hall. The next
section presents the design for the game with emphasis on the interesting interactions (and data structures
needed to make them work) such as “hurt by” and “trades for”. The section after looks at reading self-
describing data files and the section after that looks at the game code.
Review 14.1
(a) Why would it make more sense to have a vending machine be a Critter rather than an Item? Does the player
need to be aware of this choice?
(b) What advantages would there be of enforcing a more structured UUID? All locations begin “LOC:”, for
example. Would there be any disadvantages?
(c) Why does it seem that all decisions are being made to make the life of the game author easier?
386 CHAPTER 14. STRING PROCESSING: INTERACTIVE FICTION
(d) Think about how you would store the map. It is a collection of Location objects. How would you find a
particular location given its UUID? How long would that take on a large map?
56
Ke y #2
bumble Hall
!Dr. Dun
bumble
Dr. Dun
Major's Room
F lo re n ce
Critte r Ke y #2
56
Figure 14.3 shows the map for the game. The squares are the locations. The directions between them are
the obvious ones (north is to the top of the figure). The rooms contain critters and items. In the two rooms on
the east with both a critter and an item the critter actually has the item and you must trade for it. This design
provides motivation for the data files which we will read in the next section.
Notice the block marks on the two links in the northwest. These indicate that those links can only be
crossed if key conditions are met. The game has “positive” and “negative” keys: some keys are items or
critters which must be present before a link is available while others are items or critters which cannot be
present for a link to be available. The “!” in front of “Dr. Dunbumble” on the link between the Entryway and
the Hallway indicates that the professor blocks that connection. There is no “!” on “Key #256” so that item
must be in the player’s possession before they can get to The Quad
14.3. READING THE DATA 387
• be order independent - with field identifiers the fields can come in any order
We will start by writing a program, CountCharacters modeled on our earlier EchoFile program, which will
read a file named on the command-line character-by-character, counting the total number of characters in the
file; the listing only shows the count method which actually reads the file.
46 public void count() {
47 if (file.exists() && file.canRead()) {
48 FileReader reader = null;
49 try {
50 reader = new FileReader(file);
51 int characterCount = 0;
52
53 int ch = reader.read();
54 while (ch != EOF) {
55 ++characterCount;
56 ch = reader.read();
57 }
58 System.out.println(file.getName() + ”: ” + characterCount);
59 } catch (FileNotFoundException e) {
60 System.err.println(”PANIC: This should never happen!”);
61 e.printStackTrace();
62 } catch (IOException e) {// file was opened before this exception
63 System.err.println(”Problem while reading \”” + file.getName() +
64 ”\”.”);
65 e.printStackTrace();
66 } finally {
67 try {
68 if (reader != null) {
69 reader.close();
70 }
71 } catch (IOException e1) {
72 System.err.println(
73 ”Error closing Reader assciated with + \”” +
74 file.getName() + ”\”.”);
75 e1.printStackTrace();
76 }
77 }
78 } else {
79 System.err.println(”Unable to open \”” + file.getName() +
80 ”\” for input”);
81 }
82 }
CountCharacters is structured just like ExistsFile (see Listing 10.10) where the main method treats all
command-line arguments as file names, constructs an object of the right type (CountCharacters in this case)
with the file name and then calls the processing method, count. The field file is a File associated with the
given file name.
count is twice as long as echo (Listing 10.11) and most of the code is part of the exception handling. Recall
that when Java processes a try...catch statement the code in the try block is executed (lines 49-59) and if an
exception is thrown, the first catch block following the try which matches the type of exception thrown will
be executed.
390 CHAPTER 14. STRING PROCESSING: INTERACTIVE FICTION
Whether an exception was thrown or not, after finishing the try..catch block, the program will execute
the finally block (if any) associated with the try. This makes the finally block a great place to close files.
If the new in line 50 succeeded, reader is non-null and should always be closed. It is in line 69 which is only
executed if reader was assigned a value in line 50.
Back to the try: just like a Scanner a FileReader can be constructed from a File object. Assuming no
exceptions any open Reader provides a read() method which returns an int. The return type is int to accom-
modate Unicode returns. Each call to read() reads one character from the underlying stream, advancing the
current position in the file, and returns the value. If the stream has been exhausted read() returns -1. This
class defines a constant, EOF to make the loop on lines 54-57 easier to read.
If I run the program on its own input file, the following output is generated:
~/Chapter14% java core.CountCharacters core/CountCharacters.java
CountCharacters.java: 2330
Notice that the name of the class for Java to run is the package name, a dot, and the name of the class. The
name of the file which is a parameter to the program is the name of the folder, a slash, and the full name of
the file.
When constructing a Scanner, if we pass it a File, under the hood, the Scanner constructs an
InputFileStream, the byte reading version of a FileReader. We can provide the Scanner with a FileReader to
use as its input source.
50 public void echo() {
51 if (file.exists() && file.canRead()) {
52 FileReader reader = null;
53 try {
54 reader = new FileReader(file);
55 Scanner echoScanner = new Scanner(reader);
56 String line;
57 while (echoScanner.hasNextLine()) {
58 line = echoScanner.nextLine();
59 System.out.println(line);
60 }
61 } catch (FileNotFoundException e) {
62 System.out.println(”PANIC: This should never happen!”);
63 e.printStackTrace();
64 } finally {
65 if (reader != null) {
66 try {
67 reader.close();
68 } catch (IOException e) {
69 System.err.println(
70 ”Error closing Reader assciated with + \”” +
71 file.getName() + ”\”.”);
72 e.printStackTrace();
73 }
74 }
75 }
76 } else {
77 System.out.println(”Unable to open \”” + file.getName() +
78 ”\” for input”);
79 }
80 }
14.3. READING THE DATA 391
EchoFileWithReader behaves just like Chapter 10’s EchoFile. The File is used to construct an FileReader
which is then used to construct the Scanner. The FileReader is what is closed in the finally block; it is possible
the reader was initialized and echoScanner was not so, to make sure the file is closed no matter what, we close
the low-level FileReader if it is non-null.
36 ch = in.read();
37 if ((ch == ’{’) || (ch == ’}’)) {
38 ch = ’|’;
39 }
40 return ch;
41 }
53 public int read(char[] text, int offset, int length)
54 throws IOException {
55 if (endOfStream) {
56 return -1;// end already reached
57 }
58
59 int charCount = 0;
60 for (int i = offset; i < (offset + length); i++) {
61 int temp = this.read();
62 if (temp == -1) {
63 endOfStream = true;
64 break;
392 CHAPTER 14. STRING PROCESSING: INTERACTIVE FICTION
65 }
66 text[i] = (char) temp;
67 charCount++;
68 }
69 return charCount;
70 }
80 public long skip(long n) throws IOException {
81 char[] chArray = new char[(int) n];
82 int charCountSkipped = this.read(chArray);
83 return charCountSkipped;
84 }
85 }
The FilterReader has a protected constructor. This is so that no one can construct one (this is similar
to writing an abstract class), it is only possible to construct some subclass of FilterReader which declares a
public constructor. NoCurlyFilterReader does that. It takes a Reader as its parameter and passes the object
to FilterReader’s constructor. It also initializes a field which flags whether or not we have reached then end
of the input stream. This will be explained below.
Lines 33-41 override the read() method. You can see, at line 36, that we call read() on the internal Reader
provided as a protected field in FilterReader. If we didn’t want to make any changes to the characters we
could just pass ch back. Instead we check if the character is a curly brace. If it is, we set the value of ch to a
vertical bar. Then line 40 returns ch.
There is one other read method we must override because it calls in.read directly (so it would not have
any changed characters). That method is read(char[] text, int offset, int length). The [] show that the
first parameter is an array. length is the number of characters that should be read into the array and offset
is the index where the first read character is to be stored. This is known as a buffered read where the array of
characters is a buffer for holding some number of characters. This is the most general header of a buffered
read and while there are other buffered read methods, they are all defined in terms of this one. Another win
for Do Not Repeat Yourself: because the most general version can do what all the others do (with the right
parameters), it is the only one that needs to be written or, in our case, overridden.
Looking at the comments in the JavaDoc for read, we find
read
public abstract int read(char[] text,
int offset,
int length)
throws IOException
Parameters:
text - Destination buffer
offset - Offsetset at which to start storing characters
length - Maximum number of characters to read
Returns:
The number of characters read, or -1 if the end of the
stream has been reached
14.3. READING THE DATA 393
Throws:
IOException - If an I/O error occurs
The characters must be read into the array starting at text[offset] and continuing until
text[offset+length-1]. The return value is the number of characters actually read into the array or, if
the end of the input stream has been reached, -1.
This is where endOfStream comes in. When reading the last bunch of characters from the input, the
buffered version of read will read in fewer than the required number. If the first call to read() (line 62)
returns -1, then the buffered method returns 0 for the character count read. If, instead, the tenth call returns
-1, then the buffered call returns 9. In any case, when the call to read() returns -1, indicating the end of the
input stream has been reached, endOfStream is set true. Any future call to the buffered read will return -1 (see
line 55 following).
So long as read() does not return -1, then the each entry in text, starting with text[offset], is set to
the next character read. To set an element of an array, the array name, the square brackets, and an index,
appear on the left-hand side of an assignment operator as in line 66. The character count, the value returned,
is incremented each time a character is added to the array.
This buffered version of read makes use of read() in line 61; this is used to read every character put in
the buffer. This is important because it keeps us from having to repeat character replacement logic in the
buffered read method. This exact buffered read code can work with multiple read() implementations (as we
shall see below)4 .
Line 80 begins the override of skip(int). The FilterReader version of the method just calls super.skip(n),
returning the result. This method makes use of the buffered read methods which eventually calls the one
defined at line 33. Because this filter replaces a single character with a different single character the number
of characters read does not change so overriding skip is not, strictly speaking, necessary. Since read() could
(and later, will) change the number of characters it reads and it returns, it is good practice to override skip
as well as the general buffered read.
Decorators
A FilterReader or a class which extends FilterReader is an example of a recurring pattern in computer sci-
ence, a pattern which comes up often enough to have its own name. These classes are decorators. A decorator
is a class which wraps around an object (or a set of objects) of the same type.
One popular software design pattern book, Head First Design Patterns, by Freeman, et. al.[FFBD04], discusses
decorators in terms of coffee drinks. The following discussion is loosely based on theirs (but much less com-
plete).
A coffee drink is something you can purchase at the café. An abstract class, CoffeeDrink, represents all
drinks. Part of the public protocol of CoffeeDrink is getPrice() so that the system can determine what to
charge customers.
When you order a drink you can request a flavor shot of chocolate, vanilla, or cinnamon. Any drink plus a
flavor shot yields a coffee drink. The flavor shot is a decorator: it is both a coffee drink and a modifier of a coffee
drink. It is possible that the coffee drink being decorated is another flavor shot.
Look at the inheritance diagram, Figure 14.4. The three coffee drinks, Coffee, Espresso, and FlavorShot
are shown, each extending the abstract class CoffeeDrink. Each box represents a class and each arrow rep-
resents the extends relationship, pointing at the child class.
When ordering an Espresso, the system constructs a Espresso object. This situation is shown in Fig-
ure 14.5(a). What happens when you get a Coffee with a chocolate FlavorShot? Just like the barista makes the
coffee and then augments it with the flavoring the system constructs a Coffee object and passes that object
into the constructor for a FlavorShot. The resulting composite object is seen in Figure 14.5(b). Finally, when
a customer orders an espresso with vanilla and cinnamon, the result is seen in Figure 14.5(c).
4 In fact, the implementations of read(char[], int, int) and skip(int) are slight variations on versions written for a completely
CoffeeDrink
ex
ds public abstract te
t en double getPrice(); nd
ex s
Coffee Espresso
extends
public public
double getPrice(){ double getPrice(){
return 1.50; return 3.50;
} }
FlavorShot
private CoffeeDrink base;
private String flavor;
public double getPrice(){
return base.getPrice() +
0.50;
}
When checking out, the system ask the CoffeeDrink object to getPrice() so it knows how much
to charge. For object (a) in the figure Espresso.getPrice() is called and returns 3.50. For object (b)
FlavorShot.getPrice() is called. Notice that the first thing it does is call getPrice() for whatever drink the
flavor shot was based on. This calls Coffee.getPrice() which returns 1.50. When Coffee.getPrice() returns,
FlavorShot.getPrice() adds 0.50 to the value and return it for a price of 2.00.
The double-flavored espresso in (c) is handled similarly with the sequence of calls going from
FlavorShot.getPrice() (cinnamon) to FlavorShot.getPrice() (vanilla) to Espresso.getPrice(). These re-
turn, in reverse order, 3.50, 4.00 (vanilla), and 4.50 (cinnamon). The flavors in parentheses are just to differ-
entiate the two flavor shots (to show the last-in-first-out nature of the calls).
Back to our FilterReader. Notice that read() does call the inner Reader’s read() method. This is a com-
mon aspect of a decorator: it wraps around some object, providing the same public protocol, and the routines
implementing the protocol call into the wrapped object. The decorator does more than just forward calls,
though. It also decorates the results or changes the values coming back. By implementing the same public
protocol (either by extending the same object or literally implementing the same Java interface), the deco-
rated object can be used in all the same places the undecorated object could be used.
53 public void echo() {
54 if (file.exists() && file.canRead()) {
55 Reader reader = null;
56 try {
57 reader = new NoCurlyFilterReader(new FileReader(file));
58 Scanner echoScanner = new Scanner(reader);
59 String line;
60 while (echoScanner.hasNextLine()) {
14.3. READING THE DATA 395
(b)
(c)
Figure 14.5: Decorated CoffeeDrink Objects
61 line = echoScanner.nextLine();
62 System.out.println(line);
63 }
64 } catch (FileNotFoundException e) {
65 System.out.println(”PANIC: This should never happen!”);
66 e.printStackTrace();
67 } finally {
68 if (reader != null) {
69 try {
70 reader.close();
71 } catch (IOException e) {
72 System.err.println(
73 ”Error closing Reader assciated with + \”” +
74 file.getName() + ”\”.”);
75 e.printStackTrace();
76 }
77 }
78 }
79 } else {
80 System.out.println(”Unable to open \”” + file.getName() +
81 ”\” for input”);
82 }
83 }
The echo method of EchoFileWithFilterReader is, except for line numbers, identical to the echo method
of EchoFileWithReader except for the line assigning a value to reader (line 57 here, line 54 there). Here the
reader is a NoCurlyFilterReader wrapped around a FileReader. The Scanner, written to use the interface, does
not care that the object type is different. It reads the file which is decorated by the FilterReader so that all
curly braces are replaced with vertical bars.
396 CHAPTER 14. STRING PROCESSING: INTERACTIVE FICTION
It is possible to decorate something by removing something as well as by adding something. How would a
FilterReader remove end-of-line comments from a text file as it reads it? Such a filter would help in writing
a self-describing file reader.
Preprocessing Comments
The first step to compiling a Java program is to break it down into tokens. A token is a string that has meaning to
Java: keywords, operators (+, ==, etc.), punctuation ({, }, ;, etc.), integer and double literals, and string literals.
You should convince yourself that processing the stream of input characters to tokenize the source code is
not a trivial task (consider how you would handle quoted strings with escape characters or even identifiers).
Imagine, while tokenizing, you also had to juggle comments: when you’re inside a comment, the sequence
“int” means nothing; when outside comments, “int” is a keyword.
The Java tokenizer would be much simpler to write if only programmers would leave out comments5 . If
comments are necessary, perhaps we could split the tokenizing task in two: first process the stream of char-
acters into another stream containing only those characters not in comments. That is, filter out the comment
text. The tokenizer is then much easier to write and the two processes together, comment stripper and tok-
enizer, are probably simpler to write and understand than a comment-avoiding tokenizer6 .
It is similarly easier to preprocess a data file to remove comments and then process the comment-free
records rather than read information with a Scanner and have to worry about inside/outside comment. A
FilterReader can process an incoming stream of characters into a different sequence of characters, filtering
out all of the comments in the self-describing data file.
To further simplify the job of stripping comments we will only support end-of-line comments. So, how
can you filter out Java-style end-of-line comments?
Given a Reader reading the following character sequence:
Line one // beginning
// this whole line is blank
three/visible right here
//
Line five (blank above^)
SansCommentFilterReader is-a FilterReader. It scans its input for the // sequence and skips over the
characters from the first slash to the end of the current line. The characters are skipped by reading them
from the input FileReader but not returning any of them as the result of calling read. End-of-line comments
will be “replaced” with the end-of-line marker.
Figure 14.6 shows our file on disk being read by a FileReader. The sequence of characters read is shown on
the arrow going into the FilterReader. The sequence returned, one after the other, by FileReader.read() is
shown on the arrow going into SansCommentFilterReader. The sequence in and the sequence out of FileReader
are identical.
5 Most students would be more than happy to oblige.
6 This is how most programming languages are compiled. An early phase goes through and removes comments from the sequence of
characters and only later is the sequence of characters changed into a sequence of tokens.
14.3. READING THE DATA 397
L i n e o n e / / b e g i n n i n g \n... FileReader
L i n e o n e / / b e g i n n i n g \n...
SansCommentFilterReader
L i n e o n e \n...
Scanner
The arrow going from the SansCommentFilterReader to the Scanner shows the characters returned by
SansCommentFilterReader.read(). The Scanner is performing next, nextLine, skip, and other methods read-
ing the information. The sequence of characters it gets back from the sans comment filter is no longer (and
in this case, shorter) than the number of characters the sans comment filter gets back from the file reader.
SansCommentFilterReader is identical to NoCurlyFilterReader except for read() so we will examine that
method.
20 * @param in the {@link Reader} where this reader gets characters.
21 */
22 public NoCurlyFilterReader(Reader in) {
23 super(in);
24 this.endOfStream = false;
25 }
48 * @param length number of characters desired
49 *
50 * @return number of char read or -1 if past end of stream
51 */
52 @Override
53 public int read(char[] text, int offset, int length)
54 throws IOException {
55 if (endOfStream) {
56 return -1;// end already reached
57 }
58
398 CHAPTER 14. STRING PROCESSING: INTERACTIVE FICTION
59 int charCount = 0;
60 for (int i = offset; i < (offset + length); i++) {
61 int temp = this.read();
62 if (temp == -1) {
63 endOfStream = true;
64 break;
65 }
66 text[i] = (char) temp;
67 charCount++;
68 }
69 return charCount;
70 }
71
72 /**
73 * Skip over the given number of characters.
74 *
75 * @param n number of characters to skip over
76 *
77 * @return number of char skipped or -1 if past end of stream
78 */
79 @Override
80 public long skip(long n) throws IOException {
There is one new field, readAheadCh: having read ’/’ it is necessary to read the next character before the
code can determine whether the character marks the beginning of a comment. When the next character is a
’/’, as in the first line of the data file, the read method goes into a loop to skip over the comment.
What if the next character is not a slash? In the second line of the data file read reads a ’/’ followed by
a ’v’. read should return the slash (there is not an end-of-line comment here) but we have a problem.The
method has already read the next character, the ’v’; it must remember the good character and return it the
next time read() is called. readAheadCh stores the value of any character read accidentally. It is set to -1 (in
the constructor and in read()) when it does not contain a useful character.
So, lines 53-57 handle returning the saved value (and resetting readAheadCh to -1) when necessary. When
the method returns in line 56 execution of the method stops (so line 59 is never reached in that case).
If there is no read ahead character to return, we decorate the value returned by the Reader in line 59. If
ch is anything other than a slash, then it is just returned. If it is a slash, we must read ahead. If the read ahead
is not a slash, the slash is the right character to return and we save the read ahead.
If the read ahead was a second slash then we skip over the rest of the current line. A line is ended by a
’n’ (newline), a ’r’ (a carriage return) or a combination of the two characters. We don’t have to worry about
how the current line is terminated: just use a sentinel-controlled loop to read until one of the end-of-line
characters is seen and return that character. This puts the end-of-line marker exactly where the first slash
was (to anyone watching just the decorated output).
In SansCommentFilterReader it is necessary to override skip because the number of characters read from
in does not relate to the number of characters returned from read(). skip should skip over characters which
would have been returned to the user of the Reader.
Review 14.3
(a) What is a decorator? What does it have to do with coffee? What does it have to do with reading a file
character by character?
14.4. ATTRIBUTE-VALUE PAIRS 399
(c) How would a Coffee with a double shot of chocolate be represented using the coffee objects in Figure 14.4?
How would its price be calculated?
(d) Why might the following comment be a problem if Java did not preprocess comments?
/**
* class makes sure that cool stuff works.
*/
class CoolStuff {
...
}
(e) Why was such a big deal made of overriding skip in SansCommentFilterReader?
Command Aliases
Consider processing the various commands the player types into a text adventure game. With compass di-
rections as movement commands, a player would type “north” to go north. The next time they want to move
they would, again, type “north”. And so on. Think about the code you would use to process this. Assume there
are do<Direction>() methods which handle movement.
if (command.equalsIgnoreCase(”north”)) {
doNorth();
} else if (command.equalsIgnoreCase(”south”)) {
doSouth();
} else if (command.equalsIgnoreCase(”east”)) {
doEast();
} else if (command.equalsIgnoreCase(”west”)) {
doWest();
} else {
unknownCommand();
}
This snippet assumes the movement directions are the only possible commands; in the real game there
will be commands like “get” and “put”, perhaps “attack” or “talk”. Consider the user’s experience typing the
movement commands. “north” is only five characters but typing it over and over is a waste of the player’s
time. It would be nice if “n” was enough.
So, how would you change the first Boolean expression in the snippet to handle either “n” or “north”?
400 CHAPTER 14. STRING PROCESSING: INTERACTIVE FICTION
if (command.equalsIgnoreCase(”north”) ||
command.equalsIgnoreCase(”n”)) {...
That would then need to be repeated for each of the directions. But what if our game has a convention
that the user is always facing north and we want “forward” and “f” to be aliases for “north” (or “n”). An alias
is a different name for the same thing. We want to support an arbitrary number of aliases for each command.
Putting the aliases in the code violates the concept of separating program and data. Some number of basic
commands will have to be coded in the game but aliases should be more flexible: it should be possible to add
an alias for an existing command without having to recompile the code.
A file full of alias-command pairs (where the alias is the attribute and the command is the value) could be
read into a dictionary. Then, instead of rewriting the if statement when we wanted new aliases, we can just
update the alias file used to initialize the dictionary.
Assume the variable dictionary is a Dictionary which has been filled from the alias file. Dictionary has
a get method which, given an attribute, will return the corresponding value. It will return null if there is no
match. Then we could rewrite the code snippet above to
String normalizedCommand = dictionary.get(command);
if (normalizedCommand.equalsIgnoreCase(”north”)) {
doNorth();
} else if (normalizedCommand.equalsIgnoreCase(”south”)) {
doSouth();
} else if (normalizedCommand.equalsIgnoreCase(”east”)) {
doEast();
} else if (normalizedCommand.equalsIgnoreCase(”west”)) {
doWest();
} else {
unknownCommand();
}
e =<east>
east =<
east> // and so on
// western stuff here
west = <west >
w = < west >
The odd spacing and ordering is on purpose: being able to read a file like this improves our confidence
that the code works with free-form files. The object reading code counts on the free-form reading capabilities
so it is good to give it a workout now.
Dictionary
Another name for an attribute in a dictionary is the key, the special value used to look up an entry. This
protocol is modeled on the Map interface provided in java.util; Map is part of the Java Collections package like
List and ArrayList. That is why the method to test if a given attribute is in the Dictionary is called hasKey.
The first constructor builds an empty Dictionary. The second reads a file with lines of the form
<alias>=<command>. The file can have comments if the Scanner is constructed with a SansCommentFilterReader
and it can contain blank lines which will just be ignored. Extra blank space around the equal sign and at the
beginning and ending of the lines is also ignored.
The get method looks up the entry in the Dictionary. If there is an entry, the value is returned. If no
such entry exists, get returns null. hasKey returns true if there is an entry with the given attribute and false
otherwise. put associates the given value with the given attribute; put returns the old value of the attribute
or null if it had no previous value.
The entries in the Dictionary are AttributeValuePair objects stored in the allEntries ArrayList. The
public protocol for AttributeValuePair is
public class AttributeValuePair
implements Comparable<AttributeValuePair> {
public AttributeValuePair(String attribute, String value)...
public boolean compareTo(AttributeValuePair rhs)...
public String getAttribute()...
public String getValue()...
public String setValue()...
public String toString()...
The constructor takes two Strings, the attribute and the value, it initializes all the fields in the object.
The getter/setter method are standard except that there is no setAttribute method. Once a pair is made, the
attribute is immutable: it cannot be changed. It makes sense to keep it fixed because comparison is by attribute
only (compareTo returns the result of comparing the attribute fields of the two AttributeValuePair objects).
If the attribute could change, then the sorted order of a list of AttributeValuePair objects could be modified
by assignment to one of the elements of the list.
20 public static void main(String[] args) {
21 if (args.length > 0) {
22 Scanner aliases = null;
23 try {
24 aliases = new Scanner(new SansCommentFilterReader(
25 new FileReader(new File(args[0]))));
26 Dictionary dictionary = new Dictionary(aliases);
27 System.out.println(”=====================”);
28 System.out.println(”dictionary = \n” + dictionary);
29 System.out.println(”=====================”);
30 } catch (FileNotFoundException e) {
31 System.err.println(”Unable to open ” + args[0] + ” for input.”);
32 } finally {
33 if (aliases != null) {
402 CHAPTER 14. STRING PROCESSING: INTERACTIVE FICTION
34 aliases.close();
35 }
36 }
37 } else {
38 System.err.println(”usage: java TestDictionary dictionaryFName”);
39 System.err.println(” where dictanaryFName is a file”);
40 }
41 }
core.TestDictionary constructs a Dictionary with a file name provided on the command-line and then
prints the contents of the dictionary to standard output. When running it with the sample aliases given earlier,
the output is:
~/Chapter14% java core.TestDictionary aliases.txt
=====================
dictionary =
e = east
east = east
n = north
north = north
s = south
south = south
w = west
west = west
=====================
This is what we would expect because the Dictionary was described as being kept in sorted order. We will
look at how Dictionary.get takes advantage of this ordering in Section 14.6.
This self-describing file gives the names of the fields in the Location. The description (and, perhaps other
fields) are unduly constrained if we limited values to a single line. We need to be able to read a multi-line
value.
14.4. ATTRIBUTE-VALUE PAIRS 403
Much better: with an arbitrary length of description we can really capture the feel of the game. Attribute-
value pairs must be broken into three pieces: attribute, equal sign, and value. The attribute is a run of non-
whitespace characters terminated by a space or by an equal sign. The equal sign is just that, the string ”=” but
it can appear following any amount of white space. Finally, the value is either any run of non closing angle
bracket characters enclosed between angle brackets, ”<” and ”>” or the remainder of the current line after
the ”=”. Giving the author choices makes their life easier but we must write code that can recognize the two
different formats and respond accordingly.
readAttribute uses a regular expression pattern to skip over whitespace. A regular expression is a compact
expression for a pattern of characters which can be interpreted by a regular expression matcher. The Java
404 CHAPTER 14. STRING PROCESSING: INTERACTIVE FICTION
type java.util.regex.Pattern represents regular expressions in Java. The grammar of regular expressions
is similar to what we have used in describing Java templates7
Regular expressions, shortened as regexes, use a simple, formal language to express character sequences.
The Scanner class has several methods with use regexes: findInLine, findInHorizon, and skip. Earlier we
used the pattern string ”.*”; the . is a regex matching any single character and the * modifies any preceding
regex to match 0-or-more copies of itself.
What does the pattern string on line 22, ”\\s*” match? \s is a pattern matching any whitespace character
(a space, end-of-line marker, tab, or Unicode equivalents). The extra \ is necessary so that the string passed
in to skip is \s (the backslash is an escape character in a String so to get a backslash character in a String
the escape must be escaped: ”\” is the single character String containing a backslash). As stated above,
the * modifies the regex before it to match zero or more copies of itself. Thus the pattern is any number of
whitespace characters.
When Scanner.next is called, it skips over any number of copies of a pattern called the delimiter. The
default delimiter is \s, any whitespace character. After finding a character not in the delimiter, next collects
up all of the characters until it finds another delimiter match. This more detailed view explains how next,
with the default delimiter, skips over all whitespace and returns one word at a time. To a Scanner, a word is
any contiguous sequence of non-whitespace characters.
It is possible to change the delimiter used by a Scanner. This can be useful for reading specially formatted
input like our attribute. The description above says an attribute ends with whitespace or an equal sign. The
pattern string in line 29, ”[\\s=]” is a pattern consisting of a whitespace or = character. The square brackets
are a regular expression matching any character in the list of characters in the brackets. \s is the list of
whitespace characters and = is that character; this delimiter matches whitespace or equals.
The call to next in line 29 stops where it should. In line 27 we skipped over leading space (delimiter) so
when line 29 executes the scanner is scanning a non-whitespace character. We save the old delimiter and
restore it so that readAttribute does not change how the Scanner works in the rest of the program.
56 public static String readMatch(Scanner in, String match) {
57 return in.findInLine(match);
58 }
readMatch takes a Scanner and a String to match. The findInLine method takes a pattern string and,
ignoring the delimiter completely, searches for a match from the current read point. If a match is found
before the end of the current line in the stream, then the matching string is returned and the current read
point is moved past the match. No match means it returns null and the current read point is left where it was.
For finding the =, we just pass in the pattern string ”=”. This will match the equal sign and the Scanner
will skip over anything before it finds a match. Thus any whitespace to the left of = is skipped by this method.
77 public static String readValue(Scanner in) {
78 String retval = ””;
79 String openMark = readMatch(in, ”<”);
80 if (openMark == null) {
81 retval = in.nextLine();
82 } else {
83 retval = in.findWithinHorizon(”[^>]*”, 0);
84 in.skip(”>[\r\n]*”);// consume end of line (and blank lines)
7 EBNF, the Extended Backus-Naur Form, can actually describe patterns that are more complex than those a standard regular expres-
sion can describe. The theory of computation spends a good amount of time examining what can and cannot be recognized by the two
types of grammars. Java “regular expressions” are actually more powerful than standard regular expressions; wait until you take theory
and ask the professor about it.
14.4. ATTRIBUTE-VALUE PAIRS 405
85 }
86 return retval.trim();
87 }
readValue uses matchString to figure out if it is looking at an angle brace or something else. This makes
use of how Scanner.findInLine works when the pattern matches and when it fails to match. If ”<” matches,
then the current read point is just past the < character. That means it is at the beginning of the contained text,
the value that should be read. If the pattern fails to match, the value returned is null (for the if statement)
and the current read pointer remains unchanged.
If openMark is null (no match), then the return value is just the rest of the line as read with nextLine. If
openMark is non-null (a match), then the return value is set using findWithinHorizon. findWithinHorizon is
like findInLine except it searches for some number of characters rather than until an end-of-line marker.
The use of 0 for the horizon means search for the rest of the file. The pattern string, ”[^>]*” means 0-or-
more characters which are not >. The [^ as the opening of the list of characters means “all characters except
the following” so the pattern [^>] is any one character that is not a closing angle bracket. The * means any
number (including 0).
The skip in line 84 uses the pattern string to skip over the > and any end-of-line markers following it.
72 }
73
The method assumes it is called with the current read point somewhere before the opening Location
line in a location record file. Line 69 reads the next word from the file; since the file is opened with a
SansCommentFileReader, the next word should be the name of the class. This factory, rather than handling
multiple different class names, validates that the class name found in the file matches “Location”. If it doesn’t
match then there is no point in reading the record so we return null. If the Location constructor is called,
then the processAttributes method is called.
40 protected static GameObject processAttributes(
41 Scanner gameObjectScanner, String endClassID, GameObject go) {
42 String attribute = ReadAndWrite.readAttribute(gameObjectScanner);
43 while (!attribute.equalsIgnoreCase(endClassID)) {
44 /* ignore */ ReadAndWrite.readMatch(gameObjectScanner, ”=”);
45 String value = ReadAndWrite.readValue(gameObjectScanner);
46 go.handleAttributeValuePair(attribute, value);
47
48 attribute = ReadAndWrite.readAttribute(gameObjectScanner);
49 }
50 return go;
51 }
In processAttributes the three reading helpers are used. The first reads the attribute and if the attribute
is the end-of-record string, then the record is done so the method returns. Otherwise the attribute and value
are passed to the object’s handleAttributeValuePair; this method is overridden in Location (and Critter and
Item) so the call is dynamically made to Location.handleAttributeValuePair.
316 setNKey(value);
317 return true;
318 } else if (attribute.equalsIgnoreCase(”sKey”)) {
319 setSKey(value);
320 return true;
321 } else if (attribute.equalsIgnoreCase(”wKey”)) {
322 setWKey(value);
323 return true;
324 } else if (attribute.equalsIgnoreCase(”east”)) {
325 setEast(value);
326 return true;
327 } else if (attribute.equalsIgnoreCase(”north”)) {
328 setNorth(value);
329 return true;
330 } else if (attribute.equalsIgnoreCase(”south”)) {
331 setSouth(value);
332 return true;
333 } else if (attribute.equalsIgnoreCase(”west”)) {
334 setWest(value);
335 return true;
336 } else if (attribute.equalsIgnoreCase(”winningLocation”)) {
337 setWinningLocation(Boolean.parseBoolean(value));
338 return true;
339 }
340 return false;
341 }
The first thing that happens, line 304, is that the GameObject version of this method is called. Thus
GameObject gets first shot at handling the given attribute name. If it does (we will see the code below), it
returns true and the Location version is done: it just returns true to signal that the field was consumed
somewhere at or above Location.
If the method reaches line 309, this is a large if/else if structure. It checks the attribute string against
the names of fields it knows. If there is a match, the value is interpreted (as the appropriate type) and the
method returns true.
The code in GameObject.handleAttributeValuePair is very similar to that in Location, just without the
call to the super version of the method (this is the base class of all game objects).
302 protected boolean handleAttributeValuePair(String attribute,
303 String value) {
304 if (attribute.equalsIgnoreCase(”uuid”)) {
305 setUUID(value.toLowerCase());
306 return true;
307 } else if (attribute.equalsIgnoreCase(”name”)) {
308 setName(value);
309 return true;
310 } else if (attribute.equalsIgnoreCase(”description”)) {
311 setDescription(value);
312 return true;
313 }
408 CHAPTER 14. STRING PROCESSING: INTERACTIVE FICTION
The three fields that it “knows” are uuid, name, and description. Thus if the attribute con-
tains any of the three, the value is assigned to the right field. The return value of true tells
Location.handleAttributeValuePair that the attribute has been handled.
Beyond String
How can a GameObject which expects a value other than a String for some field process the value in the file?
That is, using the attribute-value code will always give back a string as the value. How can a GameObject get a
different type?
This section draws code from the Critter object because the critter has a health field, a numeric field,
a “hurt by” field which can appear multiple times with multiple values, and a “trades for” field, a field
where the value is structured. The code snippets here are very short, showing only the one entry in the
handleAttributeValuePair method of Critter.
An Integer
Handling the health attribute in the critter file is similar to handling any String valued attribute except
that the value, passed in from the file, is parsed as an integer by the Integer class. parseInt reads the string
it is given as an Integer. Java automatically converts from the returned Integer to an int when that is what
the parameter for setHealth expects. Double.parseDouble and Boolean.parseBoolean do just what you would
expect (Location uses parseBoolean for the winningLocation field).
These methods can throw exceptions if you pass them strings containing ill-formed numbers (or Booleans).
Multiple Values
A Critter can be hurt by items it receives. That sounds kind of silly unless you imagine the critter is Superman
and the item is kryptonite. Any given critter can be susceptible to any number of items in the game. The hurtBy
field is a ArrayList of UUID (String).
177 } else if (attribute.equalsIgnoreCase(”hurtBy”)) {
178 addHurtBy(value.toLowerCase());
179 return true;
The addHurtBy method does what you would expect (calls hurtBy.add) to add the value to the list. This
means that hurtBy can appear multiple times in any one critter’s entry in the cast file.
14.5. INCREMENTAL DEVELOPMENT 409
Structured Values
A Critter might be willing to give the player something if they are given something. In the game if the player
gives the vending machine a quarter, the vending machine will give the player the cheddar carp (assuming
the player has not already gotten them; they are only given if they are held by the vending machine).
Because a given critter might have any number of trades they are willing to make, tradesFor is a list. What
is it a list of? Trading involves two UUIDs: the UUID of what the critter gets and the UUID of what the critter
gives. How can we store two UUIDs in one string? Or, barring that, how can we specify how two fields, one for
gets and one for gives, go together.
180 } else if (attribute.equalsIgnoreCase(”tradesFor”)) {
181 String gets = value.substring(0, value.indexOf(’:’)).trim().toLowerCase();
182 String gives = value.substring(value.indexOf(’:’)+1).trim().toLowerCase();
183
The answer is to pick a character which marks the dividing point in the field and split the string on that
value. This method uses the colon, :. The indexOf method returns the index (with the first location being 0)
of the first matching character. The substring method in line 181pulls out the characters from the beginning
to the colon and puts them in gets. Line 182 does the same thing but with the characters after the colon. Both
are trimmed and converted to lowercase (all UUID are forced to lowercase for using built-in search routines).
Then a pair is made out of the two using a class called TradesFor. The class just associates the two UUIDs and
has two getters: getGetsUUID and getGivesUUID. The list tradesFor is used when the player gives something
to a critter; see Section 14.7.
Review 14.4
(b) What do angle brackets around a value mean? Should the angle brackets be “escapable” like quotes in
Java? How would you begin to do that?
(c) Why is the factory pattern used? Your answer should say something about polymorphism, constructors,
and at what time (compile-time, run-time) we know what kind of object to construct.
(d) What is a regular expression? What does the pattern ”\\S*” match (uppercase “S”).
(e) What does the use of : as a separator in the tradesFor mean for what constitutes a usable UUID?
Hopefully the first item seems familiar. Simplification is one of the goals of the book, a theme underlying
the idea of using the right level of abstraction.
The idea of delivering working code frequently is what motivated this section. We just spent a long, long
section discussing how to read structured text files. While reading text files is necessary to make a text ad-
venture game, a program which just reads structured text files is not very interesting.
The measure of success is delivering working code. The key is that the definition of working for our pur-
poses is delivering a game. Perhaps a simple, not quite complete game, but a game all the same.
The agile approach, because of the focus on working code, is considered at odds with the waterfall model,
a model where very complete documentation of the interfaces and implementations is done before coding.
Some students reading that working code is the most important thing begin to wonder why they waste time
on writing comments. Comments are not code!
But comments are necessary in excellent code. Agility requires delivering code in multiple phases. That
means the code will be read by the agile programmer over the course of development as it is enhanced in each
phase. Thus comments are part of technical excellence and make the code maintainable (sustainable coding
practices and pacing are another of the dozen principles).
This is a very cursory introduction to the idea of agile development. There are many books and web sites
devoted to different methodologies that support the agile approach. The next section will look at how to break
our text adventure game into phases, phases which are, more or less, games.
The openFileForInput takes the name of the file and returns a Scanner wrapped around a
SansCommentFilterReader which is, in turn, wrapped around a FileReader reading the given file. Thus
when readMapFile uses the Scanner, comments are automatically skipped (never to be seen). Because open-
ing the file is more complex than just calling new Scanner, it makes sense to break it out into its own method.
Further, notice that the method returns null if there is a problem (i.e., an Exception is thrown). Thus the read
method can tell there was a problem and return null as the value for the list.
The read method uses an eof-controlled while loop: it checks if there is a next token (non-blank space) and
if there is it tries to read a Location. If there was no problem reading the Location, then the new Location is
added to the map. Finally the map is returned. In the constructor for a GameWithMapOnly, the map field is set
to the result of the read method.
56 public GameWithMapOnly() {
57 aliases = new Dictionary(openFileForInput(”school.aliases”));
58 map = readMapFile(”school.map”);
59 if (map != null) {
60 player = map.get(0).getUUID();
61 }
62 }
The aliases field is also initialized in the constructor. What is line 60 all about? We need some way to keep
track of where the player is in the game. Since we have no Critter objects we cannot use one to keep track
of the player. So, we can keep track of the player by having a String field, player that holds the UUID of the
location where the player is. Line 60 makes the arbitrary decision that the player begins in the first location
in the location file (the one put at index 0 in map). This is arbitrary but since we are just moving around in the
map, it is as good a place to start as any other.
412 CHAPTER 14. STRING PROCESSING: INTERACTIVE FICTION
Moving
So, what does the game loop look like? It needs to show the current state of the game: this is just print-
ing out the Location where the player is. Then it needs to get input from the user: we will define a
Keyboard class which wraps a Scanner around System.in and provides next, nextLine, nextWithPrompt, and
nextLineWithPrompt. It will also have versions of the old answeredYes method. All the methods are static.
Finally, the state of the game must be updated: we will use the multi-way if described to motivate the
Dictionary adding just an exit command to permit the player to exit the game.
play is the main game loop. The location referred to by player is looked up (we will examine how it is
looked up in the next section) and the whole thing is displayed. The toString method dumps all of the field
values:
~/Chapter14% java core.GameWithMapOnly
Game Begins
gamestuff.Location [
uuid = thequad
name = The Quad
description = The fresh air, the trees, the odd, bright disk in the
sky. It must be, must be the ”sun” you have heard tell of. You
remember that sound, too, the birds twittering (no, not Twittering) as
they cartwheel across the sky.
inventory = []
north = NO_SUCH_LOCATION
south = t-hallentrance
east = NO_SUCH_LOCATION
west = NO_SUCH_LOCATION
]
The values are those read in from school.map in the first position. The cursor is waiting on the line below
the listing for a command to by typed.
181 private void processCommand(String command) {
182 String normalizedCommand = aliases.get(command);
183 if (normalizedCommand.equalsIgnoreCase(”north”)) {
184 doNorth();
185 } else if (normalizedCommand.equalsIgnoreCase(”south”)) {
186 doSouth();
14.5. INCREMENTAL DEVELOPMENT 413
When the command is typed, it is passed in to processCommand which looks it up as an alias. So, if the
player enters south, the alias for that command was also south. In processCommand, south matches in line 185
so doSouth is called.
112 private boolean doSouth() {
113 Location here = locationByUUID(player);
114 boolean canMove = (here.getSouth() != Location.NO_SUCH_LOCATION);
115 if (canMove) {
116 player = here.getSouth();
117 }
118 return canMove;
119 }
How can we move south? First we check if it is safe to move south. If it is then we do. Looking at the
short doSouth method, do you see any problems? What, in particular, do you think of line 114? It looks like
GameWithMapOnly has to reach quite deeply into Location and know a lot about what getSouth will return
when there is no such place. This works for the current program but noticing the mixing of different levels
of abstraction in the same method can give you some places where fixing up the code would make the next
iteration better.
It would be better if we had a canMoveSouth method in Location so that doSouth could just call it to see if
it was possible to move.
Besides that problem, all we do is get the Location from map with a call to findLocation with the UUID of
the location. Then find the UUID of the location to the south, if it is not the “null” UUID, then we can move
and we do by updating the player (UUID of where the player is) with the southern UUID.
Now, how does locationByUUID search map to find the current location? How long does such a search take?
And do we care?
Review 14.5
(a) What does the word “agile” mean? Why do you think it was applied to the given software development
approach?
(b) How do agile developers measure progress? How does that match with the grading rubric applied to your
class programs? What causes any differences?
(c) Given what you know about the fields in Location, sketch the canMoveSouth method which would be part
of Location. Would the method be public or private?
414 CHAPTER 14. STRING PROCESSING: INTERACTIVE FICTION
(d) Did the util.ReadAndWrite methods make the Location reading code in the game easier to follow? Why
or why not.
This code matches search code we have seen before. The count control variable runs across all the index
values for map. If a match is found, the matching Location is returned. If there is no match, then, after checking
every single Location, the method returns null.
Consider how long it takes to search your notebook. Each page takes a certain amount of time to check.
That time involves getting to the page, finding the date field, interpreting the date field, and then comparing
it. Note that that time depends on who is doing the search. If your dates are in cursive, then the deciphering
stage would take much longer for Dr. Ladd’s seven-year old son than it would for you, the author of the notes.
So, saying it takes one hour to search for the notes from the first Monday doesn’t really tell us anything. How
many notes were there in the book? Who was reading them?
How long does a sequential search of map take? Again, it depends on what computer you are running, what
Java interpreter, what other programs are running, and how many locations are in map. Saying it took one
quarter second does not tell us anything.
14.6. FINDING A MATCH 415
Computer scientists tend to talk about average times and express time as a function of the size of the data
on which the algorithm works. For map, the size of the data is the number of entries to be searched. What we
are interested in is not how long it takes to find the MajorsRoom location but rather how long it takes to find
any entry (on average) and what happens to that time as the size of map varies.
So, how many entries must be checked when we search for a given Location? We will make a simplifying
assumption: the map is completely random. There is no order in the list; thus searching for any given UUID
from the front of the list has the same chance of ending on any given entry; also, each entry has the same
chance of being searched for. How many entries must be checked if the UUID is not in the list? That is easy:
map.size(). Let’s call that value n. How about for an entry that is in the map? The random nature of the list
means that any given entry is found, on average, half way through. For every time we search for the last entry
we also search for the first entry. Every long run, past halfway, is averaged together with a short run, less
than half way. Rest assured that the number of entries compared, on average, is n/2.
What happens if we double the size of map? Whatever amount of time the search took on a given machine
will be doubled. And if we multiply the size by 10? Ten times slower. This is known as a linear algorithm.
After checking the last book there is no where else to go. The number of comparisons here is lg(n), the
logarithm of the size of the original search area. If the number of books were doubled, the number of compar-
isons would go up by 1 rather than double. This search, the binary search is faster than the sequential search.
8 The Library of Congress number or Dewey Decimal number serve the same purpose. Assume we know Knuth’s book’s number in the
But it requires that the entries be sorted by the field being search for. If the library were ordered on
publication date and we only had the author of the book we wanted, it would be no better than a random
ordering. To use this algorithm on a list, we need to maintain a sorted list. You will recall that the Dictionary
class keeps its list in order by attribute.
140 private int indexOfKey(String matchKey) {
141 int matchingNdx = -1;
142 int lowerNdx = 0;
143 int upperNdx = allEntries.size();
144 // invariant: if matchKey is in allEntries then
145 // matchNdx is on the range [lowerNdx, upperNdx).
146 while (rangeSize(lowerNdx, upperNdx) > 0) {// any remaining entries?
147 int midNdx = (lowerNdx + upperNdx) / 2;
148 int compareMidToMatch = allEntries.get(midNdx).getAttribute()
149 .compareTo(matchKey);
150 if (compareMidToMatch > 0) {
151 // allEntries[midNdx] < matchKey; search upper half of remaining
152 upperNdx = midNdx;
153 } else if (compareMidToMatch == 0) {
154 // allEntries[midNdx] == matchKey; return midNdx
155 matchingNdx = midNdx;
156 break;
157 } else {// if (compareMidToMatch > 0
158 // matchKey < allEntries[midNdx]; search lower half of remaining
159 lowerNdx = midNdx + 1;
160 }
161 }
162 return matchingNdx;
163 }
The search method uses a helper method, rangeSize which just returns the number of entries in the index
range [lowerNdx, upperNdx). The range is asymmetric, including lowerNdx but excluding upperNdx. This is
how the indexes for an ArrayList are reported if we use 0 and allEntries.size(). This is how we have talked
about index ranges through out the book.
Lines 141-143 initialize everything. The comment on lines 144-145 talks about an invariant. An invariant
is a property of a loop which is always true at the top of the loop. That is, the property holds when the loop
begins and going all the way through the body of the loop makes sure the property is again true. In the body
of the loop it is possible that the invariant is, momentarily, violated but the body of the loop must restore it.
If the invariant on 144-145 is invariant, then our search works. The if inside the loop either finds the
match (it happens to be at midNdx) or it makes the range smaller. Thus it closes in on the matching entry by
throwing away half of the entries.
The comments explain where the matching entry must be after the comparison. The > and the < in the
comments are used to make the ordering easier to read; they mean “later in the lexicographic order” and
“earlier in the lexicographic order” respectively.
The tricky part of the code is the difference between line 152 and 159. The important thing is that the size
of the range being search must get smaller each time through the loop. Since midNdx will be between lowerNdx
and upperNdx, it follows that making sure we exclude the entry at midNdx makes sure at least one element is
removed. Thus line 159 moves lowerNdx up past midNdx since that entry has already been checked. Similarly,
line 152 does the same with upperNdx but without subtracting 1 because of the asymmetry of the range. By
14.7. MAKING IT A REAL GAME 417
setting upperNdx to midNdx, midNdx is excluded from the range. So long as the range gets smaller each time,
eventually we will find the match or the range size will be 0. We are done in either case.
So, why don’t we always use binary search? Because the elements must be in sorted order by the search
key. When you searched for your notes, the notebook would have had to be in sorted order by date for you to
take advantage of binary search. If you did a lot of searches by date, it would be worth sorting it. If, however,
you search by date only once in a very long while, it might be worth the cost of sequential search to avoid the
task of sorting.
Similarly, if you search by date commonly, then sorting make sense. If you then occasionally search by
course name you would not want to have to resort the notebook twice (once into course name order and then
back into date order when you perform the more common search).
Because the dictionary is search with each command and the alias list might grow large, it was determined
to sort it and use binary search.
Review 14.6
(a)
165 protected boolean handleAttributeValuePair(String attribute,
166 String value) {
167 if (super.handleAttributeValuePair(attribute, value)) {
168 return true;
169 }
170
171 if (attribute.equalsIgnoreCase(”location”)) {
172 locationID = value.toLowerCase();
In Listing 14.24, how long does itemByName take to find the matching item (or to determine that there is no
match)? Identify n and then consider how much work is done, on average, to find an arbitrary entry.
(b) How many steps would it take to find a book (using the binary search approach) to find a book in a library
with
(a) 256 books
(b) 500 books (Hint: It is no worse than the next bigger power of 2.)
(c) 100000 books
(d) 1048576 books
(d) Explain what could go wrong if going through the while loop in Dictionary.indexOf failed to reduce the
size of the range.
Finish Initialization
GameWithMapOnly had the name of the alias and map files hard-coded into it.It would be better (from a game-
mod point of view) if those files could be specified without recompiling. Game hard-codes the name of the
configuration file, gameconfig.txt and then uses the processConfigurationFile method:
651 * false otherwise
652 */
653 private boolean processConfigurationFile(String configurationFName) {
654 aliasesFName = null;
655 castFName = null;
656 mapFName = null;
657 propsFName = null;
658
The code is similar to the field handling code for game objects. The files can be named in any order. The
method returns true if a name was provided for every file, false otherwise. The truth value is used in the
constructor to determine whether or not to try reading the data files; if there is a missing data file name, just
bail out.
How does the game know that the constructor bailed out? Constructors do not have a return type. One
way to handle a problem in a constructor is to throw an exception but that takes more Java than we have.
Instead, Game has a ready field and a isReady method. It is set to false unless all the configuration completes.
That way, main looks like this:
127 public static void main(String[] args) {
128 theGame = new Game();
129 if (theGame.isReady()) {
130 theGame.play();
131 } else {
132 System.err.println(”Trouble initalizing game.”);
14.7. MAKING IT A REAL GAME 419
133 }
134 }
Either the game is initialized or the player gets an error message (remember, System.err is the error
output stream).
What is theGame and why is it not declared as a local variable? It must be a field but main is static: that
means there is no this reference. So what is theGame? It is a static field. There is only one for the class Game.
Why is it a field? So that this Game class can offer a getCurrentGame method9 .
116 public static Game getCurrentGame() {
117 return theGame;
118 }
The method is called with Game.getCurrentGame() and it is used by the various game objects to set messages
which will be displayed the next time through the game loop.
Connecting Things Up
The methods to read the cast file and the props files (containing Critter and Item descriptions respectively)
are similar to the map reading code. There are three ArrayLists in Game, one for each type of game object.
After the three files are read, the data is not ready to use.
Looking back at Figure 14.2 you see that each Critter has its location UUID stored in it but the critter is not
in the visitors list of the appropriate location. The interconnected structure of locations containing critters
that contain items (which can also be in locations) has to be untangled for saving and loading. The use of UUID
is one step in that direction.
After reading all the information from the three files, the critters must all be placed in their locations (and
the items given to their owners). The distributeCritters method does this by going through the cast list
and placing each critter in the right location:
222 * critters with their locations.
223 */
224 private void distributeCritters() {
225 for (int i = 0; i != cast.size(); ++i) {
226 Critter critter = cast.get(i);
227 Location location = locationByUUID(critter.getLocationID());
228 location.add(critter);
The Location.add(Critter) method automatically fixes up the location field in the given critter. Makes
moving critter from one spot to another easier.
Finally, how do we find the player. In the map-only phase we just plunked them down in an arbitrary
location. The player, now, is a Critter. That way they have an inventory, a health, whatever critters have.
Which critter is the player? The one with the UUID of player, of course. So, after loading all the files and
fixing up the loot and the critters, the game sets the Critter field player to refer to the appropriate member
of the cast.
9 The code in main setting theGame is not the same as that found in FANG but with the exception of the name of the field,
getCurrentGame is identical.
420 CHAPTER 14. STRING PROCESSING: INTERACTIVE FICTION
Pickup/Drop
Movement is similar to that in the map-only phase except that the current location is part of a Critter rather
than a field. The section on keys below covers movement in greater depth.
How can the player interact with the environment. In particular, how can they pick something up?
The normalized command is “pickup”. So to pickup the quarter in Room 101 at the beginning of the game
is easy.
A large, cavernous room. This is where you took CS1. It still causes a
bit of a shiver when you remember sitting in the back corner taking the
final exam. Questions about ArrayList, searching, and ... it is all too
much. There is a door at the back of the room leading north. Another,
less conspicuous doorway leads from the front of the room to the south;
that way lies the computer lab. There is some crud here! There is some
other crud here!
You see Quarter.
Game> pickup quarter
You picked up Quarter.
Game>
Imagine what happens internally: the user types something in and in the main loop commandLine is set to
Keyboard.nextLine()10 . Inside processCommand the command line (passed in as a parameter) is taken apart.
How?
It is possible to construct a Scanner which takes its input from a String rather than from a stream attached
to a file; that permits us to parse a line using next (and, if we have to, skip and setDelimiter). The listing below
shows the creation of the command line scanner and the first branch of the if. The middle branches of the if
are elided down to the “pickup” case.
595 private void processCommand(String commandLine) {
596 if (commandLine.isEmpty()) {
597 return;
598 }
599 // Scan across the command line as entered by the user
600 Scanner lineProcessor = new Scanner(commandLine);
601 String command = lineProcessor.next();
602
Line 600 constructs a new Scanner passing it a String. You might have wondered why various Files and
streams could be constructed with a file name but Scanner could not. Now you know: pass a String to a
Scanner constructor and scan the string.
10 Keyboard wraps up a static Scanner wrapped around standard input; it looks like the keyboard field in TwentyQuestions from
Chapter 12
14.7. MAKING IT A REAL GAME 421
command and normalizedCommand are just like what the were in the map-only phase. The equalsIgnoreCase
checks for the various commands. Line 620 calls doPickup. It passes in the whole scanner wrapped around the
user’s input line.
So, what happens in doPickup? Use next to pull of the next word from the line, check the current location
for an Item with that name (GameObject.itemByName is just the ticket). All is well when picking up the quarter.
There are two things we want to be able to support: picking up multiple things in one command and multi-
word item names. Either one alone would be easy. Single-word-names and multiple objects at once: loop over
the words remaining in the command line and pickup each one. Multi-word-names and one object at a time:
pull the rest of the line in as the name and pick it up. Both together: not so easy.
382 private boolean doPickup(Scanner commandLine) {
383 Location curr = locationByUUID(player.getLocationID());
384 boolean gotOne = false;
385 String pickedup = ””;
386 String separator = ””;
387 while (commandLine.hasNext()) {
388 Item theItem = itemByCommandLine(curr, commandLine);
389 if (theItem != null) {
390 give(curr, player, theItem);
391 pickedup += separator + theItem.getName();
392 gotOne = true;
393 }
394 }
395 if (gotOne) {
396 addMessage(”You picked up ” + pickedup + ”.”);
397 }
398 return gotOne;
399 }
474 private Item itemByCommandLine(GameObject owner,
475 Scanner lineProcessor) {
476 String name = ””;
477 String separator = ””;
478 while (lineProcessor.hasNext()) {
479 name += separator + lineProcessor.next();
480 separator = ” ”;
481 Item theItem = owner.itemByName(name);
482 if (theItem != null) {
483 return theItem;
484 }
485 }
486 return null;
487 }
The code shows two methods. The real work is in itemByCommandLine. The name of an item is built up out
of words. The first word is pulled off of lineProcessor and the owner (current location) is checked for an item
by that name. If there is not one, pull off another word, put it (after a space) after the name so far and try
again. If we run out of words before getting a match, return null. If we get a match before running out of
words, return a reference to the match.
What happens when the player types “pickup cheddar carp quarter” in a location where both “cheddar
carp” and “quarter” are available?
422 CHAPTER 14. STRING PROCESSING: INTERACTIVE FICTION
" quarter"
" " "quarter" <match>
Figure 14.7 shows the values of the variable lineProcessor and name inside of itemByCommandLine when
it is called from doPickup. The quotes are there so we, humans, can see what is going on. Remember that
lineProcessor is a Scanner; the part that is showing is the part of the string remaining to be scanned.
When itemByCommandLine is called the first time, name has not yet been initialized and no call has been
made to itemByName on the owner (the current location). The value in the scanner is all three words after
“pickup”. The while loop is entered, line 479 pulls the next word off of lineProcessor and appends it on the
end of name (after a copy of separator which is empty the first time through). Then line 481 calls itemByName
with the current name.
This is the second line in the first box in the figure. The call returns null so the loop goes around again.
Line 479 pulls off another word, it is appended (with a space this time) and the new name is looked up. This
time there is a match and that reference is returned to doPickup.
Because Scanner is an object type all variables are references to it. When passing it into a method a new
reference is made but it refers to the original value. lineProcessor in doPickup has the same value it had at
the end of itemByCommandLine.
doPickup does its thing (transfers the item returned from itemByCommandLine from the location’s inventory
to the player’s inventory) and then it loops back, checking if lineProcessor.hasNext() is true. It is so we enter
the second box in the figure. On entry the value of lineProcessor is as shown. Entering the loop at line 478
the last word in lineProcessor is pulled out and the name is used to look up an Item and it matches so that
reference is returned. In doPickup the quarter is transferred from the location to the player and the loop
finishes (hasNext() is false), doPickup is done and two items were transferred from the current location to
the player’s inventory.
Looking at doPickup, notice that as each item went from the location to the user a message was added.
The next time through the game loop the message will be displayed (and the description of the location will
be suppressed for one cycle). This lets the actions send messages to the user but have them displayed by the
main game loop (keeps every method from having a System.out.println in it).
You might be wondering why itemByCommandLine is broken out as its own routine. Think about what
doDrop must look like. It looks for named items in the player’s inventory (rather than the location’s) and trans-
fers matching items from the player to the location. Having a helper method to do the look up makes the
methods a lot easier to write.
14.7. MAKING IT A REAL GAME 423
Trading
Trading is giving something to a critter. If you are in a location and there is a critter there, you can “give”
some item to the critter. It is also possible that the critter might be willing to give something back for a specific
item.
First think about processing the command line. When the player types “give vending machine quarter”
what must doGive do? Just like itemByCommandLine it must pull off words and check for matches to the name.
Instead of checking the location’s inventory, it checks the visitors list. When it finds a match, the rest of the
line should be the name of an item in the player’s inventory. Look it up and give the item to the critter if both
were found.
But what if the player instead typed “give quarter vending machine”? It should have the same effect. This
time we look up “quarter” in the player’s inventory and then the rest of the line is the name of a critter in the
current location.
How do we know which way to parse the command parameters? Without making an arbitrary decision
and forcing players to conform the program cannot tell the difference. It attempts both parses in parallel,
choosing whichever works first.
314 private boolean doGive(Scanner commandLine) {
315 String firstName = commandLine.next();
316 Location curr = locationByUUID(player.getLocationID());
317 Item theItem = player.itemByName(firstName);
318 Critter theCritter = curr.critterByName(firstName);
319
348
Line 320 is a loop that keeps going until either an item in the player’s inventory matches the name, a critter
in the current location matches the name, or we run out of words to add to name. This is a slightly different
programming style for building up a name. Here the first word is pulled off before the loop and the itemByName
and critterByName calls appear before the loop and in the loop.
This version does not need the separator variable to keep track of when to add a space between elements
but it does have repeated code. This is a case where neither has any obvious superiority so this code shows
both.
When one or the other matches the loop exits. The if at 327 uses the rest of the line to look up the other.
Line 335 checks that both an item and a critter have been named. If so, then the player gives the item to the
critter. The code also checks if the critter will trade any item for the given item. If so and they have the item
to trade, they give the trade item to the player.
Remember the game designer included a tradesfor entry in the critter’s record in the cast file. It gave
two UUIDs, the one they wanted and the one they would give. willTradeFor looks in the list of pairs for one
beginning with the UUID the player is giving. The value of trade in line 336 is either null (no trade offered)
or the UUID of the item they will give. If there is a UUID to give, see if they own it and give it to the player (if
statement at 342).
Having written a give helper function to transfer items from one GameObject to another permitted focusing
on the doGive level of abstraction (figuring out when and how to call give) rather than inventory handling.
Combat
It is hard to call it combat, really. The game mechanic for “hurting” a critter is to let the critter record specify
items (by UUID) which can hurt the critter. When the critter is given an item on the list of items that hurt
them, they have their health reduced by the item’s attack strength.
In the game, Dr. Dunbumble is hurt by the Ungraded Homework. Giving him the homework reduces his 1
health by 1, causing him to leave the game. (It might improve the game if a critter could specify a “leaving”
description. See Programming Problem 2.)
Keys
It is good that Dr.Dunbumble leaves. He is an “anti-key” to getting to the Entryway. How does that work? Look
at doWest (and compare it to the doSouth shown in Listing 14.21):
421 private boolean doWest() {
422 Location here = player.getLocation();
423 boolean canMove = (here.getWest() != Location.NO_SUCH_LOCATION) &&
424 unlocked(here.getWKey(), player, here);
425 if (canMove) {
14.7. MAKING IT A REAL GAME 425
426 moveTo(locationByUUID(here.getWest()));
427 }
428 return canMove;
429 }
882 private boolean unlocked(String key, Critter wantsToMove, Location here) {
883 boolean passable = true;
884 if (key != null) {
885 if (key.charAt(0) == ’!’) {// It is a ”not” key
886 key = key.substring(1);// get rid of !
887 passable = (!wantsToMove.hasItemUUID(key) &&
888 (!here.hasCritterUUID(key)));
889 } else {
890 passable = (wantsToMove.hasItemUUID(key) ||
891 (here.hasCritterUUID(key)));
892 }
893 }
894 if (!passable) {
895 addMessage(”Your way appears to be blocked.”);
896 }
897 return passable;
898 }
The doWest routine is not too much longer than the previous version. It just adds a call to unlock. The unlock
method is somewhat complicated (it is also the very last method in the Game.java source file and the last one
we explore here).
unlocked is called with a key, the wKey value of the location. wKey holds the UUID of a key item or critter
to go west from the current location. By default all direction keys are null. That means that most of the time
unlocked returns true because the if at line 884 is never entered.
When the key is non-null it is a UUID of an item or it is “!” followed by a UUID. The difference is that a
UUID alone must be present for moving; a “!” UUID must be absent for moving.
To go west from Hallway has wKey of “!CrazyProfessor”. Line 885 checks for the “!” using the charAt method
of String. Like ArrayList.get it takes an integer and the characters are numbered from 0. If the first one is
an exclamation point, then strip it off and look for the critter with that UUID in the here location and the item
with that UUID in the inventory of the wantsToMove critter. If neither is there then passable is set true.
Alternatively, if there is no “!” then check for the item and critter and set passable to true if either is
present.
Review 14.7
(a) Does the Game ever display the UUID of any GameObject? Why or why not?
(b) How would picking up and dropping items be different if the player typed in the UUIDs?
(c) Why is unlocked broken out into its own method? Would it make sense to move a copy of the code into
each of the directional movement methods?
(d) What character is used to indicate an “anti-key”? What impact, if any, does that have on the UUIDs which
game authors can use?
426 CHAPTER 14. STRING PROCESSING: INTERACTIVE FICTION
(e) What field in a Critter record is used to specify items they will give for other items? What is the structure
of the value?
14.8 Summary
Java Templates
Review Exercise 14.1 Give an example of a sorted array that could cause an infinite loop if line 159 of
Dictionary.indexOf was changed to read
Programming Problems
Programming Problem 14.2 What if we wanted to add a “leaving message” for a critter. When its health
is reduced to 0, the critter will leave the game (as it does now) but if it specifies a leavingDescription, then
instead of saying “critter-name has left.” the leavingDescription would be displayed.
This would let the critter have some parting soliloquy or let some last description be imparted. Dr. Dun-
bumble could shout, “No, no, no more homework! The semester is over!” and run, screaming, from the
building.
(a) What method(s) would have to be changed to add a leavingDescription field for Critter?
(b) What method(s) would have to be changed to display the message (if one is provided) when the critter
leaves?
ii. Does the name of the method give any clue that it has to do with critters leaving the game?
ii. Could you break the “leaving” functionality out into a more appropriately named method?
Appendix
A
Java Language Keywords
The following table lists the reserved words in Java (as of version 6.0); none of these may be used as an identifier
in your Java program. Note that while not in the table, true, false, and null are also off limits (they are literals
(of the boolean and reference types).
427
Appendix
B
References
[FFBD04] Elisabeth Freeman, Eric Freeman, Bert Bates, and Kathy Dierra.
Head First Design Patterns.
O’Reilly Media, Inc., Sebastapol, CA, USA, 2004.
[Pan70] PanzerBlitz.
Avalon Hill Games, 1970.
[RBC+ 06] Eric Roberts, Kim Bruce, James H. Cross, II, Robb Cutler, Scott Grissom, Karl Klee, Susan Rodger,
Fran Trees, Ian Utting, and Frank Yellin.
The acm java task force: final report.
In SIGCSE ’06: Proceedings of the 37th SIGCSE technical symposium on Computer science education, pages
131–132, New York, NY, USA, 2006. ACM.
[Tol54] J. R. R. Tolkien.
The Lord of the Rings.
Houghton Mifflin, 1954.
433
Appendix
D
FANG Color Names
The table on the following page lists all of the color names built-in to the FANG Palette class. The name is
given with the “Web” color string specifying the red, green, and blue channels of the color. These names
can be used directly in the static getColor method of fang.attribute.Palette or in the static getColor
method of fang.core.Game. Note that both getColor methods are case-insensitive and they compress out all
whitespace within the name string.
435
436 APPENDIX D. FANG COLOR NAMES
437
438 INDEX