How do I have two methods in separate classes wait before continuing execution? - java

I have two classes, BlackJack and BlackJackPane, BlackJack has a method called dealCards() which gives a class called Player a Card by looping from the first Player to the last Player twice like in a real game of blackjack where each player is dealt one card at a time.
The BlackJackPane is a JFrame that contains JButtons and JLabels. The JButton deal when clicked has a .actionCommand("deal") and an ActionListener with a Lambda expression that calls the instance variable blackjack in BlackJackPane to deal cards, blackjack.dealCards(). The ActionListener also determines what kind of card from a regular 52 card deck the Player was dealt to display the proper Card the Player has.
I would PREFER to have the dealCards() method in BlackJack wait after passing a Card to a Player for a JLabel to be created and displayed in the JFrame, however, I do not know how to make this work across classes. I am open to other creative options or tips on how to improve my code.
I am trying to keep the classes encapsulated and I/O confined in BlackJackPane.
This is my class BlackJack
public class BlackJack
{
private final Deck deck;
private final ArrayList<Player> player;
private final int numOfPlayers;
public BlackJack(int players)
{
this.numOfPlayers = players + 1; // +1 for dealer, dealer is always first in arrayList
this.deck = new Deck(Deck.TYPE[0]);
this.player = new ArrayList();
for (int i = 0; i < players + 1; i++)
{
this.player.add(new Player((i == 0))); //i == 0 is to say true to Player constructor that it is the dealer or cpu
}
}
public void DealCards()
{
deck.shuffle();
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < player.size(); j++)
{
player.get(j).getHand().add(deck.getCard(0));
deck.remove(0);
}
}
}
}
This is the BlackJackPane
public class BlackJackPane extends JFrame
{
private BlackJack blackjack;
private JLabel pokerTable;
private JButton hit, stay, split, deal, hiddenCard;
public BlackJackPane(BlackJack blackjack) throws IOException
{
this.blackjack = blackjack;
display();
}
private void display() throws IOException
{
deal.addActionListener((event) ->
{
if (event.getActionCommand().equals("deal"))
{
blackjack.DealCards();
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < blackjack.getNumOfPlayers(); j++)
{
if (blackjack.getPlayer(j).isCpu() && i != 1) //One card dealer has is face down
{
} else
{
if (blackjack.getPlayer(j).getHand().get(i).getFace() == 0) // face = 2
{
if (blackjack.getPlayer(j).getHand().get(i).getSuit() == 0) //clubs
{
//Code to make and display the JLabel within the JFrame
} else if (blackjack.getPlayer(j).getHand().get(i).getSuit() == 1) //diamonds
{
} else if (blackjack.getPlayer(j).getHand().get(i).getSuit() == 2) //hearts
{
} else //spades
{
}
} else if() //and so on for 52 cards
}
}
}
}
});
}
}

You could redefine your dealCards method to take some callback to be invoked after each card is dealt.
public void dealCards(BiConsumer<Player, Card> onDealtCallback) {
for (Player player : players) {
Card dealt = this.getNextCardToDeal();
onDealtCallback.accept(player, dealt);
}
}
And then invoke it with something like:
public void callIt() {
this.blackJack.dealCards(this::drawLabel);
}
public void drawLabel(Player player, Card card) {
// TODO
}
I can't say this is the most object-oriented approach, and it doesn't give a nice separation of state and UI. But it should do what you're trying to do.
(Note: this isn't the place for code review, so I'm resisting the urge to offer further suggestions on 'how to improve your code'.)

Related

How can I implement a range of player turns? I.E 3-10 players in Java

