Skip to content
Commits on Source (6)
...@@ -13,12 +13,6 @@ ...@@ -13,12 +13,6 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="78671b20-6a1e-42c8-be28-50b7f38fa7ff" name="Default Changelist" comment=""> <list default="true" id="78671b20-6a1e-42c8-be28-50b7f38fa7ff" name="Default Changelist" comment="">
<change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/edu/bu/ec504/spr24/brain/Board.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/edu/bu/ec504/spr24/brain/Board.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/edu/bu/ec504/spr24/brain/Brain.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/edu/bu/ec504/spr24/brain/Brain.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/edu/bu/ec504/spr24/brain/LazyBrain.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/edu/bu/ec504/spr24/brain/LazyBrain.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/edu/bu/ec504/spr24/sameGameTris/CircleColor.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/edu/bu/ec504/spr24/sameGameTris/CircleColor.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/edu/bu/ec504/spr24/sameGameTris/SameGameTris.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/edu/bu/ec504/spr24/sameGameTris/SameGameTris.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/edu/bu/ec504/spr24/sameGameTris/SameGameTris.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/edu/bu/ec504/spr24/sameGameTris/SameGameTris.java" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
...@@ -50,17 +44,23 @@ ...@@ -50,17 +44,23 @@
</component> </component>
<component name="PropertiesComponent">{ <component name="PropertiesComponent">{
&quot;keyToString&quot;: { &quot;keyToString&quot;: {
&quot;ASKED_ADD_EXTERNAL_FILES&quot;: &quot;true&quot;,
&quot;Application.EC504 SameGameTris.executor&quot;: &quot;Run&quot;,
&quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;, &quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;, &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;WebServerToolWindowFactoryState&quot;: &quot;false&quot;, &quot;WebServerToolWindowFactoryState&quot;: &quot;false&quot;,
&quot;git-widget-placeholder&quot;: &quot;master&quot;,
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;, &quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;, &quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;, &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;, &quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;project.structure.last.edited&quot;: &quot;Project&quot;, &quot;project.structure.last.edited&quot;: &quot;Project&quot;,
&quot;project.structure.proportion&quot;: &quot;0.0&quot;, &quot;project.structure.proportion&quot;: &quot;0.0&quot;,
&quot;project.structure.side.proportion&quot;: &quot;0.36091954&quot;, &quot;project.structure.side.proportion&quot;: &quot;0.36091954&quot;,
&quot;run.code.analysis.last.selected.profile&quot;: &quot;pProject Default&quot;, &quot;run.code.analysis.last.selected.profile&quot;: &quot;pProject Default&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;preferences.lookFeel&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot; &quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
} }
}</component> }</component>
...@@ -85,6 +85,13 @@ ...@@ -85,6 +85,13 @@
</method> </method>
</configuration> </configuration>
</component> </component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="jdk-17.0.10-corretto-17.0.10-f644763e9732-24582b8d" />
</set>
</attachedChunks>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" /> <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="SvnConfiguration"> <component name="SvnConfiguration">
<configuration /> <configuration />
...@@ -102,6 +109,9 @@ ...@@ -102,6 +109,9 @@
<workItem from="1706042051986" duration="6820000" /> <workItem from="1706042051986" duration="6820000" />
<workItem from="1706057188033" duration="218000" /> <workItem from="1706057188033" duration="218000" />
<workItem from="1706070762735" duration="2123000" /> <workItem from="1706070762735" duration="2123000" />
<workItem from="1706112889389" duration="6798000" />
<workItem from="1706139932552" duration="8790000" />
<workItem from="1707268803363" duration="371000" />
</task> </task>
<task id="LOCAL-00001" summary="Apparently working version."> <task id="LOCAL-00001" summary="Apparently working version.">
<created>1580084914114</created> <created>1580084914114</created>
...@@ -156,7 +166,23 @@ ...@@ -156,7 +166,23 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1706051180665</updated> <updated>1706051180665</updated>
</task> </task>
<option name="localTasksCounter" value="8" /> <task id="LOCAL-00008" summary="Working on concurrency issues.">
<option name="closed" value="true" />
<created>1706114342828</created>
<option name="number" value="00008" />
<option name="presentableId" value="LOCAL-00008" />
<option name="project" value="LOCAL" />
<updated>1706114342829</updated>
</task>
<task id="LOCAL-00009" summary="Fixed concurrency issue.">
<option name="closed" value="true" />
<created>1706152811353</created>
<option name="number" value="00009" />
<option name="presentableId" value="LOCAL-00009" />
<option name="project" value="LOCAL" />
<updated>1706152811353</updated>
</task>
<option name="localTasksCounter" value="10" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
...@@ -169,22 +195,8 @@ ...@@ -169,22 +195,8 @@
<MESSAGE value="Cleaning up." /> <MESSAGE value="Cleaning up." />
<MESSAGE value="Working version ..." /> <MESSAGE value="Working version ..." />
<MESSAGE value="Nice and clean ..." /> <MESSAGE value="Nice and clean ..." />
<option name="LAST_COMMIT_MESSAGE" value="Nice and clean ..." /> <MESSAGE value="Working on concurrency issues." />
</component> <MESSAGE value="Fixed concurrency issue." />
<component name="XDebuggerManager"> <option name="LAST_COMMIT_MESSAGE" value="Fixed concurrency issue." />
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" type="java-line">
<url>file://$PROJECT_DIR$/src/edu/bu/ec504/spr24/sameGameTris/SameGameTris.java</url>
<line>697</line>
<option name="timeStamp" value="1" />
</line-breakpoint>
<line-breakpoint enabled="true" type="java-line">
<url>file://$PROJECT_DIR$/src/edu/bu/ec504/spr24/sameGameTris/SameGameTris.java</url>
<line>208</line>
<option name="timeStamp" value="3" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component> </component>
</project> </project>
\ No newline at end of file
...@@ -11,6 +11,8 @@ import edu.bu.ec504.spr24.sameGameTris.SameGameTris; ...@@ -11,6 +11,8 @@ import edu.bu.ec504.spr24.sameGameTris.SameGameTris;
*/ */
public abstract class Brain implements Runnable { public abstract class Brain implements Runnable {
// CONSTANTS
// FIELDS // FIELDS
protected final GUI myGUI; // stores the GUI class attached to this Brain protected final GUI myGUI; // stores the GUI class attached to this Brain
/** /**
...@@ -32,15 +34,14 @@ public abstract class Brain implements Runnable { ...@@ -32,15 +34,14 @@ public abstract class Brain implements Runnable {
final public void allDone() {allDone = true;} final public void allDone() {allDone = true;}
/** /**
* Starts the Brain a'thinking. * Asks the Brain to for a move, and effects it on the GUI.
* This is the code for requests decisions about which circles you Brain selects.
* *
* @see java.lang.Runnable#run() * @see java.lang.Runnable#run()
*/ */
final public void run() { final public void run() {
while (!allDone && !myGUI.gameOverQ()) { if (!allDone && !myGUI.gameOverQ()) {
Board.Pos theNextMove = nextMove(); Board.Pos theNextMove = nextMove();
myGUI.makeMove(theNextMove.xx, theNextMove.yy); // i.e. click on the lower left corner myGUI.makeMove(theNextMove.xx, theNextMove.yy);
} }
} }
......
package edu.bu.ec504.spr24.sameGameTris; package edu.bu.ec504.spr24.sameGameTris;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout; import java.awt.FlowLayout;
import java.awt.GridLayout; import java.awt.GridLayout;
import java.awt.event.*; import java.awt.event.*;
import java.io.Serial; import java.io.Serial;
import java.util.Random; import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
...@@ -45,13 +48,23 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener { ...@@ -45,13 +48,23 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener {
* Rate at which new "Tetrising" circles are being produced, in microseconds. * Rate at which new "Tetrising" circles are being produced, in microseconds.
* Production rate increases invesely proportional to the number of circle selections clicked so far. * Production rate increases invesely proportional to the number of circle selections clicked so far.
*/ */
static private long PRODUCT_CICLE_US = 5000000; static private long PRODUCT_CIRCLE_US = 10000000;
/** /**
* Rate at which new "Tetrising" circles descend on the board, in microseconds. * Rate at which new "Tetrising" circles descend on the board, in microseconds.
* Production rate increases invesely proportional to the number of circle selections clicked so far. * Production rate increases invesely proportional to the number of circle selections clicked so far.
*/ */
static private long DROP_CIRCLE_US = 5000000; static private long DROP_CIRCLE_US = 5000000;
/**
* The number of microseconds between Brain moves
*/
private static final long BRAIN_WAIT_US = 1000000;
/**
* The amount of time to wait between games, to allow for existing elements to settle.
*/
private static final long TIME_BETWEEN_GAMES_MS = 1000;
/** /**
* The number of empty rows on the top of the game. * The number of empty rows on the top of the game.
*/ */
...@@ -68,9 +81,10 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener { ...@@ -68,9 +81,10 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener {
static public final int NUM_COLORS = 3; static public final int NUM_COLORS = 3;
/** /**
* The number of milliseconds to display (highlight) a move before actually making it. * The number of milliseconds to wait after selecting a region before disappearing it.
* This allows the user to see the region being selected
*/ */
static private final int DISPLAY_TIME_MS = 1000; static private final int DISPLAY_WAIT_MS = 700;
/** /**
* The file where high scores are stored. * The file where high scores are stored.
...@@ -96,7 +110,16 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener { ...@@ -96,7 +110,16 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener {
private SelfAwareCircle[][] circles; private SelfAwareCircle[][] circles;
private String highScoreName = null; // the name to use for the high score private String highScoreName = null; // the name to use for the high score
static final Random randGen = new Random(); // package-private random number generator used for the entire package (i.e., repeatedly seeding with the same value produces the same randomness). static final Random randGen = new Random(); // package-private random number generator used for the entire package (i.e., repeatedly seeding with the same value produces the same randomness).
static private int numClicks = 1; // Must be a positive integer; counts the number of clicks on circles since the last time the board was reset. /**
* Counts the number of clicks on circles since the last time the board was reset.
* @requires must be a positive integer; c
*/
static private int numClicks = 1;
/**
* true iff the game is over (i.e., not running)
*/
boolean gameOver = false;
/** /**
* An instance of the SGE GUI. * An instance of the SGE GUI.
*/ */
...@@ -115,6 +138,8 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener { ...@@ -115,6 +138,8 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener {
// ... Brain elements // ... Brain elements
Brain theBrain; // the Brain (automated player) for the game Brain theBrain; // the Brain (automated player) for the game
Thread brainThread; // the thread that will run the Brain Thread brainThread; // the thread that will run the Brain
final ScheduledExecutorService brainExec = newScheduledThreadPool(2); // set up the brain threads
private ScheduledFuture<?> brainFuture = null; // schedule for Brain events (i.e., make moves)
// ... Tetris executor // ... Tetris executor
final ScheduledExecutorService tetrisExec = newScheduledThreadPool(2); // sets up tetris threads final ScheduledExecutorService tetrisExec = newScheduledThreadPool(2); // sets up tetris threads
...@@ -243,6 +268,9 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener { ...@@ -243,6 +268,9 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener {
* (i.e. every circle is surrounded by cleared circles or circles of a different color) * (i.e. every circle is surrounded by cleared circles or circles of a different color)
*/ */
public boolean gameOverQ() { public boolean gameOverQ() {
if (gameOver)
return true;
for (int xx = 0; xx < width; xx++) for (int xx = 0; xx < width; xx++)
for (int yy = 0; yy < height; yy++) { for (int yy = 0; yy < height; yy++) {
if (!circles[xx][yy].isCleared()) { if (!circles[xx][yy].isCleared()) {
...@@ -262,17 +290,21 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener { ...@@ -262,17 +290,21 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener {
/* /*
* "Clicks" on the circle at location (xx,yy) * "Clicks" on the circle at location (xx,yy)
*/ */
@Override
public void makeMove(int xx, int yy) { public void makeMove(int xx, int yy) {
circles[xx][yy].mouseEntered(null); // pretend the mouse was pressed at location (xx,yy) circles[xx][yy].mouseEntered(null); // pretend the mouse was pressed at location (xx,yy)
try { Timer t = new Timer();
Thread.sleep(DISPLAY_TIME_MS); // wait a bit for the user to register the move TimerTask tt = new TimerTask() {
} catch (InterruptedException ignored) { @Override
} public void run() {
circles[xx][yy].mouseExited(null); final SelfAwareCircle theCircle = circles[xx][yy];
circles[xx][yy].mousePressed(null); theCircle.mouseExited(null);
circles[xx][yy].mouseReleased( theCircle.mousePressed(null);
null); // pretend that the mouse button was released at the location theCircle.mouseReleased(
null); // pretend that the mouse button was released at the location
}
};
t.schedule(tt, DISPLAY_WAIT_MS);
} }
/** /**
...@@ -425,17 +457,23 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener { ...@@ -425,17 +457,23 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener {
dropCircleFuture.cancel(true); dropCircleFuture.cancel(true);
} }
/**
* @return true iff tetrising is going on
*/
public boolean isTetrisingQ() {
return produceCircleFuture!=null && produceCircleFuture.isCancelled()==false;
}
/** /**
* Sets up threads that drop circles onto the board and move them down over time. * Sets up threads that drop circles onto the board and move them down over time.
* As {@link #numClicks} gets larger, circles come more often and drop faster. * As {@link #numClicks} gets larger, circles come more often and drop faster.
*/ */
public void updateTetrising() { public void restartTetrising() {
// cancel the old schedules // cancel the old schedules
cancelTetrisingSchedules(); cancelTetrisingSchedules();
// start up the new ones // start up the new ones
produceCircleFuture = tetrisExec.scheduleAtFixedRate(new initDropCircles(), 0, produceCircleFuture = tetrisExec.scheduleAtFixedRate(new initDropCircles(), 0,
PRODUCT_CICLE_US / numClicks, TimeUnit.MICROSECONDS); PRODUCT_CIRCLE_US / numClicks, TimeUnit.MICROSECONDS);
dropCircleFuture = tetrisExec.scheduleAtFixedRate(new moveDropCircles(), 0, DROP_CIRCLE_US / numClicks, dropCircleFuture = tetrisExec.scheduleAtFixedRate(new moveDropCircles(), 0, DROP_CIRCLE_US / numClicks,
TimeUnit.MICROSECONDS); TimeUnit.MICROSECONDS);
} }
...@@ -448,8 +486,7 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener { ...@@ -448,8 +486,7 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener {
// close down the brain // close down the brain
highScoreName = null; highScoreName = null;
if (theBrain != null) stopBrain();
theBrain.allDone();
// score panel // score panel
totalPoints.setText("0"); totalPoints.setText("0");
...@@ -470,6 +507,9 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener { ...@@ -470,6 +507,9 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener {
// cancel tetrising schedules // cancel tetrising schedules
numClicks = 1; // reset the number of circle clicks in the system numClicks = 1; // reset the number of circle clicks in the system
cancelTetrisingSchedules(); cancelTetrisingSchedules();
// reset gameOver
gameOver = false;
} }
/* /*
...@@ -509,40 +549,40 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener { ...@@ -509,40 +549,40 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener {
GUIlock.lock(); // acquire a lock GUIlock.lock(); // acquire a lock
try { try {
// 1. SHIFT VERTICALLY // // 1. SHIFT VERTICALLY
for (int xx = 0; xx < width; xx++) { // for (int xx = 0; xx < width; xx++) {
int firstClr = height - 1; // the lowest cleared entry in the column // int firstClr = height - 1; // the lowest cleared entry in the column
int firstFull = // int firstFull =
height - 1; // the lowest uncleared entry in the column that has not yet been processed // height - 1; // the lowest uncleared entry in the column that has not yet been processed
boolean moveOn = false; // set to true in order to move on to the next column // boolean moveOn = false; // set to true in order to move on to the next column
while (!moveOn) { // while (!moveOn) {
// find the lowest clear entry in the column (if it exists) // // find the lowest clear entry in the column (if it exists)
try { // try {
while (!circles[xx][firstClr].isCleared()) // while (!circles[xx][firstClr].isCleared())
firstClr--; // firstClr--;
} catch (ArrayIndexOutOfBoundsException e) { // } catch (ArrayIndexOutOfBoundsException e) {
moveOn = true; // moveOn = true;
continue; // i.e. no cleared circle found in this column --- go to the next column // continue; // i.e. no cleared circle found in this column --- go to the next column
} // }
//
if (firstFull > firstClr) // if (firstFull > firstClr)
firstFull = firstClr; // only move items "down" the column // firstFull = firstClr; // only move items "down" the column
//
// find the lowest non-cleared entry in the column (if it exists) // // find the lowest non-cleared entry in the column (if it exists)
try { // try {
while (circles[xx][firstFull].isCleared()) // while (circles[xx][firstFull].isCleared())
firstFull--; // firstFull--;
} catch (ArrayIndexOutOfBoundsException e) { // } catch (ArrayIndexOutOfBoundsException e) {
moveOn = true; // moveOn = true;
continue; // i.e. the whole column is clear --- for now, go to the next column // continue; // i.e. the whole column is clear --- for now, go to the next column
} // }
//
moveCircle(circles[xx][firstFull], circles[xx][firstClr]); // moveCircle(circles[xx][firstFull], circles[xx][firstClr]);
//
firstFull--; // firstFull--;
firstClr--; // iterate // firstClr--; // iterate
} // }
} // }
// 2. SHIFT HORIZONTALLY // 2. SHIFT HORIZONTALLY
// Check to see if any column is now empty // Check to see if any column is now empty
...@@ -569,8 +609,9 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener { ...@@ -569,8 +609,9 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener {
// 3. (LAST ITEM) CHECK IF THE GAME HAS ENDED // 3. (LAST ITEM) CHECK IF THE GAME HAS ENDED
// This happens if every circle is surrounded by cleared circles or circles of a different color // This happens if every circle is surrounded by cleared circles or circles of a different color
if (gameOverQ()) if (gameOverQ()) {
doGameOver("exhausted all circles"); // all done doGameOver("exhausted all circles"); // all done
}
} finally { } finally {
GUIlock.unlock(); // release the circles for other processes GUIlock.unlock(); // release the circles for other processes
} }
...@@ -582,40 +623,78 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener { ...@@ -582,40 +623,78 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener {
* @param feedback Some string to present to the user; typically the reason that the game is over. * @param feedback Some string to present to the user; typically the reason that the game is over.
*/ */
private void doGameOver(String feedback) { private void doGameOver(String feedback) {
// close out other threads and variables
gameOver = true;
cancelTetrisingSchedules(); // cancel Tetrising threads cancelTetrisingSchedules(); // cancel Tetrising threads
stopBrain(); // stop the thinking Brain
// check the high score
int score = Integer.parseInt(totalPoints.getText()); int score = Integer.parseInt(totalPoints.getText());
highScore hs = new highScore(HIGH_SCORE_FILE); highScore hs = new highScore(HIGH_SCORE_FILE);
// check the high score
if (hs.newRecordQ(score)) { // i.e. a new record if (hs.newRecordQ(score)) { // i.e. a new record
JOptionPane.showMessageDialog(null, _showMessage(null,
"Game Over (" + feedback + ") - You got a new high score of " + score + " points!\n" + "Game Over (" + feedback + ") - You got a new high score of " + score + " points!\n" +
"Your weighted score for this sized board was " + highScore.hsComp.weightedScore( "Your weighted score for this sized board was " + highScore.hsComp.weightedScore(
new highScore.HSdata("", width, height, score)) + "."); new highScore.HSdata("", width, height, score)) + ".");
if (highScoreName == null) if (highScoreName == null)
highScoreName = JOptionPane.showInputDialog(null, highScoreName = _showInput(null,
"You got a new high score!\nPlease enter your name:"); "You got a new high score!\nPlease enter your name:");
if (highScoreName != null) { // i.e. the user is interested in high scores if (highScoreName != null) { // i.e. the user is interested in high scores
// populate the high score item // populate the high score item
highScore.HSdata datum = new highScore.HSdata(highScoreName, width, height, score); highScore.HSdata datum = new highScore.HSdata(highScoreName, width, height, score);
hs.putScore(datum); // enter the name into the high score list hs.putScore(datum); // enter the name into the high score list
} }
} else } else {
JOptionPane.showMessageDialog(null, _showMessage(null,
"Game Over (" + feedback + ") - You did not make the high score. You had " + score "Game Over (" + feedback + ") - You did not make the high score. You had " + score
+ " points.\n" + + " points.\n" +
"Your weighted score for this sized board was " + highScore.hsComp.weightedScore( "Your weighted score for this sized board was " + highScore.hsComp.weightedScore(
new highScore.HSdata("", width, height, score)) + "."); new highScore.HSdata("", width, height, score)) + ".");
}
_showMessage(null, "Current high scores:\n" + hs.display());
// // cleanup
// cleanUp();
// setupGUI();
// "that's all folks"
System.exit(0);
}
/**
* Do a task in the future.
* @param task The task to complete.
* @param whenMS How many milliseconds in the future to do the task.
*/
private void _doInFuture(TimerTask task, long whenMS) {
Timer timer = new Timer();
timer.schedule(task, whenMS);
}
JOptionPane.showMessageDialog(null, "Current high scores:\n" + hs.display()); /**
* Shows a message on the parent component and waits for the user to click "OK".
* @param parent The component on which to show the dialog box.
* @param message The message to display.
*/
private void _showMessage(Component parent, String message) {
JOptionPane.showMessageDialog(parent, message);
}
// i.e. time for a new game /**
cleanUp(); * Shows an input request on the parent component and returns the user's input.
setupGUI(); * @param parent The component on which to show the dialog box.
* @param message The input request
*/
private String _showInput(Component parent, String message) {
return JOptionPane.showInputDialog(parent, message);
} }
/**
* Handle menu events
* @param e The event to handle.
*/
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
Object source = e.getSource(); Object source = e.getSource();
...@@ -665,11 +744,24 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener { ...@@ -665,11 +744,24 @@ public class SameGameTris extends GUI implements ActionListener, ItemListener {
private void startBrain(Brain theBrain) { private void startBrain(Brain theBrain) {
this.theBrain = theBrain; this.theBrain = theBrain;
highScoreName = theBrain.myName(); // the computer gets credit for any subsequent high score highScoreName = theBrain.myName(); // the computer gets credit for any subsequent high score
brainThread = new Thread(theBrain, "Brain Thread"); // brainThread = new Thread(theBrain, "Brain Thread");
brainThread.start(); // brainThread.start();
brainFuture = brainExec.scheduleAtFixedRate(theBrain, 0, BRAIN_WAIT_US, TimeUnit.MICROSECONDS);
} }
// For handling the menu checkBox /**
* Stops the Brain that makes move.
*/
private void stopBrain() {
if (theBrain != null) {
theBrain.allDone();
brainFuture.cancel(true);
}
}
/**
* Handles the menu checkBox
*/
public void itemStateChanged(ItemEvent e) { public void itemStateChanged(ItemEvent e) {
Object source = e.getItemSelectable(); Object source = e.getItemSelectable();
......
...@@ -16,7 +16,7 @@ import java.util.concurrent.atomic.AtomicInteger; ...@@ -16,7 +16,7 @@ import java.util.concurrent.atomic.AtomicInteger;
*/ */
class SelfAwareCircle extends Component implements MouseListener, SelfAwareListener { class SelfAwareCircle extends Component implements MouseListener, SelfAwareListener {
// Constants // Constants
private final Color _hltColor = new Color(200,200,200); // the color to use for highlighting a button private final Color _hltColor = new Color(200, 200, 200); // the color to use for highlighting a button
// FIELDS // FIELDS
public final int xx; public final int xx;
...@@ -25,9 +25,9 @@ class SelfAwareCircle extends Component implements MouseListener, SelfAwareListe ...@@ -25,9 +25,9 @@ class SelfAwareCircle extends Component implements MouseListener, SelfAwareListe
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final ArrayList<SelfAwareListener> _listeners = new ArrayList<>(); // who is listening to events fired by this circle private final ArrayList<SelfAwareListener> _listeners = new ArrayList<>(); // who is listening to events fired by this circle
private static int _id =0; private static int _id = 0;
private final int _myID; // the circle's ID private final int _myID; // the circle's ID
private Visibility _state = Visibility.plain; // the current state of the circle private Visibility _state = Visibility.plain; // the current state of the circle
private final SameGameTris sameGameTrisGUI; // a link to the GUI that created this circle private final SameGameTris sameGameTrisGUI; // a link to the GUI that created this circle
/** /**
...@@ -39,7 +39,7 @@ class SelfAwareCircle extends Component implements MouseListener, SelfAwareListe ...@@ -39,7 +39,7 @@ class SelfAwareCircle extends Component implements MouseListener, SelfAwareListe
/* /*
* Records the visibility of an object. * Records the visibility of an object.
*/ */
public enum Visibility{ public enum Visibility {
/* /*
* the circle is highlighted * the circle is highlighted
*/ */
...@@ -51,7 +51,8 @@ class SelfAwareCircle extends Component implements MouseListener, SelfAwareListe ...@@ -51,7 +51,8 @@ class SelfAwareCircle extends Component implements MouseListener, SelfAwareListe
/* /*
* the circle is cleared (i.e. background color) * the circle is cleared (i.e. background color)
*/ */
clear} clear
}
// CONSTRUCTORS // CONSTRUCTORS
...@@ -59,203 +60,204 @@ class SelfAwareCircle extends Component implements MouseListener, SelfAwareListe ...@@ -59,203 +60,204 @@ class SelfAwareCircle extends Component implements MouseListener, SelfAwareListe
* Creates a circle of the specified color at the specified location * Creates a circle of the specified color at the specified location
* *
* @param clr The color of the new circle. * @param clr The color of the new circle.
* @param xx The xx location of the circle in the grid of circles. * @param xx The xx location of the circle in the grid of circles.
* @param yy The yy location of the circle in the grid of circles. * @param yy The yy location of the circle in the grid of circles.
*/ */
SelfAwareCircle(CircleColor clr, int xx, int yy) { SelfAwareCircle(CircleColor clr, int xx, int yy) {
// record field parameters // record field parameters
this.xx = xx; this.xx = xx;
this.yy = yy; this.yy = yy;
this.clr = clr; // record the color of the button this.clr = clr; // record the color of the button
if (clr == CircleColor.NONE) // circles with no color should be cleared if (clr == CircleColor.NONE) // circles with no color should be cleared
setClear(); setClear();
this.sameGameTrisGUI = SameGameTris.getInstance(); // record the GUI that created this button this.sameGameTrisGUI = SameGameTris.getInstance(); // record the GUI that created this button
addMouseListener(this); // register for mouse events addMouseListener(this); // register for mouse events
_myID = _id++; _myID = _id++;
} }
/** /**
* Creates a circle of the specified color at the specified location, based on {@link #SelfAwareCircle(CircleColor, int, int) .} * Creates a circle of the specified color at the specified location, based on {@link #SelfAwareCircle(CircleColor, int, int) .}
*/ */
SelfAwareCircle(int xx, int yy) { SelfAwareCircle(int xx, int yy) {
this(CircleColor.randColor(), xx, yy); this(CircleColor.randColor(), xx, yy);
} }
// METHODS
// ... helpers
// .. info methods and accessors/setters // METHODS
static int getRegionLength() { // ... helpers
return regionLength.get();
}
public int getId() { // .. info methods and accessors/setters
return _myID; static int getRegionLength() {
} return regionLength.get();
}
Visibility get_state() { public int getId() {
return _state; return _myID;
} }
public CircleColor getColor() { Visibility get_state() {
return clr; return _state;
} }
public CircleColor getColor() {
return clr;
}
/** /**
* Resets the color of this circle to a random color. * Resets the color of this circle to a random color.
*/ */
void resetColor() { void resetColor() {
clr=CircleColor.randColor(); clr = CircleColor.randColor();
} }
void set_state(Visibility _state) {
if (_state == Visibility.clear)
setClear(); // i.e. there is extra overhead in clearing
else
this._state = _state;
repaint();
}
boolean isCleared() {
return this._state == Visibility.clear;
}
void setClear() {
_state = Visibility.clear;
repaint();
}
void set_state(Visibility _state) { /*
if (_state ==Visibility.clear) * Copies immutable elements of this SelfAwareCircle to "recipient"
setClear(); // i.e. there is extra overhead in clearing * *except* position-dependent items (e.g. _listeners, xx, yy)
else */
this._state = _state; void copyTo(SelfAwareCircle recipient) {
repaint(); recipient.clr = clr;
} if (recipient._state == _state)
return; // i.e. nothing to do
boolean isCleared() {
return this._state == Visibility.clear;
}
void setClear() { // special cases
_state = Visibility.clear; // ... copying a cleared circle to an uncleared circle
repaint(); if (_state == Visibility.clear)
} recipient.setClear(); // includes removing a mouse listener
/* // copy the state
* Copies immutable elements of this SelfAwareCircle to "recipient" recipient.set_state(_state);
* *except* position-dependent items (e.g. _listeners, xx, yy) }
*/
void copyTo(SelfAwareCircle recipient) {
recipient.clr=clr;
if (recipient._state == _state)
return; // i.e. nothing to do
// special cases
// ... copying a cleared circle to an uncleared circle
if (_state == Visibility.clear)
recipient.setClear(); // includes removing a mouse listener
// copy the state
recipient.set_state(_state);
}
// ... Mouse methods // ... Mouse methods
public void mouseClicked(MouseEvent e) {} public void mouseClicked(MouseEvent e) {
}
/* /*
* Highlights the button by changing its icon and firing off an event * Highlights the button by changing its icon and firing off an event
* @param state determines whether to highlight or unhighlight the circle * @param state determines whether to highlight or unhighlight the circle
*/ */
private void _highlightButton(Visibility stt) { private void _highlightButton(Visibility stt) {
if (isCleared()) // don't go further with already cleared circles if (isCleared()) // don't go further with already cleared circles
return; return;
// set the state // set the state
if (stt==Visibility.clear) // clear has its own special treatment if (stt == Visibility.clear) // clear has its own special treatment
setClear(); // properly clear this circle (including removal of mouse listener) setClear(); // properly clear this circle (including removal of mouse listener)
else else
set_state(stt); // set the non-cleared state appropriately set_state(stt); // set the non-cleared state appropriately
// deal with the region length // deal with the region length
regionLength.getAndIncrement(); // update the region length by the newly highlighted item regionLength.getAndIncrement(); // update the region length by the newly highlighted item
// announce the rollover to other circles // announce the rollover to other circles
_fireButtonRolloverEvent(stt); _fireButtonRolloverEvent(stt);
} }
/* /*
* Mouse entered the object area: * Mouse entered the object area:
* * Change its button icon * * Change its button icon
* * Broadcast the change to other buttons * * Broadcast the change to other buttons
* @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent) * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
*/ */
public void mouseEntered(MouseEvent e) { public void mouseEntered(MouseEvent e) {
regionLength.set(0); // i.e. reinitialize regionLength.set(0); // i.e. reinitialize
_highlightButton(Visibility.highlight); _highlightButton(Visibility.highlight);
} }
/** /**
* Mouse exited the object area. * Mouse exited the object area.
* * Change its button icon * * Change its button icon
* * Broadcast the change to other buttons * * Broadcast the change to other buttons
*/ */
public void mouseExited(MouseEvent e) { public void mouseExited(MouseEvent e) {
regionLength.set(0); // i.e. reinitialize regionLength.set(0); // i.e. reinitialize
_highlightButton(Visibility.plain); _highlightButton(Visibility.plain);
} }
/** /**
* The mouse button was pressed. * The mouse button was pressed.
*/ */
public void mousePressed(MouseEvent e) { public void mousePressed(MouseEvent e) {
regionLength.set(0); // i.e. reinitialize regionLength.set(0); // i.e. reinitialize
} }
/** /**
* The mouse button was released. * The mouse button was released.
*/ */
public void mouseReleased(MouseEvent e) { public void mouseReleased(MouseEvent e) {
_highlightButton(Visibility.clear); _highlightButton(Visibility.clear);
// register the score
sameGameTrisGUI.totalPoints.setText("" +
(Integer.parseInt(sameGameTrisGUI.totalPoints.getText()) +
sameGameTrisGUI.score(SelfAwareCircle.getRegionLength(), getColor())));
// request that the board be shifted appropriately // register the score
sameGameTrisGUI.shiftCircles(); sameGameTrisGUI.totalPoints.setText("" +
(Integer.parseInt(sameGameTrisGUI.totalPoints.getText()) +
sameGameTrisGUI.score(SelfAwareCircle.getRegionLength(), getColor())));
// update Tetrising // request that the board be shifted appropriately
sameGameTrisGUI.updateNumClicks(); sameGameTrisGUI.shiftCircles();
sameGameTrisGUI.updateTetrising();
} // update Tetrising
sameGameTrisGUI.updateNumClicks();
if (!sameGameTrisGUI.gameOverQ())
sameGameTrisGUI.restartTetrising();
}
// ... event methods // ... event methods
public synchronized void addSelfAwareListener(SelfAwareListener l) { public synchronized void addSelfAwareListener(SelfAwareListener l) {
_listeners.add(l); _listeners.add(l);
} }
public synchronized void removeSelfAwareListener(SelfAwareListener l) { public synchronized void removeSelfAwareListener(SelfAwareListener l) {
_listeners.remove(l); _listeners.remove(l);
} }
/* /*
* fires off events to highlight or unhighlight circles * fires off events to highlight or unhighlight circles
* @param state determines whether to highlight or unhighlight circles * @param state determines whether to highlight or unhighlight circles
*/ */
private synchronized void _fireButtonRolloverEvent(SelfAwareCircle.Visibility state) { private synchronized void _fireButtonRolloverEvent(SelfAwareCircle.Visibility state) {
CircleRolloverEvent bre = new CircleRolloverEvent(this, xx, yy, clr, state); CircleRolloverEvent bre = new CircleRolloverEvent(this, xx, yy, clr, state);
for (SelfAwareListener _listener : _listeners) { for (SelfAwareListener _listener : _listeners) {
SelfAwareCircle temp = (SelfAwareCircle) _listener; SelfAwareCircle temp = (SelfAwareCircle) _listener;
temp.rollingOver(bre); temp.rollingOver(bre);
}
} }
}
/* /*
* What to do when receiving information from another button of a rolling over event * What to do when receiving information from another button of a rolling over event
* @param e the event broadcast by the other button * @param e the event broadcast by the other button
*/ */
public void rollingOver(CircleRolloverEvent e) { public void rollingOver(CircleRolloverEvent e) {
if (_state == e.getVisibility() || isCleared()) // i.e. this circle has already been processed if (_state == e.getVisibility() || isCleared()) // i.e. this circle has already been processed
return; return;
if (e.getColor().equals(clr)) { // check if the color is the same as mine
_highlightButton(e.getVisibility());
}
// in all other cases, only update the text field (i.e. stop the recursion)
sameGameTrisGUI.regionPoints.setText(""+(SelfAwareCircle.getRegionLength())); // update the gui text field
}
// ... drawing methods if (e.getColor().equals(clr)) { // check if the color is the same as mine
public void paint(Graphics g) { _highlightButton(e.getVisibility());
// set the color, depending on whether this is being highlighted }
switch (_state) { // in all other cases, only update the text field (i.e. stop the recursion)
sameGameTrisGUI.regionPoints.setText("" + (SelfAwareCircle.getRegionLength())); // update the gui text field
}
// ... drawing methods
public void paint(Graphics g) {
// set the color, depending on whether this is being highlighted
switch (_state) {
case highlight: case highlight:
g.setColor(_hltColor); // change to highlighting color g.setColor(_hltColor); // change to highlighting color
break; break;
...@@ -265,12 +267,12 @@ class SelfAwareCircle extends Component implements MouseListener, SelfAwareListe ...@@ -265,12 +267,12 @@ class SelfAwareCircle extends Component implements MouseListener, SelfAwareListe
case clear: case clear:
g.setColor(getParent().getBackground()); // i.e. set to background color g.setColor(getParent().getBackground()); // i.e. set to background color
break; break;
} }
// size of component // size of component
Dimension size = getSize(); Dimension size = getSize();
// draw the circle // draw the circle
g.fillOval(0, 0, size.width, size.height); g.fillOval(0, 0, size.width, size.height);
} }
} }