Using NetBeans GUI editor to create a bowling program for school. Is it possible for me to add a JPanel to a JList? if so how?
It's not really possible (meaning it won't behave as you'd expect) - what you actually want is a list LayoutManager that will lay out the components in a vertical or horizontal list. So instead of using JList, you'd use a JPanel with a list-like layout manager.
Try these:
BoxLayout will put all the JPanels in a single column/row
GridLayout will put all the JPanels in a single column/row and make them all the same size
Try This. it's works for me.
class PanelRenderer implements ListCellRenderer {
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
JPanel renderer = (JPanel) value;
renderer.setBackground(isSelected ? Color.red : list.getBackground());
return renderer;
}
}
public void ShowItemList(List<JPanel> paneList, JPanel container) {
DefaultListModel model = new DefaultListModel();
for (JPanel pane:paneList) {
model.addElement(pane);
}
final JList list = new JList(model);
list.setFixedCellHeight(40);
list.setSelectedIndex(-1);
list.setCellRenderer(new JPanelToJList.PanelRenderer());
JScrollPane scroll1 = new JScrollPane(list);
final JScrollBar scrollBar = scroll1.getVerticalScrollBar();
scrollBar.addAdjustmentListener(new AdjustmentListener() {
#Override
public void adjustmentValueChanged(AdjustmentEvent e) {
System.out.println("JScrollBar's current value = " + scrollBar.getValue());
}
});
container.add(scroll1);
}
goto this tutorial:
it has the same concept with your problem...
http://docs.oracle.com/javase/tutorial/uiswing/components/combobox.html#renderer
(EDIT: I've used this for a while now and am surprised. Fastest list component I've seen so far. Why haven't I seen this before?)
I just whipped up something that is not a JList, so it lacks a lot of features, but you can rather easily add those.
But what you get is this: A list (All members equal size.) that can easily hold ~2 billion panels with no memory or performance problems - see demo code. Also, the JPanels can contain anything you want, those components will work normally.
In the demo, the JPanel members have no inner JPanels and are completely transparent to mouse events (except for the JButtons, and that's good): A listener added to the overall container receives them, as shown in the demo. If you add more component hierarchy, things might get tricky, IDK.
Anyway, this thing is lightning fast and, most of all, gets the job done: JPanels in a list that you can operate but also select. (No selection code built-in, but like I said: Easy to do. Mouse hover demo code inside.)
Demo class:
final public class FastPanelListDemo {
private static JFrame window = null;
private static FastPanelList panelList = null;
public static void main(final String[] args) {
SwingUtilities.invokeLater(() -> {
setLookAndFeelDefault();
panelList = new FastPanelList(FastPanelList.FPLOrientation.VERTICAL,
FastPanelListDemo::supplyPanel,
0.1,
0.95,
false,
80,
Integer.MAX_VALUE);
final Container contentPane = panelList.container;
contentPane.setPreferredSize(new Dimension(300, 800));
contentPane.setBackground(Color.GRAY);
window = new JFrame("FastPanelList demo");
window.setContentPane(contentPane);
window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
contentPane.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(final MouseEvent e) {
final JPanel itemUnderMouse = panelList.getItemUnderMouse(e);
if (itemUnderMouse != null) {
itemUnderMouse.setBackground(new Color((float) Math.random(),
(float) Math.random(),
(float) Math.random()));
}
}
});
});
}
private static JPanel supplyPanel(final int panelIndex) { // Just supply something that extends JPanel. You can put as much data in as you want. E.g. "boolean isMouseHovering" etc.
final JLabel label = new JLabel("panel " + panelIndex);
label.setHorizontalAlignment(SwingConstants.CENTER);
label.setVerticalAlignment(SwingConstants.CENTER);
final JButton button = new JButton("click me");
button.addActionListener(e -> {
JOptionPane.showMessageDialog(window,
"That was button " + panelIndex + ".",
"* CLICK *",
JOptionPane.INFORMATION_MESSAGE);
});
final JPanel panel = new JPanel(new BorderLayout(0,
0));
panel.setBorder(BorderFactory.createEmptyBorder(10,
10,
10,
10));
panel.setBackground(new Color((float) Math.random(),
(float) Math.random(),
(float) Math.random()));
panel.add(label, BorderLayout.CENTER);
panel.add(button, BorderLayout.EAST);
return panel;
}
private static void setLookAndFeelDefault() {
setLookAndFeel("Windows",
UIManager.getSystemLookAndFeelClassName(),
UIManager.getCrossPlatformLookAndFeelClassName(),
"Windows Classic",
"Nimbus",
"Metal",
"CDE/Motif");
}
/**
* #param intendedLAFIs ANYTHING, but ideally a LookAndFeel name or several. The first value that equalsIgnoreCase
* an installed LookAndFeelInfo.getName() will be used.
*/
private static void setLookAndFeel(final String... intendedLAFIs) {
if (intendedLAFIs != null && intendedLAFIs.length > 0) {
final UIManager.LookAndFeelInfo[] installedLAFIs = UIManager.getInstalledLookAndFeels();
LAFILOOP:
for (String intendedLAFI : intendedLAFIs) {
for (final UIManager.LookAndFeelInfo lafi : UIManager.getInstalledLookAndFeels()) {
if (lafi.getName().equalsIgnoreCase(intendedLAFI)) {
try {
UIManager.setLookAndFeel(lafi.getClassName());
break LAFILOOP;
} catch (Exception e) {
continue LAFILOOP;
}
}
}
}
} else {
throw new IllegalArgumentException("intendedLAFIs is null or empty.");
}
}
}
FastPanelList class:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
/**
* FastPanelList v[2pre1, 2019-05-28 10!00 UTC] by Dreamspace President
*/
final public class FastPanelList<T extends JPanel> {
public enum FPLOrientation {
HORIZONTAL(Adjustable.HORIZONTAL),
VERTICAL(Adjustable.VERTICAL);
final public int orientationAsConstant;
FPLOrientation(final int orientationAsConstant) {
this.orientationAsConstant = orientationAsConstant;
}
}
final public FPLOrientation orientation;
final private Function<Integer, T> panelSupplier;
final private double fractionOfExtentToScrollPerArrowClick;
final private double fractionOfExtentToScrollPerTrackClick;
final private double fractionOfExtentToScrollPerMouseWheelStep;
final private boolean hideScrollbarWhenUnnecessary;
final private JScrollBar scrollBar;
final private int scrollBarWidth; // The default width it normally has in any GUI.
final public JPanel container; // The container of it all.
private int panelSize = 0; // The horizontal or vertical extent of each contained panel.
private int panelCount = 0; // The amount of panels, indeed max Integer.MAX_VALUE.
private long contentSize = 0; // The sum total extent of all "contained panels". (They're not really contained, but nobody will see that.)
private long actualScrollPosition = 0; // The true scroll position, think contentSize.
private Dimension lastKnownContainerSize = new Dimension(0, 0);
private Map<Integer, T> knownPanels = new HashMap<>(); // All panels of which some pixels are currently potentially visible are cached here.
/**
* #param orientation Whether horizontal or the more common vertical arrangement.
* #param panelSupplier Your code that supplies the panels as needed on the fly. The
* argument will NEVER be null - and your return value, too, must never
* be null.
* #param fractionOfExtentToScrollPerArrowClick E.g. 0.1 for 10% of the visible area to become hidden/shown when you
* click a scrollbar arrow.
* #param fractionOfExtentToScrollPerTrackClick E.g. 0.95 for 95% of the visible area to become hidden/shown when
* you click in the scrollbar track.
* #param hideScrollbarWhenUnnecessary Guess.
* #param panelSize Can later also be done via setter. (Not tested.) KEEP IN MIND THAT
* THIS IS NOT YET SCALED, so if you have Desktop scaling 200% and are
* running Java 8, you need to double the value (e.g. use my GUIScaling
* class to automate this).
* #param panelCount dto.
*/
public FastPanelList(final FPLOrientation orientation,
final Function<Integer, T> panelSupplier,
final double fractionOfExtentToScrollPerArrowClick,
final double fractionOfExtentToScrollPerTrackClick,
final double fractionOfExtentToScrollPerMouseWheelStep,
final boolean hideScrollbarWhenUnnecessary,
final int panelSize,
final int panelCount) {
if (orientation == null) {
throw new IllegalArgumentException("orientation is null.");
}
if (panelSupplier == null) {
throw new IllegalArgumentException("panelSupplier is null.");
}
this.orientation = orientation;
this.panelSupplier = panelSupplier;
this.fractionOfExtentToScrollPerArrowClick = Math.max(0, fractionOfExtentToScrollPerArrowClick);
this.fractionOfExtentToScrollPerTrackClick = Math.max(0, fractionOfExtentToScrollPerTrackClick);
this.fractionOfExtentToScrollPerMouseWheelStep = Math.max(0, fractionOfExtentToScrollPerMouseWheelStep);
this.hideScrollbarWhenUnnecessary = hideScrollbarWhenUnnecessary;
setPanelSize(panelSize);
setPanelCount(panelCount);
scrollBarWidth = determineScrollBarDefaultWidth();
scrollBar = new JScrollBar(orientation.orientationAsConstant, 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE);
scrollBar.addAdjustmentListener(e -> update());
container = new JPanel(null); // NULL: We want to layout everything manually.
// container.add(scrollBar);
container.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(final ComponentEvent e) {
update();
}
});
container.addMouseWheelListener(this::mouseWheelEvent);
}
public void mouseWheelEvent(final MouseWheelEvent e) {
final int rotation = e.getWheelRotation();
final int extent = scrollBar.getModel().getExtent();
final int increment = (int) Math.max(1, Math.min(extent,
extent * fractionOfExtentToScrollPerMouseWheelStep));
scrollBar.setValue(scrollBar.getValue() + (rotation * increment));
}
private int determineScrollBarDefaultWidth() { // Called only ONE time.
final JScrollPane dummyForDefaultSize = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
dummyForDefaultSize.setPreferredSize(new Dimension(1000, 1000));
dummyForDefaultSize.setSize(dummyForDefaultSize.getPreferredSize());
dummyForDefaultSize.doLayout();
return dummyForDefaultSize.getVerticalScrollBar().getSize().width;
}
/**
* FastPanelList requires each item to have the exact same size. This is where you define it (if you reconsidered
* after your constructor call).
*
* #param panelSize Will become >=1
*/
public void setPanelSize(final int panelSize) {
this.panelSize = Math.max(1, panelSize);
}
/**
* FastPanelList easily manages Integer.MAX_VALUE (about 2 billion) panels with no memory or performance problems.
* You define the amount here. You don't add/remove panels in this thing: Instead, you will be asked to provide
* panels as required depending on screen layout etc.
*
* #param panelCount Will become >=0
*/
public void setPanelCount(final int panelCount) {
this.panelCount = Math.max(0, panelCount);
}
/**
* Clears the internal JPanel cache. Necessary if you want to repopulate the list. Setting the panel count and
* calling update() is not sufficient. (Call update AFTER this method.)
*/
public void clear() {
knownPanels.clear();
}
public T getItemUnderMouse(final MouseEvent e) {
return getItemUnderMouse(e.getX(), e.getY());
}
public T getItemUnderMouse(final int xInComponent,
final int yInComponent) {
final long realPositionUnderMouse = (actualScrollPosition + (orientation == FPLOrientation.HORIZONTAL ? (long) xInComponent : (long) yInComponent));
final int indexUnderMouse = (int) (realPositionUnderMouse / panelSize);
return knownPanels.get(indexUnderMouse);
}
/**
* This method is very lenient.
*
* #param index Anything.
* #param callSupplierIfNotCached Depends on what you're trying to achieve. E.g. use FALSE if you want to set the
* background color of one of the visible JPanels. The method would return null if
* the panel is not visible, because then it is also no longer cached.
* #return NULL if index is NULL, or is less than 0, or is equal to or greater than panelCount. Else the cached
* JPanel (or whatever it secretly is via "extends"), meaning one of the panels that are currently visible or at the
* edge of visibility. In all other cases, either null will be returned - or your supplier will be called, so you
* get your own JPanel spat right back at you.
*/
public T getItem(final Integer index,
final boolean callSupplierIfNotCached) {
T ret = null;
if (index != null && index >= 0 && index < panelCount) {
ret = knownPanels.get(index);
if (ret == null && callSupplierIfNotCached) {
ret = panelSupplier.apply(index);
if (ret == null) {
throw new IllegalArgumentException("panelSupplier returned null for index " + index);
}
}
}
return ret;
}
/**
* #return a NEW Map containing the Map entries of the internal knownPanels map. These maps contain all panels that
* are currently visible on screen. The index is identical to the number handed to your Supplier.
* <p>
* The purpose of that internal map is to not request EVERY panel anew every time, but only the panels that are
* scrolled in at the edge of the screen. Obviously, this is also very useful to you, because you can call this
* method to get all panels to change their look, data, whatever. And ONLY THOSE panels need to be changed. All
* others ... don't exist. They only exist in the fantasy of the user. Until they scroll there, then some have
* become real while others have fallen out of existence.
*/
public Map<Integer, T> getCachedItems() {
return new HashMap<>(knownPanels);
}
/**
* This layouts the component. This is done automatically when the scrollbar is moved or the container is resized,
* but any other action would require YOU to call this.
*/
public void update() {
container.removeAll();
lastKnownContainerSize = container.getSize();
final int containerSize;
if (orientation == FPLOrientation.HORIZONTAL) {
scrollBar.setLocation(0, lastKnownContainerSize.height - scrollBarWidth);
scrollBar.setSize(lastKnownContainerSize.width, scrollBarWidth);
containerSize = lastKnownContainerSize.width;
} else {
scrollBar.setLocation(lastKnownContainerSize.width - scrollBarWidth, 0);
scrollBar.setSize(scrollBarWidth, lastKnownContainerSize.height);
containerSize = lastKnownContainerSize.height;
}
contentSize = (long) panelCount * (long) panelSize;
final long invisibleStuff = contentSize - containerSize;
actualScrollPosition = Math.max(0, Math.min(invisibleStuff,
(long) (getScrollBarPosRatio() * (invisibleStuff))
));
final int extent;
if (contentSize > 0) {
final double visibleRatio = containerSize / (double) contentSize;
extent = (int) Math.max(0, Math.min(Integer.MAX_VALUE, Integer.MAX_VALUE * visibleRatio));
} else {
extent = Integer.MAX_VALUE;
}
final int unitIncrement = (int) Math.max(1, Math.min(extent,
extent * fractionOfExtentToScrollPerArrowClick));
final int blockIncrement = (int) Math.max(1, Math.min(extent,
extent * fractionOfExtentToScrollPerTrackClick));
scrollBar.getModel().setExtent(extent);
scrollBar.setUnitIncrement(unitIncrement);
scrollBar.setBlockIncrement(blockIncrement);
scrollBar.setVisible(!hideScrollbarWhenUnnecessary || extent < Integer.MAX_VALUE);
final Dimension panelSizes = getPanelSize();
long n = actualScrollPosition;
final long endOfScreen = actualScrollPosition + containerSize + panelSize;
final Map<Integer, T> newKnownPanels = new HashMap<>();
while (n < endOfScreen) { // Loop ongoing = need more panels to fill the view.
// Calc index of current panel.
final long panelIndex = n / panelSize;
if (panelIndex > Integer.MAX_VALUE) {
throw new Error();
} else if (panelIndex >= panelCount) {
break;
}
final int panelIndexInt = (int) panelIndex;
// Obtain current panel - if possible from cache, else from external provider (which might likely create it from scratch).
T panel = knownPanels.get(panelIndexInt);
if (panel == null) {
panel = panelSupplier.apply(panelIndexInt);
if (panel == null) {
throw new IllegalArgumentException("panelSupplier returned null for index " + panelIndex);
}
}
newKnownPanels.put(panelIndexInt, panel);
// Set position and size.
final int panelPos = (int) ((panelIndex * panelSize) - actualScrollPosition);
final Point location;
if (orientation == FPLOrientation.HORIZONTAL) {
location = new Point(panelPos, 0);
} else {
location = new Point(0, panelPos);
}
panel.setLocation(location);
panel.setSize(panelSizes);
n += panelSize;
}
knownPanels = newKnownPanels; // Will now contain all panels needed for display. All panels that were in the map, but are no longer needed, are now gone forever.
// Layout.
container.add(scrollBar);
for (JPanel panel : newKnownPanels.values()) {
container.add(panel);
panel.revalidate();
}
container.repaint(); // required
}
/**
* #return the correct width&height a contained JPanel needs to have. Is applied by update() automatically.
*/
public Dimension getPanelSize() {
if (orientation == FPLOrientation.HORIZONTAL) {
return new Dimension(panelSize,
lastKnownContainerSize.height - (scrollBar.isVisible() ? scrollBarWidth : 0));
} else {
return new Dimension(lastKnownContainerSize.width - (scrollBar.isVisible() ? scrollBarWidth : 0),
panelSize);
}
}
/**
* #return 0 to 1, expressing position of scroll bar handle.
*/
public double getScrollBarPosRatio() {
final int scrollRangeSize = Integer.MAX_VALUE - scrollBar.getVisibleAmount(); // Which should really be named getExtent(). Or rather the other way round.
return scrollBar.getValue() / (double) scrollRangeSize;
}
}
Related
I have been learning about arrays and an interesting question popped up in my head.
I was wondering that with the current Java version, is there a way for me to print a character string n and make it appear for a brief moment at every index of an array consisting of only "", and then towards the end of the array, it can stop when it reaches the end index of the array.
For example if here is the given array and string n = "2" :
[2,"","","",""]
the code will continously update like
["2","","","",""]
["","2","","",""]
["","","2","",""]
["","","","2",""]
["","","","","2"]
and the end result would be
["","","","","2"]
I would like to see the whole movement of "2" being played out without printing any excess arrays ( no more than one array should be in the output).
Is this possible? If yes, can you please suggest what should I look over to learn how to do this?
You can do this with Java but you won't be able to do it reliably within all consoles or terminals. You can however do it reliably if you utilize a GUI mechanism like a JOptionPane or JDialog and display that during console operation, for example:
The above example is a JDialog. Below is the code (read comments within):
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.util.Arrays;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
public class AnimatedMoveArrayElementDemo {
/* Default. The length of the Array to animate.
Can be changed via command-line (/L:n) argument. */
private int lengthOfArray = 8;
/* Default. The Array value to move from beginning to end.
Can be changed via command-line (/N:n) argument. */
private int arrayValueToMove = 2;
/* In Milliseconds (1000 = 1 Second).
Can be changed via command-line (/S:n) argument. */
private int animationSpeed = 1000;
/* Default. The dialog display font size.
Can be changed via command-line (/F:n) argument. */
private int displayFontSize = 24;
private String[] stringArray = {};
int arrayIndex = 0;
Timer animationTimer;
JButton startButton;
JLabel arrayLabel;
public static void main(String[] args) {
// App started this way to avoid the need for statics
new AnimatedMoveArrayElementDemo().startApp(args);
}
private void startApp(String[] args) {
if (args.length > 0) {
readCommandLineArguments(args);
}
fillArray();
createAndShowDialog();
}
private void createAndShowDialog() {
JDialog dialog = new JDialog();
dialog.setTitle("Moving Array Element To The End Position");
dialog.setBackground(Color.white);
dialog.getContentPane().setBackground(Color.white);
dialog.setAlwaysOnTop(true);
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setModal(true);
arrayLabel = new JLabel();
resetDisplayLabel();
arrayLabel.setOpaque(false);
arrayLabel.setHorizontalAlignment(JLabel.CENTER);
arrayLabel.setVerticalAlignment(JLabel.CENTER);
arrayLabel.setFont(new Font(arrayLabel.getFont().getFamily(),
arrayLabel.getFont().getStyle(), displayFontSize));
dialog.add(arrayLabel, BorderLayout.NORTH);
int calculatedWidth = getStringPixelWidth(arrayLabel.getFont(),
arrayLabel.getText().replaceAll("<.+?>", "")) + 50;
int calculatedHeight = getStringPixelHeight(arrayLabel.getFont(),
arrayLabel.getText().replaceAll("<.+?>", "")) + 100;
dialog.setPreferredSize(new Dimension(calculatedWidth, calculatedHeight));
JPanel buttonPanel = new JPanel();
buttonPanel.setOpaque(false);
startButton = new JButton("Start Animation");
startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("Start Animation")) {
if (arrayIndex > stringArray.length - 1) {
resetDisplayLabel();
arrayIndex = 0;
}
startButton.setActionCommand("Stop Animation");
// Using a Swing Timer...for animation
ActionListener performTask = new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
arrayIndex++;
if (arrayIndex > stringArray.length - 1) {
animationTimer.stop();
startButton.setText("Restart Animation");
startButton.setActionCommand("Start Animation");
return;
}
stringArray[arrayIndex - 1] = "\"\"";
stringArray[arrayIndex] = String.valueOf(arrayValueToMove);
String arrayString = "<html>" + Arrays.toString(stringArray) + "</html>";
arrayString = arrayString.replace(String.valueOf(arrayValueToMove),
"\"<font color=red>" + String.valueOf(arrayValueToMove)
+ "</font>\"");
arrayLabel.setText(arrayString);
}
};
animationTimer = new Timer(animationSpeed, performTask);
animationTimer.start();
startButton.setText("Stop Animation");
}
else {
animationTimer.stop();
startButton.setText("Start Animation");
startButton.setActionCommand("Start Animation");
}
}
});
buttonPanel.add(startButton);
dialog.add(buttonPanel, BorderLayout.SOUTH);
dialog.pack();
dialog.setLocationRelativeTo(null);
java.awt.EventQueue.invokeLater(() -> {
dialog.setVisible(true);
});
}
private void fillArray() {
stringArray = new String[lengthOfArray];
for (int i = 0; i < stringArray.length; i++) {
if (i == 0) {
stringArray[i] = "\"" + arrayValueToMove + "\"";
}
else {
stringArray[i] = "\"\"";
}
}
}
private void resetDisplayLabel() {
fillArray();
String arrayString = "<html>" + Arrays.toString(stringArray) + "</html>";
arrayString = arrayString.replace(String.valueOf(arrayValueToMove),
"<font color=red>" + String.valueOf(arrayValueToMove)
+ "</font>");
arrayLabel.setText(arrayString);
}
/**
* This application can currently accept four specific integer command-line
* arguments prefixed with a specific Command related to that argument.
*
* #param args (Command-Line varArgs [optional])<pre>
*
* Length Of Array: The length (# of elements) of the String[] Array
* to animate. The longer the array the smaller the
* Command: /L font size you <u>may</u> want to use so to fit the array
* into the display window. The display window will
* automatically size itself to try and accommodate
* the array length. The default is 8.
*
* Examples of acceptable command-line commands for
* this argument are: /L{value}, /L:{value}, etc.
* Basically, The command can be anything as long as
* it starts with /L (or /l) and contains no spaces
* or digit(s). Digits are reserved for the actual
* argument value passed along with the command, for
* example: /L:8 (/L: 8 is not acceptable) or you
* could use: /Length=8. Anything can be between the
* /L and the integer argument value. Either will tell
* the application the the length of the Array to
* display will contain 8 elements. No whitespaces
* are permitted within a Command-Line Command.
*
* Array Value To Move: This would be the integer value that is placed
* within the first element of the String Array at
* Command: /N index 0. The default value is: <b>2</b> however
* you can change this value to whatever you like.
*
* Examples of acceptable command-line commands for
* this argument are: /N{value}, /N:{value}, etc.
* Basically, The command can be anything as long as
* it starts with /N (or /n) and contains no spaces
* or digit(s). Digits are reserved for the actual
* argument value passed along with the command, for
* example: /N:8 (/N: 8 is not acceptable) or you
* could use: /Number=8. Anything can be between the
* /N and the integer argument value. Either will tell
* the application the the number within the Array to
* display will be the number 8. No whitespaces are
* permitted within a Command-Line Command.
*
* Animation Speed: Default is a value of 1000 milliseconds which is
* basically equivalent to 1 second. You can set the
* Command: /S animation speed to whatever you like but do keep
* in mind that you could set a speed that will be so
* fast that you can't tell there is any animation.
*
* The value passed with this command would be an
* integer value representing Milliseconds.
*
* Examples of acceptable command-line commands for
* this argument are: /S{value}, /S:{value}, etc.
* Basically, The command can be anything as long as
* it starts with /S (or /s) and contains no spaces
* or digit(s). Digits are reserved for the actual
* argument value passed along with the command, for
* example: /S:800 (/S: 800 is not acceptable) or you
* could use: /Speed=800. Anything can be between the
* /S and the integer argument value. Either will tell
* the application that the animation speed for the
* Array display will be 800ms. No whitespaces are
* permitted within a Command-Line Command.
*
* Display Font Size: Default is a font size of 24 but any font size can
* be used to display the Animation and the display
* Command: /F window will automatically size accordingly.
*
* Examples of acceptable command-line commands for
* this argument are: /F{value}, /F:{value}, etc.
* Basically, The command can be anything as long as
* it starts with /F (or /f) and contains no spaces
* or digit(s). Digits are reserved for the actual
* argument value passed along with the command, for
* example: /F:36 (/F: 36 is not acceptable) or you
* could use: /Font=36. Anything can be between the
* /F and the integer argument value. Either will tell
* the application that the animation Font size for the
* Array display will be 36pt. No whitespaces are allowed
* within a Command-Line Command.</pre>
*/
private void readCommandLineArguments(String[] args) {
String command = "";
int value;
for (String arg : args) {
// Split Alpha and Numeric.
String[] argParts = arg.split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)");
command = argParts[0].substring(0, 2);
value = 0;
if (argParts.length == 2) {
value = Integer.parseInt(argParts[1]);
}
switch (command.toUpperCase()) {
case "/L":
this.lengthOfArray = value;
break;
case "/N":
this.arrayValueToMove = value;
break;
case "/S":
this.animationSpeed = value;
break;
case "/F":
this.displayFontSize = value;
break;
default:
System.err.println("Unknown Command-Line Argument!");
}
}
}
/**
* Returns the pixel width of the supplied String.<br>
*
* #param font (Font) The String Font to base calculations from.<br>
*
* #param characterString (String) The string to get the pixel width for.<br>
*
* #return (int) The pixel width of the supplied String.
*/
public int getStringPixelWidth(Font font, String characterString) {
FontMetrics metrics = new FontMetrics(font) {
private static final long serialVersionUID = 1L;
};
Rectangle2D bounds = metrics.getStringBounds(characterString, null);
return (int) bounds.getWidth();
}
/**
* Returns the pixel height of the supplied String.<br>
*
* #param font (Font) The String Font to base calculations from.<br>
*
* #param characterString (String) The string to get the pixel height for.<br>
*
* #return (int) The pixel height of the supplied String.
*/
public int getStringPixelHeight(Font font, String characterString) {
FontMetrics metrics = new FontMetrics(font) {
private static final long serialVersionUID = 1L;
};
Rectangle2D bounds = metrics.getStringBounds(characterString, null);
return (int) bounds.getHeight();
}
}
import java.io.*;
import java.util.Arrays;
public class MyClass {
public static void main(String args[]) {
String n = "2";
String array[] = new String[10];
Arrays.fill(array, "");
array[0] = n;
int i = 0;
System.out.println(Arrays.toString(array));
while(i < array.length-1){
// swap
String temp = array[i+1];
array[i+1] = array[i];
array[i] = temp;
System.out.println(Arrays.toString(array));
i++;
}
}
}
You could try something like the following:
public static void main(String[] args) throws InterruptedException {
annimate("2");
}
private static void annimate(String uniqueElement) throws InterruptedException {
String[] array = new String[]{"2", "", "", "", ""};
int uniqueElemIndex = 0;
while (uniqueElemIndex < array.length) {
System.out.println(Arrays.toString(array));
for (int i = 0; i < array.length; i++) {
if (array[i].equals(uniqueElement)) {
uniqueElemIndex = i;
break;
}
}
if (uniqueElemIndex + 1 < array.length) {
String elem = array[uniqueElemIndex];
array[uniqueElemIndex + 1] = elem;
array[uniqueElemIndex] = "";
}
uniqueElemIndex++;
Thread.sleep(500);
}
}
This outputs the following:
[2, , , , ]
[, 2, , , ]
[, , 2, , ]
[, , , 2, ]
[, , , , 2]
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
So I am making a simple tic tac toe game and ran into a problem at the last minute
I am trying to draw a line at the win location but on the final win location(index), the line gets hidden behind the JButton not entirly sure why it is doing this.
I know alot of people say do not use getGraphics(), and I am wondering if that is the source of my issues they say to override the paintComponent method but that is not working for me either
I have attached a pic of what the result is looking like and code snips of how I am trying to perform these actions
PS I am using a JFrame, if any more code is needed I will be glad to show it
if(win[i] == 264){ // if one of the the combinations equal 'X','X','X' which equals 264, then there is a winner
System.out.println("X is the winner!!!");
System.out.println("Game Over!");
number = i;
draw(); }// call draw method
private void draw(){ // drawing a line at winning location
Graphics2D g1 = (Graphics2D) GUI.getFrame().getGraphics(); // declaring graphics on our Jframe
Stroke stroke3 = new BasicStroke(12f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER); // make our strokes cap off round
if(number == 0){ // statements will determine the win location, so at win0, XXX,
g1.setStroke(stroke3); // we will add stroke to our line
g1.drawLine(0,104,500,104); // draw the line starting at the 0,104 and end it at coordinates 500,104
}
here is a more runnable code, it is alot though
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class tic implements Runnable {
final static int row = 3; // our rows
final static int col = 3; // our col
final static int sizeOfBoard = row * col;
// the size of our board is not going to change so we make it final
static JButton[] clickButton;
char[] templateOfBoard; // our board, TicTacToe field, static
char userTurn; // users turn , only one letter, tracks whether it is a X or O
int count; // keeps track of user moves
static JFrame frame; // our JFrame
int number;
public tic(JFrame frame) {
tic.frame = new JFrame("TicTacToe GAME");
clickButton = new JButton[9];
count = 0; // number of turns starts at 0;
number = 0;
setUserTurn('X'); // first turn will always be X
setTemplateOfBoard(new char[sizeOfBoard]); // size of the board we are going to make it
try{
for(int spaces=0; spaces<sizeOfBoard; spaces++){ // size of Board is in the GUI class
getTemplateOfBoard()[spaces] = ' '; // the board is being created, looping through all rows and col
//every index of the board not has a char value equal to a space
//determine if everything came out correctly
//should equal of a total of 9
// 3x3
}
System.out.println("Board template created"); // means the board now has all spaces
}
catch(Exception e){
System.out.println("Could not initalize the board to empty char");
e.printStackTrace();
}
}
public static void main(String[] args){
try{
SwingUtilities.invokeLater(new tic(frame)); // run
}
catch(Exception e){ // wanted to test to ensure that Runnable could be invoked
System.out.println("Could not excute Runnable application");
e.printStackTrace();
}
}
public void run() {
setup(); // going to run out setup method, what our game is made out of
}
public void setup() {
// setting up the Board
// board is composed of JButton
// and a 3x3 frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // when the user closes the window JFrame will exit
//going to design the board now
//the dimensations of the board = sizeOfBoard
getFrame().setLayout(new GridLayout(row, col)); // this is the outline rows * col
// sizes out row * col based on what we define those numbers as
//i.e 3x3
getFrame().setBounds(0,0,500,500); // location at 0,0, size 500 x 500
Border border = new LineBorder(Color.DARK_GRAY, 2); // color of JButton border
System.out.println("Your board game is being created!");
try{
getFrame().setVisible(true); // shows the board,
// this is going to display everything to the screen
System.out.println("Board is now visable");
}
catch(Exception e){
System.out.println("Board was not displayed");
}
// 9 different buttons, for every index there will be a button
for(int i =0; i<sizeOfBoard;i++){ // going to fill the board with clickableButtons by looping through every index and placing a button there
final int move = i;
clickButton[i] = new JButton(); // at a certain index there is a new button
clickButton[i].setSize(250,250); // size of each button
clickButton[i].setBackground(Color.WHITE); // color of the JButton
getFrame().add(clickButton[i]); // we are going to add the actual the button at that index on the frame
clickButton[i].setFont(new Font("Arial", Font.BOLD, 70)); // size of the text
clickButton[i].setBorder(border); // adding border
clickButton[i].getModel().addChangeListener(new ChangeListener() { //going to overRide what happens when we rollover and press a Butotn
public void stateChanged(ChangeEvent e) {
ButtonModel button = (ButtonModel) e.getSource(); // manages the state of the button, i.e lets me control what happens to the button
if(clickButton[move] != null){ // if we do not include this argument
// the buttons are not made yet on the new game, meaning clickButton[i] = null
//so boolean(!button.isRollover()) will return true, since on the new game you can not have your mouse hovered over
// but when it returns true, it will return a null value, giving a null pointer exception
// so best thing to do, is to only run these cases below when the buttons are not null
if (button.isRollover()) { // when the mouse hovers over the index
clickButton[move].setBackground(Color.BLACK); // color will equal black
}
else if(!button.isRollover()){ // when the button is not hovered over
clickButton[move].setBackground(Color.WHITE); // color will be whte, just like our background
}
}
}
});
clickButton[i].addActionListener(new ActionListener() {
//our click events, going to override to let it know what we want to happen
//once we click on the button
public void actionPerformed(ActionEvent e) {
clickButton[move].setEnabled(false); //going to disable the button after it is clicked
//ORDER: button gets clicked first, then the test is added
mouseListener(e, move); // our mouseListenerEvent in game class
//
}
});
}
}
public static void playAgain() {
try{
System.out.println("NEW GAME");
SwingUtilities.invokeLater(new tic(frame)); // run the run(class) again
}
catch(Exception e){ // wanted to test to ensure that Runnable could be invoked
System.out.println("Could not excute Runnable application");
e.printStackTrace();
}
}
public static JFrame getFrame() {
return frame;
}
public tic userMove(int moveMade){
getTemplateOfBoard()[moveMade] = getUserTurn();
// index of the board, or in simpler terms, where the user
// inserts there turn i.e X or O, 0-8
//System.out.println(userMove);
//boolean statement to determine the turns
// So user X starts first
//if the turn is X, the nextTurn is now O,
if(getUserTurn() == 'X'){
setUserTurn('O');
}
else {
setUserTurn('X');
}
count++;
return this; // going to return the userTurn
// issue actually entering the userTurn is not giving right value, but using 'this' does
}
// for some odd reason the toString is causing some issues, keep getting #hash code
//saw online to override it like this
// will make the board out of emepty strings
// going to return a string representation of an object
public String toString(){
return new String(getTemplateOfBoard());
}
public void mouseListener(ActionEvent e, int moveMade){
// mouse click events
// what happens after a button is clicked
if(getTemplateOfBoard()[moveMade] == ' '){ // the user can only space a click, so an letter on the field if it is empty
((JButton)e.getSource()).setText(Character.toString(getUserTurn())); // when the button is clicked, we want an X placed there
if (getUserTurn() == 'X'){
UIManager.getDefaults().put("Button.disabledText",Color.RED); // when the but gets disabled the test will turn red
}
else{
UIManager.getDefaults().put("Button.disabledText",Color.BLUE);
}
//calling the method userTurn to determine who goes next
//problem is that is expects a String
//going to override the toString method
userMove(moveMade); // calling userMove in moveMade, moveMade is the index at which the user put either an X or a O
winner(); // we want to check each time to ensure there was/was not a winner
}
}
public tic winner() { // determines who is the winner
//list below defines all the possible win combinations
// the index of where a X or O can be place
// placed the locations to a int value
int win1 = templateOfBoard[0] + templateOfBoard[1] + templateOfBoard[2];
int win2 = templateOfBoard[3] + templateOfBoard[4] + templateOfBoard[5];
int win3 = templateOfBoard[6] + templateOfBoard[7] + templateOfBoard[8];
int win4 = templateOfBoard[0] + templateOfBoard[3] + templateOfBoard[6];
int win5 = templateOfBoard[1] + templateOfBoard[4] + templateOfBoard[7];
int win6 = templateOfBoard[2] + templateOfBoard[5] + templateOfBoard[8];
int win7 = templateOfBoard[0] + templateOfBoard[4] + templateOfBoard[8];
int win8 = templateOfBoard[2] + templateOfBoard[4] + templateOfBoard[6];
int[] win = new int[]{win1,win2,win3,win4,win5,win6,win7,win8};
// making a array to go through all the possibile wins
//possible total of wins is 8
for(int i = 0;i<win.length;i++){
// looping through the win possibilities
if(win[i] == 264){ // if one of the the combinations equal 'X','X','X' which equals 264, then there is a winner
System.out.println("X is the winner!!!");
System.out.println("Game Over!");
number = i;
draw(); // call draw method
return this; // if statement is true, it will return this(gameOver)
}
else if(win[i] == 237 ){ // if one of the the combinations equal 'O','O','O' which equals 234, then there is a winner
System.out.println("O is the winner!!!");
System.out.println("Game Over!");
number = i;
//draw(); // call draw method
return this;
}
if (count == 9) {
// if none of the statements above are true, it automatically comes done to here
//so if there is nine moves and no win, it is a draw
}
}
return this;
// going to return this method ;
}
private void draw(){ // drawing a line at winning location
Graphics2D g1 = (Graphics2D) getFrame().getGraphics(); // declaring graphics on our Jframe
Stroke stroke3 = new BasicStroke(12f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER); // make our strokes cap off round
if(number == 0){ // statements will determine the win location, so at win0, XXX,
g1.setStroke(stroke3); // we will add stroke to our line
g1.drawLine(0,104,500,104); // draw the line starting at the 0,104 and end it at coordinates 500,104
}
else if(number == 1){
g1.setStroke(stroke3);
g1.drawLine(0,257,500,257);
}
else if(number == 2){
g1.setStroke(stroke3);
g1.drawLine(0,411,500,411);
}
else if(number == 3){
g1.setStroke(stroke3);
g1.drawLine(88,0,88,500);
}
else if(number == 4){
g1.setStroke(stroke3);
g1.drawLine(250,0,250,500);
}
else if(number == 5){
g1.setStroke(stroke3);
g1.drawLine(411,0,411,500);
}
else if(number == 6){
g1.setStroke(stroke3);
g1.drawLine(-22,0,500,500);
}
else if(number == 7){
g1.setStroke(stroke3);
g1.drawLine(520,0,0,500);
}
}
// want to be able to access the private variables
//so we will make getter and setter methods for the ones that we need
public char getUserTurn() { // getter method for userTurn
return userTurn;
}
public void setUserTurn(char userTurn) { // setter method
this.userTurn = userTurn;
}
public char[] getTemplateOfBoard() { //getter method
return templateOfBoard;
}
public void setTemplateOfBoard(char[] templateOfBoard) { // setter method
this.templateOfBoard = templateOfBoard;
}
}
Painting over the top of components can be troublesome, you can't override the paintComponent method of the container which contains the components, because this paints in the background, you can't override the paint method of the container, as child components can be painted without the parent container been notified...
You could add a transparent component over the whole lot, but this just introduces more complexity, especially when a component already already exists ...
public class ConnectTheDots {
public static void main(String[] args) {
new ConnectTheDots();
}
public ConnectTheDots() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
PaintPane pp = new PaintPane();
JFrame frame = new JFrame("Test");
frame.setGlassPane(pp);
pp.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DotsPane(pp));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class PaintPane extends JPanel {
private List<JButton[]> connections;
private JButton lastSelected;
public PaintPane() {
setOpaque(false);
connections = new ArrayList<>(25);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (lastSelected != null) {
g2d.setColor(Color.RED);
int x = lastSelected.getX() + ((lastSelected.getWidth() - 8) / 2);
int y = lastSelected.getY() + ((lastSelected.getHeight() - 8) / 2);
g2d.fillOval(x, y, 8, 8);
}
for (JButton[] group : connections) {
g2d.setColor(Color.BLUE);
Point startPoint = group[0].getLocation();
Point endPoint = group[1].getLocation();
startPoint.x += (group[0].getWidth() / 2);
startPoint.y += (group[1].getHeight()/ 2);
endPoint.x += (group[0].getWidth() / 2);
endPoint.y += (group[1].getHeight()/ 2);
g2d.draw(new Line2D.Float(startPoint, endPoint));
}
g2d.dispose();
}
protected void buttonClicked(JButton btn) {
if (lastSelected == null) {
lastSelected = btn;
} else {
connections.add(new JButton[]{lastSelected, btn});
lastSelected = null;
}
revalidate();
repaint();
}
}
public class DotsPane extends JPanel {
private PaintPane paintPane;
public DotsPane(final PaintPane pp) {
paintPane = pp;
ActionListener al = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JButton btn = (JButton) e.getSource();
paintPane.buttonClicked(btn);
}
};
setLayout(new GridLayout(6, 6));
for (int index = 0; index < 6 * 6; index++) {
JButton btn = new JButton(".");
add(btn);
btn.addActionListener(al);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
Take a look at How to Use Root Panes for more details
I am creating the Sudoku game and I am trying to provide options to save, save as, and open games. I am using JFileChooser to do this. I am able to save (or "save as") but when I try to open a saved file, I get an error. I am new to programming and I'm hoping someone could spot the issue and educate me on how to read in the contents of the Sudoku board when I am saving (as well as how to deal with re-creating the Sudoku board when I open the file). I hear there is an easier way to deal with this using InputStream/OutputStream instead of Reader/Writer...
Here is my code for the inner class that implements this (I don't know if there's a way to post my entire class without exceeding the character limit for this text box.):
EDIT:
// this inner class provides a JMenuBar object at the top of
// the board
class MenuAtTop extends JMenuBar implements ActionListener{
// SudokuMain object we are dealing with
private SudokuMain main;
// the "File" menu
private JMenu fileMenu;
// the "New Game" option
private JMenuItem newGame;
// the "Open" option
private JMenuItem open;
// the "Save" option
private JMenuItem save;
// the "Save As" option
private JMenuItem saveAs;
// the "Reset" option
private JMenuItem reset;
// the "Quit" option
private JMenuItem quit;
// the ability to choose files
private JFileChooser choose;
// the saved file
// // compiler would not allow "static" keyword
private File fileSaved = null;
private Object opener;
// JDialog object to create a dialog box to prompt
// user for new game information
private JDialog createNewWin;
/**
* Constructs MenuAtTop object.
*
* #param m The SudokuMain object to be referred to.
*/
public MenuAtTop(final SudokuMain m) {
main = m;
opener = null;
choose = new JFileChooser();
// instantiate and bind to reference
fileMenu = new JMenu("File");
add(fileMenu);
// instantiate and bind to reference
newGame = new JMenuItem("New Game");
newGame.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
ActionEvent.CTRL_MASK));
fileMenu.add(newGame);
newGame.addActionListener(this);
open = new JMenuItem("Open");
open.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,
ActionEvent.CTRL_MASK));
fileMenu.add(open);
// add action listener to "Open" option
open.addActionListener(this);
save = new JMenuItem("Save");
save.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
ActionEvent.CTRL_MASK));
fileMenu.add(save);
// add action listener to "Save" option
save.addActionListener(this);
saveAs = new JMenuItem("Save As");
saveAs.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,
ActionEvent.CTRL_MASK));
fileMenu.add(saveAs);
// add action listener to "Save As" option
saveAs.addActionListener(this);
reset = new JMenuItem("Reset");
reset.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R,
ActionEvent.CTRL_MASK));
fileMenu.add(reset);
// add action listener to "Reset" option
reset.addActionListener(this);
quit = new JMenuItem("Quit");
quit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
ActionEvent.CTRL_MASK));
fileMenu.add(quit);
// add action listener to "Quit" option
quit.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(newGame)) {
setEnabled(false);
// create dialog box prompting for the new board information
createNewWin = new Dialog1(main, "Create New Board", true);
// make it visible
createNewWin.setVisible(true);
fileSaved = null;
} else if(e.getSource().equals(open)) {
int returnVal = choose.showOpenDialog(main.win);
if(returnVal == JFileChooser.APPROVE_OPTION) {
boolean error = false;
File openFile = choose.getSelectedFile();
try {
FileInputStream fis = new FileInputStream(openFile);
ObjectInputStream ois = new ObjectInputStream(fis);
opener = ois.readObject();
SudokuBase sudoku = (SudokuBoard) opener;
ois.close();
} catch (Exception exc) {
exc.printStackTrace();
JOptionPane.showMessageDialog(main.win, "Error opening file.");
error = true;
}
// "opener" reads something and it is of type SudokuBase
if(opener != null && opener instanceof SudokuBase){
main.north.remove(main.rowColRegStates);
main.west.remove(main.symbols);
main.east.remove(main.view);
main.view = new SudokuView((SudokuBase) opener);
main.rowColRegStates = new ShowStates(main.view);
main.symbols = new SetSymbols(main.view);
main.north.add(main.rowColRegStates);
main.west.add(main.symbols);
main.east.add(main.view);
main.win.requestFocus();
fileSaved = openFile;
} else {
if(error) {
JOptionPane.showMessageDialog(main.win, "Incorrect file type.");
}
}
}
} else if(e.getSource().equals(save)) {
if(fileSaved == null) {
saveAsPrompt();
} else {
try {
FileOutputStream fos = new FileOutputStream(fileSaved);
ObjectOutputStream oos = new ObjectOutputStream(fos);
board.writeToStream(fos);
oos.writeObject(board);
oos.close();
} catch (Exception exc) {
JOptionPane.showMessageDialog(main.win, "Error saving file.");
}
}
} else if(e.getSource().equals(saveAs)) {
saveAsPrompt();
} else if(e.getSource().equals(reset)) {
int n = JOptionPane.showConfirmDialog(main.win,
"Any player values will" +
" be lost. Proceed?",
"Warning!", 2);
if(n == JOptionPane.OK_OPTION) {
main.board.reset();
main.view.repaint();
}
} else if(e.getSource().equals(quit)) {
closePrompt();
}
}
// This method prompts the user to choose a file to save to,
// and then saves the file.
private int saveAsPrompt() {
boolean saveError;
int rtn = choose.showSaveDialog(main.win);
if(rtn == JFileChooser.APPROVE_OPTION) {
saveError = false;
File fileSaveAs = choose.getSelectedFile();
try {
board.writeToStream(new FileOutputStream(fileSaveAs));
} catch (Exception e) {
JOptionPane.showMessageDialog(main.win, "Error saving file.");
saveError = true;
}
if(!saveError) {
fileSaved = fileSaveAs;
}
}
return rtn;
}
// This method prompts the user whether they want to save before
// closing, only if changes occurred.
private void closePrompt() {
if(true) {
int n = JOptionPane.showConfirmDialog(main.win, "Save game?");
if(n == JOptionPane.YES_OPTION) {
int saved = saveAsPrompt();
if(saved != JFileChooser.CANCEL_OPTION){
main.win.dispose();
}
} else if(n == JOptionPane.NO_OPTION) {
main.win.dispose();
}
}
else { // no changes were made
main.win.dispose();
}
}
}
Here's the class that holds the data (it is extended by SudokuBoard):
// Allow short name access to following classes
import java.util.Observable;
import java.io.InputStream;
import java.io.OutputStream;
public abstract class SudokuBase extends Observable {
// rows per region
private int rows;
// columns per region
private int columns;
// size of a region (rows * columns)
private int size;
// array of each element of entire sudoku board
private int[] grid;
// the masked 8-bit "given" value constant
private static final int GIVEN_MASK = 0x00000100;
// the bitwise complement of the masked "given" constant,
// which produces an unmasked constant
private static final int GIVEN_UNMASK = ~ GIVEN_MASK;
/**
* Enumerated type to store constants that indicate the "State" of
* a specified row, column, or region.
*/
public enum State {COMPLETE, INCOMPLETE, ERROR};
/**
* Constructs SudokuBase object.
*
* #param layoutRows The number of rows per region.
* #param layoutColumns The number of columns per region.
*/
public SudokuBase(int layoutRows, int layoutColumns) {
rows = layoutRows;
columns = layoutColumns;
size = columns * rows;
grid = new int[size*size];
}
/**
* Gets the number of rows per region.
*
* #return The rows per region.
*/
public int getRowsPerRegion() {
return rows;
}
/**
* Gets the number of columns per region.
*
* #return The columns per region.
*/
public int getColumnsPerRegion() {
return columns;
}
/**
* Gets the size of the region (rows * columns).
*
* #return The size of the region.
*/
public int getBoardSize() {
return size;
}
// gets the index of the specified row and column for the grid
private int getIndex(int row, int col) {
// handle invalid arguments
if(row < 0 || row >= size || col < 0 || col >= size) {
String msg = "Error in location";
throw new IllegalArgumentException(msg);
}
return row * size + col;
}
/**
* Gets the value of the element at the specified row
* and column on the grid.
*
* #param row The specified row.
* #param col The specified column.
* #return The value of the element at the specified row and column.
*/
public int getValue(int row, int col) {
return grid[getIndex(row, col)] & GIVEN_UNMASK;
}
/**
* Sets the desired value at the specified row and column.
*
* #param row The specified row.
* #param col The specified column.
* #param value The specified value to be set.
*/
public void setValue(int row, int col, int value) {
// handle invalid argument
if(value < 0 || value > size) {
String msg = "Value out of range: " + value;
throw new IllegalArgumentException(msg);
}
// handle attempt to set a value for a "given" location
if(isGiven(row, col)) {
String msg = "Cannot set given location: " + row + ", " + col;
throw new IllegalStateException(msg);
}
grid[getIndex(row, col)] = value;
setChanged();
notifyObservers();
}
/**
* This method checks the status of the "givens" bit.
*
* #param row The specified row.
* #param col The specified column.
* #return Whether or not the specified location is a "given" value.
*/
public boolean isGiven(int row, int col) {
return (grid[getIndex(row, col)] & GIVEN_MASK) == GIVEN_MASK;
}
/**
* This method sets non-zero values on the Sudoku board with the
* "givens" bit.
*/
public void fixGivens() {
for(int i = 0; i < grid.length; i++)
if(grid[i] != 0)
grid[i] |= GIVEN_MASK;
setChanged();
notifyObservers();
}
/**
* This abstract method gets the "State" (COMPLETE, INCOMPLETE,
* or ERROR) of the specified row.
*
* #param n The specified row.
* #return The "State" of the row.
*/
public abstract State getRowState(int n);
/**
* This abstract method gets the "State" (COMPLETE, INCOMPLETE,
* or ERROR) of the specified column.
*
* #param n The specified column.
* #return The "State" of the column.
*/
public abstract State getColumnState(int n);
/**
* This abstract method gets the "State" (COMPLETE, INCOMPLETE,
* or ERROR) of the specified region.
*
* #param n The specified region.
* #return The "State" of the region.
*/
public abstract State getRegionState(int n);
/**
* Represents the Sudoku board as a grid of appropriate characters.
*
* #return The string representation of the Sudoku board.
*/
public String toString() {
String board = "";
for(int i = 0; i < size; i ++) {
for(int j = 0; j < size; j ++)
board += charFor(i, j) + " ";
board += "\n";
}
return board;
}
// this method provides a character for all possible values encountered on the
// Sudoku board, to be utilized in "toString()"
private String charFor(int i, int j) {
int v = getValue(i, j);
// negative value (invalid)
if(v < 0) {
return "?";
} else if(v == 0) { // blank or zero value
return ".";
} else if(v < 36) { // value from 1 to (size * size)
return Character.toString(Character.forDigit(v, 36)).toUpperCase();
} else { // non-numeric input or v >= size * size (both invalid)
return "?";
}
}
/**
* This method reads from an input stream.
*
* #param is The input stream to read from.
*/
protected void readFromStream(InputStream is) {
}
/**
* This method writes to an output stream.
*
* #param os The output stream to write to.
*/
protected void writeToStream(OutputStream os) {
try {
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.close();
} catch(IOException e) {
}
}
/**
* Gets the "raw" value directly, not having checked whether there is an
* unfixed error message.
*
* #param row The row where the raw value is located.
* #param col The column where the raw value is located.
* #return The raw value.
*/
protected int getRawValue(int row, int col) {
return grid[getIndex(row, col)];
}
/**
* Sets the raw value directly, not having checked whether there is an
* unfixed error message.
*
* #param row The row where the raw value is to be located.
* #param col The column where the raw value is to be located.
*/
protected void setRawValue(int row, int col, int value) {
grid[getIndex(row, col)] = value;
}
protected void reset() {
for(int row = 0; row < rows; row++) {
for(int col = 0; col < columns; col++) {
if(!isGiven(row, col)) {
grid[getIndex(row, col)] = 0;
}
}
}
}
}
Well I cannot give a full answer and I do not want to browse the full source code. But a few pointers for you to find some solution:
Never catch Exceptions like that while developing an application:
} catch (Exception e) {
JOptionPane.showMessageDialog(main.win, "Error saving file.");
saveError = true;
}
With this, you completely loose the chance to detect errors. At least add the following line to your exception handling:
e.printStackTrace();
Normally you would log the exception and so on, but with that you see the source of your error at the console. Better than nothing.
To your more specific problem:
You seem to write an Object to a file holding all the configuration. In your read method something goes wrong. Probably you do not read the same object as you write or something like that. Hard to say without any code. Try to get the exception stack trace and figure out what the problem is. If you cannot figure it out, edit your question with more specific information and I will try to give better directions.
EDIT:
Here is a small example showing serialization of objects for a Sudoku like game:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class SerializationExample {
public static void main(String args[]) throws IOException, ClassNotFoundException {
final File target = new File(System.getProperty("java.io.tmp"), "mySerializedObject.txt");
Map<Integer, Integer> initialState = new HashMap<Integer, Integer>();
initialState.put(1, 1);
initialState.put(21, 3);
// ...
GameState state = new GameState(10, initialState);
state.setField(2, 2);
state.setField(3, 8);
System.out.println("Game state before writing to file: " + state);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(target));
out.writeObject(state);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream(target));
Object gameStateReadFromFile = in.readObject();
GameState readGameState = (GameState)gameStateReadFromFile;
System.out.println("Read from file: " + readGameState);
}
private static class GameState implements Serializable {
private int[] fields;
private int boardSize;
private int[] fixedFields;
public GameState(int boardSize, Map<Integer, Integer> initialState) {
this.boardSize = boardSize;
this.fields = new int[boardSize * boardSize];
this.fixedFields = new int[this.fields.length];
for (Entry<Integer, Integer> entry : initialState.entrySet()) {
this.fixedFields[entry.getKey()] = entry.getValue();
}
}
public void setField(int index, int value) {
this.fields[index] = value;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("\nFixed fields: ");
appendArray(builder, this.fixedFields);
builder.append("\nSet fields: ");
appendArray(builder, this.fields);
return builder.toString();
}
private void appendArray(StringBuilder builder, int[] fieldArray) {
for (int i = 0; i < fieldArray.length; ++i) {
if (fieldArray[i] != 0) {
builder.append("row ").append(i / this.boardSize).append(" column ").append(i % this.boardSize)
.append(" has value ")
.append(fieldArray[i]).append(",");
}
}
}
}
}
I am trying to accomplish the above functionality, and am having little success.
I am using GridLayout with 2 columns and 2 rows to show the user a puzzle-like game, where there are 4 (200x200 pixel) JPanels (3 colored, 1 default bgColor) which fill the whole contentPane. Clicking on a colored panel resolves in an assessment if the panel is next to the gray panel. If so, they should swap. I have accomplished every step to the last one, where they swap. Here's the code:
public class MainClass extends JFrame {
//Generated
private static final long serialVersionUID = 4710628273679698058L;
private SpecialPanel redPanel;
private SpecialPanel greenPanel;
private SpecialPanel yellowPanel;
private SpecialPanel grayPanel;
public MainClass() {
super("Puzzle");
initPanels();
setSize(410, 410);
setLayout(new GridLayout(2, 2));
this.add(redPanel);
this.add(greenPanel);
this.add(grayPanel);
this.add(yellowPanel);
}
private void initPanels() {
redPanel = new SpecialPanel();
greenPanel = new SpecialPanel();
yellowPanel = new SpecialPanel();
grayPanel = new SpecialPanel();
grayPanel.setGreyPanel(true);
redPanel.setBackground(Color.RED);
greenPanel.setBackground(Color.GREEN);
yellowPanel.setBackground(Color.YELLOW);
grayPanel.setBackground(this.getBackground());
redPanel.setSize(200, 200);
greenPanel.setSize(200, 200);
yellowPanel.setSize(200, 200);
grayPanel.setSize(200, 200);
MouseAdapter ma = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent arg0) {
super.mouseClicked(arg0);
SpecialPanel sp = (SpecialPanel) arg0.getComponent();
if (sp.getIsGray()) {
//Do nothing
} else if (checkIfNeighbourToGray(sp, grayPanel)) {
//Swap them
System.out.println("Swap notification");
swap(sp, grayPanel);
//Update UI
} else {
//Again, do nothing for the clicked panel is diagonal to the gray panel
}
}
private boolean checkIfNeighbourToGray(SpecialPanel sp, SpecialPanel grayPanel) {
Point startPointSp = sp.getLocation();
double x = startPointSp.getX();
double y = startPointSp.getY();
double width = sp.getWidth();
double height = sp.getHeight();
Point grayPoint = grayPanel.getLocation();
double xG = grayPanel.getX();
double yG = grayPanel.getY();
double widthG = grayPanel.getWidth();
double heightG = grayPanel.getHeight();
//Gray panel is RIGHT of clicked one
if (x + width == xG && y + height == yG + heightG) {
return true;
}
//Gray panel is LEFT of clicked one
else if (x - width == xG && y + height == yG + heightG) {
return true;
}
//Gray panel is ABOVE of clicked one
else if (x == xG && x + width == xG + widthG) {
return true;
}
//Gray panel is UNDER of clicked one
else if (xG == x && yG + widthG == x + width) {
return true;
}
return false;
}
private void swap(SpecialPanel sp, SpecialPanel grayPanel) {
//Swap logic
}
};
redPanel.addMouseListener(ma);
greenPanel.addMouseListener(ma);
yellowPanel.addMouseListener(ma);
grayPanel.addMouseListener(ma);
}
public static void main(String[] args) {
MainClass mc = new MainClass();
mc.setVisible(true);
}
}
The class SpecialPanel is extending JPanel with just a new Boolean property IsGrayPanel initially set to false + getter and setter.
NOTE: This is being done in a "I have basic Swing skills" manner, although I have learned more in the meantime about java swing, I was just limited back then with basic swing functionality, so I should keep it that way.
Therefore my question is how to properly swap the two panels which are next to each other, including UI update?
there wouldn't need to move with JPanels into container, otherwise you woud need to use bunch of quite useless code to remove two JPanels from from contianer with two indexes then to layout back to container with swaping indexes,
(if is about Color only) only to swap setBackground(Color.Xxx) betweens two JPanels
puzzle-like game is about Images, puzzle or minesweaper is about, (not clear from your ...)
put Images as Icon/ImageIcons to JLabel instead of JPanel and on Mouse Events to switch (setIcon()) with Icons betweens JLabels, load Icons to local variables
(easiest of ways) JToggleButton and Icon, there is logic similair as for JLabel, but about showing Icon on setPressedIcon()
I'm not sure if this works. It looks too easy.
JPanel tempPanel = sp;
sp = grayPanel;
grayPanel = tempPanel;
this.setVisible(false);
this.setVisible(true);
this.validate(); //try this first
this.repaint(); // if it doesnt work, add this function.
I started refactoring this program I was working on and hit a major road block... I have one class that acts as a nucleus, with about 6 other smaller (but still important) classes working together to run the program... I took one method [called 'populate()'] out the nucleus class and made an entirely new class with it [called 'PopulationGenerator'], but when I try to create an object of the newly created class anywhere in the nucleus class I get stuck in a never ending loop of that new class
I've never had this issue when trying to create objects before... Here's the nucleus class before refactoring:
public class Simulator
{
// Constants representing configuration information for the simulation.
// The default width for the grid.
private static final int DEFAULT_WIDTH = 120;
// The default depth of the grid.
private static final int DEFAULT_DEPTH = 80;
// The probability that a fox will be created in any given grid position.
private static final double FOX_CREATION_PROBABILITY = 0.02;
// The probability that a rabbit will be created in any given grid position.
private static final double RABBIT_CREATION_PROBABILITY = 0.08;
// List of animals in the field.
private List<Animal> animals;
// The current state of the field.
private Field field;
// The current step of the simulation.
private int step;
// A graphical view of the simulation.
private SimulatorView view;
/**
* Construct a simulation field with default size.
*/
public Simulator()
{
this(DEFAULT_DEPTH, DEFAULT_WIDTH);
}
/**
* Create a simulation field with the given size.
* #param depth Depth of the field. Must be greater than zero.
* #param width Width of the field. Must be greater than zero.
*/
public Simulator(int depth, int width)
{
if(width <= 0 || depth <= 0) {
System.out.println("The dimensions must be greater than zero.");
System.out.println("Using default values.");
depth = DEFAULT_DEPTH;
width = DEFAULT_WIDTH;
}
animals = new ArrayList<Animal>();
field = new Field(depth, width);
// Create a view of the state of each location in the field.
view = new SimulatorView(depth, width);
view.setColor(Rabbit.class, Color.orange);
view.setColor(Fox.class, Color.blue);
// Setup a valid starting point.
reset();
}
/**
* Run the simulation from its current state for a reasonably long period,
* (4000 steps).
*/
public void runLongSimulation()
{
simulate(4000);
}
/**
* Run the simulation from its current state for the given number of steps.
* Stop before the given number of steps if it ceases to be viable.
* #param numSteps The number of steps to run for.
*/
public void simulate(int numSteps)
{
for(int step = 1; step <= numSteps && view.isViable(field); step++) {
simulateOneStep();
}
}
/**
* Run the simulation from its current state for a single step.
* Iterate over the whole field updating the state of each
* fox and rabbit.
*/
public void simulateOneStep()
{
step++;
// Provide space for newborn animals.
List<Animal> newAnimals = new ArrayList<Animal>();
// Let all rabbits act.
for(Iterator<Animal> it = animals.iterator(); it.hasNext(); ) {
Animal animal = it.next();
animal.act(newAnimals);
if(! animal.isAlive()) {
it.remove();
}
}
// Add the newly born foxes and rabbits to the main lists.
animals.addAll(newAnimals);
view.showStatus(step, field);
}
/**
* Reset the simulation to a starting position.
*/
public void reset()
{
step = 0;
animals.clear();
populate();
// Show the starting state in the view.
view.showStatus(step, field);
}
/**
* Randomly populate the field with foxes and rabbits.
*/
private void populate()
{
Random rand = Randomizer.getRandom();
field.clear();
for(int row = 0; row < field.getDepth(); row++) {
for(int col = 0; col < field.getWidth(); col++) {
if(rand.nextDouble() <= FOX_CREATION_PROBABILITY) {
Location location = new Location(row, col);
Fox fox = new Fox(true, field, location);
animals.add(fox);
}
else if(rand.nextDouble() <= RABBIT_CREATION_PROBABILITY) {
Location location = new Location(row, col);
Rabbit rabbit = new Rabbit(true, field, location);
animals.add(rabbit);
}
// else leave the location empty.
}
}
}
}
EDIT:
Here's this same class AFTER refactoring ...
import java.util.Random;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.awt.Color;
/**
* A simple predator-prey simulator, based on a rectangular field
* containing rabbits and foxes.
*
* Update 10.40:
* Now *almost* decoupled from the concrete animal classes.
*
* #TWiSTED_CRYSTALS
*/
public class Simulator
{
// Constants representing configuration information for the simulation.
// The default width for the grid.
private static final int DEFAULT_WIDTH = 120;
// The default depth of the grid.
private static final int DEFAULT_DEPTH = 80;
// The current state of the field.
private Field field;
// The current step of the simulation.
private int step;
// A graphical view of the simulation.
private SimulatorView view;
//Population Generator class... coupled to fox and rabbit classes
private PopulationGenerator popGenerator;
// Lists of animals in the field. Separate lists are kept for ease of iteration.
private List<Animal> animals;
/**
* Construct a simulation field with default size.
*/
public Simulator()
{
this(DEFAULT_DEPTH, DEFAULT_WIDTH);
}
/**
* Create a simulation field with the given size.
* #param depth Depth of the field. Must be greater than zero.
* #param width Width of the field. Must be greater than zero.
*/
public Simulator(int depth, int width)
{
if(width <= 0 || depth <= 0) {
System.out.println("The dimensions must be greater than zero.");
System.out.println("Using default values.");
depth = DEFAULT_DEPTH;
width = DEFAULT_WIDTH;
}
animals = new ArrayList<Animal>();
field = new Field(depth, width);
// Create a view of the state of each location in the field.
//
// view.setColor(Rabbit.class, Color.orange); // PG
// view.setColor(Fox.class, Color.blue); // PG
// Setup a valid starting point.
reset();
}
/**
* Run the simulation from its current state for a reasonably long period,
* (4000 steps).
*/
public void runLongSimulation()
{
simulate(4000);
}
/**
* Run the simulation from its current state for the given number of steps.
* Stop before the given number of steps if it ceases to be viable.
* #param numSteps The number of steps to run for.
*/
public void simulate(int numSteps)
{
for(int step = 1; step <= numSteps && view.isViable(field); step++) {
simulateOneStep();
}
}
/**
* Run the simulation from its current state for a single step.
* Iterate over the whole field updating the state of each
* fox and rabbit.
*/
public void simulateOneStep()
{
step++;
// Provide space for animals.
List<Animal> newAnimals = new ArrayList<Animal>();
// Let all animals act.
for(Iterator<Animal> it = animals.iterator(); it.hasNext(); ) {
Animal animal = it.next();
animal.act(newAnimals);
if(! animal.isAlive()) {
it.remove();
}
}
animals.addAll(newAnimals);
}
/**
* Reset the simulation to a starting position.
*/
public void reset()
{
PopulationGenerator popGenerator = new PopulationGenerator();
step = 0;
animals.clear();
popGenerator.populate();
// Show the starting state in the view.
view.showStatus(step, field);
}
public int getStep()
{
return step;
}
}
... and the new class
import java.util.ArrayList;
import java.util.Random;
import java.util.List;
import java.awt.Color;
public class PopulationGenerator
{
// The default width for the grid.
private static final int DEFAULT_WIDTH = 120;
// The default depth of the grid.
private static final int DEFAULT_DEPTH = 80;
// The probability that a fox will be created in any given grid position.
private static final double FOX_CREATION_PROBABILITY = 0.02;
// The probability that a rabbit will be created in any given grid position.
private static final double RABBIT_CREATION_PROBABILITY = 0.08;
// Lists of animals in the field. Separate lists are kept for ease of iteration.
private List<Animal> animals;
// The current state of the field.
private Field field;
// A graphical view of the simulation.
private SimulatorView view;
/**
* Constructor
*/
public PopulationGenerator()
{
animals = new ArrayList<Animal>();
field = new Field(DEFAULT_DEPTH, DEFAULT_WIDTH);
}
/**
* Randomly populate the field with foxes and rabbits.
*/
public void populate()
{
// Create a view of the state of each location in the field.
view = new SimulatorView(DEFAULT_DEPTH, DEFAULT_WIDTH);
view.setColor(Rabbit.class, Color.orange); // PG
view.setColor(Fox.class, Color.blue); // PG
Simulator simulator = new Simulator();
Random rand = Randomizer.getRandom();
field.clear();
for(int row = 0; row < field.getDepth(); row++) {
for(int col = 0; col < field.getWidth(); col++) {
if(rand.nextDouble() <= FOX_CREATION_PROBABILITY) {
Location location = new Location(row, col);
Fox fox = new Fox(true, field, location);
animals.add(fox);
}
else if(rand.nextDouble() <= RABBIT_CREATION_PROBABILITY) {
Location location = new Location(row, col);
Rabbit rabbit = new Rabbit(true, field, location);
animals.add(rabbit);
}
// else leave the location empty.
}
}
view.showStatus(simulator.getStep(), field);
}
}
here's the Field class that the PopulationGenerator calls... I havent changed this class in any way
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
/**
* Represent a rectangular grid of field positions.
* Each position is able to store a single animal.
*
* #TWiSTED_CRYSTALS
*/
public class Field
{
// A random number generator for providing random locations.
private static final Random rand = Randomizer.getRandom();
// The depth and width of the field.
private int depth, width;
// Storage for the animals.
private Object[][] field;
/**
* Represent a field of the given dimensions.
* #param depth The depth of the field.
* #param width The width of the field.
*/
public Field(int depth, int width)
{
this.depth = depth;
this.width = width;
field = new Object[depth][width];
}
/**
* Empty the field.
*/
public void clear()
{
for(int row = 0; row < depth; row++) {
for(int col = 0; col < width; col++) {
field[row][col] = null;
}
}
}
/**
* Clear the given location.
* #param location The location to clear.
*/
public void clear(Location location)
{
field[location.getRow()][location.getCol()] = null;
}
/**
* Place an animal at the given location.
* If there is already an animal at the location it will
* be lost.
* #param animal The animal to be placed.
* #param row Row coordinate of the location.
* #param col Column coordinate of the location.
*/
public void place(Object animal, int row, int col)
{
place(animal, new Location(row, col));
}
/**
* Place an animal at the given location.
* If there is already an animal at the location it will
* be lost.
* #param animal The animal to be placed.
* #param location Where to place the animal.
*/
public void place(Object animal, Location location)
{
field[location.getRow()][location.getCol()] = animal;
}
/**
* Return the animal at the given location, if any.
* #param location Where in the field.
* #return The animal at the given location, or null if there is none.
*/
public Object getObjectAt(Location location)
{
return getObjectAt(location.getRow(), location.getCol());
}
/**
* Return the animal at the given location, if any.
* #param row The desired row.
* #param col The desired column.
* #return The animal at the given location, or null if there is none.
*/
public Object getObjectAt(int row, int col)
{
return field[row][col];
}
/**
* Generate a random location that is adjacent to the
* given location, or is the same location.
* The returned location will be within the valid bounds
* of the field.
* #param location The location from which to generate an adjacency.
* #return A valid location within the grid area.
*/
public Location randomAdjacentLocation(Location location)
{
List<Location> adjacent = adjacentLocations(location);
return adjacent.get(0);
}
/**
* Get a shuffled list of the free adjacent locations.
* #param location Get locations adjacent to this.
* #return A list of free adjacent locations.
*/
public List<Location> getFreeAdjacentLocations(Location location)
{
List<Location> free = new LinkedList<Location>();
List<Location> adjacent = adjacentLocations(location);
for(Location next : adjacent) {
if(getObjectAt(next) == null) {
free.add(next);
}
}
return free;
}
/**
* Try to find a free location that is adjacent to the
* given location. If there is none, return null.
* The returned location will be within the valid bounds
* of the field.
* #param location The location from which to generate an adjacency.
* #return A valid location within the grid area.
*/
public Location freeAdjacentLocation(Location location)
{
// The available free ones.
List<Location> free = getFreeAdjacentLocations(location);
if(free.size() > 0) {
return free.get(0);
}
else {
return null;
}
}
/**
* Return a shuffled list of locations adjacent to the given one.
* The list will not include the location itself.
* All locations will lie within the grid.
* #param location The location from which to generate adjacencies.
* #return A list of locations adjacent to that given.
*/
public List<Location> adjacentLocations(Location location)
{
assert location != null : "Null location passed to adjacentLocations";
// The list of locations to be returned.
List<Location> locations = new LinkedList<Location>();
if(location != null) {
int row = location.getRow();
int col = location.getCol();
for(int roffset = -1; roffset <= 1; roffset++) {
int nextRow = row + roffset;
if(nextRow >= 0 && nextRow < depth) {
for(int coffset = -1; coffset <= 1; coffset++) {
int nextCol = col + coffset;
// Exclude invalid locations and the original location.
if(nextCol >= 0 && nextCol < width && (roffset != 0 || coffset != 0)) {
locations.add(new Location(nextRow, nextCol));
}
}
}
}
// Shuffle the list. Several other methods rely on the list
// being in a random order.
Collections.shuffle(locations, rand);
}
return locations;
}
/**
* Return the depth of the field.
* #return The depth of the field.
*/
public int getDepth()
{
return depth;
}
/**
* Return the width of the field.
* #return The width of the field.
*/
public int getWidth()
{
return width;
}
}
your problem is not in the Field class but below it. The Simulator constructor calls reset() which creates a new PopulationGenerator object, then calls populate() on that object. The populate() method calls Simulator simulator = new Simulator(); which creates a new Simulator object which continues the cycle. Solution: don't create a new Simulator object in PopulationGenerator, but instead pass the existing simulator to PopulationGenerator through its constructor or through a setSimulator(...) method.
e.g.,
class PopulationGenerator {
// ... etc...
private Simulator simulator; // null to begin with
// pass Simulator instance into constructor.
// Probably will need to do the same with SimulatorView
public PopulationGenerator(Simulator simulator, int depth, int width) {
this.simulator = simulator; // set the instance
// ... more code etc...
}
public void populate() {
// don't re-create Simulator here but rather use the instance passed in
}
}