Udało mi się zrobić bota dla wersji tekstowej, trzeba było trochę poszperać w internecie. Na razie jest tak, że dla planszy 3x3 jest depth 5 albo większy, dla planszy 4x4 działa tylko depth 3
Tutaj kod dla wprowadzania znaków przez komputer oraz algorytm minimax:
def insertLetter(mark,n,i):
if not (BoardList[n][i]=='x' or BoardList[n][i]=='o'):
BoardList[n].pop(i)
BoardList[n].insert(i, mark)
PrintBoard()
else:
exit()
return
def Computer(max_depth):
max_score=-float('inf')
best_moveR = 0
best_moveC = 0
for n in range(0,BoardSize):
for i in range(0,BoardSize):
if BoardList[n][i]==' ':
BoardList[n].pop(i)
BoardList[n].insert(i,'o')
score = minimax(0,False,max_depth)
BoardList[n].pop(i)
BoardList[n].insert(i,' ')
if score > max_score:
max_score = score
best_moveR = n
best_moveC= i
random_moveR = random.randint(0, BoardSize)
random_moveC= random.randint(0, BoardSize)
#insertLetter('o', random_moveR,random_moveC)
insertLetter('o', best_moveR,best_moveC)
return best_moveR,best_moveC
def minimax(depth, isMaximizing,max_depth):
if HorizontalWinC('o') or PlayerWinsVC('o',V) or PlayerWinsD1C('o', D1, win) or PlayerWinsD2C('o',D2,win):
return 1
if HorizontalWinC('x') or PlayerWinsVC('x',V) or PlayerWinsD1C('x', D1, win) or PlayerWinsD2C('x',D2,win):
return -1
if DrawC(win,'o') or DrawC(win,'x') or depth==max_depth:
return 0
if isMaximizing:
max_score = -float('inf')
for n in range(0,BoardSize):
for i in range(0,BoardSize):
if BoardList[n][i]==' ':
BoardList[n].pop(i)
BoardList[n].insert(i,'o')
score = minimax(depth+1,False,max_depth)
BoardList[n].pop(i)
BoardList[n].insert(i,' ')
max_score = max(max_score, score)
return max_score
else:
min_score = float('inf')
for n in range(0,BoardSize):
for i in range(0,BoardSize):
if BoardList[n][i]==' ':
BoardList[n].pop(i)
BoardList[n].insert(i,'x')
score = minimax(depth+1,True,max_depth)
BoardList[n].pop(i)
BoardList[n].insert(i,' ')
min_score = min(min_score, score)
return min_score
Funkcje dla wykrywania wygranej przez komputer:
def DrawC(win,mark):
#diagonal draw 1
for i in range(0,BoardSize):
if BoardList[i][i]==mark:
D1Draw[mark]=True
#this is for testing
'''
print('D1Draw',D1Draw)
'''
#diagonal draw 2
for i in range(0,BoardSize):
if BoardList[BoardSize-1-i][i]==mark:
D2Draw[mark]=True
#this is for testing
'''
print('D2Draw',D2Draw)
'''
VDrawCount=0
HDrawCount=0
#vertical draw
for i in range(0,BoardSize):
if V['x'][i]>=1 and V['o'][i]>=1:
VDrawCount+=1
#horizonal draw
for i in range(0,BoardSize):
if BoardList[i].count('x')>=1 and BoardList[i].count('o')>=1:
HDrawCount+=1
#this is for testing
'''
print('VDrawCount',VDrawCount)
print('HDrawCount',HDrawCount)
'''
#draw conditions
if (D1Draw['x']==True and D1Draw['o']==True
and D2Draw['x']==True and D2Draw['o']==True
and VDrawCount==BoardSize
and HDrawCount==BoardSize):
return win, True
def PlayerWinsD1C(mark,D1,win):
D1=0
#diagonal win 1
for i in range(0,BoardSize):
if BoardList[i][i]==mark:
D1+=1
#print(D1)
#this is for testing
'''
print('D1',mark,D1)
'''
if D1==BoardSize:
return True
def PlayerWinsD2C(mark,D2,win):
D2=0
#diagonal win 2
for i in range(0,BoardSize):
if BoardList[BoardSize-1-i][i]==mark:
D2+=1
#print(D2)
#this is for testing
'''
print('D2',mark,D2)
'''
if D2==BoardSize:
return True
def HorizontalWinC(mark):
for i in range(0,BoardSize):
#print(BoardList[i].count(mark))
# horizontal win
if BoardList[i].count(mark)==BoardSize:
return True
Natomiast napotkałem na problem. Gdy przepisałem to do wersji 3d, funkcje najprawdopodobniej przestały zwracać wartość "True" (debugowałem żeby znaleźć problem).
Nie wiem czym to jest spowodowane. Gdy dam żeby było zawsze True to coś się dzieje, znaczy pozostała część kodu działa:
W Panda3d cała zawartość uruchamiana jest pod klasą MyApp, stąd przed każdą zmienną dopisek "self":
Sama plansza jest wpisana na zewnątrz klasy MyApp, ale próbowałem też ją wpisać wewnątrz MyApp i rezultat był taki sam, nic to nie zmienia.
WERSJA 3d: Plansza
Board0=['','','','']
Board1=['','','','']
Board2=['','','','']
Board3=['','','','']
BoardList=[Board0,Board1,Board2,Board3]
Kod dla ruchu komputera:
def computer(self, max_depth):
self.max_score=-1000
self.best_moveR = 0
self.best_moveC = 0
self.random_moveR = random.randint(0, self.BoardSize-1)
self.random_moveC = random.randint(0, self.BoardSize-1)
for i in range(0,self.BoardSize):
for n in range(0,self.BoardSize):
if BoardList[n][i]=='':
BoardList[n].pop(i)
BoardList[n].insert(i,'o')
self.score = self.minimax(0,False,max_depth)
BoardList[n].pop(i)
BoardList[n].insert(i,'')
if self.score > self.max_score:
self.max_score = self.score
self.best_moveR = n
self.best_moveC = i
print(self.best_moveR)
print(self.best_moveC)
'''
try:
self.insertLetter('o', self.random_moveR,self.random_moveC)
except:
try:
self.insertLetter('o', self.random_moveR,self.random_moveR)
except:
self.insertLetter('o', self.best_moveR,self.best_moveC)
'''
self.insertLetter('o', self.best_moveR,self.best_moveC)
return self.best_moveR,self.best_moveC
def minimax(self, depth, isMaximizing,max_depth):
if self.HorizontalWinC('o') or self.PlayerWinsVC('o') or self.PlayerWinsD1C('o') or self.PlayerWinsD2C('o'):
return 1
if self.HorizontalWinC('x') or self.PlayerWinsVC('x') or self.PlayerWinsD1C('x') or self.PlayerWinsD2C('x'):
return -1
if self.DrawC('o') or self.DrawC('x') or depth==max_depth:
return 0
print(self.HorizontalWinC('o'),self.PlayerWinsVC('o'),self.PlayerWinsD1C('o'),self.PlayerWinsD2C('o'))
print(self.HorizontalWinC('x'),self.PlayerWinsVC('x'),self.PlayerWinsD1C('x'),self.PlayerWinsD2C('x'))
print(self.DrawC('o'),self.DrawC('x'))
if isMaximizing:
self.max_score = -1000
for n in range(0,self.BoardSize):
for i in range(0,self.BoardSize):
if BoardList[n][i]=='':
BoardList[n].pop(i)
BoardList[n].insert(i,'o')
self.score = self.minimax(depth+1,False,max_depth)
BoardList[n].pop(i)
BoardList[n].insert(i,'')
if self.score > self.max_score:
self.max_score = self.score
return self.max_score
else:
self.max_score = 800
for n in range(0,self.BoardSize):
for i in range(0,self.BoardSize):
if BoardList[n][i]=='':
BoardList[n].pop(i)
BoardList[n].insert(i,'x')
self.score = self.minimax(depth+1,True,max_depth)
BoardList[n].pop(i)
BoardList[n].insert(i,'')
if self.score < self.max_score:
self.max_score = self.score
return self.max_score
Oraz funkcje, które nie zwracają teraz "True" (są bardzo podobne jak w przypadku wersji tekstowej)
def HorizontalWinC(self,mark):
for i in range(0,self.BoardSize):
print(BoardList[i].count(mark))
#checking horizontal win
if BoardList[i].count(mark)==self.BoardSize:
return True
def PlayerWinsVC(self,mark):
for i in range(0,self.BoardSize):
self.VC[mark][i]=0
for i in range(0,self.BoardSize):
#counting marks x or o in vertical lines
for n in range(0,self.BoardSize):
if BoardList[n][i]==mark:
self.VC[mark][i]+=1
print('VC'+str(self.VC))
if self.VC[mark][i]==self.BoardSize:
return True
def PlayerWinsD1C(self,mark):
self.D1C[mark]=0
for i in range(0,self.BoardSize):
if BoardList[i][i]==mark:
self.D1C[mark]+=1
print('D1C'+str(self.D1C))
if self.D1C[mark]==self.BoardSize:
return True
def PlayerWinsD2C(self,mark):
self.D2C[mark]=0
for i in range(0,self.BoardSize):
if BoardList[self.BoardSize-1-i][i]==mark:
self.D2C[mark]+=1
print('D2C'+str(self.D2C))
if self.D2C[mark]==self.BoardSize:
return True
def DrawC(self,mark):
self.D1DrawC[mark]=False
self.D2DrawC[mark]=False
for i in range(0,self.BoardSize):
self.VC[mark][i]=0
for i in range(0,self.BoardSize):
if BoardList[i][i]==mark:
self.D1DrawC[mark]=True
print('D1DrawC',self.D1DrawC)
#this is for testing
#diagonal draw 2
for i in range(0,self.BoardSize):
if BoardList[self.BoardSize-1-i][i]==mark:
self.D2DrawC[mark]=True
print('D2DrawC',self.D2DrawC)
#this is for testing
self.VDrawCountC=0
self.HDrawCountC=0
#vertical draw
for i in range(0,self.BoardSize):
if self.VC['x'][i]>=1 and self.VC['o'][i]>=1:
self.VDrawCountC+=1
print('VDrawCountC',self.VDrawCountC)
#horizonal draw
for i in range(0,self.BoardSize):
if BoardList[i].count('x')>=1 and BoardList[i].count('o')>=1:
self.HDrawCountC+=1
print('HDrawCountC',self.HDrawCountC)
#this is for testing
if (self.D1DrawC['x']==True and self.D1DrawC['o']==True
and self.D2DrawC['x']==True and self.D2DrawC['o']==True
and self.VDrawCountC==self.BoardSize
and self.HDrawCountC==self.BoardSize):
return True
Nie wiem czemu to teraz nie działa, jedyna różnica jest taka, że wszystko jest w klasie MyApp, która jest uruchomiona w pętli, może ktoś zna na to odpowiedź, albo korzystał z tego silnika... ?
Liczniki liczą podobnie jak w przypadku wersji tekstowej.
ps: wiem już że nazwy funkcji pisze się małą literą, ale jeszcze tego nie poprawiłem :(