1
+ import numpy as np
2
+ import random
3
+ import pygame
4
+ import sys
5
+ import math
6
+
7
+ BLUE = (0 ,0 ,255 )
8
+ BLACK = (0 ,0 ,0 )
9
+ RED = (255 ,0 ,0 )
10
+ YELLOW = (255 ,255 ,0 )
11
+
12
+ ROW_COUNT = 6
13
+ COLUMN_COUNT = 7
14
+
15
+ PLAYER = 0
16
+ AI = 1
17
+
18
+ PLAYER_PIECE = 1
19
+ AI_PIECE = 2
20
+
21
+ def createBoard ():
22
+ board = np .zeros ((ROW_COUNT ,COLUMN_COUNT ))
23
+ return board
24
+
25
+ def dropPiece (board , row , col , piece ):
26
+ board [row ][col ] = piece
27
+
28
+ def isPlaceValid (board , col ):
29
+ return board [ROW_COUNT - 1 ][col ] == 0
30
+
31
+ def GetNextRow (board , col ):
32
+ for r in range (ROW_COUNT ):
33
+ if board [r ][col ] == 0 :
34
+ return r
35
+
36
+ def printBoard (board ):
37
+ print (np .flip (board , 0 ))
38
+
39
+ def winningMove (board , piece ):
40
+ # Check horizontal locations for win
41
+ for c in range (COLUMN_COUNT - 3 ):
42
+ for r in range (ROW_COUNT ):
43
+ if board [r ][c ] == piece and board [r ][c + 1 ] == piece and board [r ][c + 2 ] == piece and board [r ][c + 3 ] == piece :
44
+ return True
45
+
46
+ # Check vertical locations for win
47
+ for c in range (COLUMN_COUNT ):
48
+ for r in range (ROW_COUNT - 3 ):
49
+ if board [r ][c ] == piece and board [r + 1 ][c ] == piece and board [r + 2 ][c ] == piece and board [r + 3 ][c ] == piece :
50
+ return True
51
+
52
+ # Check positively sloped diaganols
53
+ for c in range (COLUMN_COUNT - 3 ):
54
+ for r in range (ROW_COUNT - 3 ):
55
+ if board [r ][c ] == piece and board [r + 1 ][c + 1 ] == piece and board [r + 2 ][c + 2 ] == piece and board [r + 3 ][c + 3 ] == piece :
56
+ return True
57
+
58
+ # Check negatively sloped diaganols
59
+ for c in range (COLUMN_COUNT - 3 ):
60
+ for r in range (3 , ROW_COUNT ):
61
+ if board [r ][c ] == piece and board [r - 1 ][c + 1 ] == piece and board [r - 2 ][c + 2 ] == piece and board [r - 3 ][c + 3 ] == piece :
62
+ return True
63
+
64
+ def scoringScorePosition (selectedGroup , piece ):
65
+ score = 0
66
+ opp_piece = PLAYER_PIECE
67
+ if piece == PLAYER_PIECE :
68
+ opp_piece = AI_PIECE
69
+
70
+ if selectedGroup .count (piece ) == 4 :
71
+ score += 100
72
+ elif selectedGroup .count (piece ) == 3 and selectedGroup .count (0 ) == 1 :
73
+ score += 5
74
+ elif selectedGroup .count (piece ) == 2 and selectedGroup .count (0 ) == 2 :
75
+ score += 2
76
+
77
+ if selectedGroup .count (opp_piece ) == 3 and selectedGroup .count (0 ) == 1 :
78
+ score -= 4
79
+
80
+ return score
81
+
82
+ def scorePosition (board , piece ):
83
+ score = 0
84
+
85
+ ## Score center column
86
+ center_array = [int (i ) for i in list (board [:, COLUMN_COUNT // 2 ])]
87
+ center_count = center_array .count (piece )
88
+ score += center_count * 3
89
+
90
+ ## Score Horizontal
91
+ for r in range (ROW_COUNT ):
92
+ row_array = [int (i ) for i in list (board [r ,:])]
93
+ for c in range (COLUMN_COUNT - 3 ):
94
+ selectedGroup = row_array [c :c + 4 ]
95
+ score += scoringScorePosition (selectedGroup , piece )
96
+
97
+ ## Score Vertical
98
+ for c in range (COLUMN_COUNT ):
99
+ col_array = [int (i ) for i in list (board [:,c ])]
100
+ for r in range (ROW_COUNT - 3 ):
101
+ selectedGroup = col_array [r :r + 4 ]
102
+ score += scoringScorePosition (selectedGroup , piece )
103
+
104
+ ## Score posiive sloped diagonal
105
+ for r in range (ROW_COUNT - 3 ):
106
+ for c in range (COLUMN_COUNT - 3 ):
107
+ selectedGroup = [board [r + i ][c + i ] for i in range (4 )]
108
+ score += scoringScorePosition (selectedGroup , piece )
109
+
110
+ for r in range (ROW_COUNT - 3 ):
111
+ for c in range (COLUMN_COUNT - 3 ):
112
+ selectedGroup = [board [r + 3 - i ][c + i ] for i in range (4 )]
113
+ score += scoringScorePosition (selectedGroup , piece )
114
+
115
+ return score
116
+
117
+ def isTerminalNode (board ):
118
+ return winningMove (board , PLAYER_PIECE ) or winningMove (board , AI_PIECE ) or len (getValidLocations (board )) == 0
119
+
120
+ def minimax (board , depth , alpha , beta , maximizingPlayer ):
121
+ validLocations = getValidLocations (board )
122
+ isTerminal = isTerminalNode (board )
123
+ if depth == 0 or isTerminal :
124
+ if isTerminal :
125
+ if winningMove (board , AI_PIECE ):
126
+ return (None , 100000000000000 )
127
+ elif winningMove (board , PLAYER_PIECE ):
128
+ return (None , - 10000000000000 )
129
+ else : # Game is over, no more valid moves
130
+ return (None , 0 )
131
+ else : # Depth is zero
132
+ return (None , scorePosition (board , AI_PIECE ))
133
+ if maximizingPlayer :
134
+ value = - math .inf
135
+ column = random .choice (validLocations )
136
+ for col in validLocations :
137
+ row = GetNextRow (board , col )
138
+ b_copy = board .copy ()
139
+ dropPiece (b_copy , row , col , AI_PIECE )
140
+ new_score = minimax (b_copy , depth - 1 , alpha , beta , False )[1 ]
141
+ if new_score > value :
142
+ value = new_score
143
+ column = col
144
+ alpha = max (alpha , value )
145
+ if alpha >= beta :
146
+ break
147
+ return column , value
148
+
149
+ else : # Minimizing player
150
+ value = math .inf
151
+ column = random .choice (validLocations )
152
+ for col in validLocations :
153
+ row = GetNextRow (board , col )
154
+ b_copy = board .copy ()
155
+ dropPiece (b_copy , row , col , PLAYER_PIECE )
156
+ new_score = minimax (b_copy , depth - 1 , alpha , beta , True )[1 ]
157
+ if new_score < value :
158
+ value = new_score
159
+ column = col
160
+ beta = min (beta , value )
161
+ if alpha >= beta :
162
+ break
163
+ return column , value
164
+
165
+ def getValidLocations (board ):
166
+ validLocations = []
167
+ for col in range (COLUMN_COUNT ):
168
+ if isPlaceValid (board , col ):
169
+ validLocations .append (col )
170
+ return validLocations
171
+
172
+ def bestMoveForAI (board , piece ):
173
+
174
+ validLocations = getValidLocations (board )
175
+ bestScore = - 10000
176
+ bestMove = random .choice (validLocations )
177
+ for col in validLocations :
178
+ row = GetNextRow (board , col )
179
+ temp_board = board .copy ()
180
+ dropPiece (temp_board , row , col , piece )
181
+ score = scorePosition (temp_board , piece )
182
+ if score > bestScore :
183
+ bestScore = score
184
+ bestMove = col
185
+
186
+ return bestMove
187
+
188
+ def drawBoard (board ):
189
+ for c in range (COLUMN_COUNT ):
190
+ for r in range (ROW_COUNT ):
191
+ pygame .draw .rect (screen , BLUE , (c * SQUARESIZE , r * SQUARESIZE + SQUARESIZE , SQUARESIZE , SQUARESIZE ))
192
+ pygame .draw .circle (screen , BLACK , (int (c * SQUARESIZE + SQUARESIZE / 2 ), int (r * SQUARESIZE + SQUARESIZE + SQUARESIZE / 2 )), RADIUS )
193
+
194
+ for c in range (COLUMN_COUNT ):
195
+ for r in range (ROW_COUNT ):
196
+ if board [r ][c ] == PLAYER_PIECE :
197
+ pygame .draw .circle (screen , RED , (int (c * SQUARESIZE + SQUARESIZE / 2 ), height - int (r * SQUARESIZE + SQUARESIZE / 2 )), RADIUS )
198
+ elif board [r ][c ] == AI_PIECE :
199
+ pygame .draw .circle (screen , YELLOW , (int (c * SQUARESIZE + SQUARESIZE / 2 ), height - int (r * SQUARESIZE + SQUARESIZE / 2 )), RADIUS )
200
+ pygame .display .update ()
201
+
202
+ board = createBoard ()
203
+ printBoard (board )
204
+ isGameOver = False
205
+
206
+ pygame .init ()
207
+
208
+ SQUARESIZE = 100
209
+
210
+ width = COLUMN_COUNT * SQUARESIZE
211
+ height = (ROW_COUNT + 1 ) * SQUARESIZE
212
+
213
+ size = (width , height )
214
+
215
+ RADIUS = int (SQUARESIZE / 2 - 5 )
216
+
217
+ screen = pygame .display .set_mode (size )
218
+ drawBoard (board )
219
+ pygame .display .update ()
220
+ pygame .display .set_caption ('Connect 4' )
221
+ myfont = pygame .font .SysFont ("monospace" , 75 )
222
+
223
+ turn = random .randint (PLAYER , AI )
224
+
225
+ while not isGameOver :
226
+
227
+ for event in pygame .event .get ():
228
+ if event .type == pygame .QUIT :
229
+ sys .exit ()
230
+
231
+ if event .type == pygame .MOUSEMOTION :
232
+ pygame .draw .rect (screen , BLACK , (0 ,0 , width , SQUARESIZE ))
233
+ posx = event .pos [0 ]
234
+ if turn == PLAYER :
235
+ pygame .draw .circle (screen , RED , (posx , int (SQUARESIZE / 2 )), RADIUS )
236
+
237
+ pygame .display .update ()
238
+
239
+ if event .type == pygame .MOUSEBUTTONDOWN :
240
+ pygame .draw .rect (screen , BLACK , (0 ,0 , width , SQUARESIZE ))
241
+ #print(event.pos)
242
+ # Ask for Player 1 Input
243
+ if turn == PLAYER :
244
+ posx = event .pos [0 ]
245
+ col = int (math .floor (posx / SQUARESIZE ))
246
+
247
+ if isPlaceValid (board , col ):
248
+ row = GetNextRow (board , col )
249
+ dropPiece (board , row , col , PLAYER_PIECE )
250
+
251
+ if winningMove (board , PLAYER_PIECE ):
252
+ label = myfont .render ("Player 1 win!!" , 1 , RED )
253
+ screen .blit (label , (40 ,10 ))
254
+ isGameOver = True
255
+
256
+ turn += 1
257
+ turn = turn % 2
258
+
259
+ printBoard (board )
260
+ drawBoard (board )
261
+
262
+
263
+ # # Ask for Player 2 Input
264
+ if turn == AI and not isGameOver :
265
+
266
+ #col = random.randint(0, COLUMN_COUNT-1)
267
+ #col = bestMoveForAI(board, AI_PIECE)
268
+ col , minimax_score = minimax (board , 5 , - math .inf , math .inf , True )
269
+
270
+ if isPlaceValid (board , col ):
271
+ #pygame.time.wait(500)
272
+ row = GetNextRow (board , col )
273
+ dropPiece (board , row , col , AI_PIECE )
274
+
275
+ if winningMove (board , AI_PIECE ):
276
+ label = myfont .render ("Player 2 wins!!" , 1 , YELLOW )
277
+ screen .blit (label , (40 ,10 ))
278
+ isGameOver = True
279
+
280
+ printBoard (board )
281
+ drawBoard (board )
282
+
283
+ turn += 1
284
+ turn = turn % 2
285
+
286
+ if isGameOver :
287
+ pygame .time .wait (5000 )
0 commit comments