Dots and Boxes game using python with pygame library
pull/217/head
KAVINKUMAR VS 2024-05-14 14:16:36 +05:30
rodzic 0ae3586f6b
commit 85203eb2c7
18 zmienionych plików z 970 dodań i 0 usunięć

Wyświetl plik

@ -0,0 +1,77 @@
from typing import List
class Box:
ALL_BOXES: List["Box"] = []
BOXES_DONE = 0
def __init__(self, y, x):
self.idx = y, x
self._top = None
self._bottom = None
self._left = None
self._right = None
self.sides = 0
self.color = None
Box.ALL_BOXES.append(self)
def top_idx(self):
return self.idx, (self.idx[0], self.idx[1] + 1)
def bottom_idx(self):
return (self.idx[0] + 1, self.idx[1]), (self.idx[0] + 1, self.idx[1] + 1)
def left_idx(self):
return self.idx, (self.idx[0] + 1, self.idx[1])
def right_idx(self):
return (self.idx[0], self.idx[1] + 1), (self.idx[0] + 1, self.idx[1] + 1)
@property
def top(self):
return self._top
@top.setter
def top(self, top):
self._top = top
self.sides += 1
if self.sides == 4:
self.color = top
Box.BOXES_DONE += 1
@property
def bottom(self):
return self._bottom
@bottom.setter
def bottom(self, bottom):
self._bottom = bottom
self.sides += 1
if self.sides == 4:
self.color = bottom
Box.BOXES_DONE += 1
@property
def left(self):
return self._left
@left.setter
def left(self, left):
self._left = left
self.sides += 1
if self.sides == 4:
self.color = left
Box.BOXES_DONE += 1
@property
def right(self):
return self._right
@right.setter
def right(self, right):
self._right = right
self.sides += 1
if self.sides == 4:
self.color = right
Box.BOXES_DONE += 1

Wyświetl plik

