Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Cube drawing
#1
Hi, I need some help.
I need to draw a cube exactly like this ["attachment"] but on a condition I can manuevar on that cube model after.
If anyone could help, I would really appreciate it! :)

Attached Files

Thumbnail(s)
   
Reply
#2
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()
Reply
#3
Thank you so much! Cool
Sorry for bothering you, but I'd like to play against computer, do you know the easiest way to implement that, maybe?
https://gofile.io/d/utiZhf <-- something like this
something like this, whereever you score 3 in a row first, you win.
Reply
#4
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.
Reply
#5
No no, without wrapping around a corner, but you got my point, yes I kinda agree with the part the first player always wins then.
To stop confusing you further and further haha, I think these could be great winning combinations:
https://gofile.io/d/dARqHT

But you've got my point that the game's over after the first winning combination (depsite which field was it on). And then you could paint the whole cube in the colors of the winner as you did in the beginning (that haven't come to my mind at first, but when I saw you did that, it seemed like a great idea!)

And I noticed that you can play the same field like the other player did.

Now on, to make it more interersting, there could be some points given, for example. if you win by 3 in a row, that's one point, if you win by the pattern with 4 fields, that's two points etc. and maybe first who scores 6 points, he's an overall winner of the game.



And about playing against computer, I tried to implement some code parts from all over the internet, but it didn't work :/
Reply
#6
Extra winning patterns is easy to implement.
import pygame
 
class Square:
    """I am a square in a tic-tac-toe board in 3D"""
    colors = {None:"white", 0:"green", 1:"red"}

    def __init__(self, side, center=(0, 0, 0)):
        self.marker = None
        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 empty(self):
        """Return True if square not filled"""
        return self.marker is None
    
    def rotate(self, rotation):
        """Rotate square (rx, ry, rz) degrees"""
        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.colors[self.marker], 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 Cube:
    # Now supports X and diamond patterns
    winning_combos = (
        (0, 2, 4, 6, 8), (9, 11, 13, 15, 17), (18, 20, 22, 24, 26),
        (1, 3, 5, 7), (10, 12, 14, 16), (19, 21, 23, 25),
        (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),
    )

    """A tic-tac-toe board plastered on the side of a cube"""
    def __init__(self, side):
        def make_squares(move, rotation):
            """Make all the squares on a face.  Rotate and the
            move the squares so they are on the "face" of the cube.
            """
            dx = side / 3
            gap = 6
            squares = [
                Square(dx-gap, (-dx, -dx, 0)),
                Square(dx-gap, (0, -dx, 0)),
                Square(dx-gap, (dx, -dx, 0)),
                Square(dx-gap, (-dx, 0, 0)),
                Square(dx-gap, (0, 0, 0)),
                Square(dx-gap, (dx, 0, 0)),
                Square(dx-gap, (-dx, dx, 0)),
                Square(dx-gap, (0, dx, 0)),
                Square(dx-gap, (dx, dx, 0))
            ]
            for square in squares:
                square.rotate(rotation)
                square.move(move)
            return squares

        self.squares = [
            *make_squares((0, 0, -side/2), (0, 0, 0)),
            *make_squares((side/2, 0, 0), (0, 270, 0)),
            *make_squares((0, -side/2, 0), (90, 0, 0))]
        for index, square in enumerate(self.squares):
            square.index = index
        self.marker = 0
        self.reset()
 
    def find_square(self, point):
        """Return square that was clicked, or None"""
        for square in self.squares:
            if square.contains(point):
                return square if square.empty() else None
        return None

    def click(self, point):
        """Place marker on clicked square"""
        if self.done:
            # Start now game
            self.reset()
        elif square := self.find_square(point):
            # Place marker and check for a win
            square.marker = self.marker
            self.marker_count += 1
            self.done = self.check_win(square) or self.marker_count >= 27
            self.marker = (self.marker + 1) % 2
 
    def check_win(self, square):
        """Check if most recent move results in a win"""
        def all_match(marker, combo):
            """Return True if all squares in combo match marker"""
            return all((self.squares[i].marker == marker for i in combo))

        marker = square.marker
        for combo in self.winning_combos:
            if square.index in combo and all_match(marker, combo):
                self.reset()
                self.done = True
                for i in combo:
                    self.squares[i].marker = marker
                return True
        return False
 
    def reset(self):
        """Reset all squares to empty"""
        for square in self.squares:
            square.marker = None
        self.done = False
        self.marker_count = 0
 
    def rotate(self, rotation):
        """Rotate cube (rx, ry, rz) degrees """
        for square in self.squares:
            square.rotate(rotation)
 
    def move(self, move):
        """Move cube offset (dx, dy, dz) pixels"""
        for square in self.squares:
            square.move(move)
 
    def draw(self, surface):
        """Draw all the squares."""
        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()
Reply
#7
Yeah, you got my idea, thank you again!

One more question which I've already asked, do you have any idea how to make this game to play against computer instead of a multiplayer version and to choose a color before starting the game?

And I kindly ask you if you also have any idea to implement the part with counting points after someone wins a game.
Reply
#8
Use the length of the winning combo as the basis for score calculations. Do some internet research on writing a python program to play tic-tac-toe. That problem has been solved many times.
Reply
#9
Alright, thank you!
And sorry for being annoying haha, but, in the end, could you help me with that you play the game against computer, not other/second player?

I was really trying to search any code parts which might help me solve the problem, but none of them actually did.
Reply
#10
You didn't look very hard. I googled "python tic-tac-toe algorithm" and got several great links on the first page. Ignore the random selection type algorithms. They don't respond to your play. Defeating them is too simple to be any fun. The minimax algorithm is fairly easy to implement. If you want to have any chance at beating the computer you need to limit the "depth" of the search, otherwise the best a human player can hope for is a draw.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Drawing a net of a cube freethrownucleus 26 4,783 May-05-2023, 10:23 PM
Last Post: freethrownucleus
  2D-Cube-Tic-Tac-Toe freethrownucleus 0 1,124 Mar-10-2023, 07:07 PM
Last Post: freethrownucleus
  PyGlet Trouble Drawing Cube. Windspar 3 5,639 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