Найти - Пользователи
Полная версия: крестики нолики с ИИ
Начало » Python для новичков » крестики нолики с ИИ
1 2 3
AlexxGorr
Здравствуйте!
помогите разобраться с ошибкой
Traceback (most recent call last):
File “D_Work/_experience/Python/OOP/TTT_minimax.py”, line 199, in <module>
g.start()
File “D_Work/_experience/Python/OOP/TTT_minimax.py”, line 110, in start
return self.loop()
File “D_Work/_experience/Python/OOP/TTT_minimax.py”, line 98, in loop
turn_ai = AI.ask_enter(self.field)
File “D_Work/_experience/Python/OOP/TTT_minimax.py”, line 188, in ask_enter
intended_value = AI.minimax(board, 0, False)
File “D_Work/_experience/Python/OOP/TTT_minimax.py”, line 155, in minimax
if game.winner(game.token, board):
File “D_Work/_experience/Python/OOP/TTT_minimax.py”, line 57, in winner
if field[i] == field[i] == field[i] == token_var:
TypeError: ‘int’ object is not subscriptable


ког программы:
https://github.com/AlexxGorr/SkillFactory_AlexxGorr/blob/main/TTT_minimax.py
py.user.next
А зачем он игру от доски наследует? И зачем он пользователя от доски наследует?
Доска там максимум сагрегирована должна быть в игре. А пользователь у доски должен операции вызывать, не более того.

Это вот пример, когда изучают не ООП, а “ООП”. Потом будешь говорить, что ООП - это такая ерунда, которая запутывает только всё. А всё дело в том, что учишься просто непонятно у кого. И он тебе типа “преподаёт” типа “ООП”.
AlexxGorr
2 py.user.next
это проблему не решает
py.user.next
AlexxGorr
  
intended_value = AI.minimax(board, 0, False)
Вот в этой строке, ты думаешь, что подаёшь?
AlexxGorr
Число
py.user.next
Питон подучи сначала.
AlexxGorr
ясно спасибо
ivansss
Мда, думал найду решение, но увы.
Автор отозвись если решил проблему.
AlexxGorr
Нне решил.
py.user.next
AlexxGorr
ког программы:
https://github.com/AlexxGorr/SkillFactory_AlexxGorr/blob/main/TTT_minimax.py
from copy import deepcopy
from random import randint
import time
import sys


class Board:
turn_id = 0
busy = []
busy_user = []
busy_ai = []
available = {1: None,
2: None,
3: None,
4: None,
5: None,
6: None,
7: None,
8: None,
9: None}

