As for playing against the computer, what have you tried? You should start with a regular 3x3 tic-tac-toe game.
About game rules: 3 in any row? Even wrapping around a corner? If you can bend around a corner, can you bend diagonally? I tried this and the first player always wins.
import pygame
class Square:
"""I am a square in a tic-tac-toe board in 3D"""
def __init__(self, side, center=(0, 0, 0)):
self.color = "white"
self.points = [
pygame.Vector3(-1, -1, 0) * side / 2,
pygame.Vector3( 1, -1, 0) * side / 2,
pygame.Vector3( 1, 1, 0) * side / 2,
pygame.Vector3(-1, 1, 0) * side / 2
]
self.move(center)
def rotate(self, rotation):
"""Rotate square (rx, ry, rz) degrees.
rx rotates about the x axis, ry about the y axis, rz about the z axis.
"""
for degrees, vector in zip(rotation, ((1, 0, 0), (0, 1, 0), (0, 0, 1))):
if degrees:
for point in self.points:
point.rotate_ip(degrees, vector)
def move(self, offset):
"""Move square offset (dx, dy, dz) pixels"""
for point in self.points:
dx, dy, dz = offset
point.x += dx
point.y += dy
point.z += dz
def projection(self):
"""Return points projected on xy plane"""
return [pygame.Vector2(point.x, point.y) for point in self.points]
def draw(self, surface):
"""Draw self"""
pygame.draw.polygon(surface, self.color, self.projection())
def contains(self, point):
"""Return True if my xy projection contains point."""
def t_area(a, b, c):
"""Compute area of triangle"""
return abs((a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) / 2.0)
def t_contains(a, b, c, p):
"""Return True if triangle ABC contains point p."""
# Create 3 triangles that have p as a vertex. If the sum of the area of these
# triangles = area of the triangle ABC, then p is inside the triangle
area = t_area(a, b, c)
a1 = t_area(a, p, b)
a2 = t_area(b, p, c)
a3 = t_area(c, p, a)
return area >= int(a1 + a2 + a3) # Compensate for numerical errors
# Split projected polygon into two triangles. Test if point is inside either triangle.
a, b, c, d = self.projection()
return t_contains(a, b, c, point) or t_contains(a, c, d, point)
class Face:
def __init__(self, side, center=(0, 0, 0), rotation=(0, 0, 0)):
side = side / 3
gap = 8
self.squares = [
Square(side-gap, (-side, -side, 0)),
Square(side-gap, (0, -side, 0)),
Square(side-gap, (side, -side, 0)),
Square(side-gap, (-side, 0, 0)),
Square(side-gap, (0, 0, 0)),
Square(side-gap, (side, 0, 0)),
Square(side-gap, (-side, side, 0)),
Square(side-gap, (0, side, 0)),
Square(side-gap, (side, side, 0))
]
self.rotate(rotation)
self.move(center)
def reset(self):
"""Reset board to empty"""
self.winner = None
self.markers = [None] * 9
for square in self.squares:
square.color = self.clear_color
def move(self, offset):
"""Move square offset (dx, dy, dz) pixels"""
for square in self.squares:
square.move(offset)
def rotate(self, rotation):
"""Rotate square (rx, ry, rz) degrees.
rx rotates about the x axis, ry about the y axis, rz about the z axis.
"""
for square in self.squares:
square.rotate(rotation)
class Cube:
winning_combos = (
(0, 1, 2), (3, 4, 5), (6, 7, 8),
(0, 3, 6), (1, 4, 7), (2, 5, 8),
(0, 4, 8), (2, 4, 6),
(9, 10, 11), (12, 13, 14), (15, 16, 17),
(9, 12, 15), (10, 13, 16), (11, 14, 17),
(9, 13, 17), (11, 13, 15),
(18, 19, 20), (21, 22, 23), (24, 25, 26),
(18, 21, 24), (19, 22, 25), (20, 23, 26),
(18, 22, 26), (24, 22, 20),
(1, 2, 9), (2, 9, 10), (4, 5, 12), (5, 12, 13), (8, 9, 16), (9, 16, 17),
(3, 0, 18), (0, 18, 21), (4, 1, 19), (1, 19, 22), (5, 2, 20), (2, 20, 23),
(12, 9, 20), (9, 20, 19), (13, 10, 23), (10, 23, 22), (14, 11, 26), (11, 26, 25),
(3, 1, 20), (0, 19, 23), (5, 1, 18), (2, 19, 21),
(1, 5, 15), (2, 12, 16), (7, 5, 9), (8, 12, 10),
(19, 23, 11), (20, 10, 14), (25, 22, 9), (26, 10, 12)
)
"""A tic-tac-toe board plastered on the side of a cube"""
def __init__(self, side):
self.clear_color = "white"
self.colors = ["red", "blue"]
self.marker = 0
self.faces = [
Face(side, (0, 0, -side/2), (0, 0, 0)),
Face(side, (side/2, 0, 0), (0, 270, 0)),
Face(side, (0, -side/2, 0), (90, 0, 0))
]
self.squares = []
for face in self.faces:
self.squares.extend(face.squares)
for index, square in enumerate(self.squares):
square.index = index
square.marker = None
self.reset()
def click(self, point):
"""Find where to place marker"""
def locate(point):
for square in self.squares:
if square.contains(point):
return square
return None
if self.done:
self.reset()
return
square = locate(point)
print(square.index)
if square and square.marker == None:
square.marker = self.marker
square.color = self.colors[self.marker]
self.marker = (self.marker + 1) % 2
self.check_win(square)
def check_win(self, square):
def winner(squares):
a, b, c = [self.squares[i].marker for i in squares]
if a == b == c and a is not None:
return a
return None
for combo in self.winning_combos:
if square.index in combo and winner(combo) is not None:
self.reset()
self.done = True
for index in combo:
self.squares[index].color = self.colors[self.marker]
break
def reset(self):
for square in self.squares:
square.marker = None
square.color = self.clear_color
self.done = False
def rotate(self, rotation):
"""Rotate square (rx, ry, rz) degrees.
rx rotates about the x axis, ry about the y axis, rz about the z axis.
"""
for face in self.faces:
face.rotate(rotation)
def move(self, move):
"""Move square offset (dx, dy, dz) pixels"""
for face in self.faces:
face.move(move)
def draw(self, surface):
"""Draw all the tic-tac-toe boards."""
for square in self.squares:
square.draw(surface)
def main():
pygame.display.set_caption("Tic-Tac-Toe")
surface = pygame.display.set_mode((500, 500))
# Make 3 sides of a cube, and rotate so you can see all three sides
cube = Cube(300)
cube.rotate((0, 45, 0))
cube.rotate((22.5, 0, 0))
center = surface.get_rect()
cube.move(pygame.Vector3(center.centerx, center.centery, 0))
cube.draw(surface)
pygame.display.flip()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
cube.click(pygame.Vector2(event.pos))
surface.fill("black")
cube.draw(surface)
pygame.display.flip()
if __name__ == "__main__":
pygame.init()
main()
pygame.quit()
A variation that might be playable is to play until all squares but 1 are filled (each player gets 13 squares). Then compute the number of winners for each player.