I am creating a tic tac toe game but in this case, it is more than two players. I am completely unsure how to go about this and had many iterations. It is done via JButtons gridlayout. For normal tictactoe, it works just fine:
buttons[i].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for(int i=0;i<9;i++) {
if(e.getSource()==buttons[i]) {
if(pTurn) {
if(buttons[i].getText()=="") {
buttons[i].setForeground(new Color(255,0,0));
buttons[i].setText("X");
pTurn=false;
txt.setText("O turn");
checkCondition();
}
}
else {
if(buttons[i].getText()=="") {
buttons[i].setForeground(new Color(0,0,255));
buttons[i].setText("O");
pTurn=true;
txt.setText("X turn");
checkCondition();
}
}
}
}
}
});
However, my implementation of 3+ players is not working. My logic is skewed but I am unsure where.
buttons[i].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for(int i=0;i<size;i++) {
if(e.getSource()==buttons[i]) {
for (int k = 0; k < col_row; k++ ) {
for (int j = k+1; j < col_row; j++) {
if (players[k]) {
if(buttons[i].getText()=="") {
buttons[i].setForeground(new Color(0,0,255));
buttons[i].setText(pIcon[k]);
players[k]=false;
txt.setText(pIcon[j] + " turn");
checkCondition();
}
}
else {
if(buttons[i].getText()=="") {
buttons[i].setForeground(new Color(0,0,255));
buttons[i].setText(pIcon[j]);
players[k]=true;
txt.setText(pIcon[j+1] + " turn");
checkCondition();
}
}
}
}
}
}
}
});
In this case, col_row is just the number of players. Players[] is an array of boolean players. My logic is that its checked to see if they have went but it doesn't work really.
pIcon[] is an array of player characters (X,O,A,B,C...). I am not really sure how to fix this.
Two player logic (as implemented)
mark first active player
on click iterate over all fields until clicked field is identified
if it is player A's turn do player A turn else do player B turn
n player logic (according to your boolean array idea)
mark first active player in boolean array
on click iterate over all fields until clicked field is identified
iterate over all players until active player k is found then do player k turn and mark next active player j = (k+1) modulo number of players
buttons[i].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for(int i=0;i<size;i++) {
if(e.getSource()==buttons[i]) {
// iterate over players to find next active player
for (int k = 0; k < 3; k++ ) { // 3 players
if (players[k]) {
// next active player found
if(buttons[i].getText()=="") {
buttons[i].setForeground(new Color(0,0,255));
buttons[i].setText(pIcon[k]);
players[k]=false;
int j = (k + 1) % 3; // index for next active player
players[j] = true; // mark next active player
txt.setText(pIcon[j] + " turn");
checkCondition();
break; // leave inner loop
}
}
}
}
}
}
}
});

How to debug my poker game recursion logic?

