I'm trying to do a simple code in Java : I have a class called "Bloc", which create blocks (square), which inside it I create a random width (largeur), and a random height (hauteur) between 2 ints, and I create a random number of blocks (nombreBloc). I also create an ArrayList to put every block in it, to see how much I have left.
So, I did a function called "insererBlocList" (insert the block into the ArrayList), which create "nombreBloc" (numberBloc) of blocks and put it into the ArrayList.
I've got a graphic interface, in which I have 1 panel for the windows, which inside it I have 2 other panels: One of them is to put every block I created into it.
Here is my problem, I've got a "StackOverflowError" inside my function "insererBlocList", which means there is an infinite loop, but after writing the path of code, I don't see where I did the mistake... Here is the code :
Bloc class:
public class Bloc extends JPanel{
private int hauteur, largeur, nombreBloc;
private boolean premierPassage = true;
private ArrayList<Bloc> listeBlocRestant;
private Random rand = new Random();
public Bloc() {
this.hauteur = 10 + rand.nextInt(50 - 10);
this.largeur = 10 + rand.nextInt(50 - 10);
listeBlocRestant = new ArrayList<Bloc>();
if(premierPassage == true) {
this.nombreBloc = 5 + rand.nextInt(30 - 5);
insererBlocList();
}
}
public ArrayList<Bloc> insererBlocList(){
premierPassage = false;
for(int i=0; i<nombreBloc; i++) {
Bloc bloc = new Bloc();
listeBlocRestant.add(bloc);
}
return listeBlocRestant;
}
The GUI part of the panel block:
private JPanel initPanelBloc() {
panelBloc = new Bloc();
}
Your Bloc constructor calls insererBlocList(), and insererBlocList() creates additional Bloc instances (for each of them the constructor calls insererBlocList()), which leads to an infinite chain of method calls, leading to StackOverflowError.
insererBlocList() probably shouldn't be called by the Bloc constructor.
You might thought premierPassage = false; will prevent method calling again. But insererBlocList() makes new instance of Bloc and this boolean value will be true again. Change code like this:
public class Bloc extends JPanel{
private int hauteur, largeur, nombreBloc;
private ArrayList<Bloc> listeBlocRestant;
private Random rand = new Random();
public Bloc(boolean premierPassage) {
this.hauteur = 10 + rand.nextInt(50 - 10);
this.largeur = 10 + rand.nextInt(50 - 10);
listeBlocRestant = new ArrayList<Bloc>();
if(premierPassage == true) {
this.nombreBloc = 5 + rand.nextInt(30 - 5);
insererBlocList();
}
}
public ArrayList<Bloc> insererBlocList(){
premierPassage = false;
for(int i=0; i<nombreBloc; i++) {
Bloc bloc = new Bloc(false);
listeBlocRestant.add(bloc);
}
return listeBlocRestant;
}
And change GUI part like this:
private JPanel initPanelBloc() {
panelBloc = new Bloc(true);
}
Related
I am trying to get my Asteroids game in Java to have the ArrayList container be removed once it's off screen. I figured out how to stop having it print when off screen, but can see in my console the array continues to grow. Not sure where to go from here.
I think the way I can get them to be removed when off screen is by either using the remove or set feature with arrayLists. Visually everything is disappearing right, but in the console my ArrayList is still growing. I thought about setting a limit of how long it can be, but not sure if there is a better way than that.
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Iterator;
public class AsteroidGame extends Frame {
private int FrameWidth = 500;
private int FrameHeight = 400;
static public void main(String[] args) {
AsteroidGame world = new AsteroidGame();
world.show();
world.run();
}
public AsteroidGame() {
setTitle("Asteroid Game0");
setSize(FrameWidth, FrameHeight);
addKeyListener(new keyDown());
addWindowListener(new CloseQuit());
}
public void run() {
while (true) {
movePieces();
repaint();
try {
Thread.sleep(100);
} catch (Exception e) {
}
}
}
private ArrayList asteroids = new ArrayList();
private ArrayList rockets = new ArrayList();
private Station station = new Station(FrameWidth / 2, FrameHeight - 20);
public void paint(Graphics g) {
station.paint(g);
Iterator astIt = asteroids.iterator();
while (astIt.hasNext()) {
Asteroid rock = (Asteroid) astIt.next();
if (rock.y >= 400 || rock.x >= 500){
rock = null;
} else {
rock.paint(g);
}
}
Iterator rocIt = rockets.iterator();
while (rocIt.hasNext()) {
Rocket rock = (Rocket) rocIt.next();
if (rock.y >= 400 || rock.x >= 500) {
rock = null;
} else {
rock.paint(g);
}
}
}
public void movePieces() {
if (Math.random() < 0.3) {
Asteroid newRock = new Asteroid(FrameWidth * Math.random(), 20, 10 * Math.random() - 5, 3 + 3 * Math.random());
if (newRock.y >= 500 || newRock.x >= 500){
asteroids.remove(0);
} else{
asteroids.add(newRock);
}
System.out.println(asteroids.size());
}
Iterator astIt = asteroids.iterator();
while (astIt.hasNext()) {
Asteroid rock = (Asteroid) astIt.next();
if (rock.y >= 400 || rock.x >= 500) {
rock = null;
} else {
rock.move();
station.checkHit(rock);
}
}
Iterator rocIt = rockets.iterator();
while (rocIt.hasNext()) {
Rocket rock = (Rocket) rocIt.next();
if (rock.y >= 400 || rock.x >= 500) {
rock = null;
} else {
rock.move(asteroids);
}
}
}
private class gameMover extends Thread {
public void run() {
while (true) {
movePieces();
repaint();
try {
sleep(100);
} catch (Exception e) {
}
}
}
}
Change:
rock = null;
to:
astIt.remove();
Assigning null to the variable that has been assigned the value of an element of a List does absolutely nothing to either the List or the element in it; it only affects the value that the variable holds.
As an aside, nice variable name choice of rock - it is appropraite for both types of object - either an abbreviation of "rocket" or a reasonable synonym for an astroid.
Change rock = null; to asteroids.remove(rock); or astIt.remove();
and it should be fine, also no need to set the variable to null as the garbage collector will take care of it for you.
EDIT
Actually asteroids.remove(rock); will throw an exception as said in the comments of this answer, so nevermind it and use the other.
Also I think when in movePieces() you create a new rock and you check if this new rock is outside the screen, I don't think removing the first asteroid in the ArrayList is correct as you will not add the new rock (which may be right if the rock can actually randomly spawn outside the screen) but you will also remove a maybe fine working asteroid from the ArrayList (and thus from the game and the screen).
So, personally I would change that part of code to:
if (Math.random() < 0.3) {
Asteroid newRock = new Asteroid(FrameWidth * Math.random(), 20, 10 * Math.random() - 5, 3 + 3 * Math.random());
if (!(newRock.y >= 500 || newRock.x >= 500)){
asteroids.add(newRock);
}
System.out.println(asteroids.size());
}
But tell me if this works for you.
public static ArrayList<Hand> getPossibleHands(Hand h) {
ArrayList<Hand> allPossible = new ArrayList<Hand>();
addNext(allPossible, h);
return allPossible;
}
public static void addNext(ArrayList<Hand> poss, Hand h) {
if (h.cards.size() == 5)
poss.add(h);
else
for (int i = 0; i < 52; i++) {
Card c = Card.makeCard(i);
if (!h.contains(c))
h.add(c);
addNext(poss,h);
}
}
The code above is supposed to essentially take an incomplete poker board (anywhere from 0-4 cards) and return all possible complete boards (5 cards). The logic that I feel like it should follow is as follows: recurse through every combination of adding valid (not on the board already) cards until the size of the board is equal to 5 in which case it will add the board to the list and skip over the rest of the function.
However upon using a print statement at the beginning of the function I see that hand sizes of greater than 5 or being created. Since the first part of the function should catch all hands at 5 and terminate it there, I don't see how the code executes at the rest of the function.
Your class should receive stack overflow with an empty hand.
You send new Card(0) to the hand. This is added.
Then you call add next again - and the 'for' starts from 0 again. Checks adds 1. Then starts from 0 - it is there, does not add anything and starts over. Where it starts from 0. Does not do anything. Starts from 0. Ad infinum -> StackOverFlow.
You also need to reset to previous state of the hand every time you finish with 5 cards and backtrack.
If you want a recursive solution you can try :
private static ArrayList<Hand> getPossibleHands(Hand h) {
ArrayList<Integer> except;
if (h.cards == null) except = new ArrayList<>();
else
except = h.cards.stream().map(c -> (c.getCard())).collect(Collectors.toCollection(ArrayList::new));
ArrayList<Hand> allPossible = new ArrayList<>();
addNext(allPossible, h, except);
return allPossible;
}
private static void addNext(ArrayList<Hand> poss, Hand h, ArrayList<Integer> except) {
//assuming hands 0-4 - we don't need to check on entry, only when we add
Hand localHand = h.copy();
for (int i = 0; i < 52; i++) {
if (except.contains(i)) continue;
Card c = Card.makeCard(i);
if (!localHand.contains(c)) {
addNext(poss, localHand.copy(), copyExcept(except, i));
localHand.add(c);
if (localHand.cards.size() == 5) {
poss.add(localHand);
break;
}
}
}
}
private static ArrayList<Integer> copyExcept(ArrayList<Integer> except, int i) {
ArrayList<Integer> clonedExcept = new ArrayList<>(except);
clonedExcept.add(i);
return clonedExcept;
}
import java.util.ArrayList;
public class Hand {
ArrayList<Card> cards = new ArrayList<>();
public boolean contains(Card c) {
for (Card card : cards) {
if (card.getCard() == c.getCard())
return true;
}
return false;
}
public void add(Card c) {
cards.add(c);
}
Hand copy() {
Hand temp = new Hand();
for (Card c : cards) {
temp.add(new Card(c.getCard()));
}
return temp;
}
}
class Card {
private int card;
public Card(int card) {
this.card = card;
}
public static Card makeCard(int i) {
return new Card(i);
}
public int getCard() {
return card;
}
}
Initially, h is (presumably) empty. So addNext will loop through all possible cards, and since none of them are in the hand, add each card to the hand, regardless of how many cards are currently in the hand.
It looks to me like your for loop is eventually adding whole deck to hand.
In your loop you run 52 iterations. On each iteration you (conditionally) add a card to the hand and then you call your function recursively. But after that recursive call has returned, you go to the next iteration and again add a card to the hand.
So that '5 card restriction' does not restrict anything here.
I'm pretty new to java and I'm trying to create a program that quizzes users on the difference between 2 random frequencies. Everything works except that when I try to get the difference of 2 frequencies the answer is always 0. How do I get it to display the actual difference? Here is the class I wrote to create the tones ans compute the difference:
public class Quiz{
private PitchPlay one = new PitchPlay();
private PitchPlay two = new PitchPlay();
private int frequencyOne;
private int frequencyTwo;
private int dif = new Integer(Math.abs(frequencyTwo - frequencyOne));
public int run(){
frequencyOne = new Integer((int)(Math.random() * 5000 + 50));
frequencyTwo = new Integer((int)(Math.random() * 5000 + 50));
return this.dif;
}
public int freqDif(){
return this.dif;
}
public void playQuiz(){
one.play(one.note(frequencyOne, 2, 15));//note(frequency, duration, volume)
two.play(two.note(frequencyTwo, 2, 15));
}
}
And here is the class where the quiz class is used:
public class Action implements ActionListener{
Quiz one = new Quiz();
public void actionPerformed (ActionEvent e){
if(e.getSource() == playSoundButton)
{
if(answerResponse.getText().compareTo("Correct!") == 0 || answerResponse.getText().compareTo("Play and Listen...") == 0)
{
one.run();
one.playQuiz();
}
else
{
one.playQuiz();
}
}
if(e.getSource() == submitButton)
{
String responseText = new String(responseField.getText());
if(responseText !=null && !"".equals(responseText)){
try{
Integer responseNumber = Integer.parseInt(responseText);
if(responseNumber == one.freqDif())
{
answerResponse.setText("Correct!");
answerResponse.setVisible(true);
}
else
{
answerResponse.setText("Wrong Answer. The difference is " + one.freqDif() + " hertz.");
answerResponse.setVisible(true);
}
}catch(NumberFormatException f){
f.printStackTrace(System.out);
}
}
}
}
}
private int dif = new Integer(Math.abs(frequencyTwo - frequencyOne));
This is calculated when the variable is created, not when you use the variable. You need to do the calculation after assigning the variables.
There are several issues with your code:
As others have mentioned, the line
private int dif = new Integer(Math.abs(frequencyTwo - frequencyOne));
creates a variable and assigns a value calculated from two other variables. The values of the other variables at the time of creation are used. You need to recalculate every time the values of those two variables changes. This does not happen automatically for you.
new Integer(...) creates an Integer object. When you subsequently assign this object to an int variable, the value is taken out of the object. This adds some unnecessary memory and run-time over head to your code. Instead, you should assign directly to your variables. For example,
frequencyOne = (int)(Math.random() * 5000 + 50);
Since Java cannot unload native libraries when once used (in Matlab; see SO question), i am trying to call 2 GUI classes from within Matlab. I am working on grabbing an image from camera and then saving it on disk. I want to use one Java class for communicating with camera while another class (GUI) is still open in Matlab. Is this possible? Here's the code:
1.
public class GUI
{
public static void main(String[] args)
{
// Just open up the window and start things running
MainWindow mWindow = new MainWindow();
}
public static void main2()
{
MainWindow.grabImage(0);
}
}
2.
public class MainWindow
{
static volatile int commandVal;
Thread updateThread;
static CameraImage cImage;
static int fs_c =1;
MainWindow(){
JFrame main_f = new JFrame("M");
main_f.getContentPane().setLayout(new BorderLayout());
main_f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
main_f.getContentPane().add(tabPane1, "Center");
main_f.pack();
main_f.setVisible(true);
kkk = 1;
mySerial = new CameraSerial(cWindow.getPort());
}
}
3.
public static void grabImage(int commandVal){
while (MainWindow.kkk == 1) {
if (MainWindow.fs_c == 1) {
MainWindow.commandVal = 5;
}
if (MainWindow.commandVal == 5 || MainWindow.commandVal == 6){
cImage.sendFrame(0);
JFileChooser save_d = new JFileChooser();
File saveFile = save_d.getSelectedFile();
cImage.writeImage(saveFile + ".jpg");
MainWindow.fs_c = 0;
MainWindow.commandVal = 0;
mySerial.write("\r");
System.out.println("Camera Ready...");
break;
}
else if (commandVal == -1) {
MainWindow.commandVal = 0;
mySerial.write("\r");
status_t.setText("Camera Ready...");
}
else {
try {
Thread.sleep(100);
} catch (Exception e) {
}
}
}
}
From Matlab I'm calling first Gui.main([]), and then Gui.main2(). It works for the first time. But when I call Gui.main2() again, Matlab does nothing. I think it's a bad code somewhere. Thanks for replying!
Just an assumption without having more information:
This check:
if (MainWindow.fs_c == 1) {
MainWindow.commandVal = 5;
}
And later this (note that commandVal would be 5 after the check succeeded for the first time):
MainWindow.fs_c = 0;
MainWindow.commandVal = 0;
Here's the problem:
In the first run, commandVal is set to 5, assuming MainWindow.fs_c is initially 1.
Thus MainWindow.fs_c = 0; is executed.
In the second run MainWindow.fs_c == 1 is false (MainWindow.fs_c is now 0) and MainWindow.commandVal was also set to 0. Thus the method doesn't do anything, except sleeping for 100 milliseconds.
I am making a keyboard-like buttons for my Hangman game (SEE PICTURE HERE), my problem is about the inner classes. I've read this LINK about inner classes and it says that you can only access the outside variables with FINAL type. But if I declared the variable as such, I cannot change the value of it anymore... So my problem is that I need to change the value inside the inner class. My code is as follows:
public class MainGame extends JDialog {
private String player;
private char [] wordChar;
private JButton[] buttons;
private int level;
private int score;
private int livesLeft;
private int missedGuess;
void newGame() {
level = 0;
score = 0;
livesLeft = 10;
missedGuess = 0;
//label1:
// while (livesLeft!= 0) {
//get random WORD from LIST
Word hiddenWord = new Word();
//put random word in Array
wordChar = new char[hiddenWord.getHiddenWord().length()];
wordChar = hiddenWord.getHiddenWord().toCharArray();
buttons = new JButton[wordChar.length];
for (int i = 0; i < wordChar.length; i++){
JButton guessWord = new JButton(" ");
guessWord.setFont(new Font("Microsoft Sans Serif", 1, 18));
guessWord.setEnabled(false);
jPanel3.setLayout(new GridLayout(1, wordChar.length));
jPanel3.add(guessWord);
buttons[i] = guessWord;
}
checkLetter();
}
void checkLetter() {
int checker = 0;
while(checker != wordChar.length){
jPanel1.setLayout(new GridLayout(3, 9, 3, 5));
for (char buttonChar = 'a'; buttonChar <= 'z'; buttonChar++) {
String buttonText = String.valueOf(buttonChar);
final JButton letterButton = new JButton(buttonText);
letterButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String actionCommand = e.getActionCommand();
for (int j = 0; j < wordChar.length; j++){
String text = String.valueOf(wordChar[j]);
if(actionCommand.equals(text)){
buttons[j].setText(text);
checker++; //THIS CODE IS NOT POSSIBLE!!!!
}
}
}
});
jPanel1.add(letterButton);
}
checker++;
}
}
NOTE: The code above is not complete.
The int checker is used to count how many correct letters are already guessed so that if it is equal to the length of the word, I can now proceed to the next level
How can I re-do my code?
You can declare checker as a field of outer class, and access it with some method, something like increaseChecker().
UPDATE: Something like this:
1) Create checker field in outer class:
public class OuterClassName {
private int checker;
protected void increaseChecker() {
checker++;
}
void checkLetter() {
// ...
}
}
2) Use increaseChecker() method call instead of checker++
You can't access local variables in an anonymous inner class, and that's for a good reason. The actionPerformed() method isn't guaranteed to be called inside the checkLetter() function. It will be called later, possibly (and most probably) after the function exits so its local variables will be destroyed by that time. Therefore, the newly created anonymous class implicitly gets a copy of that variable. But it wouldn't make any sense to increase the copy, that's why only final local variables can be accessed from methods of an anonymous class.
The simplest workaround is just to make checker a field of the outer class. But that wouldn't make any sense if it's accessed only within the checkLetter() function unless it is necessary for it to retain its value between checkLetter() calls. In order to find out the true answer, you need to think why you are trying to increase checker inside actionPerformed()? When should it happen and what are you trying to achieve by doing it?
Why not keep the anonymous inner class but have it call a method of the class. This way final isn't an issue.
letterButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
letterButtonActionPerformed(e); // create this method
}
});
//.....
// new method
private void letterButtonActionPerformed(ActionEvent e) {
String actionCommand = e.getActionCommand();
for (int j = 0; j < wordChar.length; j++){
String text = String.valueOf(wordChar[j]);
if(actionCommand.equals(text)){
buttons[j].setText(text);
checker++; //THIS CODE IS NOT POSSIBLE!!!!
}
}
}