I'm working on a personal project where I'm trying to create Simon game in Java. It is essentially a memory game where the user has to repeat the sequence the computer generates. So, I created a basic CLI version of the game and now I'm looking to give it a GUI. I haven't worked with Java Swing before so I'm having some trouble replicating the same behavior in GUI form.
The structure of the CLI version looked something like this:
public static void main(String[] args) {
GameContext simon = new GameContext();
simon.start();
while (!simon.getRoundEndState()) {
simon.playSequence();
simon.pickColour();
}
}
Here's what the playSequence() method looks like. It essentially generates a number between 0 and 3 and keeps adding one number to the array each round if the player gets the previous one right.
public void playSequence() {
int rand = getRandomNumber(4);
computerSequence.add(rand);
for(int i:computerSequence) {
System.out.println(i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
gameContext.setState(gameContext.getHumanPlayingState());
}
pickColour() method that asks the user for the pattern and checks if the pattern matches the one generated by the computer.
public void pickColour() {
boolean roundWon = true;
for(int i=0; i<gameContext.getSequence().size();i++){
Scanner input = new Scanner(System.in);
Integer userInput = input.nextInt();
if(userInput == gameContext.getSequence().get(i)) {
continue;
}
else {
System.out.println("Input mismatched the sequence");
roundWon = false;
break;
}
}
if (roundWon == true) {
score++;
gameContext.setState(gameContext.getComputerPlayingState());
} else {
gameContext.setRoundEndState(true);
System.out.println("Your score is: " + score);
gameContext.setState(gameContext.getInLobbyState());
}
}
Please note that I'm using the state pattern here and hence the change in states. I got the first part working where I need to light up the colors after the computer generates the the sequence. So now, playSequence() looks something like this:
public void playSequence() {
int rand = getRandomNumber(4);
computerSequence.add(rand);
gameContext.setState(gameContext.getHumanPlayingState());
}
And I added a mouse listener to the start button which looks something like this:
btnStart.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me) {
simon = new GameContext();
simon.start();
while (!simon.getRoundEndState()) {
simon.playSequence();
for(int i : simon.getSequence()) {
lightPanels(i);
Timer timer = new Timer(1000, e -> {
darkenPanels(i);
});
timer.setRepeats(false);
timer.start();
}
simon.pickColour();
}
}
});
I have 4 JPanels that now act as the input buttons. How do I change simon.pickColour() so that instead the asking the user for the correct sequence like it did in the CLI version, it would register the clicks made in the JPanels as the input.
It essentially generates a number between 0 and 3 and keeps adding one number to the array each round if the player gets the previous one right.
and
I have 4 JPanels that now act as the input buttons.
Why have 4 panels. Just have one panel that contains 4 buttons. You would then add an ActionListener to the button (not a MouseListner) to handle the clicking of the button.
The buttons would be created with code like:
for (int i = 1; i <= 4; i++)
{
JButton button = new JButton("" + i);
button.addActionListener(...);
panel.add( button );
}
The ActionListener code would then get the text of the button and add the text to an ArrayList to track the order the the buttons that have been clicked.
A working example of this approach of sharing an ActionListener for all the buttons can be found here: https://stackoverflow.com/a/33739732/131872. Note the "action command" will default from the text that is set on the button.
Related
I'm trying to write a logic in memory game that when I click on cards and they are not a pair (different ID), program should swap them back after 1s. If they are same, then leave them as they are.
The problem is that when I first click and the card appears, after second clicking on another (different) card it doesn't appear and swap the first card after 1s. someone knows why the second card does not appear after clicking?
Btw when the pair is correct, everything works fine, here is my fragment of the code responsible for that logic in listener:
final int copy = i;
card2.addActionListener((e) -> {
card2.setIcon(new ImageIcon(icons[copy].getAbsolutePath()));
if(firstClick == null)
{
firstClick = (Card)e.getSource();
}
else
{
Card secondClick = (Card)e.getSource();
if(firstClick.getID() != secondClick.getID())
{
try
{
Thread.sleep(1000);
} catch (InterruptedException e1)
{
//e1.printStackTrace();
}
firstClick.setIcon(new ImageIcon(background.getAbsolutePath()));
secondClick.setIcon(new ImageIcon(background.getAbsolutePath()));
firstClick = null;
}
else
firstClick = null;
}
});
While method actionPerformed is executing, the GUI cannot react to mouse and keyboard events, so basically your code is "freezing" your GUI for one second. I believe that the class javax.swing.Timer is what you need and at first glance it looks like the duplicate question that MadProgrammer referred to may help you.
There are 7 buttons in my project. 6 of them are categories and RandomSoru button is the one which randomly chooses one of the categories. I want to access the chosen category. "r" is the random generator.
RandomSoru.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
TriviaLinked tl = new TriviaLinked();
tl.insertAtBack(tl.CogHmap);
tl.insertAtBack(tl.TarihHmap);
tl.insertAtBack(tl.SporHmap);
tl.insertAtBack(tl.BilimHmap);
tl.insertAtBack(tl.FilmHmap);
tl.insertAtBack(tl.SanatHmap);
TriviaNode current = tl.root;
int n = r.nextInt(tl.sizeCounter());
for (int i = 0; i < n; i++) {
current = current.next;
}
if(current.hmap==tl.CogHmap)
JOptionPane.showMessageDialog(null,"Your Category is Cografya");
else if(current.hmap==tl.SporHmap)
JOptionPane.showMessageDialog(null,"Your Category is Spor");
....
Here is the Spor button
Spor.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
......
My expectation was like
else if(current.hmap==tl.SporHmap)
JOptionPane.showMessageDialog(null,"Your Category is Spor");
Spor();
else if(current.hmap.....
One way is to add the 6 buttons to an ArrayList.
Then in the ActionListener of the random button you could do something like:
Use the Collections.shuffle(...) method to randomize the buttons in the List.
Then you get the first button from the List.
Finally you invoke the doClick() method on the button.
Hi I'm new to stackoverflow so bear with me if I make mistakes.
I'm making this Java Simon Says Game for a class project. It works by a random number generator for each sequence#. I show the sequence through doClick() but remove the actionlisteners beforehand and add it afterwards.
The problem is the buttons won't unpress or unarm until all other buttons have been pressed. I've tried using thread.sleep to put a delay between each if...else statements yet it only stays pressed for longer. I've tried updating the gui through repaint(), revalidate(), updateUI() within the try... catch of the thread.sleep but that didn't work either.
I've realized this issue is mainly cosmetic because when I tried implementing setPressed or setArmed it said it wasn't being pressed but it looked pressed.
Here is the code snippet in it's most simplest form without thread.sleep or my previous attempts in comments.
public void sequence2() //This is where the issue happens. The buttons won't unpress until every button has been pressed.
{
level.setText(" Level 2"); //Level indicator
Green.removeActionListener(Listener);
Red.removeActionListener(Listener);
Yellow.removeActionListener(Listener);
Blue.removeActionListener(Listener);
if(sequence1 == 1)
{
Green.doClick(300); //Programmatically clicks the button
}
else if(sequence1 == 2)
{
Red.doClick(300);
}
else if(sequence1 == 3)
{
Yellow.doClick(300);
}
else if(sequence1 == 4)
{
Blue.doClick(300);
}
if(sequence2 == 1)
{
Green.doClick(300);
}
else if(sequence2 == 2)
{
Red.doClick(300);
}
else if(sequence2 == 3)
{
Yellow.doClick(300);
}
else if(sequence2 == 4)
{
Blue.doClick(300);
}
Green.addActionListener(Listener);
Red.addActionListener(Listener);
Yellow.addActionListener(Listener);
Blue.addActionListener(Listener);
}
I'm very new to java so I'm not skilled in multithreading or working on the Event Dispatch Thread for that manner. But if that's the only solution I'll need some more help with that.
I have the full code in a zip file with previous attempts commented out if that will help.
https://drive.google.com/file/d/0Bxg4WleC9jD2VFhoZmZBNjV6Vkk/view?usp=sharing
Invoking doClick() may be an awkward choice for this, as it uses a Timer internally. Instead, use a JToggleButton, which will allow you to control each button's appearance based on its selected state using setSelected(). A complete example is shown in the game Buttons. In the ActionListener of your Swing Timer, select the current button, play its note and increment the sequence index. When all notes have been played, unselect all the buttons.
Addendum: Can you show how you implement the timer?
In outline, given a suitable list of toggle buttons:
private static final int MAX = 4;
List<JToggleButton> buttons = new ArrayList<JToggleButton>(MAX);
private int i;
The timer's listener might look like this:
#Override
public void actionPerformed(ActionEvent e) {
Object src = e.getSource();
JToggleButton b = buttons.get(i);
if (i > MAX) { // reset i and all the buttons
for (JToggleButton b : buttons) {
b.setSelected(false);
}
timer.stop();
i = 0;
} else {
b.setSelected(true);
// play tone i
i++;
}
}
A toggle button's item listener should update the button's appearance as indicated by its state:
#Override
public void itemStateChanged(ItemEvent e) {
JToggleButton b = (JToggleButton) e.getItem();
if (b.isSelected()) {
// change icon, color etc.
} else {
// restore icon, color etc.
}
}
I am writing a Blackjack program using JFrame and trying to keep it as simple as possible. My JButton, jbHit works with a single click, however it overwrites the playersHand and playerSide slot with every click. I would like it to work with multiple clicks (3 clicks - since that is the max number of cards you can get after the first two are dealt) options It should count them so to speak so that the array index can record the card image. Here is my ActionListener code that I have so far. I am afraid I am stuck. Should I use some sort of for loop with an int i++?
//Hit Button ActionListener
jbHit.addActionListener( new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if ( playerValue < 21 ) {
//Draw a card
Card c = deck.drawCard();
playersHand.add(c);
playerSide[2].setIcon( new ImageIcon( c.getFilename() ) );
}
//If playerValue > 21, bust
else if ( playerValue > 21 ) {
//Toggle Buttons
jbDeal.setEnabled(true);
jbHit.setEnabled(false);
jbStand.setEnabled(false);
jbDoubleDown.setEnabled(false);
message = "You bust.";
}
}
});
You could create an array of "action commands" and every time you click the button, the action command changes to the next. If you reach the end, set the index back to zero. Perhaps something like this:
public static void main(String[] args)
{
JFrame frame = new JFrame();
JPanel panel = new JPanel();
JButton button = new JButton("Action");
String[] commands = {"command1", "command2", "command3"};
button.setActionCommand(commands[0]);
button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
JButton btn = (JButton)e.getSource();
String cmd = btn.getActionCommand();
System.out.println("Command: " + cmd);
if(cmd.equals("command1"))
{
btn.setActionCommand(commands[1]);
System.out.println("Command 1 was pressed");
}
else if(cmd.equals("command2"))
{
btn.setActionCommand(commands[2]);
System.out.println("Command 2 was pressed");
}
else if(cmd.equals("command3"))
{
btn.setActionCommand(commands[0]);
System.out.println("Command 3 was pressed");
}
else
System.out.println("Something went wrong!");
}
});
panel.add(button);
frame.add(panel);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
If you are using Java 7 or later, you can replace the if/else with a Switch statement.
I believe you're looking for a logical flag based on an integer, for example:
if(cardsDealt < 3) {
// DoThings();
} else {
return;
}
Which would require you to do
cardsDealt++;
at the bottom of your button click handle.
If this is not what you're asking, please re-explain the question.
It sounds like you might be a little confused about how jbHit will be called. Realize that it will be called one complete time every time the mouse is clicked. It's not like it inherently knows that it is on the second or third click. Add a class member like int clickCount; that you increment at the appropriate point inside of jbHit. Then you can alter your method's response depending on the value of clickCount.
I am working on doing a word finder puzzle game. When I am trying to get to happen is a user clicks on a letter then moves his mouse across other letters to create a word. I am having some problems with the listeners. I have been going back and fourth using mouseDragged and mouseMoved. So far mouseMoved seems to work better because it dynamically grabs values. The problem is I can't figure out how to get it only grab one value. In an ideal world it would move of a Button or label grab that value once and ignore the value till it reaches a new button or label. Currently it just grabs values at every instant a mouse is on that container. The logic for my Mouse method is below:
public void mouseMoved(MouseEvent e) {
int count = countClicked;
int num = 0;
for(JToggleButton row : puzzleGrid){
if(e.getComponent() == row && count == 1) {
if(num == 0){
num++;
for(JLabel l: solWords)
{
sb.append(row.getText());
System.out.println(l.getText()+" = "+ sb.toString());
if(l.getText().contentEquals(row.getText()))
System.out.println(row.getText());
}
}
}
}
}
I am using the value gathered from the containers to check against an array of JLabels containing the solution values.
You could store the last letter in a static variable:
static String lastLetter = null;
mouseMoved(...) {
if(row.getText().equals(lastLetter)) {
continue;
}
lastLetter = row.getText();
}