@ -0,0 +1,502 @@
import pygame
import os
import math
from enum import Enum
from typing import List
from player import Player
from box import Box
"""
red: 255, 255, 255
blue: 44, 79, 182
green: 27, 150, 13
orange: 246, 108, 0
"""
pygame.init()
WIDTH = 1000
HEIGHT = 800
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Dots and Boxes')
clock = pygame.time.Clock()
font = pygame.font.Font(None, 60)
big_font = pygame.font.Font(None, 120)
images = {}
for filename in os.listdir('images'):
images[filename.replace('.png', '')] = pygame.image.load('images/' + filename)
player_select_buttons = [
['human', 'red', pygame.Rect(0, 0, 48, 48)],
['human', 'blue', pygame.Rect(0, 0, 48, 48)],
['human', 'green', pygame.Rect(0, 0, 48, 48)],
['human', 'orange', pygame.Rect(0, 0, 48, 48)]
]
background = images['wood_background']
icon = images['icon']
title = pygame.transform.smoothscale(images['title'], (800, 100))
arrow_right = images['arrow_right']
arrow_left = images['arrow_left']
pygame.display.set_icon(icon)
start = font.render('Start', True, (50, 50, 50))
start_rect = start.get_rect(center=(WIDTH / 2, 660))
turn = font.render('Turn', True, (89, 82, 69))
turn_rect = turn.get_rect(center=(WIDTH / 2 + 30, 70))
paused_rect = images['paused'].get_rect(center=(WIDTH / 2, 80))
resume = font.render('Resume', True, (255, 255, 255))
resume_rect = resume.get_rect(midtop=(WIDTH / 2, 310))
quit_game = font.render('Quit Game', True, (255, 255, 255))
quit_game_rect = quit_game.get_rect(midtop=(WIDTH / 2, 450))
board_size = font.render('Board Size', True, (89, 82, 69))
board_size_rect = board_size.get_rect(midbottom=(WIDTH / 2, 470))
difficulty_text = font.render('Difficulty:', True, (89, 82, 69))
difficulty_rect = difficulty_text.get_rect(center=(WIDTH / 2 - 80, 585))
diff = font.render('Easy', True, (89, 82, 69))
diff_rect = diff.get_rect(center=(WIDTH / 2 + 105, 585))
menu_rect = pygame.Rect(300, 225, 400, 500)
num_left_arrow_rect = arrow_left.get_rect(left=330, centery=300)
num_right_arrow_rect = arrow_right.get_rect(right=670, centery=300)
left_size_rect = arrow_left.get_rect(center=(400, 510))
right_size_rect = arrow_right.get_rect(center=(600, 510))
class GameStates(Enum):
menu = 0
playing = 1
finish = 2
game_state = GameStates.menu
sizes = [
(4, 3),
(6, 5),
(8, 6),
(10, 8),
(12, 9),
]
size_idx = 2
size = 8, 6
num_players = 2
curr_player = 0
player_win = 0
tie = False
paused = False
spacing = 50 # 50
player_select_spacing = 90
score_spacing = 200
fade = 10
alpha = 0
last_line = None
an_cycle = 0
an_time = 10
difficulty = 1
players: List[Player] = []
boxes: List[Box] = []
lines = []
animating = False
animation_line = []
move = ()
def get_diff_type():
return ('Easy', 'Medium', 'Hard', 'Extreme')[difficulty - 1]
def get_boxes_around(y, x):
check = {}
if x > 0:
check['left'] = boxes[y][x - 1]
if x < size[0] - 1:
check['right'] = boxes[y][x + 1]
if y > 0:
check['top'] = boxes[y - 1][x]
if y < size[1] - 1:
check['bottom'] = boxes[y + 1][x]
return check
def get_dots_around(y, x):
check = []
if x > 0:
check.append((y, x - 1))
if x < size[0]:
check.append((y, x + 1))
if y > 0:
check.append((y - 1, x))
if y < size[1]:
check.append((y + 1, x))
return check
def add_vec(p, v):
return p[0] + v[0], p[1] + v[1]
def idx_pos(y, x):
left = (WIDTH - size[0] * spacing) / 2
top = (HEIGHT - size[1] * spacing) / 2
return left + x * spacing, top + y * spacing
def pos_idx(x, y):
left = (WIDTH - size[0] * spacing) / 2
top = (HEIGHT - size[1] * spacing) / 2
y_idx = int((y - top) / spacing)
x_idx = int((x - left) / spacing)
return y_idx, x_idx
def near(a, b, d=20):
return math.dist(a, b) <= d
def loop_dots():
width = size[0] * spacing
height = size[1] * spacing
left = (WIDTH - width) / 2
top = (HEIGHT - height) / 2
for x in range(size[0] + 1):
x_pos = left + x * spacing
for y in range(size[1] + 1):
y_pos = top + y * spacing
yield x_pos, y_pos
def draw_back():
width = (size[0] + 2) * spacing
height = (size[1] + 2) * spacing
board_rect = pygame.Rect((WIDTH - width) / 2, (HEIGHT - height) / 2, width, height)
pygame.draw.rect(screen, (224, 187, 122), board_rect, border_radius=25)
pygame.draw.rect(screen, (50, 50, 50), board_rect, 10, border_radius=25)
def draw_dots():
for x_pos, y_pos in loop_dots():
pygame.draw.circle(screen, 'white', (x_pos, y_pos), 10) # 10
pygame.draw.circle(screen, 'black', (x_pos, y_pos), 10, 3) # 3
def draw_boxes():
for box in Box.ALL_BOXES:
pos = idx_pos(*box.idx)
if box.color is not None:
pygame.draw.rect(screen, box.color, pos + (spacing, spacing))
def draw_lines():
for idx in range(len(lines)):
line = lines[idx]
if idx == len(lines) - 1:
pygame.draw.line(screen, (100, 100, 100), line[0], line[1], 10)
else:
pygame.draw.line(screen, (50, 50, 50), line[0], line[1], 10)
def draw_menu():
screen.blit(title, title.get_rect(center=(WIDTH / 2, 100)))
pygame.draw.rect(screen, (224, 187, 122), menu_rect, border_radius=25)
pygame.draw.rect(screen, (50, 50, 50), menu_rect, 10, border_radius=25)
text = font.render(str(num_players) + ' Players', True, (89, 82, 69))
screen.blit(text, text.get_rect(center=(WIDTH / 2, 300)))
screen.blit(arrow_left, num_left_arrow_rect)
screen.blit(arrow_right, num_right_arrow_rect)
width = (num_players - 1) * player_select_spacing
left = (WIDTH - width) / 2
for count, player_button in enumerate(player_select_buttons[:num_players]):
player_button[2].midtop = left + player_select_spacing * count, 350
screen.blit(images[player_button[0] + '_' + player_button[1]], player_button[2])
screen.blit(board_size, board_size_rect)
size_text = font.render(f'{size[0]} x {size[1]}', True, (89, 82, 69))
screen.blit(size_text, size_text.get_rect(center=(WIDTH / 2, 510)))
screen.blit(arrow_left, left_size_rect)
screen.blit(arrow_right, right_size_rect)
screen.blit(difficulty_text, difficulty_rect)
screen.blit(diff, diff_rect)
screen.blit(start, start_rect)
def draw_playing():
screen.blit(images['hamburger'], (5, 5))
screen.blit(turn, turn_rect)
draw_back()
draw_boxes()
draw_lines()
if players[curr_player].start is not None:
pygame.draw.line(screen, players[curr_player].color, idx_pos(*players[curr_player].start), mouse_pos(), 10)
if animating:
pygame.draw.line(screen, players[curr_player].color, animation_line[0], animation_line[1], 10)
draw_dots()
left = (WIDTH - score_spacing * (num_players - 1)) / 2
for count, select_button in enumerate(player_select_buttons[:num_players]):
if count == curr_player:
select_button[2].center = WIDTH / 2 - 50, 70
screen.blit(images[select_button[0] + '_' + select_button[1]], select_button[2])
select_button[2].midtop = left + score_spacing * count, 700
screen.blit(images[select_button[0] + '_' + select_button[1]], select_button[2])
score_ = font.render(str(players[count].score), True, (50, 50, 50))
screen.blit(score_, score_.get_rect(centery=select_button[2].centery, left=select_button[2].right + 15))
if paused:
overlay = pygame.Surface(size=(WIDTH, HEIGHT))
overlay.fill((0, 0, 0))
overlay.set_alpha(120)
screen.blit(overlay, (0, 0))
screen.blit(images['paused'], paused_rect)
screen.blit(resume, resume_rect)
screen.blit(quit_game, quit_game_rect)
def draw_finish():
global alpha, fade
if alpha < 255:
alpha += fade
elif alpha > 255:
alpha = 255
draw_back()
draw_boxes()
draw_lines()
draw_dots()
left = (WIDTH - score_spacing * (num_players - 1)) / 2
for count, select_button in enumerate(player_select_buttons[:num_players]):
select_button[2].midtop = left + score_spacing * count, 700
screen.blit(images[select_button[0] + '_' + select_button[1]], select_button[2])
score_ = font.render(str(players[count].score), True, (50, 50, 50))
screen.blit(score_, score_.get_rect(centery=select_button[2].centery, left=select_button[2].right + 15))
overlay = pygame.Surface(size=(WIDTH, HEIGHT))
overlay.fill((0, 0, 0))
overlay.set_alpha(math.floor(alpha / 2))
screen.blit(overlay, (0, 0))
if alpha == 255:
over = big_font.render('Game Over', True, (50, 50, 50))
screen.blit(over, over.get_rect(midtop=(WIDTH / 2, 50)))
if tie:
win = font.render('Tie', True, (50, 50, 50))
screen.blit(win, win.get_rect(center=(WIDTH / 2, 200)))
else:
win = font.render('Winner:', True, (50, 50, 50))
screen.blit(win, win.get_rect(midright=(WIDTH / 2 + 40, 200)))
winner = player_select_buttons[player_win]
winner[2].midleft = WIDTH / 2 + 55, 200
screen.blit(images[winner[0] + '_' + winner[1]], winner[2])
def animate_line(pos1, pos2):
global animating, animation_line, an_cycle, an_time
if an_time == 0:
p = 1
else:
p = an_cycle / an_time
nx = pos2[0] * p + pos1[0] * (1 - p)
ny = pos2[1] * p + pos1[1] * (1 - p)
animation_line = [pos1, (nx, ny)]
an_cycle += 1
if an_cycle > an_time:
animating = False
an_cycle = 0
def start_game():
global game_state, players, curr_player, paused, boxes, animation_line, an_cycle, animating, lines, alpha, difficulty
game_state = GameStates.playing
an_cycle = 0
animation_line = []
animating = False
alpha = 0
paused = False
players = [
Player(player_select_buttons[0][0], (255, 0, 0), difficulty),
Player(player_select_buttons[1][0], (44, 79, 182), difficulty),
Player(player_select_buttons[2][0], (27, 150, 13), difficulty),
Player(player_select_buttons[3][0], (246, 108, 0), difficulty)
][:num_players]
curr_player = 0
Box.ALL_BOXES.clear()
Box.BOXES_DONE = 0
boxes = []
lines = []
for y in range(size[1]):
row = []
for x in range(size[0]):
row.append(Box(y, x))
boxes.append(row)
def mouse_pos():
return pygame.mouse.get_pos()
def next_turn():
global curr_player
curr_player += 1
if curr_player == num_players:
curr_player = 0
def add_line(idx_pos1, idx_pos2, color):
global last_line
count = 0
if idx_pos1[0] == idx_pos2[0]: # horizontal
pos1 = min(idx_pos1, idx_pos2, key=lambda x: x[1])
if pos1[0] > 0:
box_top = boxes[pos1[0] - 1][pos1[1]]
box_top.bottom = color
if box_top.color is not None:
count += 1
if pos1[0] < size[1]:
box_bottom = boxes[pos1[0]][pos1[1]]
box_bottom.top = color
if box_bottom.color is not None:
count += 1
else:
pos1 = min(idx_pos1, idx_pos2, key=lambda x: x[0])
if pos1[1] > 0:
box_left = boxes[pos1[0]][pos1[1] - 1]
box_left.right = color
if box_left.color is not None:
count += 1
if pos1[1] < size[0]:
box_right = boxes[pos1[0]][pos1[1]]
box_right.left = color
if box_right.color is not None:
count += 1
lines.append((idx_pos(*idx_pos1), idx_pos(*idx_pos2)))
last_line = idx_pos1, idx_pos2
return count
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if game_state == GameStates.menu:
if num_players > 2 and num_left_arrow_rect.collidepoint(event.pos): # players -1
num_players -= 1
elif num_players < 4 and num_right_arrow_rect.collidepoint(event.pos): # players +1
num_players += 1
elif start_rect.collidepoint(event.pos): # start game
start_game()
elif size_idx > 0 and left_size_rect.collidepoint(event.pos): # check if user changed board size
size_idx -= 1
size = sizes[size_idx]
elif size_idx < len(sizes) - 1 and right_size_rect.collidepoint(event.pos):
size_idx += 1
size = sizes[size_idx]
elif diff_rect.collidepoint(event.pos):
difficulty = difficulty % 4 + 1
diff = font.render(get_diff_type(), True, (89, 82, 69))
diff_rect = diff.get_rect(center=(WIDTH / 2 + 105, 585))
else: # changed player type
for index, button in enumerate(player_select_buttons[:num_players]):
if button[2].collidepoint(mouse_pos()):
if button[0] == 'human':
button[0] = 'computer'
else:
button[0] = 'human'
break
elif game_state == GameStates.playing:
if paused:
if resume_rect.collidepoint(event.pos):
paused = False
elif quit_game_rect.collidepoint(event.pos):
game_state = GameStates.menu
else:
if images['hamburger'].get_rect(topleft=(5, 5)).collidepoint(event.pos):
paused = True
elif players[curr_player].player_type == 'human':
found = False
for dot in loop_dots():
if near(event.pos, dot):
players[curr_player].start = pos_idx(*dot)
found = True
break
if not found:
players[curr_player].start = None
elif game_state == GameStates.finish:
game_state = GameStates.menu
elif event.type == pygame.MOUSEBUTTONUP:
if game_state == GameStates.playing and not paused:
if players[curr_player].player_type == 'human' and players[curr_player].start is not None:
found = False
for dot in get_dots_around(*players[curr_player].start):
if near(event.pos, idx_pos(*dot)):
found = dot
break
if found:
p1 = idx_pos(*players[curr_player].start)
p2 = idx_pos(*found)
if ((p1, p2) in lines) or ((p2, p1) in lines):
found = False
if found:
players[curr_player].move = [players[curr_player].start, found]
players[curr_player].start = None
if game_state == GameStates.playing and not paused:
if players[curr_player].player_type == 'human':
move = players[curr_player].get_move(boxes, last_line)
if move is not None:
score = add_line(*move, players[curr_player].color)
if score == 0:
players[curr_player].move = None
next_turn()
else:
players[curr_player].score += score
else:
if not animating:
move = players[curr_player].get_move(boxes, last_line)
animating = True
animate_line(idx_pos(*move[0]), idx_pos(*move[1]))
if not animating:
score = add_line(*move, players[curr_player].color)
players[curr_player].move = None
if score == 0:
next_turn()
else:
players[curr_player].score += score
if Box.BOXES_DONE == size[0] * size[1]:
game_state = GameStates.finish
rankings = sorted(players, key=lambda x: x.score, reverse=True)
tie = rankings[0].score == rankings[1].score
player_win = players.index(rankings[0])
screen.blit(background, (0, 0))
if game_state == GameStates.menu:
draw_menu()
elif game_state == GameStates.playing:
draw_playing()
elif game_state == GameStates.finish:
draw_finish()
pygame.display.flip()
clock.tick(60)
pygame.quit()

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 381 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 390 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 354 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 357 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 351 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 342 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 216 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 393 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 393 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 390 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 372 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 255 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 19 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 138 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 624 KiB

