Method is called more than once from one ActionEvent - java

I'm trying to code a trivia based game with ActionEvent based buttons. I organized each specific event that is connected to JButton instances in a if-else structure and inside each of these structures, a method would be called. One of these methods would increment a variable that holds the number of the answers that are correct (cor) and wrong (inc):
public void compare(String sel , String ans)
{
//if correct
if(sel.compareToIgnoreCase(ans) == 0)
{
q.setText("Correct! 10 points added!");
score += 10;
cor++;
}
//if incorrect
else
{
q.setText("Incorrect! The correct answer was: " + ans);
inc++;
}
}
This set of code would be run from this:
public void actionPerformed(ActionEvent event)
{
if(e.equals("GUESS"))
{
userGuess = entry.getText();
answer = ans.get(currentQuestionIndex);
base.remove(entry);
base.remove(submit);
submit.setText("OK");
submit.setActionCommand("OK");
submit.addActionListener(this);
base.add(submit);
compare(userGuess, answer);
}
...
}
However, whenever the compare method is called, the inc and cor value seems to increment by an ambiguous value. For example, if I were to answer one question right, the new value for cor would be 2 instead of 1. When I answer another right, cor would be 5 instead of two. I tried using tracers in my code but so far, I appears that the program detects the actionEvent that runs compare() is being pressed multiple times, and as a result, it runs said code multiple times. How can I fix my code so that these variables can be incremented correctly by 1.

submit.addActionListener(this);
Don't add the ActionListener in the actionPerformed() method.
Each time the actionPerformed is invoked you add another listener, so when you click on the "Submit" button you have code that is executed multiple times.

Related

if-statements not being triggered when conditions are met

I am building a simple game of 21. Everything comes together okay, but when I click on the Button that I have assigned to my 'Stand' function, none of the if-statement blocks trigger event though I am meeting conditions of one or the other depending on what cards have already been dealt. I have tested all variations of the statements and I want to have some insight, or a second pair of eyes to see something I do not.
I have tested the function multiple times, and re-written it multiple times. I've tested the function with just that statement present, and it still does not trigger.
This is the function in question:
//when player hits stand button
public void Stand(TextField playerNum, TextField dealerNum, TilePane b, Button hit, Button stand, Button deal, TextField handsLostNum, TextField handsWonNum) {
//obtain current final scores when player stands
playerFinal = Integer.parseInt(playerNum.getText());
dealerFinal = Integer.parseInt(dealerNum.getText());
if (playerFinal > dealerFinal) {
hit.setVisible(false);
stand.setVisible(false);
deal.setVisible(true);
playerNum.setText("YOU WIN!");
dealerNum.setText("YOU WIN!");
handsWon += 1;
String temp = Integer.toString(handsWon);
handsWonNum.setText(temp);
}
if (dealerFinal > playerFinal) {
hit.setVisible(false);
stand.setVisible(false);
deal.setVisible(true);
playerNum.setText("YOU LOSE!");
dealerNum.setText("YOU LOSE!");
handsLost += 1;
String temp = Integer.toString(handsLost);
handsLostNum.setText(temp);
}
if (dealerFinal == playerFinal) {
playerNum.setText("DRAW! PLAY AGAIN!");
dealerNum.setText("DRAW! PLAY AGAIN!");
hit.setVisible(false);
stand.setVisible(false);
deal.setVisible(true);
}
handsWon = 0;
handsLost = 0;
} //END STAND METHOD
And the condition that helps to meet it is here:
//method to add scores to text fields
public void addScores(int pScore, int dScore, TextField playerNum, TextField dealerNum) {
//ADD PLAYER SCORE
String playerScore = playerNum.getText();
int playerCurrent = Integer.parseInt(playerScore);
int newCurrent = playerCurrent + dScore;
String newScore = Integer.toString(newCurrent);
playerNum.setText(newScore);
//ADD DEALER SCORE
String dealerScore = dealerNum.getText();
int dealerCurrent = Integer.parseInt(dealerScore);
int newDealCurrent = dealerCurrent + pScore;
String newDealScore = Integer.toString(newDealCurrent);
dealerNum.setText(newDealScore);
}
I add the scores to text fields and then pull them again later in the project. Yet, even when the values are meeting the conditions of being larger than the opponents value, the statement does not trigger.
The expected result is when I click on the 'Stand' button, the statement is triggered and then the variable that adds to the total tally is activated.
Try putting System.out in every step to check if it is actually getting there. Put one as the first statement in the Stand method like: System.out.println("In Stand method");
Then put more of those before the if statements and inside them like:
System.out.format("Before: playerFinal : %s, dealerFinal: %s, playerFinal > dealerFinal: %d %n", playerFinal, dealerFinal, playerFinal > dealerFinal);
if (playerFinal > dealerFinal) {
System.out.format("In: playerFinal : %s, dealerFinal: %s, playerFinal > dealerFinal: %d %n", playerFinal, dealerFinal, playerFinal > dealerFinal);
Do that for each of the methods, to see if that method is actually running and what the values are.
If you see that the if statements are executing and the flow going into them, but you don't see any changes on the GUI elements, then try using:
Platform.runLater(() -> {
// Your GUI changes code
playerNum.setText("YOU WIN!");
dealerNum.setText("YOU WIN!");
});
Platform.runLater receives a runnable as an argument, and is the right way to update the GUI if you are using JavaFX.
Sometimes you may save the file and run it and the IDE would not actually compile it, running the old code. In that case, you can try restarting the IDE and trying again.

JButton ActionListeners different variable in each loop

I am making a simple calculator using a JFrame and JButtons. Before I made each button, 0 thru 9, with their own action listeners, now I have realized that this is super inefficient and I should use a loop to make each button and assign the ActionListeners. So the only thing that needs to happen in each ActionListener is to add whatever number the button is to a JTextField, called nums. Here is what I have now.
for(int i = 0; i <=9; i++) {
count = i;
btns.get(i).addActionListener(new ActionListener(){ //makes action listeners for each button
public void actionPerformed(ActionEvent e){
nums.setText(nums.getText()+ count);//IMPORTANT CODE
}
});
}
So as you can see I used a terribly named variable named count. count is set to i in each iteration before the important code is used. I have to do this because an AL is it's own class and cannot access i. However count is public and static, so the ALs can use count.
My problem is that all the buttons print 9. This logically makes sense because what is happening is that the ALs in each button use the count variable, when the loop is done, count will be 9, which means each AL will essentially contain nums.setText(nums.getText()+ 9);. Instead, I need button 1 to be nums.setText(nums.getText()+ 1); 2 to be 2, ETC.
I have tried to call the text of each button, however, because you need to use an index in the ArrayList get method, you need a variable, if you use count, the same problem occurs; after the for loops has terminated, count is 9, so all the buttons print the text of the 9 button.
Any and all help is appreciated, Thanks in advance,
-Max
P.S. Just in case you don't understand why I'm getting the text and then adding count, it is because in order to type the number 12. you need to type 1 and then concatenate the 2 to the one to et 12. getText gets the 1, and adding count concatenates the 2 to make 12.
In general you will want to avoid using static fields as you loose all benefits of object-oriented programming by this. There are specific places for use of static fields, but this is not one of them. In your situation you can't use the index of the loop, i, directly since it is not a final local variable, and non-final local variables cannot be used within anonymous inner classes. So a simple solution exists:
Make count a final local variable and you should be able to use it within your anonymous inner class:
for(int i = 0; i <= 9; i++) {
final int finalCount = i;
btns.get(i).addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
nums.setText(nums.getText() + finalCount);
}
});
}
Assuming the text for each button is simply the digit you wish to append, there is another way that lets you share the same ActionListener instance for every number button.
private ActionListener numberBtnListener = new ActionListener(){
public void actionPerformed(ActionEvent e){
JButton b = (JButton) e.getSource();
nums.setText(nums.getText() + b.getText());
}
}
Then just use the same listener instance for each button:
for(JButton b : btns) {
b.addActionListener(numberBtnListener);
}
If your button text is different for some reason, you can still use the same technique by using a client property on each button to hold the value that you wish to append. E.g.:
b.putClientProperty("digit", i);
then use
nums.setText(nums.getText() + b.getClientProperty("digit"));

