Взаимодействие GUI и логики
Модернизировал игру ХО из книжки Файна, добавив минимакс-алгоритм и кнопку MOVE, которая заставляет программу делать ход первой (в авторской версии всегда начинает человек), и есть такая проблемка: если во время вычисления компьютером своего хода успеть пощёлкать по клеткам, то эти действия добавляются в EventQueue и затем сразу серией "выстреливают" на экране, а хотелось бы, чтобы игровое поле блокировалось до того момента, как программа сделает свой ход на доске. Подскажите, пожалуйста, в каком направлении копать: почитать дополнительно какие-то материалы непосредственно по SWING (если можно, чуть конретнее тему определить) или тут надо разбираться с многопоточностью в целом (notify, wait, семафоры и пр.)?
Класс GUI:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TTTJGUI implements ActionListener {
private int emptySquaresLeft = 9;
private JButton[] squares;
private JButton newgame;
private JButton move;
private JLabel label;
private JFrame frmTictactoe;
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
TTTJGUI window = new TTTJGUI();
window.frmTictactoe.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
private TTTJGUI() {
initialize();
}
private void initialize() {
frmTictactoe = new JFrame();
frmTictactoe.setResizable(false);
frmTictactoe.setSize(300, 400);
frmTictactoe.setLocationRelativeTo(null);
frmTictactoe.setTitle("TicTacToe");
frmTictactoe.setFont(new Font("Monospaced", Font.BOLD, 20));
frmTictactoe.setBackground(Color.CYAN);
frmTictactoe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
frmTictactoe.getContentPane().add(panel, BorderLayout.CENTER);
panel.setLayout(new GridLayout(3, 3));
squares = new JButton[9];
for(int i=0; i<9; i++){
squares[i] = new JButton(" ");
squares[i].addActionListener(this);
squares[i].setSize(100, 100);
squares[i].setBackground(Color.ORANGE);
squares[i].setFont(new Font("Monospaced", Font.BOLD, 50));
panel.add(squares[i]);
}
JPanel panel_1 = new JPanel();
panel_1.setBackground(Color.CYAN);
frmTictactoe.getContentPane().add(panel_1, BorderLayout.NORTH);
newgame = new JButton("New Game");
newgame.addActionListener(this);
newgame.setFont(new Font("Monospaced", Font.BOLD, 20));
panel_1.add(newgame);
move = new JButton("MOVE");
move.addActionListener(this);
move.setFont(new Font("Monospaced", Font.BOLD, 20));
panel_1.add(move);
JPanel panel_2 = new JPanel();
frmTictactoe.getContentPane().add(panel_2, BorderLayout.SOUTH);
label = new JLabel("Your turn!");
panel_2.add(label);
label.setFont(new Font("Monospaced", Font.BOLD, 25));
label.setHorizontalAlignment(SwingConstants.CENTER);
}
public void actionPerformed(ActionEvent e) {
JButton theButton = (JButton) e.getSource();
String winner = " ";
String board[];
int spot = -1;
if(theButton == newgame){
move.setEnabled(true);
for(int i=0; i<9; i++){
squares[i].setEnabled(true);
squares[i].setText(" ");
squares[i].setBackground(Color.ORANGE);
}
emptySquaresLeft = 9;
label.setText("Your turn!");
return;
}
else if(theButton == move){
move.setEnabled(false);
board = new String[9];
for(int i=0; i<9; i++){
board[i]=squares[i].getText();
}
AI.moveC(board, "O", 0, Integer.MIN_VALUE, Integer.MAX_VALUE);
spot = AI.best();
squares[spot].setText("O");
winner = lookForWinner();
return;
}
else
for(int i=0; i<9; i++){
if(theButton == squares[i] & squares[i].getText()==" "){
move.setEnabled(false);
squares[i].setText("X");
winner = lookForWinner();
if(!" ".equals(winner)){
endTheGame();
}
else{
board = new String[9];
for(i=0; i<9; i++){
board[i]=squares[i].getText();
}
AI.moveC(board, "O", 0, Integer.MIN_VALUE, Integer.MAX_VALUE);
spot = AI.best();
squares[spot].setText("O");
winner = lookForWinner();
if (!" ".equals(winner)){
endTheGame();
}
}
break;
}
}
if(winner.equals("X")){
label.setText("You win!!!");
}
else if(winner.equals("O")){
label.setText("Comp wins!");
}
else if(winner.equals("T")){
label.setText("It's a tie!");
}
}
String lookForWinner() {
emptySquaresLeft--;
if (emptySquaresLeft==0){
return "T";
}
else if((!squares[0].getText().equals(" ")&&squares[0].getText().equals(squares[1].getText())&&squares[0].getText().equals(squares[2].getText()))){
highlightWinner(0, 1, 2, squares[0].getText());
return squares[0].getText();
}
else if((!squares[3].getText().equals(" ")&&squares[3].getText().equals(squares[4].getText())&&squares[3].getText().equals(squares[5].getText()))){
highlightWinner(3, 4, 5, squares[3].getText());
return squares[3].getText();
}
else if((!squares[6].getText().equals(" ")&&squares[6].getText().equals(squares[7].getText())&&squares[6].getText().equals(squares[8].getText()))){
highlightWinner(6, 7, 8, squares[6].getText());
return squares[6].getText();
}
else if((!squares[0].getText().equals(" ")&&squares[0].getText().equals(squares[3].getText())&&squares[0].getText().equals(squares[6].getText()))){
highlightWinner(0, 3, 6, squares[0].getText());
return squares[0].getText();
}
else if((!squares[1].getText().equals(" ")&&squares[1].getText().equals(squares[4].getText())&&squares[1].getText().equals(squares[7].getText()))){
highlightWinner(1, 4, 7, squares[1].getText());
return squares[1].getText();
}
else if((!squares[2].getText().equals(" ")&&squares[2].getText().equals(squares[5].getText())&&squares[2].getText().equals(squares[8].getText()))){
highlightWinner(2, 5, 8, squares[2].getText());
return squares[2].getText();
}
else if((!squares[0].getText().equals(" ")&&squares[0].getText().equals(squares[4].getText())&&squares[0].getText().equals(squares[8].getText()))){
highlightWinner(0, 4, 8, squares[0].getText());
return squares[0].getText();
}
else if((!squares[2].getText().equals(" ")&&squares[2].getText().equals(squares[4].getText())&&squares[2].getText().equals(squares[6].getText()))){
highlightWinner(2, 4, 6, squares[2].getText());
return squares[2].getText();
}
return " ";
}
void highlightWinner(int win1, int win2, int win3, String turn) {
if(turn=="O"){
squares[win1].setBackground(Color.RED);
squares[win2].setBackground(Color.RED);
squares[win3].setBackground(Color.RED);
}
else{
squares[win1].setBackground(Color.GREEN);
squares[win2].setBackground(Color.GREEN);
squares[win3].setBackground(Color.GREEN);
}
}
void endTheGame(){
for(int i=0;i<9;i++){
squares[i].setEnabled(false);
}
}
}
Класс логики:
import static java.lang.System.out;
import java.util.ArrayList;
import java.util.HashMap;
public class AI {
static HashMap<Integer, Integer> scores = new HashMap<Integer, Integer>();
static boolean endGameX(String[] virtual) {
if (virtual[0]==virtual[1]&&virtual[0]==virtual[2]&&virtual[0]=="X"){
return true;
}
else if (virtual[3]==virtual[4]&&virtual[3]==virtual[5]&&virtual[3]=="X"){
return true;
}
else if (virtual[6]==virtual[7]&&virtual[6]==virtual[8]&&virtual[6]=="X"){
return true;
}
else if (virtual[0]==virtual[3]&&virtual[0]==virtual[6]&&virtual[0]=="X"){
return true;
}
else if (virtual[1]==virtual[4]&&virtual[1]==virtual[7]&&virtual[1]=="X"){
return true;
}
else if (virtual[2]==virtual[5]&&virtual[2]==virtual[8]&&virtual[2]=="X"){
return true;
}
else if (virtual[0]==virtual[4]&&virtual[0]==virtual[8]&&virtual[0]=="X"){
return true;
}
else if (virtual[2]==virtual[4]&&virtual[2]==virtual[6]&&virtual[2]=="X"){
return true;
}
return false;
}
static boolean endGameO(String[] virtual) {
if (virtual[0]==virtual[1]&&virtual[0]==virtual[2]&&virtual[0]=="O"){
return true;
}
else if (virtual[3]==virtual[4]&&virtual[3]==virtual[5]&&virtual[3]=="O"){
return true;
}
else if (virtual[6]==virtual[7]&&virtual[6]==virtual[8]&&virtual[6]=="O"){
return true;
}
else if (virtual[0]==virtual[3]&&virtual[0]==virtual[6]&&virtual[0]=="O"){
return true;
}
else if (virtual[1]==virtual[4]&&virtual[1]==virtual[7]&&virtual[1]=="O"){
return true;
}
else if (virtual[2]==virtual[5]&&virtual[2]==virtual[8]&&virtual[2]=="O"){
return true;
}
else if (virtual[0]==virtual[4]&&virtual[0]==virtual[8]&&virtual[0]=="O"){
return true;
}
else if (virtual[2]==virtual[4]&&virtual[2]==virtual[6]&&virtual[2]=="O"){
return true;
}
return false;
}
static int minimax(ArrayList<Integer> moves, String turn, int alpha, int beta) {
out.println(moves.size() + moves.toString() + " " + turn + " a=" + alpha + " b=" + beta);
if(turn=="O"){
for(int i=0; i<moves.size();i++){
if (moves.get(i) > alpha){
alpha = moves.get(i);
}
if(alpha >= beta){
break;
}
}
return alpha;
}
else{
for (int i=0; i<moves.size();i++){
if (moves.get(i) < beta){
beta = moves.get(i);
}
if(alpha >= beta){
break;
}
}
return beta;
}
}
static ArrayList<Integer> filter(String[] virtual) {
ArrayList<Integer> empty = new ArrayList<>();
for(int i=0; i<virtual.length; i++) {
if(virtual[i]==" ") {
empty.add(i);
if(virtual[i]!=" ") {
empty.remove(i);
}
}
}
return empty;
}
static int moveC(String[] virtual, String turn, int depth, int alpha, int beta) {
ArrayList<Integer> empty = new ArrayList<Integer>();
empty = filter(virtual);
if(endGameO(virtual)){
return 10+depth;
}
if(endGameX(virtual)){
return -(10+depth);
}
if(empty.size()==0) {
return 0;
}
ArrayList<Integer> moves = new ArrayList<>();
for (int i=0; i<empty.size(); i++) {
int spot = empty.get(i);
if(turn=="O"){
virtual[empty.get(i)] = "O";
int value = moveC(virtual, "X", depth-1, alpha, beta);
if(value > alpha){
alpha = value;
moves.add(value);
}
if(depth==0){
scores.put(spot, value);
}
}
else{
virtual[empty.get(i)] = "X";
int value = moveC(virtual, "O", depth-1, alpha, beta);
if(value < beta){
beta = value;
moves.add(value);
}
}
virtual[empty.get(i)] = " ";
if(alpha >= beta){
break;
}
}
return minimax(moves, turn, alpha, beta);
}
static int best() {
out.println(scores.size() + scores.toString() + " scores");
int value = -999;
int seed = -1;
for(HashMap.Entry<Integer, Integer> entry: scores.entrySet()) {
if(value < entry.getValue()){
value = entry.getValue();
seed = entry.getKey();
}
}
scores.clear();
return seed;
}
}