I'm working on a tic tac toe
game in processing.
I can't figure out how to make a way to have X
and O
swap to vaguely imitate swapping turns I have seen someone did it like the code below but for some reason, I either get an error or it just doesn't work when I hook it up to my preexisting code.
I should have Elaborated more I plan (attempt) to use the Minimax algorithm to make this game unwinnable
print("Begin")
global top_left, top_middle, top_right
global middle_left, center, middle_right
global bottem_left, bottem_middle, bottem_right
#these are the variables used to check if someone has put their move their already
#0 = empty
#1 = Circle
#2 = X
top_left = 0
top_middle = 0
top_right = 0
middle_left = 0
center = 0
middle_right = 0
bottem_left = 0
bottem_middle = 0
bottem_right = 0
#code for changing turns
turn = 1
def turn_changer():
global turn
if turn == 1:
turn = 2
else:
turn = 1
#board setup
def setup():
size(600,600)
#this hurt my brain trying to fully understand
#lines dividing board
def draw():
for y in range(3):
for x in range(3):
rect(200*x,200*y,200,200)
#hope this is not what geomtry is like
#top left ellipse
if top_left == 1:
ellipse(100,100,150,150)
#top left X
elif top_left == 2:
line(0,0,200,200)
line(200,0,0,200)
#top middle ellipse
if top_middle == 1:
ellipse(300,100,150,150)
#top middle X
elif top_middle == 2:
line(200,0,400,200)
line(400,0,200,200)
#top right ellipse
if top_right == 1:
ellipse(500,100,150,150)
#top right X
elif top_right == 2:
line(400,0,600,200)
line(600,0,400,200)
#middle left ellipse
if middle_left == 1:
ellipse(100,300,150,150)
#middle left X
elif middle_left == 2:
line(0,200,200,400)
line(200,200,0,400)
#middle ellipse
if center == 1:
ellipse(300,300,150,150)
#middle X
elif center == 2:
line(200,200,400,400)
line(400,200,200,400)
#middle right ellipse
if middle_right == 1:
ellipse(500,300,150,150)
#middle right X
elif middle_right == 2:
line(400,200,600,400)
line(600,200,400,400)
#bottem left ellipse
if bottem_left == 1:
ellipse(100,500,150,150)
#bottem left X
elif bottem_left == 2:
line(0,400,200,600)
line(200,400,0,600)
#bottem middle ellipse
if bottem_middle == 1:
ellipse(300,500,150,150)
#bottem middle X
elif bottem_middle == 2:
line(200,400,400,600)
line(400,400,200,600)
#bottem right ellipse
if bottem_right == 1:
ellipse (500,500,150,150)
#bottem right Xw
elif bottem_right == 2:
line(400,400,600,600)
line(600,400,400,600)
#dectects the quardnates where the mouse clicked and prints them
def mousePressed():
println( (mouseX, mouseY) )
#top left square hitbox
if (mouseX > 0 and mouseX < 200) and (mouseY > 0 and mouseY < 200):
top_left =+ turn
turn_changer()
print("top left")
#top middle square hitbox
elif (mouseX > 200 and mouseX < 400) and (mouseY > 0 and mouseY < 200):
top_middle = turn
turn_changer()
print(turn)
print("top middle")
#top right square hitbox
elif (mouseX > 400 and mouseX < 600) and (mouseY > 0 and mouseY < 200):
top_right = turn
turn_changer()
print("top right")
#middle left square hitbox
elif (mouseX > 0 and mouseX < 200) and (mouseY > 200 and mouseY < 400):
middle_left = turn
turn_changer()
print("middle left")
#center square hitbox
elif (mouseX > 200 and mouseX < 400) and (mouseY > 200 and mouseY < 400):
center = turn
turn_changer()
print("middle")
#middle right square hitbox
elif (mouseX > 400 and mouseX < 600) and (mouseY > 200 and mouseY < 400):
middle_right = turn
turn_changer()
print("middle right")
#bottem left square hitbox
elif (mouseX > 0 and mouseX < 200) and (mouseY > 400 and mouseY < 600):
bottem_left = turn
turn_changer()
print("bottem left")
#bottem middle square hitbox
elif (mouseX > 200 and mouseX < 400) and (mouseY > 400 and mouseY < 600):
bottem_middle = turn
turn_changer()
print("bottem middle")
#bottem right square hitbox
elif (mouseX > 400 and mouseX < 600) and (mouseY > 400 and mouseY < 600):
bottem_right = turn
turn_changer()
print("bottem right")
I respect that you're learning on your own, and so I took some time to learn python's basics to give you something to think about. I'm not a python buff (yet), so I may have done some misguided manipulations somewhere (so if a better coder than me is reading this and spots something awful, let me know), but I believe that this is mostly good stuff.
I used class
since I tend to think in OOP (and so will you after a while). Instead of seeing a grid with X and O, I see the game like this:
A Game manages:
A Grid (which is an object, too).
Who's turn it is (and when it's the AI turn, how the AI should play).
When the game ends.
A grid manages:
9 Cases (which are objects, too).
A Case manages:
It draws itself (so... it's coordinates and stuff).
If there's a X or O on it.
If it's been clicked
I fully realize that objects are a huge bump in the learning curve when you start programming, but I'm insisting here because I've seen A LOT of hardcoding in your code, and it's the kind of stuff which will cause you problems when you scale up your projects.
Hardcoding, like how you check which case have been clicked, isn't inherently bad, but it makes everything more difficult. It's part of the things you sometimes learn "the hard way", so here's my advice: when you ninja-code something (short code snippets which are written quickly and won't be part of something bigger), it's not great but it does the job. In any other context, it must be motivated by some specific need to be a good practice, and even there it can be avoided most of the time.
Here's commented code based on what I just wrote. I didn't do the whole tic-tac-toe game, just up to the part where it switch turns between the players or the player/AI (I put a boolean up there that let you switch between human opponent and AI opponent). What is missing is mostly the AI logic (I put a temporary one where it selects the first case it finds) and the victory conditions.
The boolean is currently in "player vs player" mode. Change it to True
to let the AI take over the O side.
# Player 1 (X) is human and play first
# Player 2 (O) is cpu
# You can change this boolean to play hotseat with a human if you want:
_AIPlayer = False
# Game own a grid, count turns and do any other game-specific concepts
# One "game of tic-tac-toe" would equal one of this object
class Game:
def __init__(self):
self.Grid = Grid(self) # creating the grid we'll use
self.TurnCount = 0 # first turn is turn number zero
def Render(self):
# when you draw the game, in fact it asks it's grid to draw itself
self.Grid.Render()
def Play(self):
# if it's the CPU's turn, let him play, else the game will wait for the player before going forward
# if there is no cpu player, the mouse can be used by player two
# the difference is that the cpu will do it's turn as a consequence of the player's turn
# and then add +1 to the turn count, while player 2 is exactly like player one but with O instead of X
# the game will check X and O to see who win, not a player class (but it could have been designed that way if needed)
if self.GetCurrentPlayer() == "O" and _AIPlayer:
self.AITurn()
def GetCurrentPlayer(self):
# return which's player is currently playing
if self.TurnCount % 2 == 0:
return "X"
else:
return "O"
def AITurn(self):
# this is a dumb placeholder
# your AI logic will be used here
# for now it just put a O on the first available case
print("AI turn")
for c in self.Grid.Cases:
if c.XO == "":
c.XO = self.GetCurrentPlayer()
break
self.TurnCount += 1
# Grid class is the whole grid
class Grid:
def __init__(self, game):
# the grid knows the game. I could use the global variable instead, but I dislike
# this kind of spaghetti. It would have worked, though.
# It's usually best to make everything you can dynamic, i.e. not hardcoded.
# It's easier to maintain and fix bugs that way, and you can upscale more easily too
# for an example, I could use this code to run several tic-tac-toe games in the
# same window at the same time with only a few modifications
self.Game = game
self.Cases = []
for i in range(3):
for j in range(3):
self.Cases.append(GridCase(i, j))
def Render(self):
# when you draw the grid, in fact it ask it's cases to draw themselves
for c in self.Cases:
c.Render()
def CaseClicked(self, xPos, yPos):
# this checks which case was clicked when it's a player
# since we don't care about the case's coordinated, we ask them if they have been clicked instead
for c in self.Cases:
if c.Clicked(xPos, yPos, self.Game.GetCurrentPlayer()):
self.Game.TurnCount += 1
return
# GridCase is each instance of 1 case in the grid
class GridCase:
def __init__(self, gridX, gridY):
# gridX and gridY are useful to know which case is part of which line
self.gridX = gridX
self.gridY = gridY
# I hardcoded the case's width and height, but you could totally make them dynamic
# and decide "on the fly" how big the grid will be. And it would still work.
self.w = 200 # width
self.h = 200 # height
# these coordinates are in pixels, and are useful to draw the case and for hit detection
self.x = self.w * gridX # x coordinate of the case
self.y = self.h * gridY # y coordinate of the case
# the "content" of the case
self.XO = "" # X or O as a character (it could be anything, I choose to stick to these)
def Render(self):
# the lines positions are dynamic: they'll be calculated from the case's perspective
# every case top left corner is in fact: (self.x, self.y)
rect(self.x, self.y, self.w, self.h)
# if the case has content, it'll be drawn at the same time than the case
if self.XO == "X":
line(self.x , self.y, self.x+self.w, self.y+self.h)
line(self.x, self.y+self.h, self.x+self.w, self.y)
elif self.XO == "O":
ellipse(self.x+(self.w/2),self.y+(self.h/2), self.w*0.75, self.h*0.75)
def SetXO(self, XO):
self.XO = XO
def Clicked(self, xPos, yPos, car):
# if the case is free and the click was inside it's boundaries, then attribute it to the current player
# the return True to tell that a sign was just placed
if self.XO == "" and xPos > self.x and xPos < self.x + self.w and yPos > self.y and yPos < self.y + self.h:
self.XO = car
return True
return False
# globals
_game = Game()
def setup():
size(600,600)
def draw():
# background wipes the screen "clean" (here it paints it black)
# then we can draw the current state of the grid
# here we could do without but I wanted you to know about it
background(0)
# draw the grid, then let the players do their thing
_game.Render()
# here you should check for game end conditions (victory or draw)
_game.Play()
def mouseClicked():
# listeing to mouse clicks
_game.Grid.CaseClicked(mouseX, mouseY)
You should copy and paste this code in a Processing.py IDE and try it. Fiddle around and read the comments. You can learn a lot here if you try. If you have questions, ask away in the comments with my handle and I'll come back and give you a hand.
And... have fun!