TicTacToe win conditions problems - java

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

Related

Method is called more than once from one ActionEvent

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.

How to iterate through all components of a panel except one

I have a question about java / swing.
I have build an input form with some JTextFields and JComboBoxes.
And I have two Buttons. Save and Abort.
If you click Save, I check all fields for inputs. So if you miss one field an error appears.
Which works fine.
On Abort I want two cases. First case is, if something in the field is changed a JOptionPane appears with the question "Do you really want to close? Data loss blabla".
But if no inputs are made, it should just close without this message.
To check if something is changed I have this:
private boolean isFormularChanged() {
boolean isChanged = false;
for (Component c : WeinDatenPanel.getComponents()){
if (!isChanged && c instanceof JTextField) {
isChanged = !((JTextField) c).getText().isEmpty();
} else if (!isChanged && c instanceof JComboBox) {
isChanged = isComboBoxChanged;
}
}
return isChanged;
}
The problem is, that my first JTextField has a MaskFormatter and Placeholders. So the .getText().isEmpty() doesnt work for this field. And isChanged is always true because of this.
Can I somehow iterate trough all the fields without my MaskFormatter field?
An ugly solution would be to do this by hand and check all my fields by hand execpt my first one. But maybe theres another, cleaner solution?

Printing individual parts of an array in a Java Gui app

OK, so I created a console app that, among other things, takes an array of numbers and prints them out one by one, line by line. Now, I have to take the class that I created for that console app, and pop it into a separate GUI app we're creating. I have all of the other methods working fine, but for the life of me I cannot get the array method to print out correctly. It just gives me the last number I typed into the text field. I'm hoping someone can give me a nudge to help me figure this part out so I can move along, and get to the whole SpringLayout stuff, (the main part of the new assignment) I am limited in what I can show you here because this is a current assignment, so I will have to stick to this stuff as specifically as I can. And please, don't just post the code as an answer, (because then I can't use it), thanks.
Here's what I had for my original project for the array method:
int [] getArray(int x)
{
breakUpNum(x);
return numAry;
}
From there, inside my new class I have this, in an attempt to get it to print:
private class ButtonTest implements ActionListener
{
public void actionPerformed(ActionEvent ae)
{
Lab1 tester = new Lab1();
int[] test4 = tester.getArray(num);
for(int i = 0; i < test4.length; i ++)
{
crossTest.getArrCross.setText("" + test4[i]);
}
}
}
Any help pointing me in the right direction would be greatly appreciated, thanks!
setText does just that, sets the text you pass to as the current text content, it does not append it.
If you were to use JTextArea, you could use it's append method...however, for a JTextField you need to have a different approach.
Now you could use getArrCross.setText(getArrCross.getText() + test4[i])...but to quite frank, that's rather inefficient, as each call to setText is going to stage a paint event...
StringBuilder sb = new StringBuilder(128);
for(int i = 0; i < test4.length; i ++)
{
sb.append(test4[i]);
}
crossTest.getArrCross.setText(sb.toString());
Now, if you want to separate each element, you need to add
if (sb.length() > 0) {
sb.append(", ");
}
Before sb.append(test4[i]);
The last bit of actionPerformed in the for loop isn't working right. setText replaces the current text with its argument, and it doesn't seem like you want to do that. To fix it, replace the line in the for loop with this:
crossTest.getArrCross.setText(crossTest.getArrCross.getText() + test4[i]);

How to get the blank jtextfield?

I am building a calculator (gas law calculator) which has four text fields, and I need three fields to be filled by numbers to calculate the fourth value. The equation is v1/p1 = v2/p2. But the problem is I don't know which three values the user will fill. So I need to find an algorithm to check each text field and determine which is empty. I am using swing classes. Jut give me direction.
Thank you!
DocumentListener
to try to avoiding KeyListener
possible way could be FocusListener also, notice Focus is asynchronous
Well, what do you expect the value of the "empty" field to be?
Of course it will be the empty string, "".
So just test for which fields contents equal the empty string (or have a length of 0).
How about this?
private boolean validateField(JComponent component)
{
if (component.getText().trim().length() == 0){
return false;
}
else{
return true;
}
}
If you want to check if JTextField is empty you just do it as follows:
Example(sudo code):
-> JTextField field = new JTextField("v1:");
-> if(field.getText().isEmpty == true){...}
else{...}

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