File size: 14,434 Bytes
38c016b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
#!/usr/bin/env python3
"""
Debug the recurring index error by adding comprehensive bounds checking.
"""
import asyncio
import sys
import logging
from pathlib import Path
# Add project root to 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
# Enable debug logging
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}")
# Check initial boundaries
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
# Check word boundaries (no adjacent letters) - with bounds check
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
# Check each letter position
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: # vertical
if row + len(word) > size:
logger.debug(f"Vertical bounds check failed: row+len(word)={row + len(word)} > size={size}")
return False
# Check word boundaries - with bounds check
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
# Check each letter position
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
# Use first intersection
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)]
# Place first word horizontally in center
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
# Place second word vertically at intersection
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...")
# Create mock vector service
vector_service = VectorSearchService()
# Create debug generator
generator = DebugCrosswordGenerator(vector_service)
# Test with various topics and difficulties
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()) |