В этом посте я расскажу, как свести (для учебных целей) задачу финального хода в игре «крестики-нолики» к проблеме выполнимости булевой формулы (КНФ).
Обозначим = «в стоит X», = «в стоит O». Составим КНФ, обозначающую возможность сделать ход «крестиками», чтобы они выиграли.
Воспользуемся SAT-солвером pycryptosat.
1) В одной и той же клетке не может одновременно находиться X и O.
Код
for i in inds: for j in inds: add_clause([('X', i, j, '-'), ('O', i, j, '-')])
2) Укажем, какие клетки заняты.
Код
for i in inds: for j in inds: if field[i][j] == 'X': add_clause([('X', i, j, '+')]) elif field[i][j] == 'O': add_clause([('O', i, j, '+')])
3) Ход X производится в одну клетку:
Эта формула получена перемножением импликаций .
Код
for i in inds: for j in inds: if field[i][j] == '.': for k in inds: for l in inds: if (i, j) != (k, l) and field[k][l] == '.': add_clause([('X', i, j, '-'), ('X', k, l, '-')])
4) В свободных клетках нет O.
Код
for i in inds: for j in inds: if field[i][j] == '.': add_clause([('O', i, j, '-')])
5) Выигрыш, если три X стоят в одной горизонтали, вертикали или диагонали.
Автоматизируем перевод этой формулы к КНФ, применив распределительный закон.
Код
# Автоматически приведем условие выигрыша из ДНФ к КНФ dnf = [[(1, 1), (1, 2), (1, 3)], [(2, 1), (2, 2), (2, 3)], [(3, 1), (3, 2), (3, 3)], [(1, 1), (1, 2), (1, 3)], [(1, 2), (2, 2), (3, 2)], [(1, 3), (2, 3), (3, 3)], [(1, 1), (2, 2), (3, 3)], [(1, 3), (2, 2), (3, 1)]] dnf_len = len(dnf) # генерируем кортежи длины 8 из элементов 0, 1, 2 for seq in itertools.product([0, 1, 2], repeat=dnf_len): clause = [] for p in range(dnf_len): i, j = dnf[p][seq[p]] clause.append(('X', i, j, '+')) add_clause(clause)
Код примера
from pycryptosat import Solver import itertools s = Solver() # индекс переменной x_ij # i, j = 1, 2, 3 # индекс = 1..9 def vind(s, i, j): assert(s == 'X' or s == 'O') ind = (i - 1) * 3 + j if s == 'O': ind += 9 return ind # добавить дизъюнкт к КНФ # vlist - список пар (i, j) def add_clause(vlist): l = [] for p, i, j, r in vlist: if r == '+': l.append(vind(p, i, j)) elif r == '-': l.append(-vind(p, i, j)) else: raise ValueError("+ or - expected") s.add_clause(l) inds = range(1, 4) # индексы клеток # инициализация игрового поля field = [['.' for _ in range(0, 4)] for _ in range(0, 4)] # игровая ситуация field[1][1] = 'X' field[1][3] = 'O' field[2][1] = 'O' field[3][3] = 'X' # вывод поля for i in inds: for j in inds: print(field[i][j], end='') print() # 1. В одной и той же клетке не может одновременно находиться X и O for i in inds: for j in inds: add_clause([('X', i, j, '-'), ('O', i, j, '-')]) # 2. Укажем, какие клетки заняты for i in inds: for j in inds: if field[i][j] == 'X': add_clause([('X', i, j, '+')]) elif field[i][j] == 'O': add_clause([('O', i, j, '+')]) # 3. Ход X производится в одну клетку for i in inds: for j in inds: if field[i][j] == '.': for k in inds: for l in inds: if (i, j) != (k, l) and field[k][l] == '.': add_clause([('X', i, j, '-'), ('X', k, l, '-')]) # 4. В свободных клетках нет O for i in inds: for j in inds: if field[i][j] == '.': add_clause([('O', i, j, '-')]) # 5. Выигрыш, если три X стоят в одной горизонтали, вертикали или диагонали # Автоматически приведем условие выигрыша из ДНФ к КНФ dnf = [[(1, 1), (1, 2), (1, 3)], [(2, 1), (2, 2), (2, 3)], [(3, 1), (3, 2), (3, 3)], [(1, 1), (1, 2), (1, 3)], [(1, 2), (2, 2), (3, 2)], [(1, 3), (2, 3), (3, 3)], [(1, 1), (2, 2), (3, 3)], [(1, 3), (2, 2), (3, 1)]] dnf_len = len(dnf) # генерируем кортежи длины 8 из элементов 0, 1, 2 for seq in itertools.product([0, 1, 2], repeat=dnf_len): clause = [] for p in range(dnf_len): i, j = dnf[p][seq[p]] clause.append(('X', i, j, '+')) add_clause(clause) sat, solution = s.solve() #print(sat) #print(solution) if not sat: print('Нет решения') else: new_field = [['.' for _ in range(0, 4)] for _ in range(0, 4)] for i in inds: for j in inds: if solution[vind('X', i, j)]: new_field[i][j] = 'X' elif solution[vind('O', i, j)]: new_field[i][j] = 'O' # вывод поля print("Решение:") for i in inds: for j in inds: print(new_field[i][j], end='') print()
Спасибо за просмотр. Буду рад ответить на вопросы.
ссылка на оригинал статьи https://habr.com/ru/articles/857352/
Добавить комментарий