def __init__(self):
# self.field = list(range(1, 10))
# self.field = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
self.field = [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']

def __str__(self):
res = ''
a = '-' * 17
b = '|'
res += f'{b}{a[:-12]}{b}{a[:-12]}{b}{a[:-12]}{b}'
for i in range(3):
res += f'\n{b} {self.field[0+3*i]} | {self.field[1+3*i]} | {self.field[2+3*i]} {b}'
res += f'\n{b}{a[:-12]}{b}{a[:-12]}{b}{a[:-12]}{b}'
return res


class Game(Board):
turn = randint(1, 5)
Board.turn_id = turn
token = 'XO'
win_situation = ((0, 1, 2),
(3, 4, 5),
(6, 7, 8),
(0, 3, 6),
(1, 4, 7),
(2, 5, 8),
(0, 4, 8),
(2, 4, 6))

def winner(self, token_var, field):
game = Game()
for i in game.win_situation:
if field[i[0]] == field[i[1]] == field[i[2]] == token_var:
return True

def not_win(self):
brd = Board()
if len(brd.busy) >= 9:
return True

def loop(self):
board = Board()
while True:
Board.turn_id += 1

print('busy: ', self.busy)
print('busy_user: ', self.busy_user)
print('busy_ai: ', self.busy_ai)
print('available: ', self.available)
print('field: ', board.field)

if self.winner(self.token[0], board.field):
print('User выиграл!')
break

if self.winner(self.token[1], board.field):
print('AI выиграл!')
break

if Game.not_win(self.field):
print('Ничья')
break

if Board.turn_id % 2 == 0:
print('Ходит User')
turn_user = User.ask_enter(self.field)
board.field[turn_user] = self.token[0]
print(board)
print()

if Board.turn_id % 2 == 1:
print('Ходит AI')
time.sleep(randint(1, 3))
turn_ai = AI.ask_enter(self.field)
board.field[turn_ai-1] = self.token[1]
print(board)
print()

for num, i in enumerate(board.field):
if i == 'X':
self.available.update({num+1: i})
if i == 'O':
self.available.update({num+1: i})

def start(self):
return self.loop()


class User(Board):
def ask_enter(self):
while True:
enter = input('Enter: ')
if ' ' in enter:
print('Не корректный ввод')
continue
if enter.isalpha():
print('Нужна цифра')
continue
if len(enter) > 1:
print('Нужна одна цифра')
continue
if int(enter) < 1:
print('Диапозон ввода от 1 до 9')
continue
if int(enter) in Board.busy:
print('Ячейка занята')
continue
enter_id = int(enter) - 1
Board.busy.append(enter_id+1)
Board.busy_user.append(enter_id+1)
return enter_id


class AI(Board):
# def ask_enter(self):
# board = Board()
# while True:
# enter = randint(1, 9)
# if enter not in board.busy:
# board.busy.append(enter)
# board.busy_ai.append(enter)
# return enter
# else:
# print(f'Ячейка занята: {enter}')
# continue

def minimax(self, board, depth, is_maximizing=None):
# sys.setrecursionlimit(50)
game = Game()
brd = Board()
if game.winner(game.token[0], board):
return 100
if game.winner(game.token[1], board):
return -100
if Game.not_win(brd.field):
return 0
if is_maximizing:
best_value = -sys.maxsize
for key in range(1, 10):
if key not in brd.busy:
board[key] = game.token[1]
intended_value = AI.minimax(board, depth + 1, False)
board[key] = ' '
best_value = max(best_value, intended_value)
else:
best_value = sys.maxsize
for key in range(1, 10):
if key not in brd.busy:
board[key] = game.token[0]
intended_value = AI.minimax(board, depth + 1, True)
board[key] = ' '
best_value = min(best_value, intended_value)
return best_value

def ask_enter(self):
brd = Board()
game = Game()
enter = None
best_value = -sys.maxsize
board = [deepcopy(i) for i in brd.field]
for key in range(1, 10):
if key not in brd.busy:
board[key] = game.token[1]
intended_value = AI.minimax(board, 0, False)
board[key] = ' '
if intended_value > best_value:
best_value = intended_value
enter = key
brd.busy.append(enter)
brd.busy_ai.append(enter)
return enter


g = Game()
g.start()
Вот этот код, чтобы было понятно, о чём идёт речь.

Вот пример, который показывает разницу между вызовом метода экземпляра класса и вызовом метода самого класса
  
>>> class A:
...     
...     def method(self, arg):
...         return arg * 2
... 
>>> a = A()
>>> 
>>> a.method(2)
4
>>> A.method(3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: method() missing 1 required positional argument: 'arg'
>>> 
>>> A.method(None, 3)
6
>>>
Соответственно, метод у класса можно вызывать, но нужно помнить, что там есть первый аргумент self, который никуда не делся.

Вот пример, который показывает, как можно сделать метод, который хранится в классе просто
  
>>> class A:
...     
...     @staticmethod
...     def method(arg):
...         return arg * 2
... 
>>> a = A()
>>> 
>>> a.method(2)
4
>>> A.method(3)
6
>>>
В таком методе self не используется. При этом такой метод можно вызывать у экземпляра класса и вызывать просто у класса одинаково.

То есть это у тебя в коде проявляется незнание питона и того, как он устроен, как в нём что работает, как в нём что вызывается и как эти аргументы присваиваются в вызовах.

Что касается ООП.

Отношение наследования обязательно подразумевает под собой отношение “одно является другим” и никак иначе.

Есть, например, фрукт, яблоко, зелёное яблоко и красное яблоко. Вот яблоко - это фрукт. Зелёное яблоко - это яблоко. Красное яблоко - это яблоко. Красное яблоко, например, зелёным яблоком не является. Но при этом красное яблоко является яблоком, а так как яблоко является фруктом, то красное яблоко является фруктом. С зелёным яблоком то же самое происходит. Зелёное яблоко не является красным яблоком. Но зелёное яблоко является яблоком, а так как яблоко является фруктом, то зелёное яблоко является фруктом.

Получается, что есть фрукт, от него происходит яблоко, а от яблока происходит зелёное яблоко и происходит красное яблоко.
              Фрукт
|
Яблоко
|
------------------------
| |
Зелёное яблоко Красное яблоко
Соответственно, если у яблока определить метод “нарисовать на экране в раскрашенном виде”, то у зёленого яблока этот метод появится и будет рисовать зелёное яблоко зелёным, а у красного яблока этот метод появится и будет рисовать красное яблоко красным. То есть метод будет только в одном месте записан, но появится при этом везде, и везде он будет работать абсолютно по разному. Это то, для чего нужно ООП, - чтобы с помощью наследования сокращать пишущийся код до каких-то мелких записей и устраивать вот такой полиморфизм методов при этом, чтобы маленькая запись делала много изменений в коде сразу, одним махом.

При этом, если какой-то метод определить у фрукта, то этот метод пойдёт не только во все яблоки, но и во все груши, во все ананасы, во все бананы, которые там появятся в будущем, когда тебе заказчик скажет “а мне ещё груши нужны теперь, добавь их в программу”. То есть тебе надо будет добавить в программу совсем немного кода, при этом он весь будет работать уже, потому что всё уже написано заранее и прорастает через наследование само собой.

Вот это то, для чего нужно ООП.

При этом твой код к ООП, где доска равна игра, а игра равна доска, а лопата равна балалайка, не имеет к ООП никакого отношения. И знание или незнание питона тут тоже ни при чём. ООП - это отдельная тема; изучение питона, даже полностью, никак не поможет тебе ООП узнать. ООП надо изучать отдельно от всех языков программирования, потому что это отдельная теория.


tags: oop theory
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB