I'm not certain if the title is the right way to word what I'm asking, sorry if it's not, but what I'm trying to do is create a memory match game using GUI. I have an array, and I've got the button printing an element from the array at random, but, the issue is, that I can have the same element printing multiple times. Is there a way to remove that element from being selected once it's used?
If there isn't a way to do that, any ideas on how I could go about getting it to use each element only once?
This is my current code:
package MemoryMatching;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MemoryGUI extends JFrame implements MemoryMatch, ActionListener{
JPanel mainPanel, boardPanel;
JButton [][] gridButtons = new JButton[3][4];
char cardArray[] = new char[12];
int numInPlay;
public MemoryGUI(){
cardArray[0] = 'A';
cardArray[1] = 'A';
cardArray[2] = 'B';
cardArray[3] = 'B';
cardArray[4] = 'C';
cardArray[5] = 'C';
cardArray[6] = 'D';
cardArray[7] = 'D';
cardArray[8] = 'E';
cardArray[9] = 'E';
cardArray[10] = 'F';
cardArray[11] = 'F';
mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
boardPanel = new JPanel();
boardPanel.setLayout(new GridLayout(4,3));
setBoard();
mainPanel.add(boardPanel, BorderLayout.CENTER);
add(mainPanel);
}
#Override
public void actionPerformed(ActionEvent e) {
JButton btnClicked = (JButton) e.getSource();
btnClicked.setEnabled(false);
char randomChar = cardArray[(int)new Random().nextInt(cardArray.length)];
btnClicked.setText(""+ randomChar);
faceUp();
}
#Override
public void setBoard() {
for(int x = 0; x < cardArray.length; x++) {
}
for(int row=0; row<gridButtons.length; row++){
for(int col=0; col<gridButtons[row].length;col++){
gridButtons[row][col] = new JButton();
gridButtons[row][col].addActionListener(this);
gridButtons[row][col].setText("No peeking");
boardPanel.add(gridButtons[row][col] );
faceDown();
}
}
}
#Override
public void isWinner() {
// TODO Auto-generated method stub
}
#Override
public void isMatch() {
}
#Override
public void faceUp() {
for(int x = 0; x < cardArray.length; x++) {
for(int y = 0; y < cardArray[x]; y++) {
}
}
}
#Override
public void faceDown() {
}
}
what I'm currently getting is something like
A B A
A F B
F D D
E F C
rather than:
B A C
D E F
A B C
F E D
The first example having three A and one C, rather than two of each as in the second example.
If possible I'd like to not be given the code outright, but a push towards the right direction.
A simple solution: don't use an array.
Instead, use a List<Integer> and shuffle that list. Then simply use the last element of the list for your purpose - and then remove the last element from the list; and shuffle again.
Of course, you could also write a bit of code to keep track of all indexes that you already used, and keep asking for random indexes until you get one that wasn't used before.
For your code, I think the following code would do the trick.
array = ArrayUtils.removeElement(array, element)
If you really want to use an array, look into Fisher–Yates shuffle for random shuffling of an array.
However, my recommendation would be to not use an array. Use a list as the other answer suggested.
Related
http://imgur.com/a/V7LPP
Grid images are in the link
I have 2 main issues with my current connect4 code, sometimes the "AI" places 2 discs in one turn and the player can't fill the entire board without the "AI" entering an infinite loop. Any help is appreciated.
/**
* Auto Generated Java Class.
*/
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class EmptyFrame1 implements ActionListener
{
//Array of JButtons
static JButton [] mnuBtn = new JButton[2];
static JButton [] btnArray = new JButton[7];
static JLabel [][] board = new JLabel[6][7];
static int [][] numBoard = new int[6][7];
static int choice = 0;
static int playerColour = 1;
static int computerColour = 2;
static int computerTurn = 0;
static ImageIcon emptyGrid = new ImageIcon("EmptyGrid.png");
static ImageIcon yellowGrid = new ImageIcon("YellowGrid.png");
static ImageIcon redGrid = new ImageIcon("RedGrid.png");
static JPanel pnlBoard;
static boolean validTurn;
static int pieceCount = 42;
public EmptyFrame1()
{
JFrame game = new JFrame("Connect 4");
//mainFrame panel to hold all components
JPanel mainFrame = new JPanel();
pnlBoard = new JPanel();
pnlBoard.setLayout(new GridLayout(7,6));
//Change mainFrame layout to vertical BoxLayout
mainFrame.setLayout(new BoxLayout(mainFrame, BoxLayout.Y_AXIS));
//For every single button
//Set the button text to its index value, add an action listener and add it to the pnlButtons
for (int i = 0; i < btnArray.length; i++)
{
btnArray[i] = new JButton("place");
btnArray[i].addActionListener(this);
pnlBoard.add(btnArray[i]);
}//end for
for (int r = 0; r < board.length; r++)
{
for (int c = 0; c < board[r].length; c++)
{
board[r][c] = new JLabel(emptyGrid);
board[r][c].setPreferredSize(new Dimension(69, 69));
pnlBoard.add(board[r][c]);
}//end for
}//end for
//Add all the panels to the mainFrame panel
mainFrame.add(pnlBoard);
//add mainFrame to the JFrame
game.add(mainFrame);
game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Added this for security
game.pack();
game.setVisible(true);
game.setResizable(false);
}
//*Action listener for all buttons*
public void actionPerformed(ActionEvent e)
{
for (int i = 0; i < btnArray.length; i++)
{
//if the current button was triggered, set the choice to the button index
if (btnArray[i] == e.getSource())
{
choice = i;
colCheck(choice, 1);
//System.out.println("User turn used");
pieceCount--;
for (int j = 0; j < btnArray.length; j++)
{
if (numBoard[0][j] == 0) btnArray[j].setEnabled(true);
}
if (pieceCount > 0)
{
validTurn = false;
while(!validTurn)
{
computerTurn = (int) (Math.random() * 7);
System.out.print(computerTurn + " ");
validTurn = colCheck(computerTurn, 2);
//System.out.println("CPU tried to move");
}
}
System.out.println();
setGrid();
pieceCount--;
System.out.println(pieceCount);
validTurn = false;
}//end if
}//end for
pnlBoard.repaint();
}//end actionPerformed
public static boolean colCheck(int choice, int currentTurn)
{
int row = -1;
for (int r = 5; r >= 0; r--)
{
if (numBoard[r][choice] == 0)
{
row = r;
break;
}
}
//System.out.println("Row That CPU Chooses: " + row);
if (row > -1)
{
numBoard[row][choice] = currentTurn;
if (row == 0)
{
btnArray[choice].setEnabled(false);
return false;
}
return true;
}
return false;
}
public static void setGrid()
{
for (int r = 0; r < numBoard.length; r++)
{
for (int c = 0; c < numBoard[r].length; c++)
{
if (numBoard [r][c] == 0)
{
board[r][c].setIcon(emptyGrid);
}
else if (numBoard [r][c] == 1)
{
board[r][c].setIcon(redGrid);
}
else if (numBoard [r][c] == 2)
{
board[r][c].setIcon(yellowGrid);
}
}
}
}
/**
* Inner helper class that defines the graphics
*/
public static void main(String[] args)
{
new EmptyFrame1();
}
//end constructor
Some problems and suggestions:
Your colCheck method and the while (!validTurn) { loop in the actionPerformed method is where the problem is located.
This method should not set numBoard value or disable buttons. In other words, it should not have any "side effects".
Instead it should only check for validity and then return a boolean value, nothing more or less
You should have another method that is called first, one that checks if no valid columns are available, if the game is effectively over. This should be called before the while loop above and should prevent the while loop from entering. This will end your endless loop problem because it's looping because no valid columns can be found for the computer turn.
You should have another method for setting the numBoard state and for enabling and disabling JButtons.
Change your colCheck to this to see where the loop is coming from:
public static boolean colCheck(int choice, int currentTurn) {
int row = -1;
for (int r = 5; r >= 0; r--) {
if (numBoard[r][choice] == 0) {
row = r;
break;
}
}
// System.out.println("Row That CPU Chooses: " + row);
if (row > -1) {
numBoard[row][choice] = currentTurn;
if (row == 0) {
btnArray[choice].setEnabled(false);
System.out.printf("debug 1 row %d %n", row);
return false;
}
System.out.printf("debug 2 row %d %n", row);
return true;
}
System.out.printf("debug 3 row %d %n", row);
return false;
}
Other issues not directly related to your problem at hand, but which should be addressed
You're grossly over-using the static modifier, and in fact none of your fields should be static. All should be private instance fields.
You've got your program logic code, the model, mixed in the same class as the GUI, the view, making it hard to debug problems and enhance the program. Much better if you could make your model a completely separate class, one that is "view-agnostic" meaning that it is testable on its own and can work with any view, be it a command line or Swing or Android UI.
Your question depends on images, EmptyGrid.png, YellowGrid.png.... that we have no access to, preventing us from testing it.
You're using magic numbers, such as 0, 1, 2 for position values, and need to avoid doing this.
Instead use an enum for empty, user and computer
This is Java not Javascript. You'll want to be clear on the difference because they're two completely different languages.
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++;
}
...
}
}
I'm building a board game for a cs course, I've started with the engine - its backbone - and I've perfected it, I'm still stuck on the GUI part though. In the engine, I have a double array that initialises the board with the pieces, and it has a getWinner and move methods. In the GUI part, I created my JFrame and set its layout to gridlayout, and I've another double array there that checks the engine's array and prints the board with the board pieces accordingly.. however, whenever I move, the engine's array registers the move but it doesn't get displayed on my JFrame.. I've no idea on how to do it..
Here's my Main class in the GUI package:
package eg.edu.guc.loa.gui;
import java.awt.*;
import java.util.ArrayList;
import javax.swing.*;
import eg.edu.guc.loa.engine.*;
import eg.edu.guc.loa.engine.Point;
#SuppressWarnings("serial")
public class LOA extends JFrame{
static Tiles[][] Jboard;
static Board b = new Board();
static Color temp;
static Color col1 = Color.DARK_GRAY;
static Color col2 = Color.LIGHT_GRAY;
static JFrame LOA = new JFrame();
JButton b1, b2;
public LOA(){
Jboard = new Tiles[8][8];
LOA.setLayout(new GridLayout(8, 8));
initBoard();
LOA.setSize(600, 600);
LOA.setVisible(true);
LOA.setLocationRelativeTo(null);
LOA.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
b.printBoard();
}
public static void initBoard(){
remove();
b.printBoard();
for(int i = 0; i<8; i++){
if (i%2 == 0){
temp = col1;
}
else{
temp = col2;
}
for(int j = 0; j<8; j++){
Jboard[i][j] = new Tiles(temp, i, j);
LOA.getContentPane().add(Jboard[i][j]);
if (temp.equals(col1)){
temp = col2;
}
else{
temp = col1;
}
if(b.getPiece(new Point(j, i)).getPlayer() == BoardCell.PLAYER_1_PIECE){
Jboard[i][j].hasChecker(true);
Jboard[i][j].setWhite(true);
Jboard[i][j] = new Tiles(temp, i, j);
}
if(b.getPiece(new Point(j, i)).getPlayer() == BoardCell.PLAYER_2_PIECE){
Jboard[i][j].hasChecker(true);
Jboard[i][j].setWhite(false);
Jboard[i][j] = new Tiles(temp, i, j);
}
}
}
}
public static void remove(){
for(int i = 0; i<8;i++){
for(int j = 0; j<8; j++){
if(Jboard[i][j] != null)
LOA.remove(Jboard[i][j]);
}
}
}
public static void main (String [] args){
new LOA();
}
}
Any help will be much appreciated.. Thanks in advance.
EDIT:
b.print() prints the Engine's array on the console, and remove() is just my attempt on removing the old frame and building a new one based on the new updated array (with moved piece), which obviously failed.
As shown in this much simpler MVCGame, you can arrange for your view to register as a listener to your model using the observer pattern. User gestures should update the model; when notified by the model, the view should simply render the current state of the model. There should be no drawing in the model and no game logic in the view.
I have a swing application which I have declared a JButton array inside it's constructor inside that class I have created a for loop in order to add a number of 114 JButton to class container.
but when that class runs it gives the exception
java.lang.ArrayIndexOutOfBoundsException: 0
On the statement that adding the Buttons to Container.
Can someone see the problem?
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Main extends JFrame implements ActionListener
{
public Main()
{
Container pane = getContentPane();
JPanel panel = new JPanel();
JButton b[];
int i;
for (i = 0; i < 114; i++)
{
b = new JButton[i];
panel.add(b[i]);
}
pane.add(panel);
}
public void actionPerformed(ActionEvent ae)
{
}
public static void main(String[] args)
{
Main m = new Main();
m.setSize(500, 500);
m.setVisible(true);
}
}
You can't made expression like that
for(i=0; i<114;i++)
{
b = new JButton[i];
panel.add(b[i]);
}
In first execution it is new JButton[0], so your array size is 0.
You should use Collection (fe. ArrayList) or fixed size JButton array.
JButton[] b = new JButton[114];
for(i=0; i<114;i++)
{
b[i] = new JButton();
panel.add(b[i]);
}
At i = 0, b = new JButton[i]; creates an array of size 0, so trying to reference b[0] (i.e. the first element) will be out of bounds.
And you never construct b[i].
You probably want to move the array construction outside the loop, something like:
b = new JButton[114];
for (i = 0; i < 114; i++)
{
b[i] = new JButton();
panel.add(b[i]);
}
since b is an array type which holding collection of JButton objects.So you need to create
one JButton object for each location of that array. The code what Dukeling is given is
correct approach. And also one thing you have forgotten,You need to define the size of
your array like JButton b[]=new JButton[size];
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.