|
| 1 | +#Hearder Files |
| 2 | +import cv2 |
| 3 | +import numpy as np |
| 4 | +from random import randint |
| 5 | +#----------------------------------------------------------------------------------- |
| 6 | + |
| 7 | +#Classes |
| 8 | +class Block() : |
| 9 | + def __init__(self,i,j) : |
| 10 | + self.value = None |
| 11 | + self.pos = (i,j) |
| 12 | + def setValue(self,value) : |
| 13 | + self.value = value |
| 14 | + |
| 15 | +#----------------------------------------------------------------------------------- |
| 16 | + |
| 17 | +class GUI() : |
| 18 | + def __init__(self,windowName) : |
| 19 | + self.windowName = windowName |
| 20 | + self.width,self.height = 400,400 |
| 21 | + self.menuHeight = 100 |
| 22 | + self.image = np.zeros((self.height+self.menuHeight,self.width,3),np.uint8) |
| 23 | + self.turn = 1 |
| 24 | + self.vsCom = 0 |
| 25 | + self.reset() |
| 26 | + #----------------------------------------------------------------------------------- |
| 27 | + #Reset Game |
| 28 | + def reset(self) : |
| 29 | + self.blocks = [] |
| 30 | + self.win = False |
| 31 | + self.change = True |
| 32 | + self.selected = False |
| 33 | + for i in range(3) : |
| 34 | + row = [] |
| 35 | + for j in range(3) : |
| 36 | + row.append([Block(i,j),(j*(self.width/3)+3,i*(self.height/3)+3),((j+1)*(self.width/3)-3,(i+1)*(self.height/3)-3)]) |
| 37 | + self.blocks.append(row) |
| 38 | + #----------------------------------------------------------------------------------- |
| 39 | + #Drawing GUI and Game Screen |
| 40 | + def draw(self) : |
| 41 | + self.image = np.zeros((self.height+self.menuHeight,self.width,3),np.uint8) |
| 42 | + for i in range(3) : |
| 43 | + for j in range(3) : |
| 44 | + start_point = self.blocks[i][j][1] |
| 45 | + end_point = self.blocks[i][j][2] |
| 46 | + cv2.rectangle(self.image,start_point,end_point,(255,255,255),-1) |
| 47 | + value = " " if self.blocks[i][j][0].value is None else self.blocks[i][j][0].value |
| 48 | + cv2.putText(self.image,value,(j*(self.width/3)+25,(i*self.height/3)+100),cv2.FONT_HERSHEY_SIMPLEX,5,(0,0,0),5) |
| 49 | + if self.checkWin() : |
| 50 | + string = ("Player "+str(self.turn)+" Wins" if self.turn!=self.vsCom else "Computer Wins") if self.turn==1 else ("Player "+str(2)+" Win" if self.turn!=self.vsCom else "Computer Win") |
| 51 | + else : |
| 52 | + if not self.checkDraw() : |
| 53 | + string = ("Player "+str(self.turn)+"'s Turn" if self.turn!=self.vsCom else "Computer's Turn") if self.turn==1 else ("Player "+str(2)+"'s Turn" if self.turn!=self.vsCom else "Computer's Turn") |
| 54 | + else : |
| 55 | + string = "Match Draw!!" |
| 56 | + cv2.putText(self.image,string,(self.width/2-70,self.height+30),cv2.FONT_HERSHEY_SIMPLEX,0.5,(255,255,255),1) |
| 57 | + cv2.putText(self.image,"R - Reset",(10,self.height+60),cv2.FONT_HERSHEY_SIMPLEX,0.5,(255,255,255),1) |
| 58 | + cv2.putText(self.image,"Esc - Exit",(10,self.height+80),cv2.FONT_HERSHEY_SIMPLEX,0.5,(255,255,255),1) |
| 59 | + string = "vs Computer" if self.vsCom==0 else "vs Human" |
| 60 | + cv2.putText(self.image,"Space - "+string,(self.width/2+10,self.height+80),cv2.FONT_HERSHEY_SIMPLEX,0.5,(255,255,255),1) |
| 61 | + |
| 62 | + if self.selected and not(self.checkWin() or self.checkDraw()): |
| 63 | + self.change = True |
| 64 | + self.selected = False |
| 65 | + self.turn *= -1 |
| 66 | + #----------------------------------------------------------------------------------- |
| 67 | + #Game Play Functions |
| 68 | + def mainLoop(self) : #Game Loop till Esc(Close) button is pressed |
| 69 | + cv2.namedWindow(self.windowName) |
| 70 | + cv2.setMouseCallback(self.windowName,self.mouseCall) |
| 71 | + while True and cv2.getWindowProperty(self.windowName,1) != -1 : |
| 72 | + if self.change : |
| 73 | + self.change=False |
| 74 | + self.draw() |
| 75 | + if self.vsCom == self.turn and not(self.checkWin() or self.checkDraw()): |
| 76 | + block = self.nextMove() |
| 77 | + block.setValue("x" if self.turn==1 else "o") |
| 78 | + self.selected = True |
| 79 | + self.change = True |
| 80 | + cv2.imshow(self.windowName,self.image) |
| 81 | + #Keyboard Hits |
| 82 | + key = cv2.waitKey(1) |
| 83 | + if key == 27 : break |
| 84 | + elif key == ord("r") or key == ord("R") : |
| 85 | + self.reset() |
| 86 | + if key == ord(" ") and not(self.checkWin() or self.checkDraw()): |
| 87 | + if self.vsCom : |
| 88 | + self.vsCom = 0 |
| 89 | + else : |
| 90 | + self.vsCom = self.turn |
| 91 | + self.change = True |
| 92 | + cv2.destroyAllWindows() |
| 93 | + |
| 94 | + def checkWin(self) : |
| 95 | + self.win = False |
| 96 | + if (self.blocks[0][0][0].value is not None and self.blocks[0][0][0].value==self.blocks[0][1][0].value==self.blocks[0][2][0].value)or(self.blocks[1][0][0].value is not None and self.blocks[1][0][0].value==self.blocks[1][1][0].value==self.blocks[1][2][0].value)or(self.blocks[2][0][0].value is not None and self.blocks[2][0][0].value==self.blocks[2][1][0].value==self.blocks[2][2][0].value)or(self.blocks[0][0][0].value is not None and self.blocks[0][0][0].value==self.blocks[1][0][0].value==self.blocks[2][0][0].value)or(self.blocks[0][1][0].value is not None and self.blocks[0][1][0].value==self.blocks[1][1][0].value==self.blocks[2][1][0].value)or(self.blocks[0][2][0].value is not None and self.blocks[0][2][0].value==self.blocks[1][2][0].value==self.blocks[2][2][0].value)or(self.blocks[0][0][0].value is not None and self.blocks[0][0][0].value==self.blocks[1][1][0].value==self.blocks[2][2][0].value)or(self.blocks[2][0][0].value is not None and self.blocks[2][0][0].value==self.blocks[0][2][0].value==self.blocks[1][1][0].value) : |
| 97 | + self.win = True |
| 98 | + return self.win |
| 99 | + |
| 100 | + def checkDraw(self) : |
| 101 | + flag = True |
| 102 | + for i in range(3) : |
| 103 | + for j in range(3) : |
| 104 | + if self.blocks[i][j][0].value == None : |
| 105 | + flag=False |
| 106 | + return flag |
| 107 | + #----------------------------------------------------------------------------------- |
| 108 | + #Computers Move Decided Using Minmax Algorithm |
| 109 | + def nextMove(self) : #Decide NextMove of Computer by this return the block to selected by the Computer |
| 110 | + flag=0 |
| 111 | + blocks = [] |
| 112 | + for i in range(3) : |
| 113 | + for j in range(3) : |
| 114 | + if self.blocks[i][j][0].value == None : |
| 115 | + blocks.append(self.blocks[i][j][0]) |
| 116 | + if not (len(blocks)==sum([len(row) for row in self.blocks]) or len(blocks)==sum([len(row) for row in self.blocks])-1 or len(blocks)==1) : |
| 117 | + scoresList={} |
| 118 | + for block in blocks : |
| 119 | + if block.value == None : |
| 120 | + if self.computerWins(block) : |
| 121 | + scoresList[block] = 50 |
| 122 | + elif self.playerWins(block) : |
| 123 | + scoresList[block] = -50 |
| 124 | + elif not self.checkDraw() : |
| 125 | + block.value = ("x" if self.turn == 1 else "o") |
| 126 | + scoresList[block] = self.min_max(1,self.vsCom) |
| 127 | + block.value = None |
| 128 | + else : |
| 129 | + scoresList[block] = 0 |
| 130 | + #Choosing Either Best Closest Winning Score or Next Closest Losing Score |
| 131 | + bestScore = (min(scoresList.values()) if abs(min(scoresList.values()))>abs(max(scoresList.values())) else max(scoresList.values())) |
| 132 | + blocks = [] |
| 133 | + for block in scoresList : |
| 134 | + if scoresList[block] == bestScore : |
| 135 | + #print(block.pos,bestScore) |
| 136 | + blocks.append(block) |
| 137 | + choice = blocks[randint(0,len(blocks)-1)] |
| 138 | + print(choice.pos) |
| 139 | + return choice |
| 140 | + |
| 141 | + def min_max(self,depth,player) : #MinMax Algorithms Function |
| 142 | + scoresList = [] |
| 143 | + for row in self.blocks : |
| 144 | + for block in row : |
| 145 | + if block[0].value == None : |
| 146 | + if self.computerWins(block[0]) : |
| 147 | + return (50-depth) |
| 148 | + elif self.playerWins(block[0]) : |
| 149 | + return (-50+depth) |
| 150 | + else : |
| 151 | + block[0].value = ("x" if self.turn == 1 else "o") |
| 152 | + scoresList.append(self.min_max(depth+1,player*-1)) |
| 153 | + block[0].value = None |
| 154 | + return (min(scoresList) if abs(min(scoresList))>abs(max(scoresList)) else max(scoresList)) |
| 155 | + |
| 156 | + def computerWins(self,block) : |
| 157 | + flag = False |
| 158 | + block.value = ("x" if self.vsCom == 1 else "o") |
| 159 | + if self.checkWin() : flag = True |
| 160 | + self.win = False |
| 161 | + block.value = None |
| 162 | + return flag |
| 163 | + |
| 164 | + def playerWins(self,block) : |
| 165 | + flag = False |
| 166 | + block.value = ("x" if self.vsCom != 1 else "o") |
| 167 | + if self.checkWin() : flag = True |
| 168 | + self.win = False |
| 169 | + block.value = None |
| 170 | + return flag |
| 171 | + #----------------------------------------------------------------------------------- |
| 172 | + #Mouse Click Functions - (For User Players) |
| 173 | + def mouseCall(self,event,posx,posy,flag,param) : |
| 174 | + if event == cv2.EVENT_LBUTTONDOWN and not self.win and self.turn!=self.vsCom: |
| 175 | + self.setBlockInPos(posx,posy) |
| 176 | + |
| 177 | + def setBlockInPos(self,x,y) : |
| 178 | + for i in range(3) : |
| 179 | + for j in range(3) : |
| 180 | + if self.blocks[i][j][0].value is None and self.blocks[i][j][1][0]<=x<=self.blocks[i][j][2][0] and self.blocks[i][j][1][1]<= y<= self.blocks[i][j][2][1]: |
| 181 | + self.blocks[i][j][0].setValue("x" if self.turn == 1 else "o") |
| 182 | + self.change = True |
| 183 | + self.selected = True |
| 184 | + break |
| 185 | + #----------------------------------------------------------------------------------- |
| 186 | + |
| 187 | +#Main Program |
| 188 | +game = GUI("TicTacToe") |
| 189 | +game.mainLoop() |
0 commit comments