I made a program that allows a player to play Tic Tac Toe against a computer. Of course, I didn't make it analyse the slightly harder algorithms, as I am just a beginner at programming. Are there any ways to improve this code? Is it quite long and hard to read.
from random import randint
from turtle import Screen
import math
import turtle
import numpy
####################################################################
def draw_board():
#setup screen
screen = Screen()
screen.setup(300, 300)
screen.setworldcoordinates(0, -300, 300, 0)
#setup turtle
turtle.hideturtle()
turtle.speed(0)
#draw colored box
turtle.color("BlanchedAlmond", "BlanchedAlmond")
turtle.begin_fill()
for i in range(4):
turtle.forward(300)
turtle.right(90)
turtle.end_fill()
turtle.width(3)
turtle.color("gold")
turtle.penup()
#draw y lines
turtle.penup()
turtle.goto(0, -100)
turtle.pendown()
turtle.forward(300)
turtle.penup()
turtle.goto(0, -200)
turtle.pendown()
turtle.forward(300)
#draw x lines
turtle.right(90)
turtle.penup()
turtle.goto(100, 0)
turtle.pendown()
turtle.forward(300)
turtle.penup()
turtle.goto(200, 0)
turtle.pendown()
turtle.forward(300)
#titles
turtle.color("black")
turtle.penup()
turtle.goto(100, 45)
turtle.pendown()
s = ("Arial", "15", "bold")
turtle.write("Column", font=s)
turtle.penup()
turtle.goto(-115, -150)
turtle.pendown()
s = ("Arial", "15", "bold")
turtle.write("Row", font=s)
#y Column
turtle.penup()
turtle.goto(45, 10)
turtle.pendown()
s = ("Arial", "13", "bold")
turtle.write("1", font=s)
turtle.penup()
turtle.goto(145, 10)
turtle.pendown()
s = ("Arial", "13", "bold")
turtle.write("2", font=s)
turtle.penup()
turtle.goto(245, 10)
turtle.pendown()
s = ("Arial", "13", "bold")
turtle.write("3", font=s)
#x Row
turtle.penup()
turtle.goto(-35, -50)
turtle.pendown()
s = ("Arial", "13", "bold")
turtle.write("1", font=s)
turtle.penup()
turtle.goto(-35, -150)
turtle.pendown()
s = ("Arial", "13", "bold")
turtle.write("2", font=s)
turtle.penup()
turtle.goto(-35, -250)
turtle.pendown()
s = ("Arial", "13", "bold")
turtle.write("3", font=s)
turtle.color("blue")
turtle.penup()
turtle.goto(315, -150)
turtle.pendown()
s = ("Arial", "12", "bold")
turtle.write("User: X", font=s)
turtle.penup()
turtle.goto(315, -170)
turtle.pendown()
s = ("Arial", "12", "bold")
turtle.write("Computer: O", font=s)
####################################################################
#computer move
def comp_move(co, us):
if not co[2][2] and not us[2][2]:
draw(2, 2, "O") #middle
else:
while True:
r = randint(1, 3)
c = randint(1, 3)
if not co[r][c] and not us[r][c]:
draw(r, c, "O")
break
####################################################################
#user move
def user_move(co, us):
while True:
r, c = [int(j) for j in input("Input row and column:").split(",")]
if r in [1,2,3] and c in [1,2,3] \
and not co[r][c] and not us[r][c]:
draw(r, c, "X")
break
else:
print("Invalid")
####################################################################
#draw X & O
def draw(r, c, symbol):
turtle.penup()
if symbol == "X":
us[r][c] = True
turtle.setheading(0)
turtle.goto(c * 100 - 90, -(r * 100 - 90))
turtle.pendown()
turtle.color("black")
turtle.goto(c * 100 - 10, -(r * 100 - 10))
turtle.penup()
turtle.left(90)
turtle.forward(80)
turtle.pendown()
turtle.left(135)
turtle.forward(80 * math.sqrt(2))
turtle.penup()
elif symbol == "O":
co[r][c] = True
turtle.setheading(0)
turtle.goto(c * 100 - 50, -(r * 100 - 50) - 40)
turtle.pendown()
turtle.color("black")
turtle.circle(40)
turtle.penup()
####################################################################
#check lines
def check_win(co, us):
global win
win = False
turtle.color("red")
turtle.width(7)
if (us[1][1] and us[2][2] and us[3][3]) \
or (co[1][1] and co[2][2] and co[3][3]):
#diagonal
turtle.penup()
turtle.goto(50, -50)
turtle.pendown()
turtle.goto(250, -250)
elif (us[1][3] and us[2][2] and us[3][1]) \
or (co[1][3] and co[2][2] and co[3][1]):
#diagonal
turtle.penup()
turtle.goto(250, -50)
turtle.pendown()
turtle.goto(50, -250)
elif (us[1][1] and us[1][2] and us[1][3]) \
or (co[1][1] and co[1][2] and co[1][3]):
#row 1
turtle.penup()
turtle.goto(50, -50)
turtle.pendown()
turtle.goto(250, -50)
elif (us[2][1] and us[2][2] and us[2][3]) \
or (co[2][1] and co[2][2] and co[2][3]):
#row 2
turtle.penup()
turtle.goto(50, -150)
turtle.pendown()
turtle.goto(250, -150)
elif (us[3][1] and us[3][2] and us[3][3]) \
or (co[3][1] and co[3][2] and co[3][3]):
#row 3
turtle.penup()
turtle.goto(50, -250)
turtle.pendown()
turtle.goto(250, -250)
elif (us[1][1] and us[2][1] and us[3][1]) \
or (co[1][1] and co[2][1] and co[3][1]):
#column 1
turtle.penup()
turtle.goto(50, -50)
turtle.pendown()
turtle.goto(50, -250)
elif (us[1][2] and us[2][2] and us[3][2]) \
or (co[1][2] and co[2][2] and co[3][2]):
#column 2
turtle.penup()
turtle.goto(150, -50)
turtle.pendown()
turtle.goto(150, -250)
elif (us[1][3] and us[2][3] and us[3][3]) \
or (co[1][3] and co[2][3] and co[3][3]):
#column 3
turtle.penup()
turtle.goto(250, -50)
turtle.pendown()
turtle.goto(250, -250)
#check winner
turtle.width(3)
turtle.color("blue")
if (us[1][1] and us[2][2] and us[3][3]) \
or (us[1][3] and us[2][2] and us[3][1]) \
or (us[1][1] and us[1][2] and us[1][3]) \
or (us[2][1] and us[2][2] and us[2][3]) \
or (us[3][1] and us[3][2] and us[3][3]) \
or (us[1][1] and us[2][1] and us[3][1]) \
or (us[1][2] and us[2][2] and us[3][2]) \
or (us[1][3] and us[2][3] and us[3][3]):
turtle.penup()
turtle.goto(50, -350)
turtle.pendown()
s = ("Arial", "20", "bold")
turtle.write("User wins!", font=s)
win = True
elif (co[1][1] and co[2][2] and co[3][3]) \
or (co[1][3] and co[2][2] and co[3][1]) \
or (co[1][1] and co[1][2] and co[1][3]) \
or (co[2][1] and co[2][2] and co[2][3]) \
or (co[3][1] and co[3][2] and co[3][3]) \
or (co[1][1] and co[2][1] and co[3][1]) \
or (co[1][2] and co[2][2] and co[3][2]) \
or (co[1][3] and co[2][3] and co[3][3]):
turtle.penup()
turtle.goto(50, -350)
turtle.pendown()
s = ("Arial", "20", "bold")
turtle.write("Computer wins!", font=s)
win = True
####################################################################
#check for two-in-a-row
def check2(p1, p2):
global moved
moved = True
comb = numpy.logical_or(p1, p2)
#row
if p1[1][1] and p1[1][2] and not comb[1][3]: draw(1, 3, "O")
elif p1[1][1] and p1[1][3] and not comb[1][2]: draw(1, 2, "O")
elif p1[1][2] and p1[1][3] and not comb[1][1]: draw(1, 1, "O")
elif p1[2][1] and p1[2][2] and not comb[2][3]: draw(2, 3, "O")
elif p1[2][1] and p1[2][3] and not comb[2][2]: draw(2, 2, "O")
elif p1[2][2] and p1[2][3] and not comb[2][1]: draw(2, 1, "O")
elif p1[3][1] and p1[3][2] and not comb[3][3]: draw(3, 3, "O")
elif p1[3][1] and p1[3][3] and not comb[3][2]: draw(3, 2, "O")
elif p1[3][2] and p1[3][3] and not comb[3][1]:
draw(3, 1, "O")
#column
elif p1[1][1] and p1[2][1] and not comb[3][1]:
draw(3, 1, "O")
elif p1[1][1] and p1[3][1] and not comb[2][1]:
draw(2, 1, "O")
elif p1[2][1] and p1[3][1] and not comb[1][1]:
draw(1, 1, "O")
elif p1[1][2] and p1[2][2] and not comb[3][2]:
draw(3, 2, "O")
elif p1[1][2] and p1[3][2] and not comb[2][2]:
draw(2, 2, "O")
elif p1[2][2] and p1[3][2] and not comb[1][2]:
draw(1, 2, "O")
elif p1[1][3] and p1[2][3] and not comb[3][3]:
draw(3, 3, "O")
elif p1[1][3] and p1[3][3] and not comb[2][3]:
draw(2, 3, "O")
elif p1[2][3] and p1[3][3] and not comb[1][3]:
draw(1, 3, "O")
#diagonal
elif p1[1][1] and p1[2][2] and not comb[3][3]:
draw(3, 3, "O")
elif p1[1][1] and p1[3][3] and not comb[2][2]:
draw(2, 2, "O")
elif p1[2][2] and p1[3][3] and not comb[1][1]:
draw(1, 1, "O")
elif p1[1][3] and p1[2][2] and not comb[3][1]:
draw(3, 1, "O")
elif p1[1][3] and p1[3][1] and not comb[2][2]:
draw(2, 2, "O")
elif p1[2][2] and p1[3][1] and not comb[1][3]:
draw(1, 3, "O")
#not moved
else:
moved = False
####################################################################
####################################################################
#main
#draw board
draw_board()
#initialize variables
co = numpy.full((4, 4), False)
us = numpy.full((4, 4), False)
#pick first p1ayer
comp = False
user = False
if randint(0, 1) == 0:
user = True
else:
comp = True
#first two moves
if comp: #comp first move in middle or corner
ran1 = randint(1, 5)
if ran1 == 1: draw(1, 1, "O")
elif ran1 == 2: draw(1, 3, "O")
elif ran1 == 3: draw(2, 2, "O")
elif ran1 == 4: draw(3, 1, "O")
elif ran1 == 5: draw(3, 3, "O")
user_move(co, us) #user move
user = False
comp = True
else: #user first move
user_move(co, us)
if not us[2][2]: #comp move middle
draw(2, 2, "O")
else: #comp move corner
ran2 = randint(1, 4)
if ran2 == 1: draw(1, 1, "O")
elif ran2 == 2: draw(1, 3, "O")
elif ran2 == 3: draw(3, 1, "O")
elif ran2 == 4: draw(3, 3, "O")
user = True
comp = False
#7 moves left
for i in range(7):
if comp:
check2(co, us) #check to win
if not moved:
check2(us, co) #check to block
if not moved:
comp_move(co, us)
comp = False
user = True
elif user:
user_move(co, us)
user = False
comp = True
check_win(co, us)
if win:
break
#draw
if not win:
turtle.penup()
turtle.goto(110, -350)
turtle.pendown()
s = ("Arial", "20", "bold")
turtle.write("Draw", font=s)
# Win: If the p1ayer has two in a row, they can p1ace a third to get three in a row.
# Block: If the opponent has two in a row, the p1ayer must p1ay the third themselves to block the opponent.
# Fork: Create an opportunity where the p1ayer has two ways to win (two non-blocked lines of 2).
# Blocking an opponent's fork: If there is only one possible fork for the opponent, the p1ayer should block it. Otherwise, the p1ayer should block all forks in any way that simultaneously allows them to create two in a row.
# Center: A p1ayer marks the center.
# Opposite corner: If the opponent is in the corner, the p1ayer p1ays the opposite corner.
# Empty corner: The p1ayer p1ays in a corner square.
# Empty side: The p1ayer p1ays in a middle square on any of the 4 sides.