How can I make this program display a counter a different way?

So I understand how to make it so when a user presses a button, the count goes up, but how would I make it so it displays the amount of times the button has been pressed in a different way. For example, if the user pressed the button 3 times, it would say 123, instead of 3.
This is my current method with does it a conventional way,
public void actionPerformed(ActionEvent event) {
count++;
label.setText("Pushes: " + count);
}
So I'm thinking maybe I can make it so it loops through each posted value and displays that?
Initialise countStr globally, than on the button click append the count with StringBuilder like:
StringBuilder countStr= new StringBuilder();
public void actionPerformed(ActionEvent event) {
count++
countStr.append(count);
label.setText("Pushes: " +countStr);
}

TicTacToe win conditions problems

New programmer here, writing a Tictactoe game using Java on Eclipse.
I have problems with my win conditions I think. It comes up with the error:
Exception in thread "main" java.lang.NullPointerException
at Game.NoughtsCrosses.(NoughtsCrosses.java:106)
at Game.Main.main(Main.java:5)
Here is my win conditions bit. It's not well made imo, but I'm having problems when compiling. Can anyone spot why? Ty!!
I have squares set up in a 3x3 grid, 0 -> 8. Each button has its own text which is set to X or O when clicked by each player.
winconditions code:
if (square[0].getText().equals(square[1].getText()) && square[1].getText().equals(square[2].getText()) != square[0].getText().isEmpty()) {
win = true;
}
Full Pastebin of code
Thanks again :) Any questions, I can elaborate :D
It looks like one of the squares text is null. One thing that is important to remember is that an empty string is not the same thing as null. In java, if you haven't specifically assigned a value to a String then it will be null. To fix this, you will want to explicitly set each squares text to "" (an empty string) when you set up your game board.
Well I took the code that you provided and after significant finagling was able to make a fully functioning Tic-Tac-Toe game. You were mostly on the right track with what you were doing you just needed to first begin with a design.
In my NoughtsCrosses class I have the following:
class Action implements ActionListener
This has a JButton attribute that I pass in through a constructor
In the actionPerformed
set the text
disable the button
increment the counter
check if someone wins
If there is a winner or draw game ends set the "Play again?" text
else call the changeTurn function
class Reset implements ActionListenter
This has a JButton attribute that I pass in through a constructor
In the actionPerformed
I call the resetGame function
function changeTurn
function resetGame
function checkForWinners
as a hint, this is my implementation of the Action class and an example of the constructor I mentioned
class Action implements ActionListener{
private JButton button;
public Action(JButton button){
this.button = button;
}
public void actionPerformed(ActionEvent e) {
button.setText(letter);
button.setEnabled(false);
counter++;
boolean gameOver = checkForWinners();
if(!gameOver)
changeTurn();
else{
newgame.setText("Play again?");
newgame.addActionListener(resetButton);
}
}
}
a call like new Action(square[i]) is what you need to make this work.
Note: the resetButton is of the Reset class I mention above much like the Action class it has the same construct that I passed newgame into.
It seems like your win-condition check is not within your actionPerformed code, but on the class level, hence it is possibly called before the window is populated with your buttons.
Try placing the check inside the actionPerformed like this: http://pastebin.com/xRViSUzy
What is the scope (most simply, which curly braces) is the problematic line inside of?
It was a bit tricky to tell based on your indentation, but it appears to me that your "if" was not inside a method (e.g. the constructor). I would guess that you intended this line and the ones around them to be executed after the lines in the body of your constructor where the squares are initialized. Instead, these lines are being run beforehand and therefore the call to "new" haven't been run yet.
I think that if you do some restructuring to move these conditions into your constructor or into another method that you call after construction then things will look a lot better.
Hope that helps.
If you're going to implement this type of solution, then simplify the job for yourself. Based on the tiny snippet of code I see above, it looks like you're really over-complicating the job you have to do.
char cell0 = //get that char, be it X or O
char cell1 = //
...
char cell8 = //
Now you can compare cells one by one to determine a victory. Your board game is set up as follows:
0 1 2
3 4 5
6 7 8
So you can just go in order:
Horizontal Solutions:
(cell0 == cell1 && cell0 == cell2)
(cell3 == cell4 && cell3 == cell5)
(cell6 == cell7 && cell6 == cell8)
Vertical Solutions
(cell0 == cell3 && cell0 == cell6)
//And so on
Cross Solutions:
(cell0 == cell4 && cell0 == cell8)
(cell2 == cell4 && cell2 == cell6)
This will check for your victory conditon.
The problem is that you have a surplus of braces in your code so that the statements in the question actually appear in an instance initializer block of the class NoughtsCrosses but none of the JButton components have been initialized yet as instance initializers are invoked before constructors which is where the JButton instantiated exist (but never called). When you attempt to invoke getText on the first element of the array square, a
NullPointerException is thrown.
To fix remove the additional braces to that the code is enclosed in the preceding ActionListener
class Action implements ActionListener {
public void actionPerformed(ActionEvent e) {
// existing code here
/// } remove
//} remove
// { remove
// win conditions. if true, set win==true; else set win
// here is where the compilation error is, next line
if (square[0].getText() == square[1].getText() ...) {
win = true;
} //etc
} <-- add this

Stackoverflow exception in java minesweeper game

I was trying to make a minesweeper game in Java, but I keep running into this error. This function sets the current square to clicked and any adjacent squares to be clicked and continues recursively. It should stop when it runs out of squares, but even when I set the field size to 2x2 with 0 mines, it overflows.
public void setClicked(boolean clicked){
this.clicked = clicked;
if(adjacentMines == 0)
for(mine m : adjacent){
if(!m.isClicked() && !m.isMine()){
setClicked(true); //Should be m.setClicked(true);
}
}
}
Problem solved, I was missing the "m." in my method call. Thanks to everyone for your help.
You need to call setClicked on the adjacent mine, not on original mine, otherwise, you'll get setClicked(true) called over and over again for the source mine
public void setClicked(boolean clicked){
this.clicked = clicked;
if(adjacentMines == 0)
for(mine m : adjacent){
if(!m.isClicked() && !m.isMine()){
m.setClicked(true); // setClicked should be called on the adjacent mine, not on itself!
}
}
}
You are calling setClicked on the same mine instead of the adjacent ones.
Change to:
public void setClicked(boolean clicked){
this.clicked = clicked;
if(adjacentMines == 0)
for(mine m : adjacent){
if(!m.isClicked() && !m.isMine()){
//missing the "m."
m.setClicked(true);
}
}
}
Well, I can only guess that setClicked() method is a member of mine, but shouldn't you be calling m.setClicked(true) inside your condition instead of just setClicked(true)?
Difficult to tell from your code snippet, but I would say you do not iterate over the cells. That means you are recursively checking the same cell over and over (to infinity and beyond woosh). The surrounding code would be very useful to see, though.

Categories