import random import string from models.category import CategoryWord from models.game import Game, Word DIRECTIONS = { "E": (0, 1), "W": (0, -1), "S": (1, 0), "N": (-1, 0), "SE": (1, 1), "SW": (1, -1), "NE": (-1, 1), "NW": (-1, -1), } MAX_PLACEMENT_ATTEMPTS = 100 class BoardFactory: def __init__(self, session): self.session = session def create(self, category_id, board_size=10, word_count=8): candidates = ( self.session.query(CategoryWord) .filter(CategoryWord.category_id == category_id) .all() ) # Filter words that fit in the grid candidates = [cw for cw in candidates if len(cw.word) <= board_size] random.shuffle(candidates) selected = candidates[:word_count] grid = [[""] * board_size for _ in range(board_size)] placed_words = [] for cw in selected: result = self._place_word(grid, cw.word, board_size) if result: row, col, direction = result placed_words.append( Word( text=cw.word, start_row=row, start_col=col, direction=direction, ) ) # Fill empty cells with random letters for r in range(board_size): for c in range(board_size): if grid[r][c] == "": grid[r][c] = random.choice(string.ascii_uppercase) game = Game( category_id=category_id, board_size=board_size, grid=grid, ) for w in placed_words: w.game = game return game, placed_words def _place_word(self, grid, word, board_size): directions = list(DIRECTIONS.keys()) for _ in range(MAX_PLACEMENT_ATTEMPTS): direction = random.choice(directions) dr, dc = DIRECTIONS[direction] word_len = len(word) # Calculate valid start ranges if dr > 0: row_range = range(0, board_size - word_len + 1) elif dr < 0: row_range = range(word_len - 1, board_size) else: row_range = range(0, board_size) if dc > 0: col_range = range(0, board_size - word_len + 1) elif dc < 0: col_range = range(word_len - 1, board_size) else: col_range = range(0, board_size) if not row_range or not col_range: continue row = random.choice(list(row_range)) col = random.choice(list(col_range)) if self._can_place(grid, word, row, col, dr, dc): self._do_place(grid, word, row, col, dr, dc) return row, col, direction return None def _can_place(self, grid, word, row, col, dr, dc): for i, letter in enumerate(word): r = row + i * dr c = col + i * dc if grid[r][c] != "" and grid[r][c] != letter: return False return True def _do_place(self, grid, word, row, col, dr, dc): for i, letter in enumerate(word): r = row + i * dr c = col + i * dc grid[r][c] = letter