Download Technical Specification

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
1
Interactive GUI-Based Simon Game in Scala Leveraging Java Swing Libraries
2
Contents
Motivation..................................................................................................................................................... 3
About Scala ................................................................................................................................................... 3
FlashTone ...................................................................................................................................................... 4
State Machine Diagram................................................................................................................................. 5
Game Thread................................................................................................................................................. 6
About StdAudio ............................................................................................................................................. 7
Java Web Start Deployment ......................................................................................................................... 8
About Java Swing Layouts ............................................................................................................................. 9
Bibliography .................................................................................................................................................. 9
3
Motivation
The game Simon, invented by Milton Bradley in 1978, is an interesting example of a deterministic finite
automaton (DFA). The code for the original board was written in Assembly Language, but modern highlevel languages such as C++, Java, or C# can easily implement the logic. In this instance, the author has
opted to utilize a higher-level superset of Java known as Scala. The object of the game is to mimic a
sequence of tones and flashing lights played by the computer. With each level completed, the length of
the sequence increases by one, with the object of the game being to reach the highest level possible.
Besides the state machine, multithreaded Java audio and GUI repaintings are additional computer
science concepts exercised.
About Scala
Scala is a superset of Java which contains elements of functional programming languages such as LISP or
ML. The Scala source is compiled to Java Virtual Machine byte code, and Java classes can be used as-is.
Functional programming aspects include: ability to pass anonymous functions as arguments (lambda
calculus), type inference, and its considering statements to be expressions with values.
One mystery of the Scala compiler, NSC, is its ability to find Java classes such as javax.swing.JFrame
referenced in Scala source without the JRE runtime jar files being in the library directory of the Scala
install. In addition, no Windows environment variable is configured to specify the JAVA install directory
or library classpath.
Another mystery of the compiler is that it is written in the Scala language. It is expected that the original
compiler was written in Java or a lower-level language and that it was used to compile the later Scalabased compiler. Then, the class generated from this Scala compiler could be used to compile its
successors.
Scala provides a concept called traits, similar to interfaces to Java. It also provides a form of weak
inheritance known as “mix-ins”. One such mix-in, the EvalLoop trait, may explain the above classpath
conundrum, as there is no default execution code in the compiler main class, NSC. In fact, though, the
main execution code is located not in the EvalLoop trait but in Driver, the superclass of Main.
Scala includes the ability to define anonymous inner classes, and the syntax is nearly identical to that in
Java. This is useful in Swing, for instance, to override a JPanel’s paint method without explicitly defining
a new subclass of JPanel.
Scala contains its own versions of Java primitive types (e.g. Scala Int for Java int) and their boxed objects
(e.g. RichInt for Integer). It is unclear whether the Scala types are automatically translated to their Java
counterparts. Interestingly, though, the postfix-increment operator (++) does not seem to be defined
for the Scala Int type.
One downside of Scala’s dynamic type inference is the inability to initialize an object to null and later
reassign it to a new instance of a class. Another mystery is the use of Java generic collections in Scala. A
method like Map<T,T>’s “put” takes parameters of undefined type T. If an explicit type is passed to
“put” in Scala, a compile error occurs. It would seem that casting Map<TextAttribute,?> to
Map<TextAttribute,Integer> using the “asInstanceOf” operator might work, and in fact no compile
4
errors occurred when such syntax was resorted to (although it seems awkward to be required to cast
the return value of a method).
FlashTone
This function will take an integer representing a color as input.
1=red
2=green
3=blue
4=yellow
5
The flash involves repainting the sector in a lighter shade of the color, and the tone aspect of the
function involves playing a sound in a background thread (at a different frequency for each color). The
repainting was initially attempted by changing the color variable for each sector and invoking the
repaint method (reverting the color and invoking repaint again after a one-second delay). However, as
paint operations in Java Swing (and thus Scala) are asynchronous, the first paint did not occur until the
flash method completed, rendering the color change meaningless.
Interestingly, after the update to the gui to deactivate the Textview and button once the game is
started, the flash redraw of the board causes a duplicate rendering of the textview, the button, and the
board in the frame. This problem was ameliorated by calling the clearRect method of the
java.awt.Graphics class in the paint override of JPanel.
State Machine Diagram
Play Level
Register User
Play Tones
Get User
Input
Pre-Start
Game Over
6
Game Thread
In order to avoid tying up the user interface’s dispatch thread with the indefinite while loop of the game,
a separate thread is allocated for the game play loop. User clicks on the board are handled in the
dispatch thread, but are checked only when the game is in the “userInputPhase”. In the user input
phase, the user will have up to ten seconds to press a key, and each color enered will be checked against
the tones played for that level. This is basically a finite state machine architecture, with the user input
state being broken down into sub-states for each tone/color to be checked. For now, if the incorrect
key is pressed, the game simply ends and a tone is played. However, the “canonical” behavior would be
to both play a tone and to flash the color of the correct key.
Checking of a color pressed involves comparing the sector of the correct color to the sector of the
location clicked (touched), based on the Cartesian coordinates of the click with the center of the circle as
the origin.
When the game ends, a “Play Again” button is enabled. When this is clicked, the game is returned to
the state user name input (see screen shot below). Note that the game thread never terminates once it
is started while the application is running. It is suspended when the game ends and resumed when the
game is restarted (with a reset of instance variables like level number and tones string). Note that
“wait” and “notify” operations could have been used in place of the deprecated “suspend” and
“resume” operations, but it would have been necessary to place these operations within synchronized
blocks in order to obtain the monitor lock on the object. Note that the process to respond to errors or
level completion has been streamlined by interrupting the ten-second sleep in the game thread’s “get
input” loop whenever either an incorrect sector click is detected or a level completion is verified. The
“interrupt” method of java.lang.Thread is called from the GUI dispatch thread and is applied to the
thread represented by the “this” object reference, namely the game thread.
7
About StdAudio
This library, developed by Robert Sedgewick and Kevin Wayne of Princeton University’s Computer
Science department (used without permission), enables sounds to be played from Java by providing only
a frequency and sample rate. Its play method takes a double and uses this double to write a distinct
byte stream to the speaker’s shared memory (accessed in Java using a Line object). Apparently, the
SourceDataLine class in object writes to the output speaker/headphone port (via the mixer) by default.
8
The primary function of this library is “play”. To use it to play a tone for one second, iterate over a for
loop with index i “sample rate” times. In each iteration, pass .5sin(2pi*i*f/sampleRate) to play. Thus,
over the “for” loop, values in [-.5, .5] are passed, with the argument to the sin function increasing by
2pi*f/sampleRate with each iteration.
Unfortunately, the StdAudio methods are not threadsafe, so the play calls must be made within a
synchronized block (use this.synchronized). In fact, as the bufferSize variable is static, the entire for loop
calling play and the succeeding close (releases the speaker resource) call should be in a synchronized
block. This locking needs to be conducted at the class level, so the “playRange” method of SoundThread
is defined at the “object” level, making it static. The “synchronized” keyword thus provides mutually
exclusive access to the class (across all instances) for a given thread. Note that one problem in playing
tones was excessively calling the “close” method excessively. This results in a drain and closure of the
“line” audio resource (shared memory for sound card). As the method is static, it can be called only
once (at the end of the game).
Excessive static occurred when the game was first started and when the Simon application was in focus,
but the problem has not recurred. This may have been due to memory usage on the test machine.
An alternative to the above procedure would have been to record tones in four .wav files and to play
these in a background thread when tones are to be souned.
Java Web Start Deployment
This step, which essentially involves packaging the Java GUI code in an archive which can be downloaded
from a web site and executed on a client PC is actually non-trivial. Steps involved include:
1.
Creating a Java Archive (JAR) file including the Scala classes needed. This involves extracting the
Scala classes from their JAR files and adding them into the new deployment JAR at the
appropriate path.
2. Signing the JAR file using a private key and appending public key to it.
3. Creating a Java Network Launch Protocol (JNLP) file describing the JAR and any command-line
options needed to run it.
4. Modifying an HTML site to link to the JNLP file.
The following commands were used to package the deployment JAR (after extracting scala-library.jar to
the current directory).
jar -cvfm simonem.jar manifest.txt *.class
jar -uvf simonem.jar *.png
jar -uvf simonem.jar scala
The manifest.txt file must contain:
Permissions: sandbox
The JNLP file will induce the start of a download module on the client PC, and a Java Virtual Machine
(JVM) will subsequently be launched.
9
Two other items of consequence in the deployment are the Java Control Panel Security (must be at
Medium to allow a self-signed certificate) and the manifest (specifies run permissions for the
downloaded classes).
About Java Swing Layouts
Placing GUI widgets in a frame or panel using Cartesian pixel coordinates produced unpredictable
results, so the author has resorted to utilizing a GridBagLayout to organize the GUI controls and displays.
Interestingly, the “gridwidth”, “gridheight”, “gridx”, and “gridy” traits seem ineffective at organizing the
layout precisely. Instead, the “fill”, “weighty”, and “weightx” traits, along with the “setSize” value of the
component to be added, seemed to determine the position and size of the widgets. However, the
“gridx” and “gridy” values are necessary to determine the row and column values in the layout.
An unexplained bug with the repainting of the circle has been noticed at random level changes. In such
a case, a portion of the green sector is painted above the circle to the left of the “start” button. It is
unclear whether the source of this behavior lies in Scala library code, Java Swing code, or the Simon
Emulator application source. It seems unlikely that the operating system is the cause of the problem, as
other applications do not exhibit the same repainting problem. In fact, the problem seems to be with
the application code which calls repaint of the board both from a background thread (during playGame)
and from the dispatch thread when a mouse click on the board occurs. When the paint occurs from a
background thread the paint method invocation must be wrapped in a SwingUtilities.invokeAndWait
call. When called from the dispatcher thread, the paintImmediately method should be used. Also, the
rectangular area for the repaint did not completely overlap with the actual rectangle. Upon correcting
these issues, the aberrant paint problems seem to have been corrected.
Bibliography
http://www.tutorialspoint.com/scala/scala_for_loop.htm Scala for loop syntax
http://www.scala-lang.org/ Scala home page
http://en.wikipedia.org/wiki/Scala_(programming_language) Wikipedia Scala discussion
http://docs.oracle.com/javase/6/docs/api/java/awt/Graphics.html Graphics class (contains fillArc)
http://introcs.cs.princeton.edu/java/stdlib/StdAudio.java.html audio library used to play tones
http://en.wikipedia.org/wiki/Simon_(game) Simon discussion.
http://docs.oracle.com/javase/tutorial/deployment/webstart/deploying.html Java
Web Start deployment steps