public static ArrayList<Hand> getPossibleHands(Hand h) {
ArrayList<Hand> allPossible = new ArrayList<Hand>();
addNext(allPossible, h);
return allPossible;
}
public static void addNext(ArrayList<Hand> poss, Hand h) {
if (h.cards.size() == 5)
poss.add(h);
else
for (int i = 0; i < 52; i++) {
Card c = Card.makeCard(i);
if (!h.contains(c))
h.add(c);
addNext(poss,h);
}
}
The code above is supposed to essentially take an incomplete poker board (anywhere from 0-4 cards) and return all possible complete boards (5 cards). The logic that I feel like it should follow is as follows: recurse through every combination of adding valid (not on the board already) cards until the size of the board is equal to 5 in which case it will add the board to the list and skip over the rest of the function.
However upon using a print statement at the beginning of the function I see that hand sizes of greater than 5 or being created. Since the first part of the function should catch all hands at 5 and terminate it there, I don't see how the code executes at the rest of the function.
Your class should receive stack overflow with an empty hand.
You send new Card(0) to the hand. This is added.
Then you call add next again - and the 'for' starts from 0 again. Checks adds 1. Then starts from 0 - it is there, does not add anything and starts over. Where it starts from 0. Does not do anything. Starts from 0. Ad infinum -> StackOverFlow.
You also need to reset to previous state of the hand every time you finish with 5 cards and backtrack.
If you want a recursive solution you can try :
private static ArrayList<Hand> getPossibleHands(Hand h) {
ArrayList<Integer> except;
if (h.cards == null) except = new ArrayList<>();
else
except = h.cards.stream().map(c -> (c.getCard())).collect(Collectors.toCollection(ArrayList::new));
ArrayList<Hand> allPossible = new ArrayList<>();
addNext(allPossible, h, except);
return allPossible;
}
private static void addNext(ArrayList<Hand> poss, Hand h, ArrayList<Integer> except) {
//assuming hands 0-4 - we don't need to check on entry, only when we add
Hand localHand = h.copy();
for (int i = 0; i < 52; i++) {
if (except.contains(i)) continue;
Card c = Card.makeCard(i);
if (!localHand.contains(c)) {
addNext(poss, localHand.copy(), copyExcept(except, i));
localHand.add(c);
if (localHand.cards.size() == 5) {
poss.add(localHand);
break;
}
}
}
}
private static ArrayList<Integer> copyExcept(ArrayList<Integer> except, int i) {
ArrayList<Integer> clonedExcept = new ArrayList<>(except);
clonedExcept.add(i);
return clonedExcept;
}
import java.util.ArrayList;
public class Hand {
ArrayList<Card> cards = new ArrayList<>();
public boolean contains(Card c) {
for (Card card : cards) {
if (card.getCard() == c.getCard())
return true;
}
return false;
}
public void add(Card c) {
cards.add(c);
}
Hand copy() {
Hand temp = new Hand();
for (Card c : cards) {
temp.add(new Card(c.getCard()));
}
return temp;
}
}
class Card {
private int card;
public Card(int card) {
this.card = card;
}
public static Card makeCard(int i) {
return new Card(i);
}
public int getCard() {
return card;
}
}
Initially, h is (presumably) empty. So addNext will loop through all possible cards, and since none of them are in the hand, add each card to the hand, regardless of how many cards are currently in the hand.
It looks to me like your for loop is eventually adding whole deck to hand.
In your loop you run 52 iterations. On each iteration you (conditionally) add a card to the hand and then you call your function recursively. But after that recursive call has returned, you go to the next iteration and again add a card to the hand.
So that '5 card restriction' does not restrict anything here.

Checking if the player move is valid in TicTacToe game

