How can I end my Javafx tic-tac-toe? - java

I made tic-tac-toe game using javafx. I'm new in java and I tried to make way to end game after winning or draw. There is no need to check if winning or losing or anything. I want put that text to end when you can't continue playing. I tried use array but I have no idea what to do.
Here is my code:
import java.util.ArrayList;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class ticTacToe extends Application {
private boolean turn;
private int[][] locations;
public RistinollaSovellus() {
turn = true;
locations = new int[3][3];
}
public static void main(String[] args) {
launch(ticTacToe.class);
}
#Override
public void start(Stage window) throws Exception {
boolean endGame = false;
BorderPane layout = new BorderPane();
Label text = new Label("Turn: X");
text.setFont(Font.font("Monospaced", 40));
layout.setTop(text);
GridPane array = new GridPane();
if (!endGame) {
for (int x = 1; x <= 3; x++) {
for (int y = 1; y <= 3; y++) {
Button btn = new Button(" ");
btn.setFont(Font.font("Monospaced", 40));
array.add(nappi, x, y);
locations[x][y] = 0;
btn.setOnAction((event) -> {
if (btn.getText().equals(" ")) {
if (turn) {
btn.setText("X");
text.setText("Turn: O");
turn = false;
} else if (!turn) {
btn.setText("O");
text.setText("Turn: X");
turn = true;
}
}
});
}
}
}
layout.setCenter(array);
Scene showing = new Scene(layout);
window.setScene(showing);
window.show();
}
}

The argument passed to btn.setOnAction() is an event listener and its logic is executed each time a user clicks the button.
https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/ButtonBase.html#setOnAction-javafx.event.EventHandler-
On a button click you want to check if the grid contains a valid tic-tac-toe configuration. To do this, you need to expand the listener and use the location array to check the current state of all buttons on the grid.
Once a valid combination of cells is found, you call Platform.exit() to stop the application.
EDIT: The x and y loop variables should start at 0 and go up to 2, otherwise an ArrayIndexOutOfBoundsException is thrown. The first index of an array is 0, not 1.

I'm not sure what you mean but I think if you implemented the game this far like if its working as it should then it'll be really easy for you to figure out how to end it.
See you can do many things here if any player wins you can just show an alert that player x won and give an option to either quit or restart the application all over, in case of a tie you can do the same.
Technically call the stop() method to stop all the processes and to end the game

Related

Looping letter input

