Крестики-нолики Java Swing (часть 1)
Всем доброго времени суток. На связи Алексей Гулынин. В прошлых статьях, посвященных Java, мы написали игру в процедурном стиле. В данной статье я бы хотел начать писать игру «Крестики-нолики» в объектном стиле с графическим интерфейсом, используя библиотеку Swing. Данная задача поможет разобраться в объектно-ориентированном программировании на Java.
Скачать уже готовый проект полностью можно по ссылке.
Для начала создадим абстрактный класс AGamer, от которого будут наследоваться 2 других наших класса — Player и AI:
public abstract class AGamer {
// метка
protected String sign;
// выстрел в координаты x и y
abstract boolean shot(int x, int y);
// проверка победы
abstract boolean win();
}
Реализуем класс игрока — Player:
public class Player extends AGamer{
// экземпляр нашего поля
MainGameField gameField;
// готовность к стрельбе, если = 0
// то ходит другой игрок
int isShotReady = 1;
// Конструктор
public Player(String sign)
{
this.sign = sign;
}
// Выстрел игрока
boolean shot(int x, int y)
{
gameField = MainGameField.getInstance();
if (!gameField.isCellBusy(x, y))
{
gameField.cell[x][y] = sign;
return true;
}
return false;
}
// Проверка победы
boolean win()
{
gameField = MainGameField.getInstance();
return gameField.checkWin(this.sign);
}
}
Реализуем класс компьютера — AI. Здесь логика точно такая же, как и с примером в процедурном стиле. Скопирую описание данного метода из прошлой статьи:
В случае "aiLevel = 0" используется объект класса Random. В данном случае компьютер просто ходит случайным образом на любую незанятую ячейку.
В случае "aiLevel = 1" компьютер анализирует ситуацию, при которой игрок может выиграть на следующем ходу. Если такой выигрышный ход существует, то компьютер ходит на эту ячейку. Как это реализовано: в двойном цикле проверяются все ячейки, и компьютер в каждую незанятую ячейку ставит метку игрока ("field[i][j] = USER_SIGN"). Далее с помощью метода "checkWin()" проверяется является ли данный ход выигрышным, если да, то эти координаты запоминаются, и компьютер ставит на них свою метку (AI_SIGN). После каждого анализа ячейка обнуляется (("field[i][j] = NOT_SIGN").
В случае "aiLevel = 2" логика работы такая же, как и на первом уровне. Только в данном случае компьютер анализирует свой ход и, если он является выигрышным, ходит.
import java.util.Random;
public class AI extends AGamer {
// Экземпляр игрового поля
MainGameField gameField;
String playerSign = "";
// Уровень интеллекта
static int aiLevel = 0;
// Конструктор
public AI(String sign, int aiLevel, String playerSign)
{
this.sign = sign;
this.playerSign = playerSign;
this.aiLevel = aiLevel;
}
// Выстрел компьютера
boolean shot(int x, int y)
{
gameField = MainGameField.getInstance();
x = -1;
y = -1;
boolean ai_win = false;
boolean user_win = false;
// Находим выигрышный ход
if (aiLevel == 2)
{
for (int i = 0; i < gameField.linesCount; i++)
{
for (int j = 0; j < gameField.linesCount; j++)
{
if (!gameField.isCellBusy(i, j))
{
gameField.cell[i][j] = sign;
if (gameField.checkWin(sign))
{
x = i;
y = j;
ai_win = true;
}
gameField.cell[i][j] = gameField.NOT_SIGN;
}
}
}
}
// Блокировка хода пользователя, если он побеждает на следующем ходу
if (aiLevel > 0)
{
if (!ai_win)
{
for (int i = 0; i < gameField.linesCount; i++)
{
for (int j = 0; j < gameField.linesCount; j++)
{
if (!gameField.isCellBusy(i, j))
{
gameField.cell[i][j] = this.playerSign;
if (gameField.checkWin(this.playerSign))
{
x = i;
y = j;
user_win = true;
}
gameField.cell[i][j] = gameField.NOT_SIGN;
}
}
}
}
}
if (!ai_win && !user_win)
{
do
{
Random rnd = new Random();
x = rnd.nextInt(gameField.linesCount);
y = rnd.nextInt(gameField.linesCount);
}
while (gameField.isCellBusy(x, y));
}
gameField.cell[x][y] = sign;
return true;
}
boolean win()
{
gameField = MainGameField.getInstance();
return gameField.checkWin(this.sign);
}
}
Создадим класс MainForm, который будет отрисовывать основную форму:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MainForm extends JFrame {
public MainForm()
{
// Заголовок формы
setTitle("XO game GUI");
// Границы формы
setBounds(300, 300, 455, 525);
// Можно ли изменять размер формы?
// в нашем случае - нет
setResizable(false);
// При закрытии - форма и программа закрываются
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
// Создаём экземпляр нашего игрового поля
MainGameField gameField = MainGameField.getInstance();
// Создаём панель для кнопок табличного стиля
JPanel buttonPanel = new JPanel(new GridLayout());
// Добавляем игровок поле в центр нашей формы
add(gameField, BorderLayout.CENTER);
// Панель для кнопок добавляем вниз формы
add(buttonPanel, BorderLayout.SOUTH);
JButton btnStart = new JButton("Начать новую игру");
JButton btnEnd = new JButton("Закончить игру");
buttonPanel.add(btnStart);
buttonPanel.add(btnEnd);
// Добавляем обработчик событий для закрытия формы
btnEnd.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e)
{
System.exit(0);
}
});
// Добавляем обработчик событий для создания новой игры
btnStart.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
System.out.println(btnStart.getText());
// Загружаем новую форму (с настройками игры)
GameSettingsForm gameSettingsForm = new GameSettingsForm();
}
});
// Показываем форму
setVisible(true);
}
}
Напоследок создадим основной класс MainClass, из которого мы будем запускать нашу игру:
public class MainClass {
public static void main(String[] args) {
MainForm gameForm = new MainForm();
}
}
В следующей статье мы напишем 2 класса GameSettingsForm и MainGameField.
В данной статье мы начали писать игру "Крестики-нолики" с графическим интерфейсом.
На связи был Алексей Гулынин, оставляйте свои комментарии, увидимся в следующих статьях.




