How to create a simple user interface to declare variables? - java

I am working on my first java project, one that simulates the behaviour of a neutrophil catching a bacterium (So random/semirandom particle behaviour). At the beginning of this program I have several variables (such as the radii of the organisms, etc) and right now they are fixed to the value I hardcoded in there. I want to create a user interface so that before the program starts, a screen pops up in which you can type in the values you want to use, and it uses those to run to program. Now I have used a swing script to create such a window and that looks a bit like this:
Now I'm wondering how I could implement it such that I can take the values used in those text boxes and assign them to my variables in my program.
This is the program I am referring to:
package nanocourse;
import java.awt.Color;
import nano.*;
import java.util.Random;
import prescreen.PreScreen;
public class Exercise3_final {
public Exercise3_final() {
int xSize = 1000;
int ySize = 800;
Canvas myScreen = new Canvas(xSize, ySize);
Pen myPen = new Pen(myScreen);
Random random = new Random();
int frame=0; //how many frames have passed since start program
//properties bacterium
int xPosBacterium=random.nextInt(xSize); //random starting position of bacterium
int yPosBacterium=random.nextInt(ySize);
int K=1000; //how many points used to draw bacterium
double [] xValueBacterium = new double[K]; //
double [] yValueBacterium = new double[K];
double bacteriumRadiusInput=20;
double bacteriumRadius=bacteriumRadiusInput; //radius of bacterium
boolean bacteriumAlive=true;
//properties biomolecules
int amountBio=30000;
boolean [] bioExist = new boolean[amountBio];
int [] xPosBio = new int [amountBio];
int [] yPosBio = new int [amountBio];
int [] dXBio = new int [amountBio];
int [] dYBio = new int [amountBio];
int [] lifetimeBio = new int [amountBio];
double chanceDegrade=0.1; //chance that a biomolecule gets degraded per frame
double chanceSynthesize=100; //chance that a biomolecule gets synthesized per frame
for(int i=0;i<amountBio;i++)
{
bioExist[i]=false; //setting existing state to false
}
//properties Neutrophil
int xPosNeutrophil=random.nextInt(xSize);
int yPosNeutrophil=random.nextInt(ySize);
int L=1000;
double [] xValueNeutrophil= new double[L];
double [] yValueNeutrophil= new double[L];
double neutrophilRadius=40;
double xVector, yVector, xNormVector,yNormVector,magnitude,xSumVector,ySumVector;
double aggressiveness=1;
while(bacteriumAlive==true) //while program is running
{
frame++;
//1. Simulating a moving Bacterium
int dXBacterium=random.nextInt(11)-5; //random motion
int dYBacterium=random.nextInt(11)-5;
xPosBacterium=xPosBacterium+dXBacterium;
yPosBacterium=yPosBacterium+dYBacterium;
if(xPosBacterium<(bacteriumRadius/2+2*myPen.getSize())) //boundaries bacterium,accounting for size bacterium
{
xPosBacterium=(int)bacteriumRadius/2+2*myPen.getSize();
}
else if(xPosBacterium>xSize - (bacteriumRadius/2+2*myPen.getSize()))
{
xPosBacterium=xSize - ((int)bacteriumRadius/2+2*myPen.getSize());
}
else if(yPosBacterium<(bacteriumRadius/2+2*myPen.getSize()))
{
yPosBacterium=((int)bacteriumRadius/2+2*myPen.getSize());
}
else if(yPosBacterium>ySize - (bacteriumRadius/2+2*myPen.getSize()))
{
yPosBacterium=ySize - ((int)bacteriumRadius/2+2*myPen.getSize());
}
//2. Simulating synthesis and secretion of biomolecules by the bacterium.
for(int i=0;i<amountBio;i++)
{
double synthesizeNumber=Math.random()*100;
if(synthesizeNumber<chanceSynthesize && i==frame)
{
bioExist[frame]=true; //make the biomolecules exist
}
if(bioExist[i]==true && frame!=1) //if biomolecule exist apply motion
{
dXBio[i]=random.nextInt(41)-20;
dYBio[i]=random.nextInt(41)-20;
xPosBio[i]=xPosBio[i]+dXBio[i];
yPosBio[i]=yPosBio[i]+dYBio[i];
}
else //if biomolecule doesn't exist, make position equal bacterium position
{
xPosBio[i]=xPosBacterium;
yPosBio[i]=yPosBacterium;
}
if(xPosBio[i]>xSize) //boundaries biomolecules
{
xPosBio[i]=xSize;
}
if(xPosBio[i]<0)
{
xPosBio[i]=0;
}
if(yPosBio[i]>ySize)
{
yPosBio[i]=ySize;
}
if(yPosBio[i]<0)
{
yPosBio[i]=0;
}
if(bioExist[i]==true)
{
lifetimeBio[i]++;
double degradationNumber=Math.random()*100;
if(degradationNumber<chanceDegrade)
{
bioExist[i]=false;
}
}
if(bioExist[i]==true && lifetimeBio[i]<=100) //if biomolecule lives shorter than 100 frames==>green
{
myPen.setColor(Color.GREEN); //drawing biomolecules
myPen.setShape(Shape.CIRCLE);
myPen.setSize(5);
}
if(bioExist[i]==true && (lifetimeBio[i]>100 && lifetimeBio[i]<=500)) //if biomolecule lives 101-500 frames==>green
{
myPen.setColor(Color.yellow); //drawing biomolecules
myPen.setShape(Shape.CIRCLE);
myPen.setSize(5);
}
if(bioExist[i]==true && (lifetimeBio[i]>500 && lifetimeBio[i]<=1000)) //if biomolecule lives 501-1000 frames==>orange
{
myPen.setColor(Color.ORANGE); //drawing biomolecules
myPen.setShape(Shape.CIRCLE);
myPen.setSize(5);
}
if(bioExist[i]==true && (lifetimeBio[i]>1000 && lifetimeBio[i]<=1500)) //if biomolecule lives 1001-1500 frames==>red
{
myPen.setColor(Color.RED); //drawing biomolecules
myPen.setShape(Shape.CIRCLE);
myPen.setSize(5);
}
if(bioExist[i]==true && lifetimeBio[i]>1500) //if biomolecule lives 2001+ frames==>magenta
{
myPen.setColor(Color.magenta); //drawing biomolecules
myPen.setShape(Shape.CIRCLE);
myPen.setSize(5);
}
if(bioExist[i]==true)
{
myPen.draw(xPosBio[i],yPosBio[i]);
}
if(Math.sqrt(Math.pow(Math.abs(xPosBio[i]-xPosNeutrophil),2)+Math.pow(Math.abs(yPosBio[i]-yPosNeutrophil), 2))<neutrophilRadius)
{
bioExist[i]=false; //degrade if inside neutrophil
}
}
if(bacteriumAlive==true)
{
for(int i = 0; i <K ; i++) //defining circle, drawing points, placed here because it needs to be on top
{
xValueBacterium[i] = bacteriumRadius*Math.cos(2*Math.PI*i/K);
yValueBacterium[i] = bacteriumRadius*Math.sin(2*Math.PI*i/K);
myPen.setColor(Color.red);
myPen.setShape(Shape.CIRCLE);
myPen.setSize(5);
myPen.draw((int)xValueBacterium[i]+xPosBacterium,(int)yValueBacterium[i]+yPosBacterium);
}
}
//5. Simulating the neutrophil eating the bacteriun
xSumVector=0;
ySumVector=0;
for(int i=0;i<amountBio;i++)
{
if(Math.abs(xPosBio[i]-xPosNeutrophil)<(30+neutrophilRadius) && Math.abs(yPosBio[i]-yPosNeutrophil)<(30+neutrophilRadius) && bioExist[i]==true)
{
xVector=xPosBio[i]-xPosNeutrophil;
yVector=yPosBio[i]-yPosNeutrophil;
magnitude=Math.sqrt(Math.pow(xVector, 2)+Math.pow(yVector, 2));
xNormVector=xVector/magnitude;
yNormVector=yVector/magnitude;
xSumVector=xSumVector+xNormVector;
ySumVector=ySumVector+yNormVector;
}
}
//3. Simulating a moving neutrophil
int dXNeutrophil=random.nextInt(11)-5+(int)aggressiveness*(int)xSumVector; //random motion
int dYNeutrophil=random.nextInt(11)-5+(int)aggressiveness*(int)ySumVector;
xPosNeutrophil=xPosNeutrophil+dXNeutrophil;
yPosNeutrophil=yPosNeutrophil+dYNeutrophil;
myPen.setSize(8);
if(xPosNeutrophil<(neutrophilRadius/2+2*myPen.getSize())) //boundaries neutrophil
{
xPosNeutrophil=(int)neutrophilRadius/2+2*myPen.getSize();
}
else if(xPosNeutrophil>xSize - (neutrophilRadius/2+2*myPen.getSize()))
{
xPosNeutrophil=xSize - ((int)neutrophilRadius/2+2*myPen.getSize());
}
else if(yPosNeutrophil<(neutrophilRadius/2+2*myPen.getSize()))
{
yPosNeutrophil=((int)neutrophilRadius/2+2*myPen.getSize());
}
else if(yPosNeutrophil>ySize - (neutrophilRadius/2+2*myPen.getSize()))
{
yPosNeutrophil=ySize - ((int)neutrophilRadius/2+2*myPen.getSize());
}
for(int i = 0; i <L ; i++) //defining circle, drawing points, placed here because it needs to be on top
{
xValueNeutrophil[i] = neutrophilRadius*Math.cos(2*Math.PI*i/L);
yValueNeutrophil[i] = neutrophilRadius*Math.sin(2*Math.PI*i/L);
myPen.setColor(Color.blue);
myPen.setShape(Shape.CIRCLE);
myPen.draw((int)xValueNeutrophil[i]+xPosNeutrophil,(int)yValueNeutrophil[i]+yPosNeutrophil);
}
if(Math.abs(xPosNeutrophil-xPosBacterium)<2*bacteriumRadiusInput && Math.abs(yPosNeutrophil-yPosBacterium)<2*bacteriumRadiusInput && bacteriumRadius >=0)
{
bacteriumRadius=bacteriumRadius-1;
if(bacteriumRadius==0)
{
bacteriumAlive=false;
}
}
if(bacteriumAlive==false)
{
bacteriumAlive=true;
xPosBacterium=random.nextInt(xSize); //random starting position of bacterium
yPosBacterium=random.nextInt(ySize);
bacteriumRadius=bacteriumRadiusInput;
}
myScreen.update(); //updating/refreshing screen
myScreen.pause(10);
myScreen.clear();
}
}
public static void main(String[] args) {
Exercise3_final e = new Exercise3_final();
}
}
Any help would be appreciated!