I'm making a hangman game for school and I've run into a problem that I simply can't solve, maybe I'm overthinking, maybe I'm not. Anyways, I need to let the user input a letter, and if that letter is in the word used for the game (pikachu. I know, stupid choice but it's pretty basic and easy so I used that) then the letter is revealed, the problem is that after inputting a letter, the user can't guess any more letters. I need a way to loop through the letter input and revealing so that I can actually play the game.
I'm sorry if the solution is so simple but I just can't figure out what needs to change in my code in order to fix my problem because I'm very new to java.
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.*;
import java.awt.event.KeyListener;
import javax.swing.*;
public class PanDisp extends JPanel {
JLabel lblOutput;
JLabel lblGuess;
JButton btnUpdateLabel;
Image imgPkmn;
FraImg fraImg;
String sSecret;
public PanDisp() {//Constructor
KeyInput keyInput = new KeyInput();
KeyInput.LabelChangeListener labelChange = keyInput.new LabelChangeListener();
sSecret = "*******";
lblGuess = new JLabel("Type will go here");
lblOutput = new JLabel(sSecret);
btnUpdateLabel = new JButton("Enter");
add(lblOutput);
add(btnUpdateLabel);
addKeyListener(new KeyInput());
setFocusable(true);
btnUpdateLabel.addActionListener(labelChange);
fraImg = new FraImg(imgPkmn);
}
public void GameOver() {
}
class KeyInput implements KeyListener {
String sInput;
String sWord = "pikachu";
String sSecret = "*******";
char chInput;
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
chInput = (char) e.getKeyChar();
sInput = String.valueOf(chInput);
lblOutput.setText(sInput);
}
#Override
public void keyReleased(KeyEvent e) {
}
class LabelChangeListener implements ActionListener {
char cWord;
int nCorrect, nIncorrect, nNum;
public void actionPerformed(ActionEvent event) {
if (sWord.contains(sInput)) {
for (int i = 0; i < sWord.length(); i++) {
sSecret.replace(sSecret.charAt(i), sWord.charAt(i));
}
nCorrect += 1;
}
else {
nIncorrect += 1;
if (nIncorrect == 7) {
GameOver();
}
}
}
}
}
}
Your problem is that your mindset is off and has to be changed. Don't think "loop", and in fact get "loop" out of the equation. You're programming in an event-driven programming environment, and the loop you're thinking of belongs in the linear console programming environment. Instead think "state of object" and "behavioral changes to state changes", and you'll move much further in this quest. So change the state of your class -- number of guesses, number of correct guesses, and then change the response to the user's input based on this state
For instance, if you wanted to create a console program that allowed a user to enter 5 Strings, and then displayed those Strings back to the user, it would be pretty straight forward, in that you'd create your String array, and then use a for loop to prompt the user 5 times to enter text, grabbing each entered String within the loop. Here "loops" like the one you're requesting work.
Linear Console Program
import java.util.Scanner;
public class Enter5Numbers1 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("Please enter 5 sentences:");
String[] sentences = new String[5];
for (int i = 0; i < sentences.length; i++) {
System.out.printf("Enter sentence #%d: ", (i + 1));
sentences[i] = scanner.nextLine();
}
System.out.println("You entered the following sentences:");
for (String sentence : sentences) {
System.out.println(sentence);
}
scanner.close();
}
}
If on the other hand you wanted to create a GUI that did something similar, that prompted the user for 5 Strings and accepted those Strings into an array, you couldn't use the same type of for loop. Instead you would need to give your class an int String counter, perhaps called enteredSentenceCount, and in a JButton's ActionListener (or Action -- which is something very similar), you would accept an entered String (perhaps typed into a JTextField called entryField), only if the enteredSentenceCount is less than 5, less than the maximum number of Strings allowed. You would of course increment the enteredSentenceCount variable each time a String is entered. And this combination of increase a counter variable and checking its value will need to substutite for the concept of a "loop". So here the "state" of the class is held in the enteredSentenceCount, and the behavioral change we want is to alter what the button's Action does depending on the enteredSentenceCount's value -- if less than 5, accept a String, and if it is equal to or greater than 5, display the entered Strings.
Event Driven GUI Program
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class Enter5Numbers2 extends JPanel {
private static final int MAX_SENTENCE_COUNT = 5; // number of Strings to enter
private static final String PROMPT_TEMPLATE = "Please enter sentence number %d:";
private String[] sentences = new String[MAX_SENTENCE_COUNT]; // array to hold entered Strings
private int enteredSentenceCount = 0; // count of number of Strings entered
private JTextField entryField = new JTextField(20); // field to accept text input frm user.
// JLabel to display prompts to user:
private JLabel promptLabel = new JLabel(String.format(PROMPT_TEMPLATE, (enteredSentenceCount + 1)));
public Enter5Numbers2() {
// create GUI
// First create Action / ActionListener for button
EntryAction entryAction = new EntryAction("Enter");
JButton entryButton = new JButton(entryAction); // pass it into the button
entryField.setAction(entryAction); // but give it also to JTextField so that the enter key will trigger it
// JPanel to accept user data entry
JPanel entryPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
entryPanel.add(entryField);
entryPanel.add(entryButton);
// allow main JPanel to display prompt
setBorder(BorderFactory.createTitledBorder("Please Enter 5 Sentences"));
setLayout(new GridLayout(2, 1));
add(promptLabel);
add(entryPanel);
}
// Action class, similar to an ActionListener
private class EntryAction extends AbstractAction {
public EntryAction(String name) {
super(name);
putValue(MNEMONIC_KEY, (int) name.charAt(0));
}
#Override
public void actionPerformed(ActionEvent e) {
// check that we haven't entered more than the max number of sentences
if (enteredSentenceCount < MAX_SENTENCE_COUNT) {
// if OK, get the entered text
String sentence = entryField.getText();
// put it in our array
sentences[enteredSentenceCount] = sentence;
entryField.setText(""); // clear the text field
entryField.requestFocusInWindow(); // set the cursor back into the textfield
enteredSentenceCount++; // increment our entered sentence count variable
promptLabel.setText(String.format(PROMPT_TEMPLATE, (enteredSentenceCount + 1))); // change prompt
}
// if the number of sentences added equals the number we want, display it
if (enteredSentenceCount == MAX_SENTENCE_COUNT) {
JTextArea textArea = new JTextArea(6, 30);
for (String sentence : sentences) {
textArea.append(sentence + "\n");
}
JScrollPane scrollPane = new JScrollPane(textArea);
JOptionPane.showMessageDialog(Enter5Numbers2.this, scrollPane, "Five Sentences Entered",
JOptionPane.PLAIN_MESSAGE);
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Enter 5 Numbers");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(new Enter5Numbers2());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}

Why is my program not cycling through the array

I have tried a bunch of arrangements for my code, and this is my last version of code. I am trying to create a window, that will cycle through my 5 questions, and the end it will display how many you got correct.
Currently after entering my first answer in the text field it jumps to the end of the array. After that it will continually stay on the same question.
Sorry if I have made many coding errors, as this is my first time creating a program after watching a bunch of tutorials. Thanks for any help!
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class testwin extends JFrame {
private JTextField input;
private JLabel problem;
private String[] questions = new String[5];
private String[] answers = new String[5];
private String[] response = new String[5];
private int total = 5;
private int result = 0;
private String mark;
public testwin(){
super("Pop Quiz");
setLayout(new FlowLayout());
questions[0] = "Solve for x \t (x + 3)*2-10 = 4";
questions[1] = "Factorize \t x^2 + 10x + 21";
questions[2] = "Find the square root of 64";
questions[3] = "Multiply 23 and 94";
questions[4] = "Add 2145, 1452, 253,1414";
answers[0] = "4";
answers[1] = "(x + 3)(x + 7)";
answers[2] = "8";
answers[3] = "2162";
answers[4] = "5264";
problem = new JLabel(questions[0]);
add(problem);
input = new JTextField("Answer goes here",20);
add(input);
mark = String.format("You got %s correct out of %s", result,total);
input.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent event){
int y = 0;
int x = 0;
if(event.getSource() == input){
while(x < questions.length){
problem.setText(questions[x]);
response[y] = String.format("%s",event.getActionCommand());
input.setText("Answer goes here");
x++;
y++;
}
}
if(x == 4)
JOptionPane.showMessageDialog(null, mark);
for(int z = 0; z < questions.length; z++){
if(response[z] == answers[z])
result++;
}
}
}
);
add(input);
setSize(250,250);
setVisible(true);
}
}
During each actionPerformed call you cycle through the whole list. Here:
while(x < questions.length){
problem.setText(questions[x]);
response[y] = String.format("%s",event.getActionCommand());
input.setText("Answer goes here");
x++;
y++;
}
So by the time the text is actually displayed again, you have set to it to each different question, but stop with the last one and that is what the user actually sees. You only want to change the text once, to the next question, everytime an action is performed.
You'll need to keep some sort of counter for which question you are on that can be accessed by the actionPerformed method
Also, as mentioned in the comments, you will want to change your result checking to use the equals method. Strings can't be compared using the == sign because for Strings == compares the reference each String object is pointing to, not the value of the String object
if(response[z].equals(answers[z]))
result++;
The issue is the while() loop inside your ActionListener, it will always iterate through the entire set of questions and finish at the last entry. This is due to the fact that your x and y variables are local to the scope of the ActionListener, and as such, are always reset to 0 and looped on each input click.
To fix this, you don't need a while loop, just make your x variable a private class field (questionIndex), use an if statement ensure the index is within the array bounds, and update the problem and response values accordingly.
Here's some psuedocode that should do the right thing:
private int questionIndex = 0;
public void actionPerformed(ActionEvent event){
if(event.getSource() == input){
if(questionIndex < questions.length){
problem.setText(questions[questionIndex]);
response[questionIndex] = String.format("%s",event.getActionCommand());
input.setText("Answer goes here");
questionIndex++;
}
...
}
}

JApplet Won't Open With Console Program

Given my code below how can I make the paint applet open up when I run the main code? I thought extends would do the trick but nothing has come up. I can't get the program to execute one body part at a time. I don't have enough time, but at the very least I would like it to show the hangman drawing as soon as the main is executed.
import java.awt.Graphics;
import java.util.Scanner;
import javax.swing.JApplet;
public class HangmanLogic extends HangmanGuy {
public static void main(String[] args) {
int count = 0;
Scanner in = new Scanner(System.in);
System.out.println("Enter a 4 or 5 letter word and the computer will play hangman against you!");
String word = in.nextLine();
char[] letter = word.toCharArray();
for (int i = 0; i < letter.length; i++) {
letter[i] = 'a';
}
for (int i = 0; i < word.length(); i++){
for (int j = 48; j < 122; j++) {
count++;
if (letter[i] == word.charAt(i)) {
break;
} else {
letter[i] = (char)((int) j + 1);
}
}
}
System.out.println("Attempt to solve: " + count);
System.out.println("Your word is: ");
for (char letters : letter) {
System.out.print(letters);
}
}
}
import java.awt.Graphics;
import javax.swing.JApplet;
import java.awt.*;
public class HangmanGuy extends JApplet
{
public void paint (Graphics Page)
{
//gallows
Page.drawLine(0,300,20,300);
Page.drawLine(10,40,10,300);
Page.drawLine(10,40,80,40);
Page.drawLine(80,40,80,55);
//torso
Page.drawOval(50,55,50,55);
Page.drawOval(50,100,50,100);
//left arm and hand
Page.drawLine(50,150,40,110);
Page.drawLine(40,110, 45,100);
Page.drawLine(40,110, 25,100);
Page.drawLine(40,110, 25,115);
//right arm and hand
Page.drawLine(100,150,120,110);
Page.drawLine(120,110, 115,95);
Page.drawLine(120,110, 125,95);
Page.drawLine(120,110, 135,115);
//left leg and foot
Page.drawLine(80,200,100,250);
Page.drawLine(100,250, 115,260);
//right leg and foot
Page.drawLine(75,200,60,250);
Page.drawLine(60,250,45,260);
}
}
Two things.
Why should it. Nothing is controlling it. Applets are suppose to be loaded and controlled by browsers;
Why are you mixing GUI and console paradigms? User input from GUI's is suppose to come from UI components and controls, not the command line.
Start by taking a look at Creating a GUI With JFC/Swing.
I'd recommend moving your UI to a JFrame instead, until you understand the basics of how to build a UI as applets bring there own issues which can stump you if you don't already have some background in how the UI works.
In fact. Start with a JPanel and when you're ready, add it to an instance of JFrame. When that works, you can try adding the panel to a JApplet

GUI wait for keyboard "enter" and continue display

I have a JAVA card game, which show four cards at each round.
Currently, the stop between each round is waiting for a \n input in console. But I'd like to change it to waiting for a keyboard "enter" on the GUI.
Following is my current code. Please let me know how should I change it?
Millions of thanks!!!
import java.awt.GridLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
public class Game {
public static void main(String args[]) throws IOException {
Deck deck = new Deck();
deck.shuffle();
int aGame = 4;
List<Card> cards = new ArrayList<Card> ();
for(int i = 0; i < 52; i++) {
cards.add(deck.deck.get(i));
if(aGame == 1) {
System.out.println(deck.deck.get(i).toString());
System.out.println("Start!!!");
JFrame f = new JFrame("Calculating 24!");
GridLayout grid = new GridLayout(2, 2);
f.setLayout(grid);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
for(Card c: cards){
f.add(new LoadImageApp(c));
}
f.pack();
f.setVisible(true);
cards.clear();
while (true) {
char c = (char) System.in.read();
if (c == '\n') {
f.setVisible(false);
break;
}
}
aGame = 4;
if(i == 51) {
deck.shuffle();
i = -1;
}
}
else if(aGame == 4) {
System.out.println("Calculate based on the following four cards!");
System.out.println(deck.deck.get(i).toString());
aGame --;
}
else {
System.out.println(deck.deck.get(i).toString());
aGame --;
}
}
}
}
If the GUI element accepting the values is a JTextField, it is possible to add an ActionListener that will typically respond when the user hits Enter
Other tips
1)
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
Change that to:
f.setDefaultCloseOperaiton(JFrame.EXIT_ON_CLOSE);
Or better..
f.setDefaultCloseOperaiton(JFrame.DISPOSE_ON_CLOSE);
The first will have the same effect as what was, but the second will also check there are no non-daemon threads running prior to exit.
2)
Do not try to mix GUIs and the command line together. The way you go about writing an application for either is significantly different.
For Swing based applications key events should be handled using Key Bindings. However it may not be apparent to the end user that ENTER is required. A more UI centric approach is to use a dialog for this purpose
JOptionPane.showMessageDialog(frame, "Press ENTER to continue");
Using console based read methods such as InputStream#read is another source of confusion for users. Use a JTextComponent such as a JTextField to read user input in Swing applications.

