Выкладывал тут в раздел игры без кода, но понял, что некоторым интересна реализация.
Итак. Описание и инструкция.
Инструкция по игре Реверси (Отелло)
Реверси – это стратегическая настольная игра для двух игроков. Цель игры – захватить как можно больше фишек противника к концу игры.
1. Цель игры:
- В конце игры побеждает игрок, у которого на доске больше фишек своего цвета.
2. Игровое поле и фишки:
- Игровое поле представляет собой квадрат 8x8 клеток.
- В начале игры на поле уже находятся четыре фишки: две белые и две черные, расположенные в центре доски.
3. Ход игры:
- Игроки ходят по очереди.
- Каждый ход игрок должен выложить свою фишку на свободную клетку так, чтобы “захватить” одну или несколько фишек противника.
- Фишки противника считаются “захваченными” (и переворачиваются на цвет игрока), если они находятся между только что положенной фишкой и другой фишкой того же цвета игрока, по прямой линии (горизонтали, вертикали или диагонали).
- Если у игрока нет возможных ходов, он пропускает ход. Если у обоих игроков нет ходов, игра заканчивается.
Пример хода:
Предположим, игрок играет черными фишками. Если игрок выложит черную фишку так, чтобы она оказалась рядом с другой черной фишкой, а между ними были белые фишки, то эти белые фишки переворачиваются и становятся черными.
4. Завершение игры:
Игра заканчивается, когда:
- Все клетки на игровом поле заполнены фишками.
- Нет ни одного возможного хода для обоих игроков (ни один игрок не может сделать ход).
5. Подсчет очков:
- После завершения игры подсчитывается количество фишек каждого цвета.
- Игрок с наибольшим количеством фишек побеждает.
Уровни сложности компьютера:
В данной игре реализованы три уровня сложности компьютера:
-
Легкий уровень:
- Компьютер выбирает случайный ход из всех возможных ходов. Это самый простой уровень, подходящий для новичков. Компьютер не будет анализировать стратегию, а просто сделает случайный ход, который соответствует правилам игры.
-
Средний уровень:
- Компьютер выбирает ход, который захватывает (переворачивает) наибольшее количество фишек противника.
- Если есть несколько ходов, захватывающих одинаковое максимальное количество фишек, компьютер выберет случайный из них. Этот уровень использует базовую стратегию, пытаясь максимизировать захват фишек противника.
-
Сложный уровень:
- Компьютер использует минимаксный алгоритм для выбора лучшего хода. Минимаксный алгоритм анализирует несколько ходов вперед (заданная глубина поиска) и пытается минимизировать максимальный потенциальный выигрыш противника и максимизировать свой собственный.
- Глубина поиска определяет, насколько “умно” будет играть компьютер. Чем больше глубина, тем лучше компьютер будет планировать свои ходы, но тем больше времени потребуется на принятие решения.
- На этом уровне компьютер будет пытаться строить стратегию, блокировать противника и занимать выгодные позиции на поле.
Как выбрать уровень сложности:
- Перед началом игры выберите желаемый уровень сложности компьютера в настройках игры (например, используя переключатель или выпадающий список).
- Вы можете изменить уровень сложности в любой момент, чтобы сделать игру более сложной или, наоборот, более легкой.
Советы по игре:
- Постарайтесь занять угловые клетки, так как фишки в углах не могут быть захвачены.
- Избегайте ходов, которые позволяют противнику занять угловые клетки.
- Планируйте свои ходы на несколько шагов вперед, чтобы предвидеть действия противника.
- Старайтесь захватывать как можно больше фишек противника, но не забывайте о стратегическом расположении своих фишек.
- Помните, что в Реверси важно не только количество захваченных фишек, но и расположение ваших фишек на доске.
Теперь о коде. Интересно же, куда будет ходить компьютер.
&НаСервере
Процедура СделатьХодКомпьютера()
// Логика хода компьютера (реализация зависит от уровня сложности).
// 1. Получаем возможные ходы.
ВычислитьВозможныеХоды();
ВозможныеХоды = ЭтаФорма.ВозможныеХоды.Скопировать();
// 2. Если ходов нет, пропускаем ход.
Если ВозможныеХоды.Количество() = 0 Тогда
Сообщить("У компьютера нет ходов.");
// Смена хода (если необходимо)
Если Объект.ТекущийИгрок = 1 Тогда
Объект.ТекущийИгрок = 2;
Иначе
Объект.ТекущийИгрок = 1;
КонецЕсли;
ВычислитьВозможныеХоды(); // Пересчитываем возможные ходы
//ОбновитьОтображениеПоля(); // Обновляем отображение
Если ЗакончитьИгру() Тогда
ЗавершитьИгру();
КонецЕсли;
Возврат; // Выходим из процедуры, т.к. ход пропущен
КонецЕсли;
// 3. Выбираем ход в зависимости от уровня сложности.
Если Сложность = 1 Тогда
ГСЧ = Новый ГенераторСлучайныхЧисел();
СлучайноеЧисло = ГСЧ.СлучайноеЧисло(0, 8);
ИндексХода = Int(ВозможныеХоды.Количество() * СлучайноеЧисло);
Если ИндексХода > ВозможныеХоды.Количество() - 1 Тогда
ИндексХода = ВозможныеХоды.Количество() - 1;
КонецЕсли;
НомерКлетки = ВозможныеХоды[ИндексХода].Значение;
ИначеЕсли Сложность = 2 Тогда
НомерКлетки = ВыбратьОптимальныйХод(ВозможныеХоды);
Иначе //Сложность = 3
НомерКлетки = ВыбратьМинимаксныйХод();
КонецЕсли;
// Если ход не был выбран, пропускаем ход
Если НомерКлетки = Неопределено Тогда
Сообщить("Не удалось выбрать ход для компьютера.");
Если ЭтотОбъект.ТекущийИгрок = 1 Тогда
ЭтотОбъект.ТекущийИгрок = 2;
Иначе
ЭтотОбъект.ТекущийИгрок = 1;
КонецЕсли;
ВычислитьВозможныеХоды();
//ЗапуститьОбновлениеОтображения();
Если ЗакончитьИгру() Тогда
ЗавершитьИгру();
КонецЕсли;
Возврат;
КонецЕсли;
// 4. Преобразуем номер клетки в координаты строки и столбца.
Строка = Int(НомерКлетки / 10) - 1;
Колонка = (НомерКлетки % 10) - 1;
// Проверка границ! Очень важно!
Если Строка < 0 Или Строка > 7 Или Колонка < 0 Или Колонка > 7 Тогда
Сообщить("Ошибка: Некорректные координаты хода!");
Возврат; // Прерываем выполнение процедуры
КонецЕсли;
// 5. Делаем ход.
СделатьХод(Строка, Колонка);
// 6. Проверяем, не закончилась ли игра.
Если ЗакончитьИгру() Тогда
ЗавершитьИгру();
КонецЕсли;
КонецПроцедуры
В игре Реверси с компьютером реализованы три уровня сложности, отличающиеся стратегией выбора хода компьютером:
1. Лёгкий уровень (Easy):
Принцип выбора хода: Случайный выбор доступного хода. Компьютер просто перебирает все возможные клетки для хода и выбирает одну из них случайным образом.
Стратегия: Отсутствует. Компьютер не анализирует позицию на доске, не предвидит последствия хода и не пытается захватить выгодные клетки.
Сложность: Самый простой уровень. Компьютер совершает много невыгодных ходов, легко подставляется и не представляет серьёзной угрозы для игрока. Он часто отдает углы и другие стратегически важные позиции.
Предназначение: Для начинающих игроков, чтобы освоить правила игры и научиться делать простые ходы.
Если Сложность = 1 Тогда
ГСЧ = Новый ГенераторСлучайныхЧисел();
СлучайноеЧисло = ГСЧ.СлучайноеЧисло(0, 8);
ИндексХода = Int(ВозможныеХоды.Количество() * СлучайноеЧисло);
Если ИндексХода > ВозможныеХоды.Количество() - 1 Тогда
ИндексХода = ВозможныеХоды.Количество() - 1;
КонецЕсли;
НомерКлетки = ВозможныеХоды[ИндексХода].Значение;
2. Средний уровень (Medium):
Принцип выбора хода: Выбор хода с учётом количества перевёрнутых фишек. Компьютер перебирает все доступные клетки для хода и выбирает ту, которая позволяет перевернуть наибольшее количество фишек противника.
Стратегия: Примитивная, но уже присутствующая. Компьютер стремится к максимальной выгоде в краткосрочной перспективе. Он захватывает как можно больше фишек за один ход.
Сложность: Средний уровень. Компьютер играет более осознанно, чем на легком уровне. Он реже совершает совсем невыгодные ходы, но всё ещё не уделяет внимания стратегически важным позициям (углы, края). Он также не может предвидеть последствия своих ходов на несколько шагов вперёд.
Предназначение: Для игроков, которые уже знакомы с правилами игры и хотят немного повысить сложность.
ИначеЕсли Сложность = 2 Тогда
НомерКлетки = ВыбратьОптимальныйХод(ВозможныеХоды);
&НаСервере
Функция ВыбратьОптимальныйХод(ВозможныеХоды)
// Выбирает ход, который переворачивает наибольшее количество фишек.
// Если таких ходов несколько, выбирает случайный из них.
// Возвращает номер клетки или Неопределено, если ходов нет.
Если ВозможныеХоды.Количество() = 0 Тогда
Возврат Неопределено;
КонецЕсли;
МаксимальноеЧислоПеревернутыхФишек = -1;
ЛучшиеХоды = Новый Массив;
Для Каждого ЭлементСписка Из ВозможныеХоды Цикл
НомерКлетки = ЭлементСписка.Значение;
Строка = Int(НомерКлетки / 10) - 1;
Колонка = (НомерКлетки % 10) - 1;
ЧислоПеревернутыхФишек = ПодсчитатьЧислоПеревернутыхФишек(Строка, Колонка, Объект.ТекущийИгрок);
Если ЧислоПеревернутыхФишек > МаксимальноеЧислоПеревернутыхФишек Тогда
МаксимальноеЧислоПеревернутыхФишек = ЧислоПеревернутыхФишек;
ЛучшиеХоды.Очистить();
ЛучшиеХоды.Добавить(НомерКлетки);
ИначеЕсли ЧислоПеревернутыхФишек = МаксимальноеЧислоПеревернутыхФишек Тогда
ЛучшиеХоды.Добавить(НомерКлетки);
КонецЕсли;
КонецЦикла;
Если ЛучшиеХоды.Количество() > 0 Тогда
ГСЧ = Новый ГенераторСлучайныхЧисел();
ИндексХода = ГСЧ.СлучайноеЧисло(0, ЛучшиеХоды.Количество() - 1);
Возврат ЛучшиеХоды[ИндексХода];
Иначе
Возврат Неопределено; // Ходов не найдено
КонецЕсли;
КонецФункции
3. Тяжёлый уровень (Hard):
Принцип выбора хода: Приоритетный выбор ходов, которые ведут к занятию угловых клеток. Если угловые клетки недоступны, то компьютер выбирает ход, переворачивающий максимальное количество фишек. Также, избегает подставления под углы.
Стратегия: Комбинированная. Включает в себя приоритет угловых клеток и оценку количества переворачиваемых фишек. Компьютер стремится захватить углы, так как это стратегически важные позиции. Он также старается избегать ситуаций, когда он подставляет противника под угловую клетку.
Сложность: Самый сложный уровень. Компьютер играет достаточно хорошо, чтобы представлять серьёзную угрозу для большинства игроков. Он хорошо контролирует углы и края доски, что дает ему значительное преимущество. Он также старается не отдавать стратегически важные позиции.
Предназначение: Для опытных игроков, которые хотят испытать свои силы против сильного противника.
Иначе //Сложность = 3
НомерКлетки = ВыбратьМинимаксныйХод();
&НаСервере
Функция ВыбратьМинимаксныйХод()
// Реализует минимаксный алгоритм для выбора оптимального хода.
// Возвращает номер клетки или Неопределено, если ходов нет.
// Параметры алгоритма
Глубина = 3; // Глубина поиска (количество ходов вперед)
// Получаем текущего игрока
ТекущийИгрок = Объект.ТекущийИгрок;
// Получаем возможные ходы
ВычислитьВозможныеХоды();
ВозможныеХоды = ЭтаФорма.ВозможныеХоды.Скопировать();
Если ВозможныеХоды.Количество() = 0 Тогда
Возврат Неопределено; // Нет возможных ходов
КонецЕсли;
// Перебираем все возможные ходы и вычисляем их оценку
ЛучшийХод = Неопределено;
ЛучшаяОценка = -999999; // Минимальное значение (для максимизации)
Для Каждого ЭлементСписка Из ВозможныеХоды Цикл
НомерКлетки = ЭлементСписка.Значение;
Строка = Int(НомерКлетки / 10) - 1;
Колонка = (НомерКлетки % 10) - 1;
// Создаем копию игрового поля
ИгровоеПолеКопия = СкопироватьИгровоеПоле();
// Делаем ход на копии поля
СделатьХодНаКопии(ИгровоеПолеКопия, Строка, Колонка, ТекущийИгрок);
// Вычисляем оценку позиции после хода
Оценка = Минимакс(ИгровоеПолеКопия, Глубина - 1, 1 - ТекущийИгрок); // 1 - ТекущийИгрок - передаем противника
// Если оценка лучше, чем лучшая найденная оценка, запоминаем этот ход
Если Оценка > ЛучшаяОценка Тогда
ЛучшаяОценка = Оценка;
ЛучшийХод = НомерКлетки;
КонецЕсли;
КонецЦикла;
Возврат ЛучшийХод;
КонецФункции
В этой реализации можно было добавить элементы, влияющие на сложность, такие как:
Глубина поиска (выставил в коде глубину 3): Компьютер мог анализировать последствия своих ходов на несколько шагов вперёд. Чем больше глубина поиска, тем сложнее уровень.
Весовые коэффициенты: При оценке позиции на доске, мы могли использовать разные весовые коэффициенты для разных типов клеток (например, углы имели бы самый высокий вес).
Ну и процедура:
&НаСервере
Процедура ВычислитьВозможныеХоды() Экспорт
// Получаем текущего игрока из реквизита обработки
Игрок = Объект.ТекущийИгрок;
// Очищаем таблицу значений ВозможныеХоды
ВозможныеХоды.Очистить();
// Определяем фишку противника
Если Игрок = 1 Тогда
ФишкаПротивника = 2;
Иначе
ФишкаПротивника = 1;
КонецЕсли;
// Перебираем все клетки игрового поля
Для Строка = 0 По 7 Цикл
Для Колонка = 0 По 7 Цикл
// Проверяем, свободна ли клетка
ИмяКолонки = "К" + Строка(Колонка + 1);
Если ЭтаФорма.ИгровоеПоле.Количество() <= Строка Тогда
Продолжить; //Строки нет в таблице
КонецЕсли;
СтрокаТаблицы = ЭтаФорма.ИгровоеПоле[Строка];
Если СтрокаТаблицы[ИмяКолонки] = 0 Тогда // Клетка свободна
// Проверяем возможность хода во всех направлениях
ВозможенХод = Ложь;
Для НаправлениеСтроки = -1 По 1 Цикл
Для НаправлениеКолонки = -1 По 1 Цикл
// Пропускаем нулевое смещение (саму клетку)
Если НаправлениеСтроки = 0 И НаправлениеКолонки = 0 Тогда
Продолжить;
КонецЕсли;
// Проверяем, возможен ли ход в этом направлении
Если ПроверитьНаправление(Строка, Колонка, НаправлениеСтроки, НаправлениеКолонки, Игрок, ФишкаПротивника) Тогда
ВозможенХод = Истина;
Прервать; // Достаточно одного направления
КонецЕсли;
КонецЦикла;
Если ВозможенХод Тогда
Прервать; // Достаточно одного направления
КонецЕсли;
КонецЦикла;
// Если ход возможен хотя бы в одном направлении, добавляем клетку в список возможных ходов
Если ВозможенХод Тогда
ЗначениеКлетки = (Строка + 1) * 10 + (Колонка + 1); // храним координаты в виде "Строка*10 + Колонка"
НоваяСтрока = ВозможныеХоды.Добавить();
НоваяСтрока.Значение = ЗначениеКлетки;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецПроцедуры
Ну, возможно, всё расписал про планирование ходов.
Ранее добавлял в раздел “Игры”, но удалил оттуда. Спасибо тем, кто скачал и протестировал. Теперь выкладываю сюда, с описанием и разбором.
Проверено на следующих конфигурациях и релизах:
- 1С:ERP Управление предприятием 2, релизы 2.5.21.104