Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
2D-Cube-Tic-Tac-Toe
#1
Could someone help me with the next, please?
This is the cube model that I've got right now: https://gofile.io/d/RxqDYR .
I would like to change it to this: https://gofile.io/d/aL1srG (don't mind the painted squares).

The new model presents a cube net with 6 sides. Each of the sides presents one tic-tac-toe board.

Here is the game's code:
"""Tic-tac-toe with a computer opponent.  Three boards played simultaneously"""
import pygame
import random
 
PLAYER = 'X'
ROBOT = 'O'
EMPTY = ' '
background = "aliceblue"
msg_color = "dodgerblue"
colors = {EMPTY: 'dodger blue', PLAYER: 'green', ROBOT: 'red'}

 
class Robot:
    def __init__(self, cube):
        self.boards = cube.boards
 
    def get_scores(self, board):
        """Get scores for all open squares on a board"""
 
        def minimax(mark, square, alpha=-1000, beta=1000, depth=0):
            """minimax algorithm with alpha/beta pruning"""
            # Place the mark and check for a win
            square.mark = mark
            if square.check_win():
                # Give extra weight to earlier wins/losses
                score = 10 - depth if mark is ROBOT else depth - 10
                board.depth = min(board.depth, depth)
            elif len(empty_squares := board.empty_squares()) == 0:
                # No plays left.  Draw.
                score = 0
            elif mark is PLAYER:
                # Pick best move for robot
                score = -1000
                for s in empty_squares:
                    score = max(score, minimax(ROBOT, s, alpha, beta, depth+1))
                    alpha = max(alpha, score)
                    if alpha > beta:
                        break
            else:
                # Guess what move player will make
                score = 1000
                for s in empty_squares:
                    score = min(score, minimax(PLAYER, s, alpha, beta, depth+1))
                    beta = min(beta, score)
                    if alpha > beta:
                        break
 
            # Remove mark and return score for the square
            square.mark = EMPTY
            return score
 
        # Collect scores for empty squares.  If board is empty,
        # minimax will return 0 for all squares
        board.depth = 10
        empty_squares = board.empty_squares()
        if len(empty_squares) == 9:
            board.scores = [[0, s] for s in empty_squares]
        else:
            # Calling minimax twice.  The first is to find the square
            # giving us best chance to win board.  Second is to prevent
            # abandoning board early and giving player an easy win.
            board.scores = [[minimax("O", s), s] for s in empty_squares]
            [minimax("X", s, depth=1) for s in empty_squares]
 
    def play(self):
        """Place robot mark."""
        # Get scores for all empty squares
        depth = 10
        for board in self.boards:
            self.get_scores(board)
            depth = min(depth, board.depth)
 
        # Select board with minimum depth.  This is the board that
        # will win/lose in the least number of moves.
        scores = []
        for board in self.boards:
            if board.depth <= depth:
                scores.extend(board.scores)
 
        # Randomly select from best scores on the selected board.
        max_score = max(score[0] for score in scores)
        squares = [score[1] for score in scores if score[0] >= max_score]
        return random.choice(squares)
 
 