I am making a tic tac toe game in java, between human-computer.
I need a Check to ensure that the player has made a valid move, e.g. that the player is not trying to make a move in a square that is already occupied.
I tried to write a method and call it from the chick() method, or to make if statment within win() but It didn't work.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class XOGAMEMAIN extends JFrame implements ActionListener {
private JButton [][]buttons = new JButton[3][3];
private JButton XButton = new JButton("play with X ");
private JButton OButton = new JButton("play with O");
private JButton CButton = new JButton("let computer start with X");
private JButton C1Button = new JButton("let computer start with O");
private JLabel statusLabel = new JLabel(" ");
private XOGAMEAI game = null;
private int human = 0;
private int computer = 0;
private boolean isPlay = false;
private String []chars=new String[]{"","X","O"};
private void setStatus(String s) {
statusLabel.setText(s);
}
private void setButtonsEnabled(boolean enabled) {
for(int i=0;i<3;i++)
for(int j=0;j<3;j++) {
buttons[i][j].setEnabled(enabled);
if(enabled) buttons[i][j].setText(".");
}
}
public XOGAMEMAIN() {
setTitle(" X/O Game ");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
JPanel centerPanel = new JPanel(new GridLayout(3,3));
Font font = new Font("Arial",Font.BOLD, 32);
for(int i=0;i<3;i++)
for(int j=0;j<3;j++) {
buttons[i][j] = new JButton(".");
buttons[i][j].setFont(font);
buttons[i][j].addActionListener(this);
buttons[i][j].setFocusable(false);
centerPanel.add(buttons[i][j]);
}
XButton.addActionListener(this);
OButton.addActionListener(this);
CButton.addActionListener(this);
C1Button.addActionListener(this);
JPanel northPanel = new JPanel();
northPanel.add(statusLabel);
JPanel southPanel = new JPanel();
southPanel.add(XButton);
southPanel.add(OButton);
southPanel.add(CButton);
southPanel.add(C1Button);
setStatus("wlc,to start chose X or O or let computer decide");
setButtonsEnabled(false);
add(northPanel,"North");
add(centerPanel,"Center");
add(southPanel,"South");
setSize(600,600);
setLocationRelativeTo(null);
}
public static void main(String []args) {
new XOGAMEMAIN().setVisible(true);
}
private void computerTurn1() { ////////// this method for computer ( when the human chose the computer buttons to start first ) with X or O
int []pos = game.nextMove(computer);
if(pos!=null) {
int i = pos[0];
int j = pos[1];
buttons[i][j].setText(chars[computer]);
game.setBoardValue(i,j,computer);
setStatus("computer did its move ");
}
checkState();
}
private void computerTurn() { // this method for computer movment( when the human chose (X,O)buttons to start ) human method==>(using click method) and after that(computer turn==> this method will run
int []pos = game.nextMove(computer);
if(pos!=null) {
int i = pos[0];
int j = pos[1];
buttons[i][j].setText(chars[computer]);
game.setBoardValue(i,j,computer);
setStatus("computer did its move , now your turn ");
}
checkState();
}
private void gameOver(String s) {
setStatus(s);
setButtonsEnabled(false);
isPlay = false;
}
private void checkState() {
if(game.isWin(human)) {
gameOver(" You've Won!");
}
if(game.isWin(computer)) {
gameOver("Sorry, You Lose!");
}
if(game.nextMove(human)==null && game.nextMove(computer)==null) {
gameOver(" Draw ");
}
}
private void click(int i,int j) { //// this method is for human , when he click any button
if(game.getBoardValue(i,j)==XOGAMEAI.EMPTY) {
buttons[i][j].setText(chars[human]);
game.setBoardValue(i,j,human);
checkState();
computerTurn();}
}
public void actionPerformed(ActionEvent event) { //// this is action event that is senseing any click from the human(every button click ==> go for a method==> this methods are declared before )
if(event.getSource()==C1Button) { /// event.getsourse mean ==> the click that human make is on C1 button ?
play4(); /// if yes go to method play4() ==> thats mean the computer will start first with O char
}
if(event.getSource()==CButton) { /// for computer to start first with X
play3();
}
if(event.getSource()==OButton) { ////////// human click to start first with O==> start play1 method
play1();}
else {
for(int i=0;i<3;i++) //// if this is not the first click ==> thats mean this will check in turn 2 for human for example , put not the first 1
for(int j=0;j<3;j++) ////it will go for method click() directly for any of the 9 buttons choices
if(event.getSource()==buttons[i][j]) ///
click(i,j); }
////
if(event.getSource()==XButton) { // the same here but the human has chose Xbutton to start first
play();
}else {
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
if(event.getSource()==buttons[i][j])
click(i,j);
}
}
private void play4() { //// this is play4() method , its happened when Computer supposed to start first with O
game = new XOGAMEAI();
human = XOGAMEAI.ONE; // human is int number=0 , now the is Matrix defined which include "","x","O" , human=o ==> char "", human=ONE ==>char X , human=TWO ==>char O
computer = XOGAMEAI.TWO; // ///
setButtonsEnabled(true); ///
isPlay = true;
computerTurn1();
///
}
private void play3() { //// for Computer to start first with X
game = new XOGAMEAI();
human = XOGAMEAI.TWO; //
computer = XOGAMEAI.ONE; // ///
setButtonsEnabled(true); ///
isPlay = true;
computerTurn1();
///
}
private void play1() { //// for O
game = new XOGAMEAI();
human = XOGAMEAI.TWO; //
computer = XOGAMEAI.ONE; //
setStatus("Your Turn"); ///
setButtonsEnabled(true); ///
isPlay = true; ///
} ////
private void play() { ////// for X
game = new XOGAMEAI();
human = XOGAMEAI.ONE;
computer = XOGAMEAI.TWO;
setStatus("Your Turn");
setButtonsEnabled(true);
isPlay = true;
}
public static class XOGAMEAI {
/* the board */
private int board[][];
/* empty */
public static final int EMPTY = 0;
/* player one */
public static final int ONE = 1;
/* player two */
public static final int TWO = 2;
public XOGAMEAI() {
board = new int[3][3];
}
/* get the board value for position (i,j) */
public int getBoardValue(int i,int j) {
if(i < 0 || i >= 3) return EMPTY;
if(j < 0 || j >= 3) return EMPTY;
return board[i][j];
}
/* set the board value for position (i,j) */
public void setBoardValue(int i,int j,int token) {////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
if(i < 0 || i >= 3) return;
if(j < 0 || j >= 3) return;
board[i][j] = token;
}
/* calculate the winning move for current token */
public int []nextWinningMove(int token) {
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
if(getBoardValue(i, j)==EMPTY) {
board[i][j] = token;
boolean win = isWin(token);
board[i][j] = EMPTY;
if(win) return new int[]{i,j};
}
return null;
}
public int inverse(int token) {
return token==ONE ? TWO : ONE;
}
/* calculate the best move for current token */
public int []nextMove(int token) {
/* lucky position in the center of board*/
if(getBoardValue(1, 1)==EMPTY) return new int[]{1,1};
/* if we can move on the next turn */
int winMove[] = nextWinningMove(token);
if(winMove!=null) return winMove;
/* choose the move that prevent enemy to win */
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
if(getBoardValue(i, j)==EMPTY)
{
board[i][j] = token;
boolean ok = nextWinningMove(inverse(token)) == null;
board[i][j] = EMPTY;
if(ok) return new int[]{i,j};
}
/* choose available move */
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
if(getBoardValue(i, j)==EMPTY)
return new int[]{i,j};
/* no move is available */
return null;
}
/* determine if current token is win or not win */
public boolean isWin(int token) {
final int DI[]={-1,0,1,1};
final int DJ[]={1,1,1,0};
for(int i=0;i<3;i++)
for(int j=0;j<3;j++) {
// we skip if the token in position(i,j) not equal current token */
if(getBoardValue(i, j)!=token){
continue;
}
for(int k=0;k<4;k++) {
int ctr = 0;
while(getBoardValue(i+DI[k]*ctr, j+DJ[k]*ctr)==token) ctr++;
if(ctr==3) return true;
}
}
return false;
}
}
}
Inside the click(int i, int j) method you check the condition if(game.getBoardValue(i,j)==XOGAMEAI.EMPTY) - doesn't that do what you what you want?
OK, I've found the bug, it was in the ActionPerformed method. The problem was, you wrote
if(xbutton pressed){...}
else{click()}
if(ybutton pressed){...}
else{click()}
And both else statements were executed. Here's the fixed code:
public void actionPerformed(ActionEvent event) { //// this is action event
//// that is senseing
//// any click from the
//// human(every button
//// click ==> go for a
//// method==> this
//// methods are
//// declared before )
if (event.getSource() == C1Button) { /// event.getsourse mean ==> the
/// click that human make is on
/// C1 button ?
play4(); /// if yes go to method play4() ==> thats mean the computer
/// will start first with O char
} else if (event.getSource() == CButton) { /// for computer to start
/// first with
/// X
play3();
}
else if (event.getSource() == OButton) { ////////// human click to start
////////// first with O==> start
////////// play1 method
play1();
} else if (event.getSource() == XButton) { // the same here but the
// human has
// chose Xbutton to start first
play();
} else {
for (int i = 0; i < 3; i++) //// if this is not the first click ==>
//// thats mean this will check in turn
//// 2 for human for example , put not
//// the first 1
for (int j = 0; j < 3; j++) //// it will go for method click()
//// directly for any of the 9
//// buttons choices
if (event.getSource() == buttons[i][j]) ///
click(i, j);
}
}

