1
\$\begingroup\$

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.
Sᴀᴍ Onᴇᴌᴀ
29.5k16 gold badges45 silver badges201 bronze badges
asked Jul 19, 2022 at 2:07
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Your code is long because it repeats a lot of code / has lots of hard coded conditionals. A lot of it could be refactored as appropriately dictionaries or dataclasses.

Try to find terms that group data or functionality. If you can give a code block a name, you can refactor it into its own method. After you have split up your code into lots of little chunks of code, you can start to look for similarities and generalizations.

For starters, I would recommend reading into programming principles like DRY For more in depth info, you can read my recent post about how to make code more readable.

answered Jul 19, 2022 at 18:30
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.