ый блок TURN содержит функции управляющие процессом игры .
После запуска программы, переменнаой gameState (Массив размерностью 9) блока Turn присваивается значение 13 (см. ниже) Кнопка №9 окрашивается в красный цвет (ход игрока 1) Программа переходит в состояние ожидания нажатия любой кнопки игрового поля После нажатия любой кнопки происходит событие EXT_PRESS После того как игрок (человек) сделал ход, управление передаётся функции choose_move() 1. Функция choose_move() Выходная переменная - move имеет тип int8 (8-ми разрядное целое), передающее своё значение в одноимённую входную переменную move функции make_move_if_available() С помощью этой функции (choose_move()) программа выбирает свой дальнейший ход,в функцию make_move_if_available() передаётся значение переменной move make_move_if_available(move) Рассмотри подробнее эту ключевую функцию Функция choose_move() состоит из 3 ступеней 1 Итерация - "Попытка выиграть этим ходом" С помощью формулы: { move = 12 - gameState[1] - gameState[3] } пытается определить выигрышный ход для себя, для этого находит недостающее до 12 число в диапазоне [0-8] и если оно не занято (пустая клетка)- это означает что игрок (компьютер) *12 - это контрольная сумма (т.е сумма значений нажатых кнопок необходимых для победы) **Так как ход ещё не выполнен, а только готовится и смещение ходов ещё не произошло, используются не gameState[0], gameState[2] (обозначающие состояния ходов текущего игрока) а gameState[1], gameState[3] свои ходы, сделанные ранее) Рассмотрим соответствующую ситуацию на примере. Допустим у компьютера сложилась следующая ситуация, до победы ему не хватает заполнить одну клетку - левую нижнюю Вот состояние его ходов: gameState[1]=5; gameState[3]=4 (до смещения) По формуле { move = 12 - gameState[1] - gameState[3] } определяется выигрышный ход move Move = 12 - 5 - 4 = 3, он равен 3 Далее отправляется на проверку в функцию Make_move_if_available(3), в данном примере функция вернула значение "истина". Компьютер совершает победный ход. Иначе если данный ход сделать было не возможно - скажем, эта клетка была уже заполнена противником Переходим к ступени 2 2 Итерация - "Не дать сопернику победить" Тут всё наоборот если нет возможности победить, с этого хода - компьютер пытается не дать победить игроку - для этого он вычисляет не хватающее число до контрольной суммы (12) С помощью формулы { move = 12 - gameState[0] - gameState[2] } ищет недостающее число в диапозоне [ 0-8 ] если оно свободно то делает ход Рассмотрим пример: Предположим у игрока следующая ситуация: gameState[0]=4; gameState[2]=1, для победы ему необходимо заполнить левый верхний квадрат (эта кнопка со значением = 7, именно этого и недостаёт до 12 -ти) Что делает компьютер чтобы предотвратить это: он сам вычисляет с помощью простенькой формулы это значение и заполняет его: move = 12 - gameState[0] - gameState[2], находит чему равен move move = 12 - 4 - 1 =7, это значение и передаётся в функцию make_move_if_available(7), а затем уже эта функция решает возможен этот ход или нет, в данном примере это возможно сделать. Если эта клетка занята, или противник не находится в ситуации когда для победы ему нужно заполнить всего одну клетку то переходим к ступени 3 3 Итерация - если дошло до этой ступени, значит ход не критический (нет опасности выиграть или проиграть) Тут происходит генерирование линейной псевдо-случайной последовательности (хода) [0-8], 2. Функция make_move_if_available()
В этой функции происходит проверка условия по Входной переменной Move передаваемая из функции choose_move() и опрос состояния кнопки button_#.enabled если условие выполняется хотя бы в одном из узлов - происходит: 1) Вызов функции shift_game_state(move) Затем выполняется выход из функции и функция возвращает 1 (присваивается переменной Yes) иначе функция возвращает 0 3. Функция shift_game_state() Далее управление передаётся функции shift_game_state(move) Эта функция модифицирует традиционную игру "крестики-нолики". Смысл заключается в том что каждый игрок может оперировать только 3-я крестиками или ноликами. Если каждый из игроков уже заполнил по 3 клетки то происходи смещение, то есть ход gameState[5] приобретает новое значение равное gameState[4], а новый ход (gameState[0]) приобретает значение вновь нажатой кнопки: { Элементы массива [6-8] в процессе игры не участвуют, полученное ими значение (13) при запуске, в ходе игры не изменяется, видимо, разработчики сделали массив gameState размерностью 9 чтобы можно было легко перейти к классической игре "Крестики-нолики" Блоки Button_0 - Button_8 - имеют одинаковую структуру. Константа thisButton имеет значение, соответствующее номеру кнопки. Каждая кнопка имеет 3 состояния: 1) Enabled - Доступна При запуске программы все из 9-ти кнопок переходят в состояние Enabled, цвет кнопок чёрный При нажатии на кнопку button_# проверяется чей ход - Red или Green и исходя из этого окрашивает клетку в соответствующий цвет. Если после нажатия игра заканчивается, все кнопки переходят в состояние Disabled и цвет проигравшей стороны темнеет (GREEN=>DIM_GREEN … - константы) Если кнопка находилась в состоянии Active и at(7,TURN) (тоесть 6 кнопок уже находятся в состоянии Active) то эта кнопка переходит в состояние Enabled и окрашивается в чёрный цвет. Функция is_ a_winner(). В случае если игра окончена переменой Yes (тип Boolean) присваивается значение 1 иначе 0. Функция вызывается после каждого хода игроков и если Yes = 1 вызывается событием Game_Over, игра заканчивается - все кнопки игрового поля переходят в состояние DISABLED GameState - это глобальная переменная (Числовой массив - размерностью 9) в пределах всего блока Turn Он являет собой состояние игры. Функция предназначена для проверки заполнены ли 3 последовательных диагональных, вертикальных или горизонтальных клетки. **Заметим то что только одна функция служит для проверки выигрыша и игрока 1 и игрока 2 - для функции совершенно не важно чей сейчас ход ей достаточно знать состояния текущего хода, тесть значения следующих индексов массива gameState: [0] [2] [4] Как так? Это становится возможным благодаря смещению ходов описанный выше, проще говоря gameState[0],[2],[4] это состояние ходов текущего игрока, который в данный момент выполнил ход и ожидает результата, а gameState[1],[3],[5] это ходы предыдущего игрока Кнопки расположены в следующем порядке, как показано на рисунке - каждая кнопка имеет свою константу ThisButton равная её номеру, сразу бросается в глаза, почему клавиши расположены в таком, казалось бы не правильном порядке, ответ очевиден - если подсчитать сумму любой последовательность 3 клеток (выигрышную последовательность) она равна 12 - именно это и является необходимым условием победы yes =((gameState[0] + gameState[2] + gameState[4]) == 12);
Что бы экспериментально доказать это я провёл серию экспериментов, для этого я ввёл дополнительную глобальную переменную (Массив размерностью 9) передающую значения локальной переменной gameState в Simulink модель, и отобразил её с помощью блока Display, который отображал состояния всех индексов массива Действительно, сумма GameState[0]+GameState[2]+GameState[4]=3+8+1=12 GameState[1]+GameState[3]+GameState[5]=4+5+6=15 Как говорилось выше при старте симуляции переменной gameState(кажному индексу) было присвоено значение 13 (оно могло быть и любой другой, главное больше 12)
|