Sounds like you need an action listener on the "Run!" button from your dialog:
_run.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// set the variables here by getting the text from the inputs
field1Var = Integer.parseInt(field1Input.getText());
field2Var = Integer.parseInt(field2Input.getText());
...
}
});

I would suggest creating a singleton class to save all the values that are captured from the first screen (options menu screen).
You can get the instance of this class anywhere in the application later on and use it.
Advantages would be:
- You will not have to carry forward the values everywhere in the application.
- The values captured will the persisted till the application is shut down.
Note: Make sure to add validations while fetching values from the options menu so that incorrect values are not set.

Related

JPanel doesn't get new values (anymore)

So, I'm trying to program a Game of Life simulation (Conway), and I want to show it in a JFrame.
For this purpose, I've created a JPanel, and it works perfectly, until I try to actually show a new generation. With prints, I've figured out, that the list is actually correct inside the newGeneration() method, but when paint(Graphics g) gets called (aka, when I try to repaint the JFrame), the list isn't updating.
I'm sure I've missed something obvious, and I'm not well versed in Java, but it's just getting so annoying. I'd really appreciate your help.
Here's my code;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Main {
public static void main(String[] args) {
new GameOfLife();
}
}
class GameOfLife {
// Initialising all class wide variables; sorted by type
JFrame frame = new JFrame("Game of Life");
JPanel panel;
Scanner gameSize = new Scanner(System.in);
String dimensions;
String splitHorizontal;
String splitVertical;
String confirmation;
Boolean accepted = false;
Integer split;
Integer horizontal;
Integer vertical;
Integer livingNeighbours;
int[][] cells;
int[][] newCells;
public GameOfLife() {
// Prompt for game Size
System.out.println("Please enter your game size in the following format; 'Horizontal,Vertical'");
// Run until viable game Size has been chosen
while (!accepted) {
dimensions = gameSize.nextLine();
// Check for correct format
if (dimensions.contains(",")) {
split = dimensions.indexOf(",");
splitHorizontal = dimensions.substring(0, split);
splitVertical = dimensions.substring(split + 1);
// Check for validity of inputs
if (splitHorizontal.matches("[0-9]+") && splitVertical.matches("[0-9]+")) {
horizontal = Integer.parseInt(dimensions.substring(0, split));
vertical = Integer.parseInt(dimensions.substring(split + 1));
// Check for game Size
if (horizontal > 1000 || vertical > 1000) {
System.out.println("A game of this Size may take too long to load.");
} else {
// Confirmation Prompt
System.out.println("Your game will contain " + horizontal + " columns, and " + vertical + " rows, please confirm (Y/N)");
confirmation = gameSize.nextLine();
// Check for confirmation, anything invalid is ignored
if (confirmation.matches("Y")) {
accepted = true;
System.out.println("Thank you for your confirmation. Please select live cells. Once your happy with your game, press Spacebar to start the Simulation.");
// Setting parameters depending on Size
frame.setSize(horizontal * 25 + 17, vertical * 25 + 40);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
}
}
// Prompt asking for new dimensions in case of invalid dimensions or non confirmation
if (!accepted) {
System.out.println("Please enter different dimensions.");
}
}
// Creating list of cells
cells = new int[horizontal][vertical];
// Showing the empty panel for selection of live cells
panel = new PaintCells(horizontal, vertical, cells);
frame.add(panel);
// Select live cells
panel.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
if (cells[(int) Math.ceil(e.getX() / 25)][(int) Math.ceil(e.getY() / 25)] == 1) {
cells[(int) Math.ceil(e.getX() / 25)][(int) Math.ceil(e.getY() / 25)] = 0;
} else {
cells[(int) Math.ceil(e.getX() / 25)][(int) Math.ceil(e.getY() / 25)] = 1;
}
frame.repaint();
}
});
// Simulation start
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
if (e.getKeyChar() == ' ') {
newGeneration();
}
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
});
}
// Generating new generations
void newGeneration() {
newCells = new int[horizontal][vertical];
// Pause inbetween generations
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*
* Way of Life Rules:
* Living cells with 2 or 3 living neighbours live on to the next generation.
* Dead cells with exactly 3 living neighbours become living cells in the next generation.
* Every other living cell dies.
*/
// iterate through every cell
for (int l = 0; l < vertical; l++) {
for (int k = 0; k < horizontal; k++) {
livingNeighbours = 0;
// check amount of neighbours
if (k - 1 > -1) {
if (l - 1 > -1) {
if (cells[k - 1][l - 1] == 1) {
livingNeighbours++;
}
}
if (l + 1 < vertical) {
if (cells[k - 1][l + 1] == 1) {
livingNeighbours++;
}
}
if (cells[k - 1][l] == 1) {
livingNeighbours++;
}
}
if (k + 1 < horizontal) {
if (l - 1 >= 0) {
if (cells[k + 1][l - 1] == 1) {
livingNeighbours++;
}
}
if (l + 1 < vertical) {
if (cells[k + 1][l + 1] == 1) {
livingNeighbours++;
}
}
if (cells[k + 1][l] == 1) {
livingNeighbours++;
}
}
if (l - 1 >= 0) {
if (cells[k][l - 1] == 1) {
livingNeighbours++;
}
}
if (l + 1 < vertical) {
if (cells[k][l + 1] == 1) {
livingNeighbours++;
}
}
// change cell value depending on amount of neighbours
if (cells[k][l] == 1) {
if (livingNeighbours < 2 || livingNeighbours > 3) {
newCells[k][l] = 0;
} else {
newCells[k][l] = 1;
}
} else {
if (livingNeighbours == 3) {
newCells[k][l] = 1;
}
}
}
}
cells = newCells;
frame.validate();
frame.paint(frame.getGraphics());
newGeneration();
}
}
// Our canvas
class PaintCells extends JPanel {
private Integer horizontal;
private Integer vertical;
private int[][] newOriginalCells;
// Get our X and Y from the original prompts
public PaintCells(Integer originalHorizontal, Integer originalVertical, int[][] originalCells) {
this.horizontal = originalHorizontal;
this.vertical = originalVertical;
this.newOriginalCells = originalCells;
}
#Override
public void paint(Graphics g) {
for (int i = 0; i < vertical; i++) {
for (int j = 0; j < horizontal; j++) {
// Check cell value
if (newOriginalCells[j][i] == 1) {
g.setColor(Color.black);
} else {
g.setColor(Color.white);
}
// paint according to value
g.fillRect(j * 25, i * 25, 25, 25);
if (newOriginalCells[j][i] == 1) {
g.setColor(Color.white);
} else {
g.setColor(Color.black);
} // maybe change style?
g.drawRect(j * 25, i * 25, 25, 25);
}
}
}
}
I'm guessing, the problem is somewhere in newGeneration(), but other than that, I really have no idea anymore.
You have a common problem which I had myself a few months ago.
Java Swing GUI system works in thread called Event Dispatch Thread (EDT). This thread handle events like mouse clicks, typing etc. and paint the components to the screen. You should use this thread not as your main thread, but as sub-thread which working only once a certain time/when event happens, and not let him run continuously. In your code, since the user choose the cell to live, this thread run non-stop (because you started the program inside a listener, which is part of the EDT), and your GUI stuck, because it's updating only at the end of the thread.
You can solve this by using javax.swing.Timer. Timer is an object that allows you do tasks once a while, and it is perfect to this problem.
Use code like this:
ActionListener actionListaner = new ActionListener(){
public void actionPerformed(ActionEvent e){
//Put here you ne genration repeating code
}
};
int delay = 1000;//You delay between generations in millis
Timer timer = new timer(delay, actionListener);
The code in the actionPerformed method will repeat every second (or any other time you want it to repeat), and every operation of the timer will recall EDT instead of let it run non-stop.

