feat: add BoardFactory for word search grid generation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Mathew Sir Guest the best
2026-05-06 22:39:43 -06:00
co-authored by Claude Opus 4.6
parent 1c4154b84b
commit e483d1127a
3 changed files with 204 additions and 0 deletions
+115
View File
@@ -0,0 +1,115 @@
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