Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
22s-ainb
Brahim-Ghaouthi
Commits
1afa00d9
Commit
1afa00d9
authored
Jun 25, 2022
by
Brahim Ghaouthi
Browse files
AI game
parent
ed10e0ce
Changes
1
Hide whitespace changes
Inline
Side-by-side
gameAi.py
0 → 100644
View file @
1afa00d9
import
copy
import
sys
import
pygame
import
random
import
numpy
as
np
#Constants
WIDTH
=
600
HEIGHT
=
WIDTH
BOARD_ROWS
=
3
BOARD_COLS
=
3
SQUARE_SIZE
=
WIDTH
//
BOARD_COLS
LINE_WIDTH
=
15
CIRC_WIDTH
=
15
CROSS_WIDTH
=
25
CIRCLE_RADIUS
=
SQUARE_SIZE
//
3
OFFSET
=
50
BG_COLOR
=
(
62
,
88
,
112
)
LINE_COLOR
=
(
42
,
176
,
228
)
CIRCLE_COLOR
=
(
239
,
231
,
240
)
CROSS_COLOR
=
(
147
,
54
,
119
)
TEXT
=
'Game Over'
#Setup
pygame
.
init
()
WINNER_FONT
=
pygame
.
font
.
SysFont
(
'comicsans'
,
WIDTH
//
6
)
screen
=
pygame
.
display
.
set_mode
((
WIDTH
,
HEIGHT
))
pygame
.
display
.
set_caption
(
'TIC TAC TOE AI'
)
screen
.
fill
(
BG_COLOR
)
# --- CLASSES ---
class
Model
:
def
__init__
(
self
):
self
.
squares
=
np
.
zeros
((
BOARD_ROWS
,
BOARD_COLS
))
self
.
empty_sqrs
=
self
.
squares
# [squares]
self
.
marked_sqrs
=
0
def
final_state
(
self
,
show
=
False
):
# vertical wins
for
col
in
range
(
BOARD_COLS
):
if
self
.
squares
[
0
][
col
]
==
self
.
squares
[
1
][
col
]
==
self
.
squares
[
2
][
col
]
!=
0
:
if
show
:
color
=
CIRCLE_COLOR
if
self
.
squares
[
0
][
col
]
==
2
else
CROSS_COLOR
iPos
=
(
col
*
SQUARE_SIZE
+
SQUARE_SIZE
//
2
,
20
)
fPos
=
(
col
*
SQUARE_SIZE
+
SQUARE_SIZE
//
2
,
HEIGHT
-
20
)
pygame
.
draw
.
line
(
screen
,
color
,
iPos
,
fPos
,
LINE_WIDTH
)
return
self
.
squares
[
0
][
col
]
# horizontal wins
for
row
in
range
(
BOARD_ROWS
):
if
self
.
squares
[
row
][
0
]
==
self
.
squares
[
row
][
1
]
==
self
.
squares
[
row
][
2
]
!=
0
:
if
show
:
color
=
CIRCLE_COLOR
if
self
.
squares
[
row
][
0
]
==
2
else
CROSS_COLOR
iPos
=
(
20
,
row
*
SQUARE_SIZE
+
SQUARE_SIZE
//
2
)
fPos
=
(
WIDTH
-
20
,
row
*
SQUARE_SIZE
+
SQUARE_SIZE
//
2
)
pygame
.
draw
.
line
(
screen
,
color
,
iPos
,
fPos
,
LINE_WIDTH
)
return
self
.
squares
[
row
][
0
]
# desc diagonal
if
self
.
squares
[
0
][
0
]
==
self
.
squares
[
1
][
1
]
==
self
.
squares
[
2
][
2
]
!=
0
:
if
show
:
color
=
CIRCLE_COLOR
if
self
.
squares
[
1
][
1
]
==
2
else
CROSS_COLOR
iPos
=
(
20
,
20
)
fPos
=
(
WIDTH
-
20
,
HEIGHT
-
20
)
pygame
.
draw
.
line
(
screen
,
color
,
iPos
,
fPos
,
CROSS_WIDTH
)
return
self
.
squares
[
1
][
1
]
# asc diagonal
if
self
.
squares
[
2
][
0
]
==
self
.
squares
[
1
][
1
]
==
self
.
squares
[
0
][
2
]
!=
0
:
if
show
:
color
=
CIRCLE_COLOR
if
self
.
squares
[
1
][
1
]
==
2
else
CROSS_COLOR
iPos
=
(
20
,
HEIGHT
-
20
)
fPos
=
(
WIDTH
-
20
,
20
)
pygame
.
draw
.
line
(
screen
,
color
,
iPos
,
fPos
,
CROSS_WIDTH
)
return
self
.
squares
[
1
][
1
]
# no win yet
return
0
def
mark_square
(
self
,
row
,
col
,
player
):
self
.
squares
[
row
][
col
]
=
player
self
.
marked_sqrs
+=
1
def
empty_squares
(
self
,
row
,
col
):
return
self
.
squares
[
row
][
col
]
==
0
def
get_empty_sqrs
(
self
):
empty_sqrs
=
[]
for
row
in
range
(
BOARD_ROWS
):
for
col
in
range
(
BOARD_COLS
):
if
self
.
empty_squares
(
row
,
col
):
empty_sqrs
.
append
((
row
,
col
))
return
empty_sqrs
def
is_full
(
self
):
return
self
.
marked_sqrs
==
9
def
is_empty
(
self
):
return
self
.
marked_sqrs
==
0
class
AI
:
def
__init__
(
self
,
level
=
1
,
player
=
2
):
self
.
level
=
level
self
.
player
=
player
def
random
(
self
,
board
):
empty_sqrs
=
board
.
get_empty_sqrs
()
idx
=
random
.
randrange
(
0
,
len
(
empty_sqrs
))
return
empty_sqrs
[
idx
]
# (row, col)
def
minimax
(
self
,
board
,
maximizing
):
# terminal case
case
=
board
.
final_state
()
# player 1 wins
if
case
==
1
:
return
1
,
None
# eval, move
# player 2 wins
if
case
==
2
:
return
-
1
,
None
# draw
elif
board
.
is_full
():
return
0
,
None
if
maximizing
:
max_eval
=
-
100
best_move
=
None
empty_sqrs
=
board
.
get_empty_sqrs
()
for
(
row
,
col
)
in
empty_sqrs
:
temp_board
=
copy
.
deepcopy
(
board
)
temp_board
.
mark_square
(
row
,
col
,
1
)
eval
=
self
.
minimax
(
temp_board
,
False
)[
0
]
if
eval
>
max_eval
:
max_eval
=
eval
best_move
=
(
row
,
col
)
return
max_eval
,
best_move
elif
not
maximizing
:
min_eval
=
100
best_move
=
None
empty_sqrs
=
board
.
get_empty_sqrs
()
for
(
row
,
col
)
in
empty_sqrs
:
temp_board
=
copy
.
deepcopy
(
board
)
temp_board
.
mark_square
(
row
,
col
,
self
.
player
)
eval
=
self
.
minimax
(
temp_board
,
True
)[
0
]
if
eval
<
min_eval
:
min_eval
=
eval
best_move
=
(
row
,
col
)
return
min_eval
,
best_move
def
eval
(
self
,
main_board
):
if
self
.
level
==
0
:
# random choice
eval
=
'random'
move
=
self
.
random
(
main_board
)
else
:
# minimax algo choice
eval
,
move
=
self
.
minimax
(
main_board
,
False
)
print
(
f
'AI has chosen to mark the square in pos
{
move
}
with an eval of:
{
eval
}
'
)
return
move
# row, col
class
View
:
def
__init__
(
self
):
self
.
board
=
Model
()
self
.
ai
=
AI
()
self
.
player
=
1
self
.
gamemode
=
'ai'
self
.
running
=
True
self
.
show_lines
()
# --- DRAW METHODS ---
def
show_lines
(
self
):
screen
.
fill
(
BG_COLOR
)
# vertical
pygame
.
draw
.
line
(
screen
,
LINE_COLOR
,
(
SQUARE_SIZE
,
0
),
(
SQUARE_SIZE
,
HEIGHT
),
LINE_WIDTH
)
pygame
.
draw
.
line
(
screen
,
LINE_COLOR
,
(
WIDTH
-
SQUARE_SIZE
,
0
),
(
WIDTH
-
SQUARE_SIZE
,
HEIGHT
),
LINE_WIDTH
)
# horizontal
pygame
.
draw
.
line
(
screen
,
LINE_COLOR
,
(
0
,
SQUARE_SIZE
),
(
WIDTH
,
SQUARE_SIZE
),
LINE_WIDTH
)
pygame
.
draw
.
line
(
screen
,
LINE_COLOR
,
(
0
,
HEIGHT
-
SQUARE_SIZE
),
(
WIDTH
,
HEIGHT
-
SQUARE_SIZE
),
LINE_WIDTH
)
def
draw_fig
(
self
,
row
,
col
):
if
self
.
player
==
1
:
# draw cross
# desc line
start_desc
=
(
col
*
SQUARE_SIZE
+
OFFSET
,
row
*
SQUARE_SIZE
+
OFFSET
)
end_desc
=
(
col
*
SQUARE_SIZE
+
SQUARE_SIZE
-
OFFSET
,
row
*
SQUARE_SIZE
+
SQUARE_SIZE
-
OFFSET
)
pygame
.
draw
.
line
(
screen
,
CROSS_COLOR
,
start_desc
,
end_desc
,
CROSS_WIDTH
)
# asc line
start_asc
=
(
col
*
SQUARE_SIZE
+
OFFSET
,
row
*
SQUARE_SIZE
+
SQUARE_SIZE
-
OFFSET
)
end_asc
=
(
col
*
SQUARE_SIZE
+
SQUARE_SIZE
-
OFFSET
,
row
*
SQUARE_SIZE
+
OFFSET
)
pygame
.
draw
.
line
(
screen
,
CROSS_COLOR
,
start_asc
,
end_asc
,
CROSS_WIDTH
)
elif
self
.
player
==
2
:
# draw circle
center
=
(
col
*
SQUARE_SIZE
+
SQUARE_SIZE
//
2
,
row
*
SQUARE_SIZE
+
SQUARE_SIZE
//
2
)
pygame
.
draw
.
circle
(
screen
,
CIRCLE_COLOR
,
center
,
CIRCLE_RADIUS
,
CIRC_WIDTH
)
def
make_move
(
self
,
row
,
col
):
self
.
board
.
mark_square
(
row
,
col
,
self
.
player
)
self
.
draw_fig
(
row
,
col
)
self
.
next_turn
()
def
next_turn
(
self
):
self
.
player
=
self
.
player
%
2
+
1
def
change_gamemode
(
self
):
self
.
gamemode
=
'ai'
if
self
.
gamemode
==
'pvp'
else
'pvp'
def
isover
(
self
):
return
self
.
board
.
final_state
(
show
=
True
)
!=
0
or
self
.
board
.
is_full
()
def
reset
(
self
):
self
.
__init__
()
def
draw_winner
(
self
,
text
):
draw_text
=
WINNER_FONT
.
render
(
text
,
1
,
(
144
,
6
,
22
))
screen
.
blit
(
draw_text
,
(
WIDTH
//
2
-
draw_text
.
get_width
()
/
2
,
HEIGHT
//
3
-
draw_text
.
get_height
()
/
2
))
pygame
.
display
.
update
()
class
Controller
:
def
main
(
self
):
game
=
View
()
board
=
game
.
board
ai
=
game
.
ai
while
True
:
for
event
in
pygame
.
event
.
get
():
# quit event
if
event
.
type
==
pygame
.
QUIT
:
pygame
.
quit
()
sys
.
exit
()
# keydown event
if
event
.
type
==
pygame
.
KEYDOWN
:
# g-gamemode
if
event
.
key
==
pygame
.
K_g
:
game
.
change_gamemode
()
# r-restart
if
event
.
key
==
pygame
.
K_r
:
game
.
reset
()
board
=
game
.
board
ai
=
game
.
ai
# 0-random ai
if
event
.
key
==
pygame
.
K_0
:
ai
.
level
=
0
# 1-random ai
if
event
.
key
==
pygame
.
K_1
:
ai
.
level
=
1
# click event
if
event
.
type
==
pygame
.
MOUSEBUTTONDOWN
:
pos
=
event
.
pos
row
=
pos
[
1
]
//
SQUARE_SIZE
col
=
pos
[
0
]
//
SQUARE_SIZE
# human mark sqr
if
board
.
empty_squares
(
row
,
col
)
and
game
.
running
:
game
.
make_move
(
row
,
col
)
if
game
.
isover
():
game
.
draw_winner
(
TEXT
)
game
.
running
=
False
# AI initial call
if
game
.
gamemode
==
'ai'
and
game
.
player
==
ai
.
player
and
game
.
running
:
# update the screen
pygame
.
display
.
update
()
# eval
row
,
col
=
ai
.
eval
(
board
)
game
.
make_move
(
row
,
col
)
if
game
.
isover
():
game
.
draw_winner
(
TEXT
)
game
.
running
=
False
pygame
.
display
.
update
()
controller
=
Controller
()
controller
.
main
()
\ No newline at end of file
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment