diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 7679cf52fab5030c48f7172ea8a82b57cf84b419..e2f56d778a657b089c5bc61a3b1b1ac50b18042c 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -10,20 +10,14 @@ - - + - - - - - - - - - - - + + + + + + @@ -97,7 +94,7 @@ - + 1580084914114 @@ -120,7 +117,23 @@ - @@ -130,6 +143,23 @@ - + + + + + file://$PROJECT_DIR$/src/edu/bu/ec504/spr24/sameGameTris/sge.java + 643 + + + file://$PROJECT_DIR$/src/edu/bu/ec504/spr24/sameGameTris/sge.java + 167 + + + \ No newline at end of file diff --git a/src/edu/bu/ec504/spr24/Brain/Board.java b/src/edu/bu/ec504/spr24/Brain/Board.java new file mode 100644 index 0000000000000000000000000000000000000000..d19b1b1e2717bed730eb11eece51a9a86b1e2796 --- /dev/null +++ b/src/edu/bu/ec504/spr24/Brain/Board.java @@ -0,0 +1,198 @@ +package edu.bu.ec504.spr24.Brain; + +import edu.bu.ec504.spr24.sameGameTris.CircleColor; +import edu.bu.ec504.spr24.sameGameTris.sge; +import java.util.LinkedList; +import java.util.Objects; + +/** + * An internal recording of a board set up. + */ +class Board { + + /** + * A 2-d Linked list of colors on the board. + */ + final LinkedList> data; + + /** + * The width and height of the board. + */ + final private int width, height; + + // constructs a board of specified width and height + Board(int width, int height) { + this.width = width; + this.height = height; + + // allocate the data structure for storing board contents + data = new LinkedList<>(); + + // set up the data structure + for (int ii = 0; ii < width; ii++) { + LinkedList temp = new LinkedList<>(); + for (int jj = 0; jj < height; jj++) {temp.add(CircleColor.NONE);} + data.add(temp); + } + } + + /** + * default constructor + */ + Board() { + this(sge.getInstance().boardWidth(), sge.getInstance().boardHeight()); + } + + /** + * copy constructor + * + * @param baseBoard the board to copy + */ + Board(Board baseBoard) { + // allocate memory for the new board + this(baseBoard.width, baseBoard.height); + + // copy over all the specific items + for (int xx = 0; xx < columns(); xx++) { + for (int yy = 0; yy < rows(xx); yy++) {modify(xx, yy, baseBoard.getAt(xx, yy));} + } + } + + /** + * @return the color of the yy'th item in the xx'th column, or CircleColor.NONE + * if the cell is not in the data container + */ + CircleColor getAt(int xx, int yy) { + try { + return data.get(xx).get(yy); + } catch (IndexOutOfBoundsException e) { + return CircleColor.NONE; + } + } + + /** + * @return the xx'th column, or null if the xx'th column does not exist in the data container + */ + private LinkedList getAt(int xx) { + try { + return data.get(xx); + } catch (IndexOutOfBoundsException e) { + return null; + } + } + + /** + * Changes yy'th item in xx'th column + * %R: The circle at this location must still be in the data field + */ + void modify(int xx, int yy, CircleColor col) { + data.get(xx).set(yy, col); + } + + /** + * Deletes yy'th item in xx'th column + * %R: The circle at this location must still be in the data field + */ + private void delete(int xx, int yy) { + data.get(xx).remove(yy); + } + + /** + * Deletes the column at (horizontal) board location xx + * * %R: The column at this location must still be in the data field + */ + private void delete(int xx) { + data.remove(xx); + } + + // public accessors + + /** + * Simulates a click on the yy'th cell of the xx'th column + * + * @return the number of cells deleted + * @modifies Deletes cells in the same region as cell (xx,yy) + * @expects The clicked cell is assumed not to be empty + */ + int clickNode(int xx, int yy) { + CircleColor myColor = getAt(xx, yy); // the color to match + + // mark the region + int count = clickNodeHelper(xx, yy, myColor); + + // clean up + // ... delete all cells with no color + // ... delete from the end to the front so that there are no problems with re-indexing + for (int ii = data.size() - 1; ii >= 0; ii--) { + for (int jj = Objects.requireNonNull(getAt(ii)).size() - 1; jj >= 0; jj--) { + if (getAt(ii, jj) == CircleColor.NONE) {delete(ii, jj);} + } + + // ... delete the column if it is empty + if (Objects.requireNonNull(getAt(ii)).size() == 0) {delete(ii);} + } + + return count; + } + + /** + * Recursive procedure for propagating a click at a location with color "col". + * All items in the same region as the clicked cell are made to have CircleColor.NONE + * + * @return the number of cells changed to CircleColor.NONE in this call (and its recursive subcalls) + * @modifies the color of some cells, but no cells are actually deleted + */ + int clickNodeHelper(int xx, int yy, CircleColor col) { + if (getAt(xx, yy) == CircleColor.NONE || + // we've either already seen this, or we've hit an empty space + getAt(xx, yy) != col) // this is not the color we want + {return 0;} + + // modify the current cell + modify(xx, yy, CircleColor.NONE); + + return 1 + // 1 is for the current cell + clickNodeHelper(xx - 1, yy, col) + // cell to the left + clickNodeHelper(xx + 1, yy, col) + // cell to the right + clickNodeHelper(xx, yy - 1, col) + // cell below + clickNodeHelper(xx, yy + 1, col) // cell above + ; + } + + /** + * @return the number of columns in the current state + */ + int columns() { + return data.size(); + } + + /** + * @param xx the column being considered (assumed to exist) + * @return the number of rows in column xx + */ + int rows(int xx) { + return data.get(xx).size(); + } + + /** + * @return a "pretty-printed" version of the data structure + */ + public String display() { + String temp = data.toString(); + return temp.replace("], [", "]\n["); + } + + /** + * Stores an (xx,yy) position on the board. + */ + static class Pos { + + final int xx; + final int yy; + + Pos(int xx, int yy) { + this.xx = xx; + this.yy = yy; + } + } +} diff --git a/src/edu/bu/ec504/spr24/Brain/Brain.java b/src/edu/bu/ec504/spr24/Brain/Brain.java index 63da5cfff3a9134f0e3d1e38385c0cd932ce26e7..11ae4f71fdcdda60db0621551aa6faf3310ccf9d 100644 --- a/src/edu/bu/ec504/spr24/Brain/Brain.java +++ b/src/edu/bu/ec504/spr24/Brain/Brain.java @@ -1,6 +1,7 @@ package edu.bu.ec504.spr24.Brain; import edu.bu.ec504.spr24.sameGameTris.GUI; +import edu.bu.ec504.spr24.sameGameTris.sge; /** * The Brain is the artificial intelligence that tries to come up with the @@ -10,32 +11,47 @@ import edu.bu.ec504.spr24.sameGameTris.GUI; */ public abstract class Brain implements Runnable { // FIELDS - protected final GUI myGUI; // the GUI class attached to this Brain - - // METHODS - + protected final GUI myGUI; // stores the GUI class attached to this Brain /** - * Constructs the brain - * @param myGUI A reference to the Graphical User Interface on which the game is being played. + * When set to true, the Brain should stop what it's doing and exit as soon as is appropriate. */ - Brain(GUI myGUI) { this.myGUI = myGUI; } + private volatile boolean allDone = false; + + // METHODS /** - * Each Brain should have a name, which is provided by this method. - * @return the name of the brain + * Constructs the brain */ - public abstract String myName(); + Brain() { this.myGUI = sge.getInstance(); } /** * This is called when the Brain is being asked to close down (i.e., the game is over). * It should clean up any data structures and/or signal threads to close, etc. */ - public abstract void allDone(); - + final public void allDone() { allDone=true; }; + /** * Starts the Brain a'thinking. - * This is the code for making decisions about which circles you Brain selects. + * This is the code for requests decisions about which circles you Brain selects. * @see java.lang.Runnable#run() */ - public abstract void run(); + final public void run() { + while (!allDone && !myGUI.gameOverQ()) { + Board.Pos theNextMove = nextMove(); + myGUI.makeMove(theNextMove.xx, theNextMove.yy); // i.e. click on the lower left corner + } + } + + // ... METHODS FOR SUBCLASSES TO DEFINE + /** + * Each Brain should have a name, which is provided by this method. + * @return the name of the brain + */ + public abstract String myName(); + + /** + * Returns the next move of the Brain. + */ + public abstract Board.Pos nextMove(); + } diff --git a/src/edu/bu/ec504/spr24/Brain/LazyBrain.java b/src/edu/bu/ec504/spr24/Brain/LazyBrain.java index 7c429134ddeae73ef754834117d86f5ee7fb00c5..0620bc36047c5e7a48ff407c050ca04e53162c27 100644 --- a/src/edu/bu/ec504/spr24/Brain/LazyBrain.java +++ b/src/edu/bu/ec504/spr24/Brain/LazyBrain.java @@ -1,9 +1,6 @@ package edu.bu.ec504.spr24.Brain; -import java.util.LinkedList; -import java.util.Objects; - -import edu.bu.ec504.spr24.sameGameTris.GUI; +import edu.bu.ec504.spr24.Brain.Board.Pos; import edu.bu.ec504.spr24.sameGameTris.CircleColor; /** @@ -13,11 +10,6 @@ public class LazyBrain extends Brain { // FIELDS - /** - * When set to true, the Brain should stop what it's doing and exit as soon as is appropriate. - */ - private volatile boolean allDone = false; - /** * The Brain's recording of the current state of the board. */ @@ -25,18 +17,11 @@ public class LazyBrain extends Brain { // METHODS - /** Constructs a Brain linked to a specified myGUI - * @param myGUI The GUI that will be instantiating the Brain - */ - public LazyBrain(GUI myGUI) { - super(myGUI); - } - /** - * {@inheritDoc} + * Constructs a Brain linked to a specified myGUI */ - public void allDone() { - allDone = true; + public LazyBrain() { + super(); } /** @@ -46,23 +31,18 @@ public class LazyBrain extends Brain { return "Lazy Brain"; } - /** - * {@inheritDoc} - */ - public void run() { + @Override + public Pos nextMove() { // Initialize and set up the internal board state with the current position currBoard = new Board(); for (int xx=0; xx max) { // record a new best move @@ -101,186 +81,5 @@ public class LazyBrain extends Brain { // INTERNAL CLASSES - /** - * Stores a board set up - */ - private class Board { - - /** - * A 2-d Linked list of colors on the board. - */ - final LinkedList< LinkedList > data; - - /** - * The width and height of the board. - */ - final private int width, height; - - // constructs a board of specified width and height - Board(int width, int height) { - this.width = width; this.height = height; - - // allocate the data structure for storing board contents - data = new LinkedList<>(); - - // set up the data structure - for (int ii=0; ii temp = new LinkedList<>(); - for (int jj=0; jjdata container - */ - private CircleColor get(int xx, int yy) { - try { - return data.get(xx).get(yy); - } catch (IndexOutOfBoundsException e) { - return CircleColor.NONE; - } - } - - /** - * @return the xx'th column, or null if the xx'th column does not exist in the data container - */ - private LinkedList get(int xx) { - try { - return data.get(xx); - } catch (IndexOutOfBoundsException e) { - return null; - } - } - /** - * Changes yy'th item in xx'th column - * %R: The circle at this location must still be in the data field - */ - private void modify(int xx, int yy, CircleColor col) { - data.get(xx).set(yy, col); - } - - /** - * Deletes yy'th item in xx'th column - * %R: The circle at this location must still be in the data field - */ - private void delete(int xx, int yy) { - data.get(xx).remove(yy); - } - - /** - * Deletes the column at (horizontal) board location xx - * * %R: The column at this location must still be in the data field - */ - private void delete(int xx) { - data.remove(xx); - } - - // public accessors - /** - * Simulates a click on the yy'th cell of the xx'th column - * @return the number of cells deleted - * @modifies Deletes cells in the same region as cell (xx,yy) - * @expects The clicked cell is assumed not to be empty - */ - int clickNode(int xx, int yy) { - CircleColor myColor = get(xx,yy); // the color to match - - // mark the region - int count = clickNodeHelper(xx, yy, myColor); - - // clean up - // ... delete all cells with no color - // ... delete from the end to the front so that there are no problems with re-indexing - for (int ii=data.size()-1; ii>=0; ii--) { - for (int jj = Objects.requireNonNull(get(ii)).size()-1; jj>=0; jj--) - if (get(ii,jj)==CircleColor.NONE) - delete(ii, jj); - - // ... delete the column if it is empty - if (Objects.requireNonNull(get(ii)).size()==0) - delete(ii); - } - - return count; - } - - /** - * Recursive procedure for propagating a click at a location with color "col". - * All items in the same region as the clicked cell are made to have CircleColor.NONE - * @modifies the color of some cells, but no cells are actually deleted - * @return the number of cells changed to CircleColor.NONE in this call (and its recursive subcalls) - */ - private int clickNodeHelper(int xx, int yy, CircleColor col) { - if (get(xx,yy) == CircleColor.NONE || // we've either already seen this, or we've hit an empty space - get(xx,yy) != col) // this is not the color we want - return 0; - - // modify the current cell - modify(xx,yy,CircleColor.NONE); - - return 1 + // 1 is for the current cell - clickNodeHelper(xx-1, yy, col) + // cell to the left - clickNodeHelper(xx+1, yy, col) + // cell to the right - clickNodeHelper(xx, yy-1, col) + // cell below - clickNodeHelper(xx, yy+1, col) // cell above - ; - } - - /** - * @return the number of columns in the current state - */ - int columns() { - return data.size(); - } - - /** - * - * @param xx the column being considered (assumed to exist) - * @return the number of rows in column xx - */ - int rows(int xx) { - return data.get(xx).size(); - } - - /** - * @return a "pretty-printed" version of the data structure - */ - public String display() { - String temp = data.toString(); - return temp.replace("], [", "]\n["); - } - - /** - * Stores an (xx,yy) position on the board. - */ - static class Pos { - final int xx; - final int yy; - Pos(int xx, int yy) {this.xx=xx; this.yy=yy;} - } - } } diff --git a/src/edu/bu/ec504/spr24/Brain/SimpleBrain.java b/src/edu/bu/ec504/spr24/Brain/SimpleBrain.java index a20c245d2d3890993706e7642804e24c2a765694..d06b2de300eba6f16c893df780b8469778b3f30c 100644 --- a/src/edu/bu/ec504/spr24/Brain/SimpleBrain.java +++ b/src/edu/bu/ec504/spr24/Brain/SimpleBrain.java @@ -1,6 +1,6 @@ package edu.bu.ec504.spr24.Brain; -import edu.bu.ec504.spr24.sameGameTris.GUI; +import edu.bu.ec504.spr24.Brain.Board.Pos; /** * A very simple Brain for the game. @@ -10,16 +10,10 @@ public class SimpleBrain extends Brain { // fields private volatile boolean allDone = false; // when set to true, the Brain should stop what it's doing and exit (at an appropriate time) - public SimpleBrain(GUI myGUI) { - super(myGUI); + public SimpleBrain() { + super(); } - /** - * {@inheritDoc} - */ - public void allDone() { - allDone = true; - } /** * {@inheritDoc} @@ -28,11 +22,8 @@ private volatile boolean allDone = false; // when set to true, the Brain should return "Simple Brain"; } - /** - * {@inheritDoc} - */ - public void run() { - while (!allDone && !myGUI.gameOverQ()) - myGUI.makeMove(0, myGUI.boardHeight()-1); // i.e. click on the lower left corner + @Override + public Pos nextMove() { + return new Pos(0, myGUI.boardHeight()-1); } } diff --git a/src/edu/bu/ec504/spr24/Brain/SmarterBrain.java b/src/edu/bu/ec504/spr24/Brain/SmarterBrain.java index 0d72fd9ddee5bbe28e5b16fe9d8696f27013f984..d666a443c018df0de85a367e203bc128b50a7d57 100644 --- a/src/edu/bu/ec504/spr24/Brain/SmarterBrain.java +++ b/src/edu/bu/ec504/spr24/Brain/SmarterBrain.java @@ -1,19 +1,14 @@ package edu.bu.ec504.spr24.Brain; -import edu.bu.ec504.spr24.sameGameTris.GUI; - +import edu.bu.ec504.spr24.Brain.Board.Pos; import javax.swing.*; /** * A smarter brain, for you to produce. */ public class SmarterBrain extends Brain { - public SmarterBrain(GUI myGUI) { - super(myGUI); - } - - @Override - public void allDone() { + public SmarterBrain() { + super(); } @Override @@ -22,7 +17,9 @@ public class SmarterBrain extends Brain { } @Override - public void run() { + public Pos nextMove() { JOptionPane.showMessageDialog(null, "Please insert brain here!"); + return null; } + } diff --git a/src/edu/bu/ec504/spr24/sameGameTris/SelfAwareCircle.java b/src/edu/bu/ec504/spr24/sameGameTris/SelfAwareCircle.java index 58541a632f9b60cc39a5cbbb24f867cc8b5981f5..f9f4217177b864a469f80e5ca6c49c030be04e1c 100644 --- a/src/edu/bu/ec504/spr24/sameGameTris/SelfAwareCircle.java +++ b/src/edu/bu/ec504/spr24/sameGameTris/SelfAwareCircle.java @@ -55,28 +55,28 @@ class SelfAwareCircle extends Component implements MouseListener, SelfAwareListe /** * Creates a circle of the specified color at the specified location + * * @param clr The color of the new circle. * @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 sgeGui A reference to the game GUI in which the circle will lie. */ - SelfAwareCircle(CircleColor clr, int xx, int yy, sge sgeGui) { + SelfAwareCircle(CircleColor clr, int xx, int yy) { // record field parameters this.xx = xx; this.yy = yy; this.clr = clr; // record the color of the button if (clr == CircleColor.NONE) // circles with no color should be cleared setClear(); - this.sgeGUI = sgeGui; // record the GUI that created this button + this.sgeGUI = sge.getInstance(); // record the GUI that created this button addMouseListener(this); // register for mouse events _myID = _id++; } /** - * Creates a circle of the specified color at the specified location, based on {@link #SelfAwareCircle(CircleColor, int, int, sge).} + * Creates a circle of the specified color at the specified location, based on {@link #SelfAwareCircle(CircleColor, int, int) .} */ - SelfAwareCircle(int xx, int yy, sge mySgeGui) { - this(CircleColor.randColor(), xx, yy, mySgeGui); + SelfAwareCircle(int xx, int yy) { + this(CircleColor.randColor(), xx, yy); } // METHODS diff --git a/src/edu/bu/ec504/spr24/sameGameTris/sge.java b/src/edu/bu/ec504/spr24/sameGameTris/sge.java index 047f815e4b2329799436e0675862b59ddbfd899a..5ea62761387b70bc57269ac7f205f63a742db84e 100644 --- a/src/edu/bu/ec504/spr24/sameGameTris/sge.java +++ b/src/edu/bu/ec504/spr24/sameGameTris/sge.java @@ -36,7 +36,7 @@ import static java.util.concurrent.Executors.newScheduledThreadPool; * A Singleton Graphical User Interface for the game. * @author Ari Trachtenberg */ -class sge extends GUI implements ActionListener, ItemListener { +public class sge extends GUI implements ActionListener, ItemListener { // CONSTANTS static public final int defaultWidth = 15, defaultHeight = 10; // sizes in terms of numbers of circles @@ -147,18 +147,26 @@ class sge extends GUI implements ActionListener, ItemListener { */ private sge() { super("Ari's samegame"); - try { - SwingUtilities.invokeAndWait( - this::setupGUI - ); - } catch (Exception e) { - System.out.println("Saw exception " + e); - } + } + + /** + * Fire up the GUI. + */ + private void run() { + try { + SwingUtilities.invokeAndWait( + this::setupGUI + ); + } catch (Exception e) { + System.out.println("Saw exception " + e); + } } public static sge getInstance() { if (sge.Instance == null) { + // no Instance has been set up yet ... make it happen. sge.Instance = new sge(); + sge.Instance.run(); } return Instance; } @@ -318,9 +326,9 @@ class sge extends GUI implements ActionListener, ItemListener { for (int ii = 0; ii < width; ii++) { // establish the button if (jj<=emptyRows) - circles[ii][jj] = new SelfAwareCircle(CircleColor.NONE, ii, jj, this); // the top emptyRows should have a circle with no color + circles[ii][jj] = new SelfAwareCircle(CircleColor.NONE, ii, jj); // the top emptyRows should have a circle with no color else - circles[ii][jj] = new SelfAwareCircle(ii, jj, this); + circles[ii][jj] = new SelfAwareCircle(ii, jj); circlePanel.add(circles[ii][jj]); } // ... set up listeners in the area @@ -614,11 +622,11 @@ class sge extends GUI implements ActionListener, ItemListener { if (source == noPlayerMenuItem) return; // i.e. no players else if (source == simplePlayerMenuItem) - startBrain(new SimpleBrain(this)); + startBrain(new SimpleBrain()); else if (source == lazyPlayerMenuItem) - startBrain(new LazyBrain(this)); + startBrain(new LazyBrain()); else if (source == smarterPlayerMenuItem) - startBrain(new SmarterBrain( this)); + startBrain(new SmarterBrain()); } else { // deselected if (theBrain != null)