קוד המקור
"""
This file contains the graphical user interface for the 8 Queens Problem.
"""
import random
import pygame
import asyncio
import platform
import json
SHADOW_COLOR = (0, 0, 100, 10)
LIGHT_CELL_COLOR = [125, 135, 150]
DARK_CELL_COLOR = [233, 236, 239]
BORDER_COLOR = (0, 90, 146)
BOARD_SIZE = 8
pygame.font.init()
font = pygame.font.Font('NotoSansSymbols2-Regular.ttf', 36)
timer_font = pygame.font.Font('SpaceMono-Regular.ttf', 36)
if 'window' in platform.__dict__:
platform.window.eval(f"localStorage.setItem('end', 'false');")
def load_and_scale_image(image_path, target_size, fill_color=(0, 0, 0, 0)):
image = pygame.image.load(image_path)
width, height = image.get_size()
scale_factor = min(target_size / width, target_size / height)
image = pygame.transform.scale(image, (int(width * scale_factor), int(height * scale_factor)))
surface = pygame.Surface((target_size, target_size), pygame.SRCALPHA)
surface.fill(fill_color)
width, height = image.get_size()
surface.blit(image, ((target_size - width) // 2, (target_size - height) // 2))
return surface
class GameGUI:
def __init__(self):
pygame.init()
pygame.mixer.init()
pygame.mixer.music.load('fable.mp3')
pygame.mixer.music.play(-1)
pygame.mixer.music.set_endevent(pygame.USEREVENT)
self.threats = 0
self.board = [[0 for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)]
self.board_size = 800
self.cell_size = self.board_size // BOARD_SIZE
self.screen = pygame.display.set_mode((self.board_size + self.cell_size, self.board_size + self.cell_size))
self.clock = pygame.time.Clock()
self.indices = None
self.diag_indices = None
self.queen_image = load_and_scale_image('queen_w.png', self.cell_size + 20)
self.dark_queen_image = load_and_scale_image('queen_b.png', self.cell_size + 20)
self.init_cell_sizes()
pygame.display.set_caption(f'{BOARD_SIZE} Queens Problem')
self.done_x_pos = 0
self.directions = [(1, 0), (-1, 0), (0, 1), (0, -1)]
self.direction = random.choice(self.directions)
self.pos = [0, 0]
self.speed = 1
self.last_time = pygame.time.get_ticks()
self.selected_queen = None
self.missing_queens = BOARD_SIZE
self.dirty = True
def init_cell_sizes(self):
self.half_cell_size = self.cell_size // 2
self.sixth_cell_size = self.cell_size // 6
self.eight_cell_size = self.cell_size // 8
def calc_threats(self, board, x, y):
threats = 0
directions = [(0, 1), (1, 0), (0, -1), (-1, 0), (1, 1), (-1, -1), (1, -1), (-1, 1)]
for direction in directions:
i, j = x, y
while True:
i += direction[0]
j += direction[1]
if i < 0 or i >= len(board) or j < 0 or j >= len(board[0]):
break
if board[i][j] == 1:
threats += 1
return threats
def draw_board(self, board=None):
if not self.dirty:
return
if board is None:
board = self.board
cell_size = self.cell_size
screen = self.screen
game_size = BOARD_SIZE
threats = 0
for i in range(game_size):
for j in range(game_size):
color = DARK_CELL_COLOR if (i + j) % 2 == 0 else LIGHT_CELL_COLOR
pygame.draw.rect(screen, color, pygame.Rect(j * cell_size, i * cell_size + cell_size, cell_size, cell_size))
for i in range(game_size):
for j in range(game_size):
if board[i][j] == 1:
cur_threats = self.calc_threats(board, i, j)
if cur_threats > 0:
threats += cur_threats
self.draw_queen(i, j, cell_size, False)
else:
self.draw_queen(i, j, cell_size)
self.threats = threats
self.draw_stats(screen, threats)
self.dirty = False
def draw_stats(self, screen, threats):
threats_icon = font.render('☠', True, (255, 255, 255))
queen_icon = font.render('♛', True, (255, 255, 255))
threats = timer_font.render(str(threats), True, (255, 255, 255))
queen = timer_font.render(str(8 - self.missing_queens) + '/8', True, (255, 255, 255))
screen.blit(threats_icon, (240, 0))
screen.blit(threats, (280, 0))
screen.blit(queen_icon, (380, 0))
screen.blit(queen, (420, 0))
def draw_timer(self, screen, time):
clock_text = font.render('⏱', True, (255, 255, 255), BORDER_COLOR)
screen.blit(clock_text, (0, 0))
minutes, seconds = divmod(time, 60)
if minutes < 1:
time_text = f'{time:4.2f}'
else:
time_text = f'{int(minutes)}:{seconds:05.2f}'
time_text = timer_font.render(time_text, True, (255, 255, 255), BORDER_COLOR)
textpos = time_text.get_rect(topleft=(50, 0))
screen.blit(time_text, textpos)
def draw_missing_queens(self):
cell_size = self.cell_size
pygame.draw.rect(self.screen, BORDER_COLOR, pygame.Rect(self.screen.get_width() - cell_size, 0, cell_size, self.screen.get_height()))
pygame.draw.rect(self.screen, BORDER_COLOR, pygame.Rect(0, 0, self.screen.get_width(), cell_size))
missing_queens = self.missing_queens
screen_width, screen_height = self.screen.get_size()
for x in range(BOARD_SIZE + 1):
for y in range(BOARD_SIZE + 1):
if missing_queens == 0:
return
for i in range(self.cell_size * self.missing_queens):
color = (i * 255 // screen_height, i * 255 // screen_height, 255)
pygame.draw.line(self.screen, color, (screen_width - cell_size, i + cell_size), (screen_width, i))
shadow_color = (max(0, color[0] - 50), max(0, color[1] - 50), max(0, color[2] - 50))
pygame.draw.line(self.screen, shadow_color, (screen_width - cell_size + 1, i + 1 + cell_size), (screen_width + 1, i + 1))
missing_queens -= 1
def draw_queen(self, x, y, cell_size, light=True):
pos_x = y * cell_size - 10
pos_y = x * cell_size - 40
queen = self.queen_image if light else self.dark_queen_image
self.screen.blit(queen, (pos_x, pos_y + cell_size))
async def draw_done(self, done_surface, screen):
done_surface.fill((0, 0, 0, 0))
done_text = font.render('Well Done!', True, (255, 255, 255), BORDER_COLOR)
textpos = done_text.get_rect(topleft=(240, 0))
done_surface.blit(done_text, textpos)
smile = [
".*....*.",
"*.*..*.*",
"........",
"........",
".*....*.",
"..*..*..",
"...**...",
"........"
]
for i in range(8):
for j in range(8):
if smile[i][j] == '*':
pygame.draw.rect(done_surface, (0, 255, 0), pygame.Rect(j * self.cell_size, (i + 1) * self.cell_size, self.cell_size, self.cell_size))
screen.blit(done_surface, (0, 0))
def handle_events(self):
board_size = BOARD_SIZE
cell_size = self.screen.get_width() // (board_size + 1)
for event in pygame.event.get():
if event.type == pygame.QUIT:
return False
elif event.type == pygame.MOUSEBUTTONDOWN:
x, y = pygame.mouse.get_pos()
if 0 <= x <= board_size * cell_size and cell_size <= y <= board_size * cell_size + cell_size:
row, col = y // cell_size, x // cell_size
row -= 1
if self.missing_queens > 0 and self.board[row][col] == 0:
self.board[row][col] = 1
self.missing_queens -= 1
self.dirty = True
break
elif self.board[row][col] == 1 and self.missing_queens < board_size:
self.missing_queens += 1
self.board[row][col] = 0
self.dirty = True
break
return True
async def run(self):
self.player_turn = True
running = True
self.draw_board(self.board)
self.draw_missing_queens()
self.draw_stats(self.screen, 0)
screen = self.screen
timer = 0
pygame.display.flip()
while running:
missing_flag = self.missing_queens > 0
self.clock.tick(60)
self.draw_board(self.board)
pygame.display.flip()
prev_missing_queens = self.missing_queens
running = self.handle_events()
if prev_missing_queens != self.missing_queens:
self.draw_missing_queens()
timer += self.clock.get_time() / 1000
self.draw_timer(screen, timer)
await asyncio.sleep(0)
if not missing_flag and self.threats == 0:
break
if running:
done_text = timer_font.render('Well Done!', True, (255, 255, 255), BORDER_COLOR)
screen.blit(done_text, done_text.get_rect(topleft=(240, 0)))
WIDTH, HEIGHT = screen.get_size()
if 'window' in platform.__dict__:
platform.window.eval(f"localStorage.setItem('time', '{int(timer)}');")
platform.window.eval("localStorage.setItem('end', 'true');")
dark_surface = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
dark_surface.fill((0, 0, 0, 100))
screen.blit(dark_surface, (0, 0))
topTimes = platform.window.eval("localStorage.getItem('topTimes');")
topTimes = json.loads(topTimes)
font = pygame.font.Font(None, 36)
time_COLOR = (253, 208, 23)
text = font.render("Top Solvers Times", True, time_COLOR)
x = WIDTH // 2 - text.get_width() // 2
screen.blit(text, text.get_rect(topleft=(x, 40)))
y = 90
x -= 40
max_name_length = max([len(line.split(":")[0]) for line in topTimes])
for line in topTimes:
name, time = line.split(":")
text = font.render(name, True, time_COLOR)
screen.blit(text, text.get_rect(topleft=(x, y)))
time_x = x + (max_name_length * font.size(" ")[0] * 2)
text = font.render(time, True, time_COLOR)
screen.blit(text, text.get_rect(topleft=(time_x, y)))
y += 50
pygame.display.flip()
pygame.display.fill((255, 255, 255))
pygame.display.flip()
done_surface = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
self.draw_board(self.board)
await self.draw_done(done_surface, screen)
game_size = BOARD_SIZE
board = self.board
cell_size = self.cell_size
for i in range(game_size):
for j in range(game_size):
if board[i][j] == 1:
self.draw_queen(i, j, cell_size)
self.dirty = False
pygame.display.flip()
await asyncio.sleep(1)
pygame.quit()
async def main():
gui = GameGUI()
await gui.run()
if __name__ == "__main__":
asyncio.run(main())