Reputation: 1246
I'm making a chess program for a project. I'm trying to add a move history box to the side of the board. The move history works fine, and the data is properly sent to the text area, but the text inside the JTextArea disappears while the AI is thinking about his move.
public void aiMove(){
if (!playing){ return; }
paintImmediately(0,0,totalX,totalY);
ai = eve.getMove(chess,wtm,aiOut); //text disappears here
chess.makeMove(ai);
wtm = !wtm;
humanMove = true;
writeMove(ai); //updates move history, text reappears here
playing = stillPlaying();
repaint();
}
private void writeMove(Move move){
char c = "abcdefgh".charAt(7-move.fromY);
char h ="abcdefgh".charAt(7-move.toY);
String s = Character.toString(c)+(move.fromX+1)+" - "+Character.toString(h)+(move.toX+1)+" ";
if (!wtm){
String q = chess.getFullMove()+". "+s+" ";
moves.setText(moves.getText()+q);
}
else {
moves.setText(moves.getText()+s+"\n");
}
}
Here's a print screen of what's happening. http://s13.postimage.org/mh7hltfk7/JText_Area_disappear.png
SOLVED Thanks to all replies. I changed aiMove() so it creates a thread. Here is what I did.
Attempt #3... swing is still so foreign to me. I didn't want to change writeMove to getMove or I would have to rewrite the human's turn slightly. Since the project is essentially done, I am trying to avoid as much work as possible :) The GUI is entirely optional anyways, I was just doing it for fun, and to try and learn a bit of swing.
public void aiMove(){
if (!playing){ return; }
if (!aiThread.isAlive()){
aiThread = new Thread(){
public void run(){
ai = eve.getMove(chess,wtm,aiOut);
chess.makeMove(ai);
wtm = !wtm;
humanMove = true;
SwingUtilities.invokeLater(new Runnable(){
public void run(){
writeMove(ai);
}
});
repaint();
playing = stillPlaying();
}
};
aiThread.start();
}
}
It also fixed a problem I had before, in that if I were to hold down the 'a' key (force ai move), it would queue up many forced ai moves. Now that doesn't happen.
Upvotes: 2
Views: 731
Reputation: 36423
The problem is your AI thinking is CPU intensive/time consuming, thus it is considered a long running task. You should not do long running tasks on GUI Event Dispatch Thread as this will cause the UI to seem frozen and thus only show updates after the task has finished.
Fortunately there are 2 different approaches you could use:
The SwingWorker subclass can define a method, done, which is automatically invoked on the event dispatch thread when the background task is finished.
SwingWorker implements java.util.concurrent.Future. This interface allows the background task to provide a return value to the other thread. Other methods in this interface allow cancellation of the background task and discovering whether the background task has finished or been cancelled.
The background task can provide intermediate results by invoking SwingWorker.publish, causing SwingWorker.process to be invoked from the event dispatch thread.
The background task can define bound properties. Changes to these properties trigger events, causing event-handling methods to be invoked on the event dispatch thread.
Alternatively create separate Thread
for AI thinking and wrap setText
call in SwingUtilities.invokeLater(...);
Thread t=new Thread(new Runnable() {
@Override
public void run() {
}
});
t.start();
UPDATE
After reading MadProgrammers comment (+1 to it) please remember to create/manipulate your GUI/Swing components on EDT via the SwingUtilities.invokeLater(..)
block. You can read more on it here.
UPDATE 2:
That edit is defeating the point, the only call on EDT in SwingUtilitites
block should be the setText
or atleast only code that manipulates a Swing component i.e
public void aiMove(){
if (!playing){ return; }
if (!aiThread.isAlive()){ //originally initialized by constructor
aiThread = new Thread(){
public void run(){
ai = eve.getMove(chess,wtm,aiOut);
chess.makeMove(ai);
wtm = !wtm;
humanMove = true;
SwingUtilities.invokeLater(new Runnable(){
public void run(){
writeMove(ai);
}
});
repaint();
playing = stillPlaying();
}
};
aiThread.start();
}
}
Upvotes: 6