Weird behavior in Java While Loop [duplicate]

This question already has an answer here:
Loop doesn't see value changed by other thread without a print statement
(1 answer)
Closed 7 years ago.
I am writing a basic Tic-Tac-Toe Single player game using basic swing graphics. I completed the game, but there is a weird problem I am facing. At one place, I used a while loop with a SOP statement. If I omit this statement, program works differently and nothing happens (like some kind of infinite loop), and if I keep it, it works just fine. I don't know what's happening in the code. Please help.
Below is the source code which causing problem. Sorry for my amateur coding style.
import java.util.Random;
public class SinglePlayer implements Runnable{
public final int MINIMUM = -1000000;
private GameBoard game;
public SinglePlayer(){
game = new GameBoard("Single Player");
}
public static void main(String[] args){
SinglePlayer gameSingle = new SinglePlayer();
gameSingle.run();
}
public void run(){
boolean machinePlayed = true, userPlayed = false;
// Outer loop is to maintain re-match option of program
while(this.game.quitTwoPlayer == false){
// Inner loop is a single game b/w user and machine
while(this.game.GameQuitStatus() == false){
/* I kept two conditions to switch b/w machine and user mode
* of game and they just keep changing to simulate the game
* b/w machine and user.
*/
if(machinePlayed == false && userPlayed){
try {
MachineMove("O");
} catch (CloneNotSupportedException e) {
e.printStackTrace();
break;
}
this.game.ChangePlayerLabels();
machinePlayed = true;
userPlayed = false;
}
else if(machinePlayed && userPlayed == false){
int earlierCount = this.game.CountSteps();
/* THIS IS THE WHILE LOOP I AM TALKING ABOUT.
* If I omit the print statement inside the body of loop,
* program behaves differently, but when I keep it,
* it working just fine.
* */
while(earlierCount == this.game.CountSteps()){
System.out.println("Player User thinking");
}
this.game.ChangePlayerLabels();
machinePlayed = false;
userPlayed = true;
}
this.game.DeclareResult();
}
this.game.dispose();
}
}
public void MachineMove(String player) throws CloneNotSupportedException{
/* If board is empty, play at center of the board */
if(this.game.CountSteps() == 0){
this.game.MakeMove(1, 1);
}
/* If center is blank, play it there. Otherwise, pick a corner randomly */
else if(this.game.CountSteps() == 1){
if(this.game.IsEmpty(1, 1))
this.game.MakeMove(1, 1);
else{
Random randomNum = new Random();
int num = randomNum.nextInt(4);
if(num == 0)
this.game.MakeMove(0, 0);
else if(num == 1)
this.game.MakeMove(2, 0);
else if(num == 2)
this.game.MakeMove(0, 2);
else if(num == 3)
this.game.MakeMove(2, 2);
}
}
else{
/* If the next move is such that it should be taken, otherwise opponent will win */
String opponent = "";
if(this.game.GetCurrentPlayer().equals("O"))
opponent = "X";
else
opponent = "O";
for(int i = 0; i<3; i++){
for(int j = 0; j<3; j++){
if(this.game.IsEmpty(i,j)){
GameBoard tempGame = new GameBoard(this.game, "Single Player");
tempGame.MakePossibleMove(i, j, opponent);
if(tempGame.GameWinner().equals(opponent + " wins")){
this.game.MakeMove(i,j);
return;
}
}
}
}
/* If the next move is not such that if missed, game is lost, then play most optimal move towards winning */
Move tempMove = new Move(MINIMUM, 0, 0);
Move bestMove = new Move(MINIMUM, 0, 0);
for(int i = 0; i<3; i++){
for(int j = 0; j<3; j++){
if(this.game.IsEmpty(i,j)){
GameBoard tempGame = new GameBoard(this.game, "Single Player");
tempMove = MakeMoves(tempGame, i, j);
if(tempMove.score > bestMove.score){
bestMove.row = tempMove.row;
bestMove.col = tempMove.col;
bestMove.score = tempMove.score;
}
}
}
}
this.game.MakeMove(bestMove.row, bestMove.col);
}
}
public Move MakeMoves(GameBoard tempGame, int row, int col){
String player = tempGame.GetCurrentPlayer();
tempGame.MakeMove(row, col);
if(tempGame.GameWinner().equals("Match Draw")){
return new Move(0, row, col);
}
else if(tempGame.GameWinner().equals("X wins")){
if(player.equals("X")){
return new Move(1, row, col);
}
else{
return new Move(-1, row, col);
}
}
else if(tempGame.GameWinner().equals("O wins")){
if(player.equals("O")){
return new Move(1, row, col);
}
else{
return new Move(-1, row, col);
}
}
else{
Move bestMove = new Move(MINIMUM, 0, 0);
Move tempBestMove = new Move(0, 0, 0);
for(int i = 0; i<3; i++){
for(int j = 0; j<3; j++){
if(tempGame.IsEmpty(i,j)){
GameBoard newGame = new GameBoard(tempGame, "Single Player");
tempBestMove = MakeMoves(newGame, i, j);
if(tempBestMove.score > bestMove.score)
bestMove = tempBestMove;
}
}
}
return bestMove;
}
}
}
class Move{
public int score;
public int row;
public int col;
public Move(int score, int row, int col){
this.score = score;
this.row = row;
this.col = col;
}
}
Your loop is likely typing up your processor, and the SOP slows the loop enough to allow other processes to occur. But regardless and most importantly, you don't want to have this loop present in the first place. You state that you have a,
Tic-Tac-Toe Single player game using basic swing graphics
Remember that Swing is an event driven GUI library, so rather than loop as you would in a linear console program, let events occur, but respond to them based on the state of the program.
In other words, give your class several fields including a boolean variable that tells whose turn it is, such as boolean playersTurn, a boolean variable gameOver, ..., and change the state of these variables as the game is played, and base the games behavior depending on these states. For instance the game would ignore the player's input if it was not his turn.