Designing a multi-thread matrix in Java

I have a matrix that implements John Conway's life simulator in which every cell represents either life or lack of it.
Every life cycle follows these rules:
Any live cell with fewer than two live neighbors dies, as if caused by under-population.
Any live cell with two or three live neighbors lives on to the next generation.
Any live cell with more than three live neighbors dies, as if by overcrowding.
Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.
Every cell will have a thread that will perform the changes by the rules listed above.
I have implemented these classes:
import java.util.Random;
public class LifeMatrix {
Cell[][] mat;
public Action currentAction = Action.WAIT_FOR_COMMAND;
public Action changeAction;
public enum Action {
CHECK_NEIGHBORS_STATE,
CHANGE_LIFE_STATE,
WAIT_FOR_COMMAND
}
// creates a life matrix with all cells alive or dead or random between dead or alive
public LifeMatrix(int length, int width) {
mat = new Cell[length][width];
for (int i = 0; i < length; i++) { // populate the matrix with cells randomly alive or dead
for (int j = 0; j < width; j++) {
mat[i][j] = new Cell(this, i, j, (new Random()).nextBoolean());
mat[i][j].start();
}
}
}
public boolean isValidMatrixAddress(int x, int y) {
return x >= 0 && x < mat.length && y >= 0 && y < mat[x].length;
}
public int getAliveNeighborsOf(int x, int y) {
return mat[x][y].getAliveNeighbors();
}
public String toString() {
String res = "";
for (int i = 0; i < mat.length; i++) { // populate the matrix with cells randomly alive or
// dead
for (int j = 0; j < mat[i].length; j++) {
res += (mat[i][j].getAlive() ? "+" : "-") + " ";
}
res += "\n";
}
return res;
}
public void changeAction(Action a) {
// TODO Auto-generated method stub
currentAction=a;
notifyAll(); //NOTIFY WHO??
}
}
/**
* Class Cell represents one cell in a life matrix
*/
public class Cell extends Thread {
private LifeMatrix ownerLifeMat; // the matrix owner of the cell
private boolean alive;
private int xCoordinate, yCoordinate;
public void run() {
boolean newAlive;
while (true) {
while (! (ownerLifeMat.currentAction==Action.CHECK_NEIGHBORS_STATE)){
synchronized (this) {//TODO to check if correct
try {
wait();
} catch (InterruptedException e) {
System.out.println("Interrupted while waiting to check neighbors");
}}
}
// now check neighbors
newAlive = decideNewLifeState();
// wait for all threads to finish checking their neighbors
while (! (ownerLifeMat.currentAction == Action.CHANGE_LIFE_STATE)) {
try {
wait();
} catch (InterruptedException e) {
System.out.println("Interrupted while waiting to change life state");
};
}
// all threads finished checking neighbors now change life state
alive = newAlive;
}
}
// checking the state of neighbors and
// returns true if next life state will be alive
// returns false if next life state will be dead
private boolean decideNewLifeState() {
if (alive == false && getAliveNeighbors() == 3)
return true; // birth
else if (alive
&& (getAliveNeighbors() == 0 || getAliveNeighbors() == 1)
|| getAliveNeighbors() >= 4)
return false; // death
else
return alive; // same state remains
}
public Cell(LifeMatrix matLifeOwner, int xCoordinate, int yCoordinate, boolean alive) {
this.ownerLifeMat = matLifeOwner;
this.xCoordinate = xCoordinate;
this.yCoordinate = yCoordinate;
this.alive = alive;
}
// copy constructor
public Cell(Cell c, LifeMatrix matOwner) {
this.ownerLifeMat = matOwner;
this.xCoordinate = c.xCoordinate;
this.yCoordinate = c.yCoordinate;
this.alive = c.alive;
}
public boolean getAlive() {
return alive;
}
public void setAlive(boolean alive) {
this.alive = alive;
}
public int getAliveNeighbors() { // returns number of alive neighbors the cell has
int res = 0;
if (ownerLifeMat.isValidMatrixAddress(xCoordinate - 1, yCoordinate - 1) && ownerLifeMat.mat[xCoordinate - 1][yCoordinate - 1].alive)
res++;
if (ownerLifeMat.isValidMatrixAddress(xCoordinate - 1, yCoordinate) && ownerLifeMat.mat[xCoordinate - 1][yCoordinate].alive)
res++;
if (ownerLifeMat.isValidMatrixAddress(xCoordinate - 1, yCoordinate + 1) && ownerLifeMat.mat[xCoordinate - 1][yCoordinate + 1].alive)
res++;
if (ownerLifeMat.isValidMatrixAddress(xCoordinate, yCoordinate - 1) && ownerLifeMat.mat[xCoordinate][yCoordinate - 1].alive)
res++;
if (ownerLifeMat.isValidMatrixAddress(xCoordinate, yCoordinate + 1) && ownerLifeMat.mat[xCoordinate][yCoordinate + 1].alive)
res++;
if (ownerLifeMat.isValidMatrixAddress(xCoordinate + 1, yCoordinate - 1) && ownerLifeMat.mat[xCoordinate + 1][yCoordinate - 1].alive)
res++;
if (ownerLifeMat.isValidMatrixAddress(xCoordinate + 1, yCoordinate) && ownerLifeMat.mat[xCoordinate + 1][yCoordinate].alive)
res++;
if (ownerLifeMat.isValidMatrixAddress(xCoordinate + 1, yCoordinate + 1) && ownerLifeMat.mat[xCoordinate + 1][yCoordinate + 1].alive)
res++;
return res;
}
}
public class LifeGameLaunch {
public static void main(String[] args) {
LifeMatrix lifeMat;
int width, length, populate, usersResponse;
boolean userWantsNewGame = true;
while (userWantsNewGame) {
userWantsNewGame = false; // in order to finish the program if user presses
// "No" and not "Cancel"
width = Integer.parseInt(JOptionPane.showInputDialog(
"Welcome to John Conway's life simulator! \n"
+ "Please enter WIDTH of the matrix:"));
length = Integer.parseInt(JOptionPane.showInputDialog(
"Welcome to John Conway's life simulator! \n"
+ "Please enter LENGTH of the matrix:"));
lifeMat = new LifeMatrix(length, width);
usersResponse = JOptionPane.showConfirmDialog(null, lifeMat + "\nNext cycle?");
while (usersResponse == JOptionPane.YES_OPTION) {
if (usersResponse == JOptionPane.YES_OPTION) {
lifeMat.changeAction(Action.CHECK_NEIGHBORS_STATE);
}
else if (usersResponse == JOptionPane.NO_OPTION) {
return;
}
// TODO leave only yes and cancel options
usersResponse = JOptionPane.showConfirmDialog(null, lifeMat + "\nNext cycle?");
}
if (usersResponse == JOptionPane.CANCEL_OPTION) {
userWantsNewGame = true;
}
}
}
}
My trouble is to synchronize the threads:
Every cell(a thread) must change its life/death state only after all threads have checked their neighbors. The user will invoke every next life cycle by clicking a button.
My logic, as can be understood from the run() method is to let every cell(thread) run and wait for the right action state that is represented by the variable currentAction in LifeMatrix class and go ahead and execute the needed action.
What I struggle with is how do I pass these messages to the threads to know when to wait and when execute next action?
Any suggestions to change the design of the program are very welcome as long as every cell will be implemented with a separate thread!
Using a CyclicBarrier should be easy to understand:
(updated to use 2 Barriers, and make use of inner class to make cell looks shorter and cleaner)
psuedo code:
public class LifeMatrix {
private CyclicBarrier cycleBarrier;
private CyclicBarrier cellUpdateBarrier;
//.....
public LifeMatrix(int length, int width) {
cycleBarrier = new CyclicBarrier(length * width + 1);
cellUpdateBarrier = new CyclicBarrier(length * width);
// follow logic of old constructor
}
public void changeAction(Action a) {
//....
cycleBarrier.await()
}
// inner class for cell
public class Cell implements Runnable {
// ....
#Override
public void run() {
while (...) {
cycleBarrier.await(); // wait until start of cycle
boolean isAlive = decideNewLifeState();
cellUpdateBarrier.await(); // wait until everyone completed
this.alive = isAlive;
}
}
}
}
I would solve this using two Phasers.
You use one Phaser to control cycles and one one to synchronize the cells when they determine if they are alive or not.
public class Cell extends Thread {
private LifeMatrix ownerLifeMat; // the matrix owner of the cell
private boolean alive;
private int xCoordinate, yCoordinate;
// Phaser that controls the cycles
private Phaser cyclePhaser;
// Phaser for cell synchronisation
private Phaser cellPhaser;
public Cell(LifeMatrix matLifeOwner, Phaser cyclePhaser, Phaser cellPhaser,
int xCoordinate, int yCoordinate, boolean alive) {
this.ownerLifeMat = matLifeOwner;
this.cyclePhaser = cyclePhaser;
this.cellPhaser = cellPhaser;
this.xCoordinate = xCoordinate;
this.yCoordinate = yCoordinate;
this.alive = alive;
// Register with the phasers
this.cyclePhaser.register();
this.cellPhaser.register();
}
public void run() {
boolean newAlive;
while (true) {
// Await the next cycle
cyclePhaser.arriveAndAwaitAdvance();
// now check neighbors
newAlive = decideNewLifeState();
// Wait until all cells have checked their state
cellPhaser.arriveAndAwaitAdvance();
// all threads finished checking neighbors now change life state
alive = newAlive;
}
}
// Other methods redacted
}
You control the cycles by having the main thread register on the cyclePhaser
and have it arrive on it to start the next cycle.

