Mar-10-2023, 07:07 PM
(This post was last modified: Mar-10-2023, 07:07 PM by freethrownucleus.)
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:
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!
