Dec-13-2022, 04:22 AM
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 Board: """Tic-tac-toe board on the side of a cube.""" winning_patterns = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6)) def __init__(self, side, center=(0, 0, 0), rotation=(0, 0, 0)): side = side / 3 gap = 8 self.clear_color = "white" self.colors = ("red", "green") 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) self.reset() 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 click(self, point, marker): """Find where clicked. If on my board, place a marker on the board and check for a winner.""" for index, square in enumerate(self.squares): if square.contains(point): if self.markers[index] == None: self.markers[index] = marker square.color = self.colors[marker] self.check_winner(marker) return square return None def check_winner(self, marker): """Check if last move was a winner""" for a, b, c in self.winning_patterns: if marker == self.markers[a] == self.markers[b] == self.markers[c]: self.winner = marker self.markers = [marker] * 9 for square in self.squares: square.color = self.colors[marker] def done(self): """Return True if this board is done""" return not(self.winner is None and None in self.markers) 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) def draw(self, surface): """Draw all the squares on the board""" for square in self.squares: square.draw(surface) class Cube: """A tic-tac-toe board plastered on the side of a cube""" def __init__(self, side): self.colors = ["red", "blue"] self.marker = 0 self.boards = [ Board(side, (0, 0, -side/2), (0, 0, 0)), Board(side, (side/2, 0, 0), (0, 90, 0)), Board(side, (0, -side/2, 0), (90, 0, 0)) ] def click(self, point): """Find where to place marker""" # Check if there are any moves left. If not, reset boards if all((board.done() for board in self.boards)): for board in self.boards: board.reset() return # Play marker on clicked square for board in self.boards: if board.click(point, self.marker): self.marker = (self.marker + 1) % 2 break 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 board in self.boards: board.rotate(rotation) def move(self, move): """Move square offset (dx, dy, dz) pixels""" for board in self.boards: board.move(move) def draw(self, surface): """Draw all the tic-tac-toe boards.""" for board in self.boards: board.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()