How to know what component was clicked from a grid?

I have created a grid that contains 10x10 buttons using 2d arrays. i tried x.getSource().getLabel() but compiler says they are not compatible. also i want to get the specific button that was clicked.
I want to get the exact button that was clicked from the grid i made and get its label. what method i need to use?
import javax.swing.JFrame; //imports JFrame library
import javax.swing.JButton; //imports JButton library
import java.awt.GridLayout; //imports GridLayout library
import javax.swing.*;
import java.awt.event.*;
import java.util.*;
public class ButtonGrid extends JFrame implements ActionListener
{
JFrame frame=new JFrame(); //creates frame
JButton[][] grid; //names the grid of buttons
public int x;
public int y;
public ButtonGrid(int width, int length)
{ //constructor
char temp;
String charput;
frame.setLayout(new GridLayout(width,length)); //set layout
grid = new JButton[width][length]; //allocate the size of grid
for(int y=0; y<length; y++)
{ //start
for(int x=0; x<width; x++)
{
temp=charRand(); //get random character
charput = ""+temp; //converts character to string
grid[x][y]=new JButton(); //creates new button
frame.add(grid[x][y]); //adds button to grid
grid[x][y].addActionListener(this);
grid[x][y].setLabel(charput); //set charput as label
}
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack(); //sets appropriate size for frame
frame.setVisible(true); //makes frame visible
}
/* generates randomiz letter for the button of the grid*/
public char charRand()
{
String consonantList = new String("BCDFGHL"); //list 1
String consonantList2 = new String("MNPRSTWY"); //list 2
String consonantList3= new String("JQXZVK"); //list 3
String vowelList = new String("AEIOU"); //list of vowels
int vowelOrConsonant; //holder of random number
int chosen; //selects the chosen random letter
Random randGen = new Random(); //generates random int value
char selected; //gets the random letter chosen by variable chosen
vowelOrConsonant = randGen.nextInt(4);
if (vowelOrConsonant == 0)
{
chosen = randGen.nextInt(5); //list of vowels
selected = vowelList.charAt(chosen); //selects a char from vowels
}
else if(vowelOrConsonant == 1)
{
chosen = randGen.nextInt(7); //list 1
selected = consonantList2.charAt(chosen); //selects a char
}
else if(vowelOrConsonant == 2)
{
chosen = randGen.nextInt(8); //list 2
selected = consonantList2.charAt(chosen); //selects a char
}
else
{
chosen = randGen.nextInt(6); //list 3
selected = consonantList.charAt(chosen);
}
return selected; //returns the random letter
}
public static void main(String[] args)
{
new ButtonGrid(10,10);//makes new ButtonGrid with 2 parameters
}
public void actionPerformed(ActionEvent x)
{
/* i get wrong output on this line.
* i want to get the exact button that was clicked and get its label.
*/
if (x.getSource()==grid[x][y])
JOptionPane.showMessageDialog(null,x.getSource().getLabel);
}
}
getSource() returns an Object, so you need to cast it to JButton, like this:
public void actionPerformed(ActionEvent x) {
JOptionPane.showMessageDialog(null, ((JButton)x.getSource()).getText());
}
Also note that getLabel() and setLabel() are deprecated and should be replaced by getText() and setText().
You can make a class that extends Jbutton. and add two fields to it of type int(X & Y ). The constructor will look like this: public MyButton(int x, y);
And when you are filling your grid, don't use directly the Jbutton class. Use your class and for X & Y supply the i & j parameters of the two for cycles that you are using. Now when you are using Action Listener for your button you can use his X & Y fields as they represent its place on the grid. Hope this helps! It tottaly worked for me and its simple as hell.

Categories

Resources