class Square:
    """A 3D square in a tic-tac-toe board"""
    def __init__(self, board, index, side, center=(0, 0, 0)):
        self.board = board
        self.index = index
        self.mark = EMPTY
        self.corners = [
            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"""
        for deg, axis in zip(rotation, ((1, 0, 0), (0, 1, 0), (0, 0, 1))):
            if deg:
                for corner in self.corners:
                    corner.rotate_ip(deg, axis)
        return self
 
    def move(self, offset):
        """Move square offset (x, y, z) pixels"""
        x, y, z = offset
        for corner in self.corners:
            corner.x += x
            corner.y += y
            corner.z += z
        return self
 
    def projection(self):
        """Return corners projected on xy plane"""
        return [pygame.Vector2(p.x, p.y) for p in self.corners]
 
    def draw(self, surface):
        """Draw projection of square on xy plane"""
        pygame.draw.polygon(surface, colors[self.mark], self.projection())
 
    def contains(self, point):
        """Return True if projection contains point."""
        def 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 has_point(a, b, c, p):
            """Return True if triange ABC contains point p."""
            return area(a, b, c) >= int(area(a, p, b) + area(b, p, c) + area(c, p, a))
 
        a, b, c, d = self.projection()
        return has_point(a, b, c, point) or has_point(a, c, d, point)
 
    def check_win(self):
        return self.board.check_win(self)
 
 
class Board:
    """A tic-tac-toe board"""
    winning_combos = (
        (0, 2, 4, 6, 8), (1, 3, 5, 7), (0, 1, 2), (3, 4, 5),
        (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), 
    )
 
    def __init__(self, side):
        dx = side / 3
        self.squares = []
        for index in range(9):
            x = (index % 3 - 1) * dx
            y = (index // 3 - 1) * dx
            self.squares.append(Square(self, index, dx-3, (x, y, 0)))
        self.scores = None
        self.depth = 10
 
    def empty_squares(self):
        """Return list of empty squares"""
        return [s for s in self.squares if s.mark is EMPTY]
 
    def check_win(self, square):
        """Return winning squares from last move, else None"""
        for combo in self.winning_combos:
            if square.index in combo:
                squares = [self.squares[i] for i in combo]
                if all(square.mark == s.mark for s in squares):
                    return squares
        return None
 
 
class Cube:
    """Three tic-tac-toe boards plastered on the faces of a cube"""
    def __init__(self, side):
        def make_board(center, rotation):
            board = Board(side)
            for square in board.squares:
                square.rotate(rotation).move(center)
            return board
 
        self.boards = [
            make_board((0, 0, -side/2), (0, 0, 0)),
            make_board((side/2, 0, 0), (0, 270, 0)),
            make_board((0, -side/2, 0), (90, 0, 0))
        ]
        self.squares = [s for b in self.boards for s in b.squares]
        self.winner = None
        self.mark = PLAYER
        self.reset()
 
    def click(self, point):
        """Try to place player's mark in clicked square."""
        if self.mark is PLAYER:
            for square in self.squares:
                if square.contains(point):
                    if square.mark is EMPTY:
                        self.play(square, self.mark)
                        return square
                    break
        return None
 
    def play(self, square, mark):
        """Place mark in square.  Test if is a winning move"""
        square.mark = mark
        self.mark_count += 1
        if winner := square.check_win():
            self.winner = winner
            for square in self.squares:
                if square not in winner:
                    square.mark = EMPTY
        self.mark = PLAYER if mark is ROBOT else ROBOT
 
    def done(self):
        """Return True if there is a winner or draw"""
        return self.winner or self.mark_count >= 27
 
    def reset(self):
        """Reset all boards to empty"""
        self.mark_count = 0
        self.winner = None
        for square in self.squares:
            square.mark = EMPTY
 
 
def main():
    """Play tic-tac-toe"""
    def blit_text(surface, msg, pos, font, color = pygame.Color('dodgerblue')):
        x, y = pos
        m = msg.split('\n')
        for line in m:
            text = pygame.font.Font(None, 48).render(line, True, msg_color)
            width, height = text.get_size()
            surface.blit(text, (x - width/2, y - height * 2))
            y += height + 2.
 
    def refresh_screen(surface, msg = None):
        """Draw the cube and an optional message"""
        surface.fill(background)
        for square in cube.squares:
            square.draw(surface)
        text_player1 = pygame.font.Font(None, 48).render("You", True, 'green')
        text_vs = pygame.font.Font(None, 48).render("vs", True, 'dodgerblue')
        text_player2 = pygame.font.Font(None, 48).render("Computer", True, 'red')
        surface.blit(text_player1, (text_player1.get_width(), text_player1.get_height()))
        surface.blit(text_vs, (center.x - text_vs.get_width() / 2, text_vs.get_height()))
        surface.blit(text_player2, (center.x + text_player2.get_width(), text_player2.get_height()))
        if msg:
            text = pygame.font.Font(None, 48).render(msg, True, msg_color)
            pos = (350, 700)
            blit_text(surface, msg, pos, None, color = pygame.Color('dodgerblue'))
        pygame.display.flip()

    pygame.display.set_caption("3D-Tic-Tac-Toe")
    surface = pygame.display.set_mode((700, 750))
    center = pygame.Vector3(350, 350, 0)
 
    # Move cube for pretty orthogonal view at center of screen
    cube = Cube(300)
    for square in cube.squares:
        square.rotate((0, 45, 0)).rotate((30, 0, 0)).move(center)
    robot = Robot(cube)
    refresh_screen(surface, None)
 
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if cube.done():
                    # Start a new game
                    cube.reset()
                    if cube.mark is ROBOT:
                        cube.play(robot.play(), ROBOT)
                    refresh_screen(surface, None)
                elif cube.click(pygame.Vector2(event.pos)):
                    # Player selected square.  Robot's turn,
                    if not cube.done():
                        refresh_screen(surface, None)
                        cube.play(robot.play(), ROBOT)
                    # Is the game over?
                    if cube.done():
                        refresh_screen(surface, "Ready for another play?\nJust click on the screen!")
                    else:
                        refresh_screen(surface, None)
                    pygame.event.clear()
 
 
if __name__ == "__main__":
    pygame.init()
    main()
    pygame.quit()
Thanks in advance!
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Drawing a net of a cube freethrownucleus 26 4,786 May-05-2023, 10:23 PM
Last Post: freethrownucleus
  Cube drawing freethrownucleus 51 9,370 Apr-12-2023, 08:04 AM
Last Post: ClaytonMorrison
  PyGlet Trouble Drawing Cube. Windspar 3 5,643 Jan-02-2018, 06:37 PM
Last Post: Windspar

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020