Java textField infinite loop

Game class:
import java.util.Random;
public class Game {
public static void main(String args[]){
//----------Sets up GUI----------
GUI gameGUI = new GUI();
gameGUI.setVisible(true);
Player.setGUI(gameGUI);
//----------Sets initial number of marbles, computer player and human player----------
Random random = new Random();
int initialNum;
//loop makes sure initialNum of marbles is between 10 and 100
do{
initialNum = random.nextInt(100);
}while(initialNum < 10);
//*****gameGUI.manageMarbles(initialNum, true);
//end loop
Pile pile = new Pile(initialNum);
gameGUI.setPile(pile);
int compChoice = random.nextInt(2) + 1; //number index (1 or 2) representing SIMPLE_COMPUTER or SMART_COMPUTER
Player computer = new Player(Player.Type.values()[compChoice]);
Player humanPlayer = new Player(Player.Type.HUMAN);
//----------Game loop----------
//Randomly determine first player
Player currentPlayer;
int playerIndex = random.nextInt(2); //will be used to determine next player in the loop
if(playerIndex == 0){ currentPlayer = computer; }
else { currentPlayer = humanPlayer; }
//Loop
while(pile.getNumMarbles() != 0){
System.out.printf("%d marbles left.\n", pile.getNumMarbles());
int removed = currentPlayer.playTurn(pile.getNumMarbles());
pile.removeMarbles(removed);
//Determine next player
playerIndex = Math.abs(playerIndex - 1); //if playerIndex = 0, it becomes 1, and vice-versa
if(playerIndex == 0){ currentPlayer = computer; }
else { currentPlayer = humanPlayer; }
}
System.out.println(currentPlayer + " won");
}
}
Player class:
import java.util.Scanner;
import java.util.Random;
public class Player {
public enum Type{HUMAN, SIMPLE_COMPUTER, SMART_COMPUTER}
private Type type;
static private GUI gui;
public Player(Player.Type type){
this.type = type;
}
public Player.Type getType(){
return type;
}
public int playTurn(int pileSize){
Random random = new Random();
if(type == Type.HUMAN){
int marbles;
do{
marbles = gui.getMarblesToRemove();
}while(marbles < 0);
return marbles;
}
else if(type == Type.SIMPLE_COMPUTER){
if(pileSize == 1){
return 1;
}
else{
int remove = random.nextInt(pileSize/2) + 1;
if(remove == (pileSize/2) + 1){ remove -= 1; }
return remove;
}
}
else if(type == Type.SMART_COMPUTER){
if(pileSize == 1){
return 1;
}
else if(pileSize == 3 || pileSize == 7 || pileSize == 15 || pileSize== 31 || pileSize== 63 || pileSize <= 3){
int remove = random.nextInt(pileSize/2) + 1;
if(remove == (pileSize/2) + 1){ remove -= 1; }
return remove;
}
else{
for(int i=1; i<=pileSize/2; i++){
int size = pileSize - i;
if(size == 3 || size == 7 || size == 15 || size == 31 || size == 63){
return i;
}
}
}
}
return 0;
}
public String toString(){
return ""+type;
}
public static void setGUI(GUI guii){
gui = guii;
}
}
GUI class:
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JTextField;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class GUI extends JFrame {
private JPanel panel;
private JButton removeButton; //button to remove marbles
private JTextField marblesAmount; //amount of marbles to remove
private static final int FIELD_WIDTH = 2;
private JLabel marblesLabel;
private JLabel errorLabel;
private Pile pile;
private int marblesToRemove;
private ClickListener listener;
static final private int WIDTH = 700, HEIGHT = 600;
public GUI(){
super.setTitle("test");
super.setSize(WIDTH, HEIGHT);
super.setDefaultCloseOperation(EXIT_ON_CLOSE);
panel = new JPanel();
marblesLabel = new JLabel("How many marbles to remove?");
errorLabel = new JLabel("");
removeButton = new JButton("Remove");
listener = new ClickListener();
removeButton.addActionListener(listener);
marblesAmount = new JTextField(FIELD_WIDTH);
panel.add(removeButton);
panel.add(marblesLabel);
panel.add(marblesAmount);
panel.add(errorLabel);
super.add(panel);
marblesToRemove = 0;
}
public void setPile(Pile pile){
this.pile = pile;
}
private class ClickListener implements ActionListener{
public void actionPerformed(ActionEvent event){
marblesToRemove = Integer.parseInt(marblesAmount.getText());
}
}
public int getMarblesToRemove(){
return marblesToRemove;
}
}
Pile Class:
public class Pile {
private int initialNum;
private int currentNum;
public Pile(int initialNum){
setNumMarbles(initialNum);
currentNum = initialNum;
}
public int getNumMarbles(){
return currentNum;
}
public void removeMarbles(int numToRemove){
currentNum = currentNum - numToRemove;
}
public void setNumMarbles(int amount){
initialNum = amount;
}
public String toString(){
return "Number of marbles: " + currentNum;
}
}
What I am trying to do is to get the function playTurn(int pileSize) in the Player class to return the variable marbles(inside the if(type == Type.HUMAN) block ) only when it is not zero. The variable marblesToRemove from the gui class is assigned to marbles, by calling the function getMarblesToRemove().
marblesToRemove is initially set to 0 in the default constructor for the gui class, and that causes the functionplayTurn(int pileSize) to go in an infinite loop. But marblesToRemove is changed to another value that is input in a JTextField (marblesAmount) when a a button(removeButton) is pressed. But the the do while loop will still be an infinite loop and the function will return nothing, why? Can someone please help? Thanks.
This is probably because of at least two issues that I can see. The first is that you are modifying the variable in one thread, and reading it in the other. This should be done using either synchronization, or some other locking mechanism (Lock or AtomicInteger, etc.).
The second issue is that your reading-thread should not be doing a 'tight' loop. This is bad practices that sends one CPU to 100% usage. It should be done using some form of notification. Again, synchronization comes to mind.
Until you fix these two issues, you will always have unreliable results.
For what it's worth, in your particular case, if I were to make an educated guess, I would guess that there are two important copies of the marblesAmount (and there are alsways multiple copies). There's the copy thati's in the L1 and registers of one CPU that's doing a tight loop waiting for that register to change, and the other is in another core on your CPU setting it to a new value. Unless you use some synchronization, or somethin from the java.util.concurrent.* library you ave no way to tell one copy of the variable to refresh the other.
I got it to work finally. I just added a line of code "System.out.print("");" in the do-while loop in the Player class.
So now it looks like
do{
marbles = gui.getMarblesToRemove();
System.out.print("");
}while(marbles < 0);
I have no idea why that works, but it does lol. Now the function actually returns the value that I set in the JTextField if it is not zero.

Categories

Resources