Я уже прочитал много тем по Tic Tac Toe на StackOverflow. И я нашел стратегию в Википедии, которая подходит для моего презентационного проекта:
Игрок может играть в идеальный крестики-нолики, если он выбирает ход с наивысшим приоритетом в следующей таблице[3].
1) Выиграть: Если у вас есть два хода подряд, сыграйте третий, чтобы получить три хода подряд. подряд.
2) Блокировать: Если у противника есть два хода подряд, сыграйте третий ход, чтобы заблокировать их.
3) Вилка: Создайте возможность, когда вы можете выиграть двумя способами.
4) Блокировать вилку противника:
Вариант 1: Создайте две вилки подряд, чтобы заставить противника защищаться, при условии, что если это не приведет к созданию вилки или выигрышу. Для Например, если "X" имеет угол, "O" имеет центр, и "X" имеет противоположный угол, "О" не должен играть в угол, чтобы выиграть. (Игра в угол в этом сценарии создает вилку для победы "X".)
Вариант 2: Если есть конфигурация, в которой противник может сделать вилку, блокируйте эту вилку.
5) Центр: Играйте в центре.
6) Противоположный угол: Если противник находится в углу, играйте в противоположный угол.
7) Пустой угол: Играйте в пустой угол.
8) Пустая сторона: Сыграйте пустую сторону.
Я следую этим шагам, и компьютер никогда не проигрывает. Однако способ, которым он атакует, не идеален. Потому что я не знаю, как сделать шаг 3. Вот что я делаю в шаге 3: сканирую каждую клетку, проверяю, создает ли токен на этой клетке вилку, затем кладу его туда.
private void step3() // Create Fork.
{
int[] dummyField = (int[])field.Clone();
// Try Level 1 Dummy
for (int i = 0; i < 9; i++)
{
if (dummyField[i] != 0) continue;
dummyField[i] = 2;
if (countFork(dummyField, 2) >= 2)
{
nextCell = i;
return;
}
dummyField[i] = 0;
}
}
Пожалуйста, дайте мне совет по поводу этого шага.
EDIT1: Функция count fork будет считать, сколько вилок есть у компьютера (токенов компьютера - 2, токенов игрока - 1, потому что я использовал этот метод и для шага 4, поэтому в функции countFork
есть параметр token).
EDIT2: Причина, по которой я говорю, что это не идеально, заключается в следующем (процессор идет первым, и его клетки синие, клетки человека - красные). Как вы можете видеть, если я помещу ячейку сверху, компьютер выиграет. Но если я поставлю правую клетку, то будет ничья, хотя компьютер все равно может победить.
EDIT3: Не знаю почему, но я закомментировал шаг 3, и компьютер играет... отлично! Я очень удивлен! Вот моя функция countFork (мне нужно перенести этот код на Alice, которая не поддерживает 2-мерные массивы, поэтому я использую getNumberFromXY для преобразования 2-мерного массива в 1-мерный):
private int countFork(int[] field, int token)
{
int result = 0;
// Vertical
int cpuTokenCount;
int spareCell;
for (int x = 0; x < 3; x++)
{
cpuTokenCount = 0;
spareCell = -1;
for (int y = 0; y < 3; y++)
{
if (field[getNumberFromXY(x, y)] == token)
cpuTokenCount++;
else if (field[getNumberFromXY(x, y)] == 0)
spareCell = getNumberFromXY(x, y);
}
if (cpuTokenCount == 2 && spareCell != -1) result++;
}
// Horizontal
for (int y = 0; y < 3; y++)
{
cpuTokenCount = 0;
spareCell = -1;
for (int x = 0; x < 3; x++)
{
if (field[getNumberFromXY(x, y)] == token)
cpuTokenCount++;
else if (field[getNumberFromXY(x, y)] == 0)
spareCell = getNumberFromXY(x, y);
}
if (cpuTokenCount == 2 && spareCell != -1) result++;
}
// Top-Left To Lower-Right Diagonal
cpuTokenCount = 0;
spareCell = -1;
for (int i = 0; i < 3; i++)
{
if (field[getNumberFromXY(i, i)] == token)
cpuTokenCount++;
else if (field[getNumberFromXY(i, i)] == 0)
spareCell = getNumberFromXY(i, i);
}
if (cpuTokenCount == 2 && spareCell != -1) result++;
// Top-Right To Lower-Left Diagonal
cpuTokenCount = 0;
spareCell = -1;
for (int i = 0; i < 3; i++)
{
if (field[getNumberFromXY(2 - i, i)] == token)
cpuTokenCount++;
else if (field[getNumberFromXY(2 - i, i)] == 0)
spareCell = getNumberFromXY(2 - i, i);
}
if (cpuTokenCount == 2 && spareCell != -1) result++;
return result;
}
EDIT4: Исправил ошибку согласно soandos, и обновил код в EDIT 3, теперь он работает идеально!