How can I randomly select a java button, I am trying to create a tic-tac-toe game where the user plays the cpu or another player. I have it working fine for 2 players but am stuck on 1 player game, I dont know if it can be done but my idea is that I just randomly select a button for the cpu check to see if its been selected previously and then assign the appropiate x or o to the selected square .
public void buttonSelected(ActionEvent click) {
Object source = click.getSource();
// loop through to see which button has been selected
if(onePlayer){
// User Vs CPU
/*if((turn % 2 == 0)){// CPU Turn
int selected;
do {
selected = new Random().nextInt(btnEmpty.length );
if (chosen[selected -1] == false){
chosen[selected -1] = true;
}
}while (chosen[selected -1] == true);
source = Integer.valueOf(selected);
for(int i=1; i<= btnNotSelected.length; i++) {
if(source == btnNotSelected[i] && turn < 10) {
btnClicked = true; // user has selected a button
// Check which user selected button and insert appropriate x or y
btnNotSelected[i].setText("X");
btnNotSelected[i].setEnabled(false); // disable selected button
pnlPlayingField.requestFocus(); // highlight selected panel
}
}
}
else{ //User Turn
for(int i=1; i<=9; i++) {
if(source == btnNotSelected[i] && turn < 10) {
btnClicked = true; // user has selected a button
// Check which user selected button and insert O
btnNotSelected[i].setText("O");
btnNotSelected[i].setEnabled(false);
chosen[i] = true;// disable selected button
pnlPlayingField.requestFocus(); // highlight selected panel
}
}
} */
turn++;
}
else if(twoPlayer){
for(int i=1; i<=9; i++) {
if(source == btnNotSelected[i] && turn < 10) {
btnClicked = true; // user has selected a button
// Check which user selected button and insert appropriate x or y
if(turn % 2 == 0){
btnNotSelected[i].setText("X");
}
else{
btnNotSelected[i].setText("O");
}
btnNotSelected[i].setEnabled(false); // disable selected button
pnlPlayingField.requestFocus(); // highlight selected panel
turn++;
}`
A one player tic-tac-toe game can certainly be done, and your strategy of selecting at random is fine. The first specific error in your commented out one player code is an infinite do-while loop. The condition on the loop always evaluates to true since chosen[selected - 1] is always true (if it is false, you set it to true right before the condition check), and therefore loops again.
Your do-while should look like this:
do {
selected = new Random().nextInt(btnEmpty.length);
} while (chosen[selected - 1] == true);
chosen[selected - 1] = true;
That way, you are setting the chosen flag after the while loop condition.
A couple of additional issues I see with the onePlayer block:
in the CPU turn block, the comparison between source (here an Integer) and btnNotSelected[i] (assuming a java button based on the working code in the twoPlayer block) will not work as you expect it to
this method is called in response to a user input, which is clicking one of the buttons. The computer will not provide any such input, so you should have another trigger that calls running the code for the computer's turn. The easiest one is just to run it after the user's turn
Without making any drastic changes to your overall coding style and strategy, I'll attempt to translate the onePlayer portion into something more functional:
public void buttonSelected(ActionEvent click) {
Object source = click.getSource();
if (onePlayer) {
// User's turn first
source.setText("O");
source.setEnabled(false);
pnlPlayingField.requestFocus();
// Mark that button as chosen
for (int i = 0; i < btnNotSelected.length; i++) {
if (source == btnNotSelected[i]) {
chosen[i] = true;
break;
}
}
// Increment turn counter
turn++;
// Check if game is over
if (turn > 9) return;
// CPU turn
int selected;
do {
selected = new Random().nextInt(btnNotSelected.length);
} while (chosen[selected]);
chosen[selected] = true;
btnNotSelected[selected].setText("X");
btnNotSelected[selected].setEnabled(false);
pnlPlayingField.requestFocus();
turn++;
} else if (twoPlayer) {
/* your preexisting twoPlayer code */
}
}
int selected;
do {
selected = new Random().nextInt(btnEmpty.length );
if (chosen[selected -1] == false){
chosen[selected -1] = true;
}
}while (chosen[selected -1] == true);
code above is an endless loop, change it to:
int selected;
do {
selected = new Random().nextInt(btnEmpty.length);
}while (chosen[selected] == true);
chosen[selected] == true;
remove -1, because nextInt(n) will give you a number "between 0 (inclusive) and n (exclusive)"
I, personally, would start out with a List of the buttons, removing each on as it become selected, this would make easier to determine what has being selected and what hasn't, but lets work with what we've got...
List<Integer> free = new ArrayList<Integer>(chosen.length);
for (int index = 0; index < chosen.length; index++) {
if (!chosen[index]) {
free.add(index);
}
}
if (!free.isEmpty()) {
Collections.shuffle(free);
int selected = free.get(0);
// Continue with game logic
}
Basically, this places an Integer in a List which represents the "free" slots. We then use Collections.shuffle to randomise the list, then we grab the first element (for want of something to grab) and continue with the game logic...
This eliminates the possibility of a infinite loop trying to find free slots that don't exist...
Related
I am coding a game in Java, and must create a method inside an enum type that returns whether the player may fill a bottle with liquid or not. The method checks whether the player's inventory contains a bottle, and whether the player is near a liquid.
In the method declaration, I am getting an error that says I need to put an "AnnotationName" after it. I do not know what an "AnnotationName" is, and on all the other methods I have created in the file there are no such errors.
There is also another error at the bottom of the enum on the closing bracket. It says "Please insert '}' to complete ClassBody". I have checked all the curly brackets in the file and they all have matches. I think this error must be tied to the above "AnnotationName" error. I have included the method below, as well as the enum type the method is a part of.
The method by itself:
//the problematic method
#Override
public boolean isAvailable() {
//create var
boolean bottleInInventory = false;
//loop thru junk sublist
for (int i = 0; i < Data.player.sizeOfSubList(0); i++) {
//if current item is a Bottle, bottleInInventory = true; otherwise do nothing
if (Data.player.getItem(ItemType.JUNK, i).getClass() == Bottle.class) {
//set var to true
bottleInInventory = true;
}
//repeat loop until a Bottle is found (or not found)
}
//check if player is near a liquid AND if they have a Bottle in their inventory
//if they satisfy both conditions, return true
try {
if (Data.nearLiquid.get(0) != null && bottleInInventory) { //if index 0 has nothing in it, there must'nt be anything in the other indexes either.
return true;
}
} catch (IndexOutOfBoundsException e) { //if the player is not near any liquids this will happen during the above if statement.
return false;
}
return false; //if player is near a liquid and not in possesion of a Bottle
}
The whole enum:
FILL_BOTTLE("Fill Bottle") {
//this method is not complete
#Override
public void opCode() {
boolean multipleBottles; //if there is more than one bottle or not
ArrayList<Bottle> bottlesInInventory = new ArrayList<Bottle>(); //output of loop
Item currentItem; //used only inside loop
for (int i = 0; i < Data.player.sizeOfSubList(0); i++) { //loop counts how many bottles you have
currentItem = Data.player.getItem(ItemType.JUNK, i);
if (currentItem.getClass() == Bottle.class) {
bottlesInInventory.add((Bottle) currentItem); /* currentItem will always be assigned to a Bottle here,
* but we still need to cast because the
} * variable type is an Item. */
}
//multipleBottles is set to true if the bottlesInInventory list has more than one item in it
if (bottlesInInventory.size() > 1) {
multipleBottles = true;
}
System.out.println("You are currently able to fill your bottle with " + Data.nearLiquid.size() + " liquids near you");
System.out.println();
System.out.println("Here they are:");
for (int i = 0; i < Data.nearLiquid.size(); i++) {
System.out.println((i + 1) + ") " + Data.nearLiquid.get(i).getName());
}
int liquid = Data.intPrompt("Which liquid would you like to fill your bottle with? ");
}
//the problematic method
#Override
public boolean isAvailable() {
//create var
boolean bottleInInventory = false;
//loop thru junk sublist
for (int i = 0; i < Data.player.sizeOfSubList(0); i++) {
//if current item is a Bottle, bottleInInventory = true; otherwise do nothing
if (Data.player.getItem(ItemType.JUNK, i).getClass() == Bottle.class) {
//set var to true
bottleInInventory = true;
}
//repeat loop until a Bottle is found (or not found)
}
//check if player is near a liquid AND if they have a Bottle in their inventory
//if they satisfy both conditions, return true
try {
if (Data.nearLiquid.get(0) != null && bottleInInventory) { //if index 0 has nothing in it, there must'nt be anything in the other indexes either.
return true;
}
} catch (IndexOutOfBoundsException e) { //if the player is not near any liquids this will happen during the above if statement.
return false;
}
return false; //if player is near a liquid and not in possesion of a Bottle
}
},
I've tried adding curly brackets, annotations (#Override), and researched on AnnotationNames but not had any luck. I can't find any documentation on what an AnnotationName is, and the curly brackets and extra annotations just created more errors.
Edit:
Here is a link to the entire file as well as a link to the github repo with my code.
Option.java
Jump The Volcano
After much time spent trying to get my actionlistener to do what I want, Ive come to a few issues debugging that Im just not comprehending.
After finding a correct pair in the game, the first correct pair works properly, disables the two selected buttons, and moves on. After this the comparisons stop working. I can't seem to find out why. After setting up System.out.print checks to see if the programming was progressing through the clicks again it was, but its not allowing another comparison to happen or button reset from the else statement.
Below I've listed my main actionlistener and my pause action listener. Thank you, and if you need more data id be happy to update.
ActionListener timerPause = new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < jToggleLength; i++) {
jToggleButtons[i].setEnabled(true);
tempIconThree = ((ImageIcon)
jToggleButtons[i].getSelectedIcon()).getDescription();
//flip the selected buttons back over
if(jToggleButtons[i].isSelected()){
jToggleButtons[i].doClick();
}
}
}
};
/**
* sets the pause timer period to 1.5 seconds
* sets it to the actionListener Timerpause
*/
pause = new Timer(1500, timerPause);
public void actionPerformed(ActionEvent ae) {
//starts timer
//increments click on each action
pause.stop();
timer.start();
click++;
//if jtogglelength = 0 the user has completed the game and the timer stops
if (jToggleLength == 0) {
timer.stop();
click = 0;
}
//on first click the jtoggle button source is set to temp and the temp icon gets description
if (click == 1) {
temp = (JToggleButton) ae.getSource();
tempIcon = ((ImageIcon) temp.getSelectedIcon()).getDescription();
}
// on click two the jtoggle button source is set to tempTwo and the tempTwo icon gets description
if (click == 2) {
tempTwo = (JToggleButton) ae.getSource();
tempIconTwo = ((ImageIcon) tempTwo.getSelectedIcon()).getDescription();
//if the button is the same button set click to zero and do nothing else
if (temp == tempTwo) {
click = 0;
//if the temp icon descriptions are equivalent move forward
} else if (tempIcon.equals(tempIconTwo)) {
click = 0;
//this for loop sets tempIconThree in every loop to compare to my first two icon descriptions
//if they match, disable that button and set the button to null
//if the button is not null add it to an arraylist
for (int j = 0; j < jToggleLength; j++) {
tempIconThree = ((ImageIcon) jToggleButtons[j].getSelectedIcon()).getDescription();
if (tempIcon.equals(tempIconThree) || tempTwo.equals(tempIconThree)) {
jToggleButtons[j].setEnabled(false);
jToggleButtons[j] = null;
if (jToggleButtons[j] != null) {
arrayEdit.add(jToggleButtons[j]);
}
}
}
//reduce the length of the array by 2
//make a new version of the main jtogglebutton array with reduced length
//add the non null elements from the arrayList to the array
jToggleLength = -2;
for (int k = 0; k < jToggleLength; k++) {
jToggleButtons = new JToggleButton[jToggleLength];
jToggleButtons[k] = arrayEdit.get(k);
}
click = 0;
//if the icon descriptions did not match
//disable all buttons, pause for 1.5 seconds, flip the
selected buttons back up
} else {
for (int i = 0; i < jToggleLength; i++) {
jToggleButtons[i].setEnabled(false);
}
pause.start();
click = 0;
}
}
I have a list of colors with numbers associated with them. I'd like to make it so that, if the user types in "2", it selects "2: Green"; if they type in "21", it selects "21: Yellow"; etc. I'm trying to use KeyPressed, and I think I need some way for the program to listen for the first number pressed and then wait a second to see if another is pressed. For example, something like this:
// The integer pressed will always be zero or positive.
private void jComboBox1KeyPressed(KeyEvent evt) {
int code = -1;
for(int i = 0; i < 10; i++) {
if (evt.getKeyCode() == i) {
if (code == -1) {
code = i;
} else {
code += i;
}
break;
}
}
// PSEUDO-CODE: if (KeyEvent newEvt is pressed within 1 sec) {
jComboBox1KeyPressed(newEvt);
}
}
Then, I suppose I'd have a key-value map with an index and the color numbers, and I'd set the selected item for jComboBox1 based on code (the selected color number).
I've got it working for a single keyPressed:
private void jComboBox1KeyPressed(java.awt.event.KeyEvent evt) {
if (evt.getKeyCode() == KeyEvent.VK_0) {
jComboBox1.setSelectedItem("2: Green");
}
else if (evt.getKeyCode() == KeyEvent.VK_1) {
jComboBox1.setSelectedItem("21: Yellow");
}
else if (evt.getKeyCode() == KeyEvent.VK_2) {
jComboBox1.setSelectedItem("13: Blue");
}
else if (evt.getKeyCode() == KeyEvent.VK_3) {
jComboBox1.setSelectedItem("2041: Red");
}
}
Here's the interface:
PLEASE NOTE: This is an example I quickly made and not the actual app I'm creating. My app involves many more number options, but as with this example, the numbers will always be positive.
I have 9 (+ default) selectable options (all the same for the 20) for my JComboBoxes.
The 20 are part of 2 JComboBox array. (10-10 for each).
I want to limit them like this:
If there is 4 selected from (for example) option 4 and the user selects a 5th one of it, one of them jumps back to default to keep the limit of 4.
How Can I do it ?
for ex:
for (int i = 0; i < roles1.length; i++) {
roles1[i] = new JComboBox();
roles1[i].setModel(new DefaultComboBoxModel(new String[] { "Not Selected", "PartnerInCrime", "Driver",
"Gunman", "Coordinator", "Hacker", "Distraction", "GadgetGuy", "Bruglar", "ConMan" }));
roles1[i].setBounds(boxPlace, 200, 100, 20);
boxPlace += 105;
getFrame().getContentPane().add(roles1[i]);
}
Here is a suggestion which is supposed to lead you in the right direction.
First of all, you have to add an ActionListener to each ComboBox, that calls a method which compares all selections with your current one.
roles1[i].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// get the current selection
JComboBox currentBox = (JComboBox)e.getSource();
String currentSelection = (String)currentBox.getSelectedItem();
// call the method and hand in your current selection
checkForSelectionExceeding(currentSelection);
}
});
In your scanning method you should memorize the amount of matches while scanning. If your limit is exceeded, reset the current box to default and stop scanning. Something like this:
private void checkForSelectionExceeding(String currentSelection){
int matches = 0;
for(int i=0; i<roles1.length; i++) {
if(roles1[i].getSelectedItem().equals(currentSelection)) {
matches++;
}
if(matches > 4) {
roles1[i].setSelectedItem("Not Selected");
break;
}
}
}
You only have to refactor this solution a little in order to scan both arrays sequentially.
Hope this helps.
If I understand your question properly, I have an idea that you may start from:
// create global HashMap that can records the occurrence of the selection of each item
Map<String, Integer> reference = new HashMap<String, Integer>();
// populate it
reference.put("PartnerInCrime", 0);
reference.put("Driver", 0);
reference.put("Gunman", 0);
reference.put("Coordinator", 0);
reference.put("Hacker", 0);
reference.put("Distraction", 0);
reference.put("GadgetGuy", 0);
reference.put("Bruglar", 0);
reference.put("ConMan", 0);
// then for every JComboBox in your array -> add action item listener to observe and control the selection like this
for(JComboBox<String> jcb : roles1){
jcb.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent ie){
if(ie.getStateChange() == ItemEvent.DESELECTED){ // decrement record
if(!ie.getItem().toString().equals("Not Selected")){
reference.put(ie.getItem().toString(), reference.get(ie.getItem().toString()) -1);
}
}
else if(ie.getStateChange() == ItemEvent.SELECTED){
if(!ie.getItem().toString().equals("Not Selected")){
if(reference.get(ie.getItem().toString())>=4){ // if already selected 4 of them
jcb.setSelectedIndex(0); // return to the default
}
else{ // else record the selection
reference.put(ie.getItem().toString(), reference.get(ie.getItem().toString()) +1);
}
}
}
}
});
}
I am creating a hangman game and I was having trouble getting the jlabel that contained each character of the word to update after the right letter button has been clicked. I have been having trouble with this as I am relatively new to working with Java Guis. Below is the action listener for the letter buttons.
private class LetterHandler implements ActionListener{
private char letterVal;
public LetterHandler(char lv){
letterVal = lv;
}
//checks if clicked button has the correct value as word char
public void actionPerformed(ActionEvent e){
for(int x = 1; x <= selectedWord.wordLength; x++){
if(selectedWord.wordValue.charAt(x - 1) == letterVal){
// JLabel letterValLabel = new JLabel(String.valueOf(letterVal));
wordSpacesArray.get(x-1).setName(String.valueOf(letterVal));
wordSpacesArray.get(x-1).revalidate();
continue;
}
else{
continue;
}
}
checkWin();
triesLeft--;
triesLeftLabel.revalidate();
}
//finds if all jlabels are complete or not
public void checkWin(){
for(int x = 1; x <= wordSpacesArray.size(); x++){
String charVal;
charVal = String.valueOf(wordSpacesArray.get(x-1));
if(charVal != "?"){
System.out.println("youWon");
System.exit(0);
}
else{
break;
}
charVal = null;
}
}
}
Any help is appreciated. Let me know if you need for of the programs code Thanks :)
There are some issues with the code. However, I'll first try to focus on your current problem:
I assume that wordSpacesArray is a list that contains the JLabels of individual letters of the word.
When this ActionListener will be notified, you try to update the labels in wordSpacesArray with the letter that corresponds to this button. However, in order to update the text that is shown on a JLabel, you have to call JLabel#setText(String) and not JLabel#setName(String). So the line should be
wordSpacesArray.get(x-1).setText(String.valueOf(letterVal));
// ^ Use setText here!
Now, concerning the other issues:
As pointed out in the comments, you should use 0-based indexing
The calls to revalidate are not necessary
The use of continue in its current for is not necessary
You should not compare strings with ==, but with equals
// if(charVal != "?") { ... } // Don't do this!
if(!charVal.equals("?")){ ... } // Use this instead
But the charVal in this case will be wrong anyhow: It will be the string representation of the label, and not its contents. So you should instead obtain the text from the label like this:
// String charVal = String.valueOf(wordSpacesArray.get(x-1)); // NO!
String charVal = wordSpacesArray.get(x-1).getText(); // Yes!
The triesLeftLabel will not be updated as long as you don't call setText on it
I think the logic of the checkWin method is flawed. You print "You won" when you find the first letter that is not a question mark. I think it should print "You won" when no letter is a question mark.
You should not call System.exit(0). That's a bad practice. Let your application end normally. (In this case, maybe by just disposing the main frame, although that would also be questionable for a game...)
So in summary, the class could probably look like this:
private class LetterHandler implements ActionListener
{
private char letterVal;
public LetterHandler(char lv)
{
letterVal = lv;
}
// checks if clicked button has the correct value as word char
#Override
public void actionPerformed(ActionEvent e)
{
for (int x = 0; x < selectedWord.wordLength; x++)
{
if (selectedWord.wordValue.charAt(x) == letterVal)
{
wordSpacesArray.get(x).setText(String.valueOf(letterVal));
}
}
checkWin();
triesLeft--;
triesLeftLabel.setText(String.valueOf(triesLeft));
}
// finds if all jlabels are complete or not
public void checkWin()
{
for (int x = 0; x < wordSpacesArray.size(); x++)
{
String charVal = wordSpacesArray.get(x).getText();
if (charVal.equals("?"))
{
// There is still an incomplete label
return;
}
}
// When it reaches this line, no incomplete label was found
System.out.println("You won");
}
}