|
|
|
|
|
""" |
|
|
Debug the recurring index error by adding comprehensive bounds checking. |
|
|
""" |
|
|
|
|
|
import asyncio |
|
|
import sys |
|
|
import logging |
|
|
from pathlib import Path |
|
|
|
|
|
|
|
|
project_root = Path(__file__).parent |
|
|
sys.path.insert(0, str(project_root)) |
|
|
|
|
|
from src.services.crossword_generator_fixed import CrosswordGeneratorFixed |
|
|
from src.services.vector_search import VectorSearchService |
|
|
|
|
|
|
|
|
logging.basicConfig(level=logging.DEBUG) |
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
class DebugCrosswordGenerator(CrosswordGeneratorFixed): |
|
|
"""Debug version with comprehensive bounds checking.""" |
|
|
|
|
|
def _can_place_word(self, grid, word, row, col, direction): |
|
|
"""Enhanced _can_place_word with comprehensive bounds checking.""" |
|
|
try: |
|
|
size = len(grid) |
|
|
logger.debug(f"_can_place_word: word={word}, row={row}, col={col}, direction={direction}, grid_size={size}") |
|
|
|
|
|
|
|
|
if row < 0 or col < 0 or row >= size or col >= size: |
|
|
logger.debug(f"Initial bounds check failed: row={row}, col={col}, size={size}") |
|
|
return False |
|
|
|
|
|
if direction == "horizontal": |
|
|
if col + len(word) > size: |
|
|
logger.debug(f"Horizontal bounds check failed: col+len(word)={col + len(word)} > size={size}") |
|
|
return False |
|
|
|
|
|
|
|
|
if col > 0: |
|
|
if row >= size or col - 1 >= size or row < 0 or col - 1 < 0: |
|
|
logger.debug(f"Horizontal left boundary check failed: row={row}, col-1={col-1}, size={size}") |
|
|
return False |
|
|
if grid[row][col - 1] != ".": |
|
|
logger.debug(f"Horizontal left boundary has adjacent letter") |
|
|
return False |
|
|
|
|
|
if col + len(word) < size: |
|
|
if row >= size or col + len(word) >= size or row < 0 or col + len(word) < 0: |
|
|
logger.debug(f"Horizontal right boundary check failed: row={row}, col+len={col + len(word)}, size={size}") |
|
|
return False |
|
|
if grid[row][col + len(word)] != ".": |
|
|
logger.debug(f"Horizontal right boundary has adjacent letter") |
|
|
return False |
|
|
|
|
|
|
|
|
for i, letter in enumerate(word): |
|
|
check_row = row |
|
|
check_col = col + i |
|
|
if check_row >= size or check_col >= size or check_row < 0 or check_col < 0: |
|
|
logger.debug(f"Horizontal letter position check failed: letter {i}, row={check_row}, col={check_col}, size={size}") |
|
|
return False |
|
|
current_cell = grid[check_row][check_col] |
|
|
if current_cell != "." and current_cell != letter: |
|
|
logger.debug(f"Horizontal letter conflict: expected {letter}, found {current_cell}") |
|
|
return False |
|
|
|
|
|
else: |
|
|
if row + len(word) > size: |
|
|
logger.debug(f"Vertical bounds check failed: row+len(word)={row + len(word)} > size={size}") |
|
|
return False |
|
|
|
|
|
|
|
|
if row > 0: |
|
|
if row - 1 >= size or col >= size or row - 1 < 0 or col < 0: |
|
|
logger.debug(f"Vertical top boundary check failed: row-1={row-1}, col={col}, size={size}") |
|
|
return False |
|
|
if grid[row - 1][col] != ".": |
|
|
logger.debug(f"Vertical top boundary has adjacent letter") |
|
|
return False |
|
|
|
|
|
if row + len(word) < size: |
|
|
if row + len(word) >= size or col >= size or row + len(word) < 0 or col < 0: |
|
|
logger.debug(f"Vertical bottom boundary check failed: row+len={row + len(word)}, col={col}, size={size}") |
|
|
return False |
|
|
if grid[row + len(word)][col] != ".": |
|
|
logger.debug(f"Vertical bottom boundary has adjacent letter") |
|
|
return False |
|
|
|
|
|
|
|
|
for i, letter in enumerate(word): |
|
|
check_row = row + i |
|
|
check_col = col |
|
|
if check_row >= size or check_col >= size or check_row < 0 or check_col < 0: |
|
|
logger.debug(f"Vertical letter position check failed: letter {i}, row={check_row}, col={check_col}, size={size}") |
|
|
return False |
|
|
current_cell = grid[check_row][check_col] |
|
|
if current_cell != "." and current_cell != letter: |
|
|
logger.debug(f"Vertical letter conflict: expected {letter}, found {current_cell}") |
|
|
return False |
|
|
|
|
|
logger.debug(f"_can_place_word: SUCCESS for word={word}") |
|
|
return True |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"❌ ERROR in _can_place_word: {e}") |
|
|
logger.error(f" word={word}, row={row}, col={col}, direction={direction}") |
|
|
logger.error(f" grid_size={len(grid) if grid else 'None'}") |
|
|
import traceback |
|
|
traceback.print_exc() |
|
|
return False |
|
|
|
|
|
def _place_word(self, grid, word, row, col, direction): |
|
|
"""Enhanced _place_word with comprehensive bounds checking.""" |
|
|
try: |
|
|
size = len(grid) |
|
|
logger.debug(f"_place_word: word={word}, row={row}, col={col}, direction={direction}, grid_size={size}") |
|
|
|
|
|
original_state = [] |
|
|
|
|
|
if direction == "horizontal": |
|
|
for i, letter in enumerate(word): |
|
|
check_row = row |
|
|
check_col = col + i |
|
|
if check_row >= size or check_col >= size or check_row < 0 or check_col < 0: |
|
|
logger.error(f"❌ _place_word horizontal bounds error: row={check_row}, col={check_col}, size={size}") |
|
|
raise IndexError(f"Grid index out of range: [{check_row}][{check_col}] in grid of size {size}") |
|
|
|
|
|
original_state.append({ |
|
|
"row": check_row, |
|
|
"col": check_col, |
|
|
"value": grid[check_row][check_col] |
|
|
}) |
|
|
grid[check_row][check_col] = letter |
|
|
else: |
|
|
for i, letter in enumerate(word): |
|
|
check_row = row + i |
|
|
check_col = col |
|
|
if check_row >= size or check_col >= size or check_row < 0 or check_col < 0: |
|
|
logger.error(f"❌ _place_word vertical bounds error: row={check_row}, col={check_col}, size={size}") |
|
|
raise IndexError(f"Grid index out of range: [{check_row}][{check_col}] in grid of size {size}") |
|
|
|
|
|
original_state.append({ |
|
|
"row": check_row, |
|
|
"col": check_col, |
|
|
"value": grid[check_row][check_col] |
|
|
}) |
|
|
grid[check_row][check_col] = letter |
|
|
|
|
|
logger.debug(f"_place_word: SUCCESS for word={word}") |
|
|
return original_state |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"❌ ERROR in _place_word: {e}") |
|
|
logger.error(f" word={word}, row={row}, col={col}, direction={direction}") |
|
|
logger.error(f" grid_size={len(grid) if grid else 'None'}") |
|
|
import traceback |
|
|
traceback.print_exc() |
|
|
raise |
|
|
|
|
|
def _remove_word(self, grid, original_state): |
|
|
"""Enhanced _remove_word with comprehensive bounds checking.""" |
|
|
try: |
|
|
size = len(grid) |
|
|
logger.debug(f"_remove_word: restoring {len(original_state)} positions, grid_size={size}") |
|
|
|
|
|
for state in original_state: |
|
|
check_row = state["row"] |
|
|
check_col = state["col"] |
|
|
if check_row >= size or check_col >= size or check_row < 0 or check_col < 0: |
|
|
logger.error(f"❌ _remove_word bounds error: row={check_row}, col={check_col}, size={size}") |
|
|
raise IndexError(f"Grid index out of range: [{check_row}][{check_col}] in grid of size {size}") |
|
|
|
|
|
grid[check_row][check_col] = state["value"] |
|
|
|
|
|
logger.debug(f"_remove_word: SUCCESS") |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"❌ ERROR in _remove_word: {e}") |
|
|
logger.error(f" grid_size={len(grid) if grid else 'None'}") |
|
|
logger.error(f" original_state={original_state}") |
|
|
import traceback |
|
|
traceback.print_exc() |
|
|
raise |
|
|
|
|
|
def _create_simple_cross(self, word_list, word_objs): |
|
|
"""Enhanced _create_simple_cross with comprehensive bounds checking.""" |
|
|
try: |
|
|
logger.debug(f"_create_simple_cross: words={word_list}") |
|
|
|
|
|
if len(word_list) < 2: |
|
|
logger.debug("Not enough words for simple cross") |
|
|
return None |
|
|
|
|
|
word1, word2 = word_list[0], word_list[1] |
|
|
intersections = self._find_word_intersections(word1, word2) |
|
|
|
|
|
if not intersections: |
|
|
logger.debug("No intersections found") |
|
|
return None |
|
|
|
|
|
|
|
|
intersection = intersections[0] |
|
|
size = max(len(word1), len(word2)) + 4 |
|
|
logger.debug(f"Creating grid of size {size} for simple cross") |
|
|
|
|
|
grid = [["." for _ in range(size)] for _ in range(size)] |
|
|
|
|
|
|
|
|
center_row = size // 2 |
|
|
center_col = (size - len(word1)) // 2 |
|
|
|
|
|
logger.debug(f"Placing word1 '{word1}' at row={center_row}, col={center_col}") |
|
|
|
|
|
for i, letter in enumerate(word1): |
|
|
check_row = center_row |
|
|
check_col = center_col + i |
|
|
if check_row >= size or check_col >= size or check_row < 0 or check_col < 0: |
|
|
logger.error(f"❌ _create_simple_cross word1 bounds error: row={check_row}, col={check_col}, size={size}") |
|
|
raise IndexError(f"Grid index out of range: [{check_row}][{check_col}] in grid of size {size}") |
|
|
grid[check_row][check_col] = letter |
|
|
|
|
|
|
|
|
intersection_col = center_col + intersection["word_pos"] |
|
|
word2_start_row = center_row - intersection["placed_pos"] |
|
|
|
|
|
logger.debug(f"Placing word2 '{word2}' at row={word2_start_row}, col={intersection_col}") |
|
|
|
|
|
for i, letter in enumerate(word2): |
|
|
check_row = word2_start_row + i |
|
|
check_col = intersection_col |
|
|
if check_row >= size or check_col >= size or check_row < 0 or check_col < 0: |
|
|
logger.error(f"❌ _create_simple_cross word2 bounds error: row={check_row}, col={check_col}, size={size}") |
|
|
raise IndexError(f"Grid index out of range: [{check_row}][{check_col}] in grid of size {size}") |
|
|
grid[check_row][check_col] = letter |
|
|
|
|
|
placed_words = [ |
|
|
{"word": word1, "row": center_row, "col": center_col, "direction": "horizontal", "number": 1}, |
|
|
{"word": word2, "row": word2_start_row, "col": intersection_col, "direction": "vertical", "number": 2} |
|
|
] |
|
|
|
|
|
logger.debug(f"_create_simple_cross: SUCCESS") |
|
|
|
|
|
trimmed = self._trim_grid(grid, placed_words) |
|
|
clues = self._generate_clues(word_objs[:2], trimmed["placed_words"]) |
|
|
|
|
|
return { |
|
|
"grid": trimmed["grid"], |
|
|
"placed_words": trimmed["placed_words"], |
|
|
"clues": clues |
|
|
} |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"❌ ERROR in _create_simple_cross: {e}") |
|
|
import traceback |
|
|
traceback.print_exc() |
|
|
raise |
|
|
|
|
|
async def test_debug_generator(): |
|
|
"""Test the debug generator to catch index errors.""" |
|
|
try: |
|
|
print("🧪 Testing debug crossword generator...") |
|
|
|
|
|
|
|
|
vector_service = VectorSearchService() |
|
|
|
|
|
|
|
|
generator = DebugCrosswordGenerator(vector_service) |
|
|
|
|
|
|
|
|
test_cases = [ |
|
|
(["animals"], "medium"), |
|
|
(["science"], "hard"), |
|
|
(["technology"], "easy"), |
|
|
(["animals", "science"], "medium"), |
|
|
] |
|
|
|
|
|
for i, (topics, difficulty) in enumerate(test_cases): |
|
|
print(f"\n🔬 Test {i+1}: topics={topics}, difficulty={difficulty}") |
|
|
try: |
|
|
result = await generator.generate_puzzle(topics, difficulty, use_ai=False) |
|
|
if result: |
|
|
print(f"✅ Test {i+1} succeeded") |
|
|
grid_size = len(result['grid']) |
|
|
word_count = len(result['clues']) |
|
|
print(f" Grid: {grid_size}x{grid_size}, Words: {word_count}") |
|
|
else: |
|
|
print(f"⚠️ Test {i+1} returned None") |
|
|
except Exception as e: |
|
|
print(f"❌ Test {i+1} failed: {e}") |
|
|
import traceback |
|
|
traceback.print_exc() |
|
|
return False |
|
|
|
|
|
print(f"\n✅ All debug tests completed!") |
|
|
return True |
|
|
|
|
|
except Exception as e: |
|
|
print(f"❌ Debug test setup failed: {e}") |
|
|
import traceback |
|
|
traceback.print_exc() |
|
|
return False |
|
|
|
|
|
if __name__ == "__main__": |
|
|
asyncio.run(test_debug_generator()) |