|
|
|
Почему у нейронки в крестики-нолики лучший ход всегда в 0? | ☑ | ||
|---|---|---|---|---|
|
0
program345
01.08.25
✎
17:32
|
привет!
Код модуля формы: &НаКлиенте
Перем КоличествоПолей; // 3x3
//Перем КоличествоПолей = 9; // 3x3
&НаКлиенте
Перем Веса; // Массив весов нейронной сети
&НаКлиенте
Перем Ввод; // Массив весов нейронной сети
// Инициализация нейронной сети
&НаКлиенте
Процедура ИнициализироватьСеть()
Веса = Новый Массив(КоличествоПолей);
Для i = 0 По КоличествоПолей-1 Цикл
Веса[i] = СлучайноеЧисло(0, 1); // Инициализация случайными весами
КонецЦикла;
КонецПроцедуры
&НаКлиенте
Функция СлучайноеЧисло(Число1,Число2)
ГСЧ = Новый ГенераторСлучайныхЧисел;
Возврат ГСЧ.СлучайноеЧисло(Число1,Число2);
КонецФункции // ()
// Функция активации (ReLU)
&НаКлиенте
Функция ReLU(х)
Возврат Макс(0, х);
КонецФункции
// Получить лучший ход на основе текущего состояния
&НаКлиенте
Функция ПолучитьЛучшийХод(СостояниеДоски)
ЛучшийХод = -1;
МаксОценка = -9999;
Для i = 0 По КоличествоПолей-1 Цикл
Если СостояниеДоски[i] = 0 Тогда // Если клетка свободна
Оценка = 0;
Для j = 0 По КоличествоПолей-1 Цикл
Оценка = Оценка + Веса[j] * СостояниеДоски[j];
КонецЦикла;
Оценка = Оценка + Веса[i] * 1; // Предполагаем наш ход в эту клетку
Оценка = ReLU(Оценка);
Если Оценка > МаксОценка Тогда
МаксОценка = Оценка;
ЛучшийХод = i;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Возврат ЛучшийХод;
КонецФункции
// Обновление весов на основе результата игры
&НаКлиенте
Процедура ОбновитьВеса(СостояниеДоски, ВыбранныйХод, Результат, СкоростьОбучения = 0.1)
// Вычисляем целевую оценку
ЦелеваяОценка = Результат; // 1 - победа, 0 - ничья, -1 - поражение
// Предсказанная оценка до обучения
Предсказание = 0;
Для i = 0 По КоличествоПолей-1 Цикл
Предсказание = Предсказание + Веса[i] * СостояниеДоски[i];
КонецЦикла;
Предсказание = ReLU(Предсказание);
// Ошибка
Ошибка = ЦелеваяОценка - Предсказание;
// Обновляем веса
Для i = 0 По КоличествоПолей-1 Цикл
Веса[i] = Веса[i] + СкоростьОбучения * Ошибка * СостояниеДоски[i];
КонецЦикла;
Веса[ВыбранныйХод] = Веса[ВыбранныйХод] + СкоростьОбучения * Ошибка * 1;
КонецПроцедуры
// Проверка победы
&НаКлиенте
Функция ПроверитьПобеду(Доска, Игрок)
// Горизонтальные линии
Если (Доска[0] = Игрок И Доска[1] = Игрок И Доска[2] = Игрок) Или
(Доска[3] = Игрок И Доска[4] = Игрок И Доска[5] = Игрок) Или
(Доска[6] = Игрок И Доска[7] = Игрок И Доска[8] = Игрок) Тогда
Возврат Истина;
КонецЕсли;
// Вертикальные линии
Если (Доска[0] = Игрок И Доска[3] = Игрок И Доска[6] = Игрок) Или
(Доска[1] = Игрок И Доска[4] = Игрок И Доска[7] = Игрок) Или
(Доска[2] = Игрок И Доска[5] = Игрок И Доска[8] = Игрок) Тогда
Возврат Истина;
КонецЕсли;
// Диагонали
Если (Доска[0] = Игрок И Доска[4] = Игрок И Доска[8] = Игрок) Или
(Доска[2] = Игрок И Доска[4] = Игрок И Доска[6] = Игрок) Тогда
Возврат Истина;
КонецЕсли;
Возврат Ложь;
КонецФункции
// Проверка ничьи
&НаКлиенте
Функция ПроверитьНичью(Доска)
Для i = 0 По КоличествоПолей-1 Цикл
Если Доска[i] = 0 Тогда
Возврат Ложь;
КонецЕсли;
КонецЦикла;
Возврат Истина;
КонецФункции
// Процедура обучения
&НаКлиенте
Процедура ОбучитьНейронку(КоличествоЭпох = 10000)
ИнициализироватьСеть();
Для эпоха = 1 По КоличествоЭпох Цикл
Доска = Новый Массив(КоличествоПолей);
Для i = 0 По КоличествоПолей-1 Цикл
Доска[i] = 0; // 0 - пусто, 1 - X, -1 - O
КонецЦикла;
Ход = 1; // 1 - X (нейронка), -1 - O (оппонент)
Пока Истина Цикл
Если Ход = 1 Тогда
// Ход нейронки
ЛучшийХод = ПолучитьЛучшийХод(Доска);
Если ЛучшийХод = -1 Тогда
Прервать; // Нет доступных ходов
КонецЕсли;
Доска[ЛучшийХод] = 1;
Если ПроверитьПобеду(Доска, 1) Тогда
ОбновитьВеса(Доска, ЛучшийХод, 1); // Победа
Прервать;
ИначеЕсли ПроверитьНичью(Доска) Тогда
ОбновитьВеса(Доска, ЛучшийХод, 0); // Ничья
Прервать;
КонецЕсли;
Иначе
// Ход оппонента (случайный)
СвободныеКлетки = Новый Массив;
Для i = 0 По КоличествоПолей-1 Цикл
Если Доска[i] = 0 Тогда
СвободныеКлетки.Добавить(i);
КонецЕсли;
КонецЦикла;
Если СвободныеКлетки.Количество() = 0 Тогда
Прервать;
КонецЕсли;
СлучайныйХод = СвободныеКлетки[СлучайноеЧисло(0, СвободныеКлетки.Количество()-1)];
Доска[СлучайныйХод] = -1;
Если ПроверитьПобеду(Доска, -1) Тогда
ОбновитьВеса(Доска, ЛучшийХод, -1); // Поражение
Прервать;
ИначеЕсли ПроверитьНичью(Доска) Тогда
ОбновитьВеса(Доска, ЛучшийХод, 0); // Ничья
Прервать;
КонецЕсли;
КонецЕсли;
Ход = -Ход; // Смена хода
КонецЦикла;
КонецЦикла;
Сообщить("Обучение завершено!");
КонецПроцедуры
// Пример игры против обученной нейронки
&НаКлиенте
Процедура ИгратьПротивНейронки()
Доска = Новый Массив(КоличествоПолей);
Для i = 0 По КоличествоПолей-1 Цикл
Доска[i] = 0;
КонецЦикла;
Ход = 1; // 1 - нейронка (X), -1 - игрок (O)
Пока Истина Цикл
// Отображаем доску
СтрокаДоски = "";
Для i = 0 По 8 Цикл
Если Доска[i] = 1 Тогда
СтрокаДоски = СтрокаДоски + "X";
ИначеЕсли Доска[i] = -1 Тогда
СтрокаДоски = СтрокаДоски + "O";
Иначе
СтрокаДоски = СтрокаДоски + i;
КонецЕсли;
Если (i+1) % 3 = 0 Тогда
СтрокаДоски = СтрокаДоски + Символы.ПС;
Иначе
СтрокаДоски = СтрокаДоски + "|";
КонецЕсли;
КонецЦикла;
Сообщить(СтрокаДоски);
Если Ход = 1 Тогда
// Ход нейронки
ЛучшийХод = ПолучитьЛучшийХод(Доска);
Если ЛучшийХод = -1 Тогда
Сообщить("Ничья!");
Возврат;
КонецЕсли;
Доска[ЛучшийХод] = 1;
Сообщить("Нейронка походила в клетку " + ЛучшийХод);
Если ПроверитьПобеду(Доска, 1) Тогда
Сообщить("Нейронка победила!");
Возврат;
ИначеЕсли ПроверитьНичью(Доска) Тогда
Сообщить("Ничья!");
Возврат;
КонецЕсли;
Иначе
// Ход игрока
Сообщить("Ваш ход (введите номер клетки 0-8):");
ВвестиЧислоМодально = ВвестиЧисло(Ввод);
Если Не ВвестиЧислоМодально Тогда
Пока Ввод < 0 Или Ввод > 8 Или Доска[Ввод] <> 0 Цикл
Сообщить("Некорректный ход. Попробуйте еще раз:");
ВвестиЧислоМодально = ВвестиЧисло(Ввод);
КонецЦикла;
КонецЕсли;
Доска[Ввод] = -1;
Если ПроверитьПобеду(Доска, -1) Тогда
Сообщить("Вы победили!");
Возврат;
ИначеЕсли ПроверитьНичью(Доска) Тогда
Сообщить("Ничья!");
Возврат;
КонецЕсли;
КонецЕсли;
Ход = -Ход; // Смена хода
КонецЦикла;
КонецПроцедуры
// Запуск обучения и игры
&НаКлиенте
Процедура Главная()
ОбучитьНейронку(10000); // Обучаем на 10,000 играх
ИгратьПротивНейронки(); // Играем против обученной нейронки
КонецПроцедуры
&НаКлиенте
Процедура ОбучитьНейронку_1(Команда)
ОбучитьНейронку();
КонецПроцедуры
&НаКлиенте
Процедура ИгратьПротивНейронки_2(Команда)
ИгратьПротивНейронки();
КонецПроцедуры
КоличествоПолей =9;
Что мне кажется тут где-то ошибка в лучшем коде или все верно?
|
|||
|
1
OldCondom
02.08.25
✎
14:12
|
Ошибка где-то в лучшем коде или вам кажется. Возможно, все верно.
|
|||
|
2
Greeen
02.08.25
✎
21:45
|
Лучше уберите этот код из открытого доступа, иначе этот ИИ сможет развиться и уничтожить человечество
|
|||
|
3
Волшебник
02.08.25
✎
21:46
|
Если противник первым ходом поставил в центр, то надо ставить в угол, иначе проиграешь
|
|||
|
4
b_ru
03.08.25
✎
01:00
|
(3) Если противник первым ходом поставил в центре, то он уже не выиграл.
|
|||
|
5
Волшебник
03.08.25
✎
15:49
|
(4) Если после первого хода противника в центр вы поставите в боковушечку, то вы уже проиграли
|
|||
|
6
Chai Nic
03.08.25
✎
10:26
|
(2) Раньше это называли "метод Монте-Карло", а теперь "нейросеть")
|
|||
|
7
Волшебник
03.08.25
✎
20:19
|
(4) Вы не понимаете, что крестики-нолики - это ничья? Вы совсем дурак и в школе не учились? Вы вообще хоть раз проигрывали в крестики-нолики? Если да, то я вам так скажу: когда вы перестали проигрывать в крестики-нолики без поддавков, тогда вы стали программистом. А потом вы начали поддаваться и ребёнок начал иногда выигрывать. Так вы стали родителем, то есть начали программировать новых программистов.
|
| Форум | Правила | Описание | Объявления | Секции | Поиск | Книга знаний | Вики-миста |