match and delete elements in arraylists

I am making a robot maze where the robot reaches a target automatically without crashing into walls. I want the robot to do the maze once, learn the correct route and then the second time be able to get there straight away without going to any deadends. I thought I could do this by making three arraylists.
One for all the squares the robot visits.
Two for all the squares that lead to a deadend.
Three for all the directions the robot goes.
If the squares that lead to a dead end are found in the first arraylist then i can delete the same indexes in the third arraylist. That way, the second time, i can just iterate the third Arraylist.
My full code is below:
import java.util.ArrayList;
import java.util.*;
import java.util.Iterator;
import java.util.stream.IntStream;
public class Explorer {
private int pollRun = 0; // Incremented after each pass.
private RobotData robotData; // Data store for junctions.
private ArrayList<Integer> nonWallDirections;
private ArrayList<Integer> passageDirections;
private ArrayList<Integer> beenbeforeDirections;
private Random random = new Random();
int [] directions = {IRobot.AHEAD, IRobot.LEFT, IRobot.RIGHT, IRobot.BEHIND};
private ArrayList<Square> correctSquares;
private ArrayList<Square> wrongSquares;
private ArrayList<Integer> correctDirections;
public void controlRobot (IRobot robot) {
// On the first move of the first run of a new maze.
if ((robot.getRuns() == 0) && (pollRun ==0))
robotData = new RobotData();
pollRun++; /* Increment poll run so that the data is not reset
each time the robot moves. */
int exits = nonwallExits(robot);
int direction;
if ((robot.getRuns() != 0))
direction = grandfinale(robot);
nonWallDirections = new ArrayList<Integer>();
passageDirections = new ArrayList<Integer>();
beenbeforeDirections = new ArrayList<Integer>();
correctSquares = new ArrayList<Square>();
correctDirections = new ArrayList<Integer>();
// Adding each direction to the appropriate state ArrayList.
for(int item : directions) {
if(robot.look(item) != IRobot.WALL) {
nonWallDirections.add(item);
}
}
for(int item : directions) {
if(robot.look(item) == IRobot.PASSAGE) {
passageDirections.add(item);
}
}
for(int item : directions) {
if(robot.look(item) == IRobot.BEENBEFORE) {
beenbeforeDirections.add(item);
}
}
// Calling the appropriate method depending on the number of exits.
if (exits < 2) {
direction = deadEnd(robot);
} else if (exits == 2) {
direction = corridor(robot);
} else {
direction = junction(robot);
robotData.addJunction(robot);
robotData.printJunction(robot);
}
robot.face(direction);
addcorrectSquares(robot);
correctDirections.add(direction);
}
/* The specification advised to have to seperate controls: Explorer and Backtrack
and a variable explorerMode to switch between them.
Instead, whenever needed I shall call this backtrack method.
If at a junction, the robot will head back the junction as to when it first approached it.
When at a deadend or corridor, it will follow the beenbefore squares until it
reaches an unexplored path. */
public int backtrack (IRobot robot) {
if (nonwallExits(robot) > 2) {
addwrongSquares(robot);
return robotData.reverseHeading(robot);
} else {
do {
addwrongSquares(robot);
return nonWallDirections.get(0);
} while (nonwallExits(robot) == 1);
}
}
// Deadend method makes the robot follow the only nonwall exit.
public int deadEnd (IRobot robot) {
return backtrack(robot);
}
/* Corridor method will make the robot follow the one and only passage.
The exception is at the start. Sometimes, the robot will start with
two passages available to it in which case it will choose one randomly.
If there is no passage, it will follow the beenbefore squares
until it reaches an unexplored path.*/
public int corridor (IRobot robot) {
if (passageExits(robot) == 1) {
return passageDirections.get(0);
} else if (passageExits(robot) == 2) {
int randomPassage = random.nextInt(passageDirections.size());
return passageDirections.get(randomPassage);
} else {
return backtrack(robot);
}
}
/* Junction method states if there is more than one passage, it will randomly select one.
This applies to crossroads as well as essentially they are the same.
If there is no passage, it will follow the beenbefore squares until it reaches an unexplored
path. */
public int junction(IRobot robot) {
if (passageExits(robot) == 1) {
return passageDirections.get(0);
} else if (passageExits(robot) > 1) {
int randomPassage = random.nextInt(passageDirections.size());
return passageDirections.get(randomPassage);
} else {
return backtrack(robot);
}
}
// Calculates number of exits.
private int nonwallExits (IRobot robot) {
int nonwallExits = 0;
for(int item : directions) {
if(robot.look(item) != IRobot.WALL) {
nonwallExits++;
}
}
return nonwallExits;
}
// Calculates number of passages.
private int passageExits (IRobot robot) {
int passageExits = 0;
for(int item : directions) {
if(robot.look(item) == IRobot.PASSAGE) {
passageExits++;
}
}
return passageExits;
}
// Calculates number of beenbefores.
private int beenbeforeExits (IRobot robot) {
int beenbeforeExits = 0;
for(int item : directions) {
if(robot.look(item) == IRobot.PASSAGE) {
beenbeforeExits++;
}
}
return beenbeforeExits;
}
// Resets Junction Counter in RobotData class.
public int reset() {
return robotData.resetJunctionCounter();
}
public void addcorrectSquares(IRobot robot) {
Square newSquare = new Square(robot.getLocation().x, robot.getLocation().y);
correctSquares.add(newSquare);
}
public void addwrongSquares(IRobot robot) {
Square badSquare = new Square(robot.getLocation().x, robot.getLocation().y);
wrongSquares.add(badSquare);
}
public int grandfinale (IRobot robot) {
IntStream.range(0, correctSquares.size())
.map(index -> correctSquares.size() - index - 1)
.filter(index -> (((wrongSquares.x).contains(correctSquares.x)) && ((wrongSquares.y).contains(correctSquares.y))).get(index))
.forEach(index -> correctDirections.remove(index));
Iterator<Integer> routeIterator = correctDirections.iterator();
while (routeIterator.hasNext()) {
break;
}
return (routeIterator.next());
}
}
class RobotData {
/* It was advised in the specification to include the variable:
private static int maxJunctions = 10000;
However, as I am not using arrays, but ArrayLists, I do not
need this. */
private static int junctionCounter = 0;
private ArrayList<Junction> junctionList = new ArrayList<Junction>();
// Resets the Junction counter.
public int resetJunctionCounter() {
return junctionCounter = 0;
}
// Adds the current junction to the list of arrays.
public void addJunction(IRobot robot) {
Junction newJunction = new Junction(robot.getLocation().x, robot.getLocation().y, robot.getHeading());
junctionList.add(newJunction);
junctionCounter++;
}
// Gets the junction counter for Junction info method in Junction class.
public int getJunctionCounter (IRobot robot) {
return junctionCounter;
}
// Prints Junction info.
public void printJunction(IRobot robot) {
String course = "";
switch (robot.getHeading()) {
case IRobot.NORTH:
course = "NORTH";
break;
case IRobot.EAST:
course = "EAST";
break;
case IRobot.SOUTH:
course = "SOUTH";
break;
case IRobot.WEST:
course = "WEST";
break;
}
System.out.println("Junction " + junctionCounter + " (x=" + robot.getLocation().x + ", y=" + robot.getLocation().y +") heading " + course);
}
/* Iterates through the junction arrayList to find the
heading of the robot when it first approached the junction.
It does this by finding the first junction in the ArrayList
that has the same x and y coordinates as the robot.*/
public int searchJunction(IRobot robot) {
Junction currentJunction = null;
Iterator<Junction> junctionIterator = junctionList.iterator();
while (junctionIterator.hasNext()) {
currentJunction = junctionIterator.next();
if ((((currentJunction.x)==(robot.getLocation().x))) && ((currentJunction.y)==(robot.getLocation().y)))
break;
}
return currentJunction.arrived;
}
// Returns the reverse of the heading the robot had when first approaching the junction.
public int reverseHeading(IRobot robot) {
int firstHeading = searchJunction(robot);
int reverseHeading = 1; // Random integer to Iniitalise variable.
switch (firstHeading) {
case IRobot.NORTH:
if (robot.getHeading() == IRobot.NORTH)
reverseHeading = IRobot.BEHIND;
else if (robot.getHeading() == IRobot.EAST)
reverseHeading = IRobot.RIGHT;
else if (robot.getHeading() == IRobot.SOUTH)
reverseHeading = IRobot.AHEAD;
else
reverseHeading = IRobot.LEFT;
break;
case IRobot.EAST:
if (robot.getHeading() == IRobot.NORTH)
reverseHeading = IRobot.LEFT;
else if (robot.getHeading() == IRobot.EAST)
reverseHeading = IRobot.BEHIND;
else if (robot.getHeading() == IRobot.SOUTH)
reverseHeading = IRobot.RIGHT;
else
reverseHeading = IRobot.AHEAD;
break;
case IRobot.SOUTH:
if (robot.getHeading() == IRobot.NORTH)
reverseHeading = IRobot.AHEAD;
else if (robot.getHeading() == IRobot.EAST)
reverseHeading = IRobot.LEFT;
else if (robot.getHeading() == IRobot.SOUTH)
reverseHeading = IRobot.BEHIND;
else
reverseHeading = IRobot.RIGHT;
break;
case IRobot.WEST:
if (robot.getHeading() == IRobot.NORTH)
reverseHeading = IRobot.RIGHT;
else if (robot.getHeading() == IRobot.EAST)
reverseHeading = IRobot.AHEAD;
else if (robot.getHeading() == IRobot.SOUTH)
reverseHeading = IRobot.LEFT;
else
reverseHeading = IRobot.BEHIND;
break;
}
return reverseHeading;
}
}
class Junction {
int x;
int y;
int arrived;
public Junction(int xcoord, int ycoord, int course) {
x = xcoord;
y = ycoord;
arrived = course;
}
}
class Square {
int x;
int y;
public Square(int cordx, int cordy){
x = cordx;
y = cordy;
}
}
IntStream.range(0, al1.length)
.filter(index -> al2.contains(al1.get(index)))
.forEach(index -> al3.remove(index));
Slightly more complex than this if removing elements from al3 shifts them left but in that case just reverse the stream before the .filter- then it will delete from the end. The easiest way to do that is:
.map(index -> al1.length - index - 1)
Without Streams the equivalent would be
for (int i = 0; i < al1.length; i++) {
if (al2.contains(al1.get(i))) {
al3.remove(i);
}
}
Similarly, if you need to delete from the right then the for loop would need to count down rather than up.
Without further details on arraylist structure it's hard to give any more hints.