Wyświetl plik

@ -0,0 +1,391 @@
import random
from box import Box
from typing import List
def line_boxes(mat, line) -> List[Box]:
p1, p2 = line
boxes = []
if p1[0] == p2[0]: # horizontal
pos1 = min(p1, p2, key=lambda x: x[1])
if pos1[0] > 0:
box_top = mat[pos1[0] - 1][pos1[1]]
boxes.append(box_top)
if pos1[0] < len(mat):
box_bottom = mat[pos1[0]][pos1[1]]
boxes.append(box_bottom)
else:
pos1 = min(p1, p2, key=lambda x: x[0])
if pos1[1] > 0:
box_left = mat[pos1[0]][pos1[1] - 1]
boxes.append(box_left)
if pos1[1] < len(mat[0]):
box_right = mat[pos1[0]][pos1[1]]
boxes.append(box_right)
return boxes
def get_boxes_around(box_mat, box: Box):
boxes = {'top': None, 'bottom': None, 'left': None, 'right': None}
if box.idx[0] > 0:
boxes['top'] = box_mat[box.idx[0] - 1][box.idx[1]]
if box.idx[0] < len(box_mat) - 1:
boxes['bottom'] = box_mat[box.idx[0] + 1][box.idx[1]]
if box.idx[1] > 0:
boxes['left'] = box_mat[box.idx[0]][box.idx[1] - 1]
if box.idx[1] < len(box_mat[0]) - 1:
boxes['right'] = box_mat[box.idx[0]][box.idx[1] + 1]
return boxes
def num_boxes_around(box_mat, box: Box):
count = 0
if box.idx[0] > 0:
count += 1
if box.idx[0] < len(box_mat) - 1:
count += 1
if box.idx[1] > 0:
count += 1
if box.idx[1] < len(box_mat[0]) - 1:
count += 1
return count
def get_empty(box):
if box.top is None:
return box.top_idx()
elif box.bottom is None:
return box.bottom_idx()
elif box.left is None:
return box.left_idx()
else:
return box.right_idx()
def get_rand_empty(box, exclude=None):
choices = []
if box.top is None:
if exclude is None or box.top_idx() != exclude:
choices.append(box.top_idx())
if box.bottom is None:
if exclude is None or box.bottom_idx() != exclude:
choices.append(box.bottom_idx())
if box.left is None:
if exclude is None or box.left_idx() != exclude:
choices.append(box.left_idx())
if box.right is None:
if exclude is None or box.right_idx() != exclude:
choices.append(box.right_idx())
return random.choice(choices)
def get_sides_box(box_mat, box: Box):
sides = {'top': None, 'bottom': None, 'left': None, 'right': None}
if box.idx[0] > 0:
sides['top'] = box_mat[box.idx[0] - 1][box.idx[1]].sides
if box.idx[0] < len(box_mat) - 1:
sides['bottom'] = box_mat[box.idx[0] + 1][box.idx[1]].sides
if box.idx[1] > 0:
sides['left'] = box_mat[box.idx[0]][box.idx[1] - 1].sides
if box.idx[1] < len(box_mat[0]) - 1:
sides['right'] = box_mat[box.idx[0]][box.idx[1] + 1].sides
return sides
def less_than_sides(box_mat, box: Box, side, num):
sides = get_sides_box(box_mat, box)[side]
if side == 'top' and box.top is not None:
return False
if side == 'bottom' and box.bottom is not None:
return False
if side == 'left' and box.left is not None:
return False
if side == 'right' and box.right is not None:
return False
if sides is None:
return True
return sides < num
def facing_out(box_mat, box: Box):
if box.idx[0] == 0 and box.top is None:
return True
if box.idx[0] == len(box_mat) - 1 and box.bottom is None:
return True
if box.idx[1] == 0 and box.left is None:
return True
if box.idx[1] == len(box_mat[0]) - 1 and box.right is None:
return True
return False
def easy(box_mat, prev_line):
if prev_line is not None:
prev_boxes = line_boxes(box_mat, prev_line)
for prev_box in prev_boxes:
if prev_box.sides == 3:
return get_rand_empty(prev_box)
boxes = Box.ALL_BOXES
box0 = list(filter(lambda x: x.sides == 0, boxes))
box1 = list(filter(lambda x: x.sides == 1, boxes))
box2 = list(filter(lambda x: x.sides == 2, boxes))
box3 = list(filter(lambda x: x.sides == 3, boxes))
if box3:
return get_rand_empty(random.choice(box3))
if box0:
return get_rand_empty(random.choice(box0))
if box1:
return get_rand_empty(random.choice(box1))
if box2:
return get_rand_empty(random.choice(box2))
def medium(box_mat, prev_line):
if prev_line is not None:
prev_boxes = line_boxes(box_mat, prev_line)
for prev_box in prev_boxes:
if prev_box.sides == 3:
return get_rand_empty(prev_box)
boxes = Box.ALL_BOXES
box0 = list(filter(lambda x: x.sides == 0, boxes))
box1 = list(filter(lambda x: x.sides == 1, boxes))
box2 = list(filter(lambda x: x.sides == 2, boxes))
box3 = list(filter(lambda x: x.sides == 3, boxes))
box_less2 = box0 + box1
if box3:
return get_rand_empty(random.choice(box3))
top = list(filter(lambda x: less_than_sides(box_mat, x, 'top', 2), box_less2))
bottom = list(filter(lambda x: less_than_sides(box_mat, x, 'bottom', 2), box_less2))
left = list(filter(lambda x: less_than_sides(box_mat, x, 'left', 2), box_less2))
right = list(filter(lambda x: less_than_sides(box_mat, x, 'right', 2), box_less2))
choices = []
choices.extend([i.top_idx() for i in top])
choices.extend([i.bottom_idx() for i in bottom])
choices.extend([i.left_idx() for i in left])
choices.extend([i.right_idx() for i in right])
if choices:
return random.choice(choices)
if box0:
return get_rand_empty(random.choice(box0))
if box1:
return get_rand_empty(random.choice(box1))
if box2:
return get_rand_empty(random.choice(box2))
def hard(box_mat, prev_line):
if prev_line is not None:
prev_boxes = line_boxes(box_mat, prev_line)
for prev_box in prev_boxes:
if prev_box.sides == 3:
return get_rand_empty(prev_box)
boxes = Box.ALL_BOXES
box0 = list(filter(lambda x: x.sides == 0, boxes))
box1 = list(filter(lambda x: x.sides == 1, boxes))
box3 = list(filter(lambda x: x.sides == 3, boxes))
if box3:
return get_rand_empty(random.choice(box3))
box_less2 = box0 + box1
top = list(filter(lambda x: less_than_sides(box_mat, x, 'top', 2), box_less2))
bottom = list(filter(lambda x: less_than_sides(box_mat, x, 'bottom', 2), box_less2))
left = list(filter(lambda x: less_than_sides(box_mat, x, 'left', 2), box_less2))
right = list(filter(lambda x: less_than_sides(box_mat, x, 'right', 2), box_less2))
choices = []
choices.extend([i.top_idx() for i in top])
choices.extend([i.bottom_idx() for i in bottom])
choices.extend([i.left_idx() for i in left])
choices.extend([i.right_idx() for i in right])
if choices:
return random.choice(choices)
chains = []
checked = []
crosses = []
options = boxes.copy()
while len(checked) < len(boxes):
current = [options[0]]
if current[0].color is not None:
checked.append(current[0])
options.remove(current[0])
continue
if current[0].sides < 2:
crosses.append(current[0])
checked.append(current[0])
options.remove(current[0])
continue
new = []
chain = []
while current:
for box in current:
checked.append(box)
chain.append(box)
options.remove(box)
around = get_boxes_around(box_mat, box)
if box.top is None and around['top'] is not None:
if around['top'] not in new and around['top'] not in chain:
if around['top'].sides >= 2:
new.append(around['top'])
if box.bottom is None and around['bottom'] is not None:
if around['bottom'] not in new and around['bottom'] not in chain:
if around['bottom'].sides >= 2:
new.append(around['bottom'])
if box.left is None and around['left'] is not None:
if around['left'] not in new and around['left'] not in chain:
if around['left'].sides >= 2:
new.append(around['left'])
if box.right is None and around['right'] is not None:
if around['right'] not in new and around['right'] not in chain:
if around['right'].sides >= 2:
new.append(around['right'])
current = new
new = []
chains.append(chain)
sorted_chains = sorted(chains, key=lambda x: len(x))
if sorted_chains:
return get_rand_empty(random.choice(sorted_chains[0]))
return get_rand_empty(random.choice(crosses))
def extreme(box_mat, prev_line):
boxes = Box.ALL_BOXES
box0 = list(filter(lambda x: x.sides == 0, boxes))
box1 = list(filter(lambda x: x.sides == 1, boxes))
box3 = list(filter(lambda x: x.sides == 3, boxes))
box_less2 = box0 + box1
top = list(filter(lambda x: less_than_sides(box_mat, x, 'top', 2), box_less2))
bottom = list(filter(lambda x: less_than_sides(box_mat, x, 'bottom', 2), box_less2))
left = list(filter(lambda x: less_than_sides(box_mat, x, 'left', 2), box_less2))
right = list(filter(lambda x: less_than_sides(box_mat, x, 'right', 2), box_less2))
choices = []
choices.extend([i.top_idx() for i in top])
choices.extend([i.bottom_idx() for i in bottom])
choices.extend([i.left_idx() for i in left])
choices.extend([i.right_idx() for i in right])
chains = []
checked = []
crosses = []
options = boxes.copy()
while len(checked) < len(boxes):
current = [options[0]]
if current[0].color is not None:
checked.append(current[0])
options.remove(current[0])
continue
if current[0].sides < 2:
crosses.append(current[0])
checked.append(current[0])
options.remove(current[0])
continue
new = []
chain = []
while current:
for box in current:
checked.append(box)
chain.append(box)
options.remove(box)
around = get_boxes_around(box_mat, box)
if box.top is None and around['top'] is not None:
if around['top'] not in new and around['top'] not in chain:
if around['top'].sides >= 2:
new.append(around['top'])
if box.bottom is None and around['bottom'] is not None:
if around['bottom'] not in new and around['bottom'] not in chain:
if around['bottom'].sides >= 2:
new.append(around['bottom'])
if box.left is None and around['left'] is not None:
if around['left'] not in new and around['left'] not in chain:
if around['left'].sides >= 2:
new.append(around['left'])
if box.right is None and around['right'] is not None:
if around['right'] not in new and around['right'] not in chain:
if around['right'].sides >= 2:
new.append(around['right'])
current = new
new = []
chains.append(chain)
sorted_chains = sorted(chains, key=lambda x: len(x))
if prev_line is not None:
prev_boxes = line_boxes(box_mat, prev_line)
for prev_box in prev_boxes:
if prev_box.sides == 3:
side = get_rand_empty(prev_box)
if not choices and len(sorted_chains) > 1:
if len(sorted_chains[1]) > 2 and len(box3) < 2:
side_boxes = line_boxes(box_mat, side)
for box in side_boxes:
if box != prev_box:
if num_boxes_around(box_mat, box) < 4 and facing_out(box_mat, box):
return get_rand_empty(box, exclude=side)
return side
if box3:
b = random.choice(box3)
side = get_rand_empty(b)
if not choices and len(sorted_chains) > 1:
if len(sorted_chains[1]) > 2 and len(box3) < 2:
side_boxes = line_boxes(box_mat, side)
for box in side_boxes:
if box != b:
if num_boxes_around(box_mat, box) < 4 and facing_out(box_mat, box):
return get_rand_empty(box, exclude=side)
return side
if choices:
return random.choice(choices)
if sorted_chains:
return get_rand_empty(random.choice(sorted_chains[0]))
return get_rand_empty(random.choice(crosses))
def expert(box_mat, prev_line):
pass # coming soon
class Player:
def __init__(self, player_type, color, difficulty=1):
self.player_type = player_type
self.score = 0
self.color = color
self.start = None
self.move = None
self.difficulty = difficulty
def get_move(self, box_mat, prev_line):
if self.player_type == 'human':
m = self.move
self.move = None
return m
if self.difficulty == 1:
return easy(box_mat, prev_line)
elif self.difficulty == 2:
return medium(box_mat, prev_line)
elif self.difficulty == 3:
return hard(box_mat, prev_line)
elif self.difficulty == 4:
return extreme(box_mat, prev_line)