Моя цель -- написать алгоритм, который будет обучаться самостоятельно
Ну а чего делайте, скажем нейронную сеть. Количество входов - количество клеток игрового поля. Пустой клетке соответствует ноль, крестику единица, нолику - минус единица. На входе сети текущее состояние игрового поля. На выходе сети принятое решение, выраженное, скажем, в координатах клетки, куда объект ставит свою метку и тп. Правда на этапе обучения сеть может делать недопустимые ходы в занятые клетки. Интерпретировать это, например, как пропуск хода.
Дальше надо думать как обучать. Первый вариант - это условно оценить каждое решение. Приближает ли оно нас к победе? Мешает ли противнику выиграть? Одновременно решает обе эти задачи? Начинаем обучаемому объекту предъявлять ПС-сгенерированные состояния игрового поля, получаем решение, вычисляем показатель качества решения, изменяем веса нейронов. Второй вариант, который мне от чего-то кажется очень привлекательным, заключается в том, что обучаемый объект проводит большое количество игр (скажем не менее 100) с рандомным противником (который просто случайным образом делает незапрещённые ходы) при этом фиксируется количество побед и оценивается "вероятность победы". Она то и будет показателем качества обучения. Корректируются веса. Проводим следующие сотню игр и тд. На начальных этапах возможно вероятность победы будет нулевая, тогда новые веса можно генерировать случайно.
Потом можно заменить рандомного противника "разумным" - либо играть самому, либо использовать какой-либо оптимальный алгоритм. После заданного количества игр алгоритм снова оценит вероятность своей победы и снова скорректирует веса нейронов.
Первая пробема сразу видна и заключена в том, что неизвестно количество слоёв и количество нейронов в слоях сети. Но экспериментировать никто не мешает.
Но всё это описанное не самообучающийся алгоритм, а обучающийся, поскольку целевая функция ему задаётся извне. Самообучающийся должен был бы сформировать её самостоятельно, но дались эти крестики-нолики программе...