How to compare images?

For a project I am making a memory game, but I am trying to make to objects disappear when the are equal. This is my code:
public void opencard(){
int duiker = 0;
int duiker2 = 0;
if (duiker == duiker2){
if(Greenfoot.mouseClicked(Duiker.class)){
setImage("duiker_tcm46-175501.jpg");
if(Greenfoot.mouseClicked(duiker2)){
duiker2 = 1;
}
duiker = 1;{
getWorld().removeObject(this);
}
}
else if(Greenfoot.mouseClicked(this)){
setImage("duiker_tcm46-175501.jpg");
Greenfoot.delay(150);
setImage("150px_RGB_WEBSAFE_D_B_G_26-29652.gif");
}
}
}
But they disappear even when I don't click on them.

Any ideas on how to optimize this code? I cant seem to come up with anything that works

public class SnowflakeWallpaper extends WallpaperService {
// Limit of snowflakes per snowflake type; 4 types * 4 snowflake = 16 total
// Should keep memory usage at a minimal
static int SNOWFLAKE_AMOUNT = 4;
Drawable drawWall;
Rect wallBounds;
// Draw all snowflakes off screen due to not knowing size of canvas at creation
static int SNOW_START = -90;
ArrayList<Snowflakes> snow = new ArrayList<Snowflakes>();
private final Handler mHandler = new Handler();
#Override
public void onCreate() {
super.onCreate();
//WallpaperManager to pull current wallpaper
WallpaperManager wManager = WallpaperManager.getInstance(this);
drawWall = wManager.getFastDrawable();
wallBounds = drawWall.copyBounds();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public Engine onCreateEngine() {
return new SnowEngine();
}
class SnowEngine extends Engine {
private final Runnable mDrawSnow = new Runnable() {
public void run() {
drawFrame();
}
};
private boolean mVisible;
SnowEngine() {
if(snow.size() < 16){
//Back snowflakes
for(int i = 0; i < SNOWFLAKE_AMOUNT; i++){
snow.add(new Snowflakes(
BitmapFactory.decodeResource(getResources(),
R.drawable.snowflakeback),
SNOW_START,
SNOW_START,
((float)(Math.random() * 2) + 1)) // Fall speed initial setup, back slowest to front fastest potentially
);
}
//MidBack snowflakes
for(int i = 0; i < SNOWFLAKE_AMOUNT; i++){
snow.add(new Snowflakes(
BitmapFactory.decodeResource(getResources(),
R.drawable.snowflakemid),
SNOW_START,
SNOW_START,
((float)(Math.random() * 4) + 1)
));
}
// Mid snowflakes
for(int i = 0; i < SNOWFLAKE_AMOUNT; i++){
snow.add(new Snowflakes(
BitmapFactory.decodeResource(getResources(),
R.drawable.snowflakemidfront),
SNOW_START,
SNOW_START,
((float)(Math.random() * 8) + 1))
);
}
// Front snowflakes
for(int i = 0; i < SNOWFLAKE_AMOUNT; i++){
snow.add(new Snowflakes(
BitmapFactory.decodeResource(getResources(),
R.drawable.snowflake),
SNOW_START,
SNOW_START,
((float)(Math.random() * 16) + 1))
);
}
}
}
#Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
}
#Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mDrawSnow);
}
#Override
public void onVisibilityChanged(boolean visible) {
mVisible = visible;
if (visible) {
drawFrame();
} else {
mHandler.removeCallbacks(mDrawSnow);
}
}
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
drawFrame();
}
#Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
}
#Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
mVisible = false;
mHandler.removeCallbacks(mDrawSnow);
}
/*
* Update the screen with a new frame
*/
void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
/*
* if the snow goes too low or too right, reset;
*/
for(int i = 0; i < snow.size(); i++){
if(snow.get(i).getX() > holder.getSurfaceFrame().width()){
snow.get(i).setX(-65);
}
if(snow.get(i).getY() > holder.getSurfaceFrame().height()){
snow.get(i).setY(-69);
}
}
// Test if the array was just create; true - randomly populate snowflakes on screen
if(snow.get(1).getX() < -70){
for(int i = 0; i < snow.size(); i++){
snow.get(i).setX((int)(Math.random() * getSurfaceHolder().getSurfaceFrame().width() +1));
snow.get(i).setY((int)(Math.random() * getSurfaceHolder().getSurfaceFrame().height() + 1));
}
}
// Change snowflake x & y
for(int i = 0; i < snow.size(); i++){
snow.get(i).delta();
}
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
// call to draw new snow position
drawSnow(c);
}
} finally {
if (c != null) holder.unlockCanvasAndPost(c);
}
// Reschedule the next redraw
mHandler.removeCallbacks(mDrawSnow);
if (mVisible) {
mHandler.postDelayed(mDrawSnow, 1000 / 100);
}
}
/*
* Draw the snowflakes
*/
void drawSnow(Canvas c) {
c.save();
// Draw bg
//********** add code to pull current bg and draw that instead of black. Maybe set this in config?
if(drawWall == null){
c.drawColor(Color.BLACK);
}else{
drawWall.copyBounds(wallBounds);
drawWall.draw(c);
}
/*
* draw up the snow
*/
for(int i = 0; i < snow.size(); i++){
c.drawBitmap(snow.get(i).getImage(), snow.get(i).getX(), snow.get(i).getY(), null);
}
c.restore();
}
}
}
Same question as Gabe - what's the problem?
Some general thoughts:
* You should avoid doing lots of work in a constructor. Your constructor does a ton of work that should (imho) be in some init/setup method instead. Easier to benchmark / profile there independently from instance creation.
You're using Math.random in many places - I assume you are singly threaded, but Math.random is synchronized. Per the javadocs: " if many threads need to generate pseudorandom numbers at a great rate, it may reduce contention for each thread to have its own pseudorandom-number generator"
You're using Math.random which gets you a double, then multiplying, then adding, then casting. This looks wasteful. Any way to get fewer ops?
You seem to have some division - see line "mHandler.postDelayed(mDrawSnow, 1000 / 100);". Sure, that's probably compiled or JIT'd away, but you should avoid division in performance critical code (it's far slower than multiplying). So any div by a constant can be replaced by multiplying by 1 / C as a static.
You have lots of repeat accessor method calls (in some cases nearly all are repeat). See snippit:
for(int i = 0; i < snow.size(); i++){
if(snow.get(i).getX() > holder.getSurfaceFrame().width()){
snow.get(i).setX(-65);
}
if(snow.get(i).getY() > holder.getSurfaceFrame().height()){
snow.get(i).setY(-69);
}
}
You should store "holder.getSurfaceFrame().width() in a temporary / local var (perhaps once per draw loop assuming your surface is resizable by the user). You might also store snow.get(i) in a local var. Better yet (style) you can use the enhanced for loop as snow is an ArrayList. So use
for (Snow mySnow : snow) {
// Do something with mySnow
}
Hope this helps. Good luck!

Categories