vimalk78 commited on
Commit
20d5f9d
·
1 Parent(s): 790e716

Fix server binding and reduce verbose logging for production deployment

Browse files

- Bind server to 0.0.0.0 in production for external connections (fixes HF Spaces connectivity)
- Add debug logging for working directory and app location in production
- Remove verbose crossword generation logs, keep only error messages
- Streamline backtracking placement logging for better performance

Signed-off-by: Vimal Kumar <vimal78@gmail.com>

crossword-app/backend/src/app.js CHANGED
@@ -115,9 +115,12 @@ app.use((error, req, res, next) => {
115
  });
116
 
117
  if (require.main === module) {
118
- app.listen(PORT, () => {
119
- console.log(`Server running on port ${PORT}`);
 
120
  console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
 
 
121
  if (process.env.NODE_ENV === 'production') {
122
  console.log(`Serving React app from /public directory`);
123
  console.log(`CORS enabled for same origin`);
 
115
  });
116
 
117
  if (require.main === module) {
118
+ const HOST = process.env.NODE_ENV === 'production' ? '0.0.0.0' : 'localhost';
119
+ app.listen(PORT, HOST, () => {
120
+ console.log(`Server running on ${HOST}:${PORT}`);
121
  console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
122
+ console.log(`Working directory: ${process.cwd()}`);
123
+ console.log(`App file location: ${__dirname}`);
124
  if (process.env.NODE_ENV === 'production') {
125
  console.log(`Serving React app from /public directory`);
126
  console.log(`CORS enabled for same origin`);
crossword-app/backend/src/services/crosswordGenerator.js CHANGED
@@ -8,30 +8,23 @@ class CrosswordGenerator {
8
  }
9
 
10
  async generatePuzzle(topics, difficulty = 'medium') {
11
- console.log(`🎯 Starting puzzle generation - Topics: ${JSON.stringify(topics)}, Difficulty: ${difficulty}`);
12
-
13
  try {
14
  const words = await this.selectWords(topics, difficulty);
15
- console.log(`📚 Selected ${words.length} words for puzzle:`, words.map(w => w.word).join(', '));
16
 
17
  if (words.length < this.minWords) {
18
- console.log(`❌ Not enough words: ${words.length} < ${this.minWords}`);
19
  throw new Error('Not enough words available for selected topics');
20
  }
21
 
22
- console.log(`🔧 Starting grid creation with ${words.length} words`);
23
  const gridResult = this.createGrid(words);
24
 
25
  if (!gridResult) {
26
- console.log(`❌ Grid creation failed - could not place words`);
27
  throw new Error('Could not place words in grid');
28
  }
29
-
30
- console.log(`✅ Grid created successfully - Size: ${gridResult.size}x${gridResult.size}, Words placed: ${gridResult.placedWords.length}`);
31
 
32
  const clues = this.generateClues(words, gridResult.placedWords);
33
 
34
- console.log(`🎉 Puzzle generation completed successfully!`);
35
  return {
36
  grid: gridResult.grid,
37
  clues: clues,
@@ -122,48 +115,40 @@ class CrosswordGenerator {
122
 
123
  createGrid(words) {
124
  if (!words || words.length === 0) {
125
- console.log(`❌ No words provided to createGrid`);
126
  return null;
127
  }
128
 
129
  const wordList = words.map(w => w.word.toUpperCase()).sort((a, b) => b.length - a.length);
130
  const size = this.calculateGridSize(wordList);
131
- console.log(`📐 Calculated grid size: ${size}x${size} for words:`, wordList.join(', '));
132
 
133
  // Try with different grid sizes and word counts
134
  for (let attempt = 0; attempt < 3; attempt++) {
135
  const currentSize = size + attempt;
136
- console.log(`🔍 Attempt ${attempt + 1}: Trying grid size ${currentSize}x${currentSize} with ${wordList.length} words`);
137
 
138
  // First try with all words
139
  let result = this.placeWordsInGrid(wordList, currentSize);
140
  if (result) {
141
- console.log(`✅ Success with all ${wordList.length} words on size ${currentSize}x${currentSize}`);
142
  return { grid: result.grid, size: currentSize, placedWords: result.placedWords };
143
  }
144
 
145
  // If that fails, try with 1-2 fewer words (but not too aggressive)
146
  if (wordList.length > 7) {
147
  const reducedWords = wordList.slice(0, wordList.length - 1);
148
- console.log(`🔄 Retrying with ${reducedWords.length} words:`, reducedWords.join(', '));
149
  result = this.placeWordsInGrid(reducedWords, currentSize);
150
  if (result) {
151
- console.log(`✅ Success with ${reducedWords.length} words on size ${currentSize}x${currentSize}`);
152
  return { grid: result.grid, size: currentSize, placedWords: result.placedWords };
153
  }
154
  }
155
  }
156
 
157
  // Only as last resort, try with minimum words
158
- console.log(`🚨 Fallback: Trying with minimum 6 words`);
159
  for (let attempt = 0; attempt < 2; attempt++) {
160
  const currentSize = size + attempt;
161
  if (wordList.length >= 6) {
162
  const minWords = wordList.slice(0, 6);
163
- console.log(`🔄 Last resort attempt ${attempt + 1}: ${minWords.length} words on ${currentSize}x${currentSize}:`, minWords.join(', '));
164
  const result = this.placeWordsInGrid(minWords, currentSize);
165
  if (result) {
166
- console.log(`✅ Success with minimum ${minWords.length} words on size ${currentSize}x${currentSize}`);
167
  return { grid: result.grid, size: currentSize, placedWords: result.placedWords };
168
  }
169
  }
@@ -171,14 +156,13 @@ class CrosswordGenerator {
171
 
172
  // Absolute last resort: simple cross pattern with just 2 words
173
  if (wordList.length >= 2) {
174
- console.log(`🆘 Emergency fallback: Simple 2-word cross pattern`);
175
  const result = this.createSimpleCross(wordList.slice(0, 2));
176
  if (result) {
177
  return result;
178
  }
179
  }
180
 
181
- console.log(`❌ All grid creation attempts failed`);
182
  return null;
183
  }
184
 
@@ -242,53 +226,40 @@ class CrosswordGenerator {
242
  }
243
 
244
  placeWordsInGrid(words, size) {
245
- console.log(`🎲 Creating ${size}x${size} grid for words:`, words.join(', '));
246
  const grid = Array(size).fill().map(() => Array(size).fill('.'));
247
  const placedWords = [];
248
 
249
- console.log(`🔄 Starting backtrack placement...`);
250
  const startTime = Date.now();
251
  const timeout = 5000; // Reduced to 5 second timeout
252
 
253
  if (this.backtrackPlacement(grid, words, 0, placedWords, startTime, timeout)) {
254
- console.log(`✅ Backtracking successful! Placed ${placedWords.length} words`);
255
  const trimmed = this.trimGrid(grid, placedWords);
256
- console.log(`📏 Grid trimmed to ${trimmed.grid.length}x${trimmed.grid[0].length}`);
257
  return { grid: trimmed.grid, placedWords: trimmed.placedWords };
258
  }
259
 
260
- console.log(`❌ Backtracking failed for ${size}x${size} grid (timeout or no solution)`);
261
  return null;
262
  }
263
 
264
  backtrackPlacement(grid, words, wordIndex, placedWords, startTime = Date.now(), timeout = 5000, callCount = 0) {
265
  // Check timeout more frequently
266
  if (callCount % 50 === 0 && Date.now() - startTime > timeout) {
267
- console.log(`⏰ Timeout reached after ${Date.now() - startTime}ms (${callCount} calls)`);
268
  return false;
269
  }
270
 
271
  if (wordIndex >= words.length) {
272
- console.log(`🎉 All ${words.length} words placed successfully!`);
273
  return true;
274
  }
275
 
276
  const word = words[wordIndex];
277
  const size = grid.length;
278
-
279
- // Limit excessive logging for performance
280
- if (callCount % 50 === 0 || wordIndex <= 2) {
281
- console.log(`🔤 Placing word ${wordIndex + 1}/${words.length}: "${word}" (${word.length} chars) [call ${callCount}]`);
282
- }
283
 
284
  // For the first word, place the longest word in the center horizontally
285
  if (wordIndex === 0) {
286
  const centerRow = Math.floor(size / 2);
287
  const centerCol = Math.floor((size - word.length) / 2);
288
- console.log(`📍 First word "${word}": trying center position (${centerRow}, ${centerCol}) horizontal`);
289
 
290
  if (this.canPlaceWord(grid, word, centerRow, centerCol, 'horizontal')) {
291
- console.log(`✅ First word "${word}" placed at center`);
292
  const originalState = this.placeWord(grid, word, centerRow, centerCol, 'horizontal');
293
  placedWords.push({ word, row: centerRow, col: centerCol, direction: 'horizontal', number: 1 });
294
 
@@ -296,11 +267,8 @@ class CrosswordGenerator {
296
  return true;
297
  }
298
 
299
- console.log(`🔙 Backtracking from first word "${word}"`);
300
  this.removeWord(grid, originalState);
301
  placedWords.pop();
302
- } else {
303
- console.log(`❌ Cannot place first word "${word}" at center`);
304
  }
305
  return false;
306
  }
@@ -308,7 +276,6 @@ class CrosswordGenerator {
308
  // For the second word, try to create a central cross with smart selection
309
  if (wordIndex === 1) {
310
  const firstWord = placedWords[0];
311
- console.log(`🔍 Smart second word selection - trying to find word that intersects with "${firstWord.word}"`);
312
 
313
  // Try all remaining words to find one that can intersect
314
  const remainingWords = words.slice(1); // All words except the first
@@ -328,41 +295,31 @@ class CrosswordGenerator {
328
 
329
  // Sort by intersection count (more intersections = better)
330
  wordsWithIntersections.sort((a, b) => b.intersectionCount - a.intersectionCount);
331
- console.log(`📊 Found ${wordsWithIntersections.length} words with intersections:`,
332
- wordsWithIntersections.map(w => `${w.word}(${w.intersectionCount})`).join(', '));
333
 
334
  // Try each word with intersections
335
  for (const candidateInfo of wordsWithIntersections) {
336
  const candidateWord = candidateInfo.word;
337
- console.log(`🔍 Trying second word "${candidateWord}" with ${candidateInfo.intersectionCount} intersections`);
338
 
339
  for (const intersection of candidateInfo.intersections) {
340
  const placement = this.calculateIntersectionPlacement(candidateWord, intersection.wordPos, firstWord, intersection.placedPos);
341
- console.log(`🎯 Trying intersection at word[${intersection.wordPos}] = placed[${intersection.placedPos}], placement:`, placement);
342
 
343
  if (placement && this.canPlaceWord(grid, candidateWord, placement.row, placement.col, placement.direction)) {
344
- console.log(`✅ Valid placement found for "${candidateWord}"`);
345
  const originalState = this.placeWord(grid, candidateWord, placement.row, placement.col, placement.direction);
346
  placedWords.push({ word: candidateWord, ...placement, number: 2 });
347
 
348
  // Create new word order with the selected second word
349
  const newWordOrder = [words[0], candidateWord, ...words.slice(1).filter(w => w !== candidateWord)];
350
- console.log(`🔄 New word order:`, newWordOrder.join(', '));
351
 
352
  if (this.backtrackPlacement(grid, newWordOrder, wordIndex + 1, placedWords, startTime, timeout, callCount + 1)) {
353
  return true;
354
  }
355
 
356
- console.log(`🔙 Backtracking from second word "${candidateWord}"`);
357
  this.removeWord(grid, originalState);
358
  placedWords.pop();
359
- } else {
360
- console.log(`❌ Invalid placement for "${candidateWord}" at`, placement);
361
  }
362
  }
363
  }
364
 
365
- console.log(`❌ No valid second word found with intersections`);
366
  return false;
367
  }
368
 
@@ -901,15 +858,13 @@ class CrosswordGenerator {
901
  }
902
 
903
  createSimpleCross(words) {
904
- console.log(`🛠️ Creating simple cross with: ${words.join(', ')}`);
905
-
906
  // Find the best intersection between the two words
907
  const word1 = words[0];
908
  const word2 = words[1];
909
  const intersections = this.findWordIntersections(word1, word2);
910
 
911
  if (intersections.length === 0) {
912
- console.log(`❌ No intersections found between ${word1} and ${word2}`);
913
  return null;
914
  }
915
 
@@ -940,7 +895,6 @@ class CrosswordGenerator {
940
  ];
941
 
942
  const trimmed = this.trimGrid(grid, placedWords);
943
- console.log(`✅ Simple cross created successfully`);
944
  return { grid: trimmed.grid, size: trimmed.grid.length, placedWords: trimmed.placedWords };
945
  }
946
 
 
8
  }
9
 
10
  async generatePuzzle(topics, difficulty = 'medium') {
 
 
11
  try {
12
  const words = await this.selectWords(topics, difficulty);
 
13
 
14
  if (words.length < this.minWords) {
15
+ console.error(`❌ Not enough words: ${words.length} < ${this.minWords}`);
16
  throw new Error('Not enough words available for selected topics');
17
  }
18
 
 
19
  const gridResult = this.createGrid(words);
20
 
21
  if (!gridResult) {
22
+ console.error(`❌ Grid creation failed - could not place words`);
23
  throw new Error('Could not place words in grid');
24
  }
 
 
25
 
26
  const clues = this.generateClues(words, gridResult.placedWords);
27
 
 
28
  return {
29
  grid: gridResult.grid,
30
  clues: clues,
 
115
 
116
  createGrid(words) {
117
  if (!words || words.length === 0) {
118
+ console.error(`❌ No words provided to createGrid`);
119
  return null;
120
  }
121
 
122
  const wordList = words.map(w => w.word.toUpperCase()).sort((a, b) => b.length - a.length);
123
  const size = this.calculateGridSize(wordList);
 
124
 
125
  // Try with different grid sizes and word counts
126
  for (let attempt = 0; attempt < 3; attempt++) {
127
  const currentSize = size + attempt;
 
128
 
129
  // First try with all words
130
  let result = this.placeWordsInGrid(wordList, currentSize);
131
  if (result) {
 
132
  return { grid: result.grid, size: currentSize, placedWords: result.placedWords };
133
  }
134
 
135
  // If that fails, try with 1-2 fewer words (but not too aggressive)
136
  if (wordList.length > 7) {
137
  const reducedWords = wordList.slice(0, wordList.length - 1);
 
138
  result = this.placeWordsInGrid(reducedWords, currentSize);
139
  if (result) {
 
140
  return { grid: result.grid, size: currentSize, placedWords: result.placedWords };
141
  }
142
  }
143
  }
144
 
145
  // Only as last resort, try with minimum words
 
146
  for (let attempt = 0; attempt < 2; attempt++) {
147
  const currentSize = size + attempt;
148
  if (wordList.length >= 6) {
149
  const minWords = wordList.slice(0, 6);
 
150
  const result = this.placeWordsInGrid(minWords, currentSize);
151
  if (result) {
 
152
  return { grid: result.grid, size: currentSize, placedWords: result.placedWords };
153
  }
154
  }
 
156
 
157
  // Absolute last resort: simple cross pattern with just 2 words
158
  if (wordList.length >= 2) {
 
159
  const result = this.createSimpleCross(wordList.slice(0, 2));
160
  if (result) {
161
  return result;
162
  }
163
  }
164
 
165
+ console.error(`❌ All grid creation attempts failed`);
166
  return null;
167
  }
168
 
 
226
  }
227
 
228
  placeWordsInGrid(words, size) {
 
229
  const grid = Array(size).fill().map(() => Array(size).fill('.'));
230
  const placedWords = [];
231
 
 
232
  const startTime = Date.now();
233
  const timeout = 5000; // Reduced to 5 second timeout
234
 
235
  if (this.backtrackPlacement(grid, words, 0, placedWords, startTime, timeout)) {
 
236
  const trimmed = this.trimGrid(grid, placedWords);
 
237
  return { grid: trimmed.grid, placedWords: trimmed.placedWords };
238
  }
239
 
 
240
  return null;
241
  }
242
 
243
  backtrackPlacement(grid, words, wordIndex, placedWords, startTime = Date.now(), timeout = 5000, callCount = 0) {
244
  // Check timeout more frequently
245
  if (callCount % 50 === 0 && Date.now() - startTime > timeout) {
246
+ console.error(`⏰ Placement timeout reached after ${Date.now() - startTime}ms`);
247
  return false;
248
  }
249
 
250
  if (wordIndex >= words.length) {
 
251
  return true;
252
  }
253
 
254
  const word = words[wordIndex];
255
  const size = grid.length;
 
 
 
 
 
256
 
257
  // For the first word, place the longest word in the center horizontally
258
  if (wordIndex === 0) {
259
  const centerRow = Math.floor(size / 2);
260
  const centerCol = Math.floor((size - word.length) / 2);
 
261
 
262
  if (this.canPlaceWord(grid, word, centerRow, centerCol, 'horizontal')) {
 
263
  const originalState = this.placeWord(grid, word, centerRow, centerCol, 'horizontal');
264
  placedWords.push({ word, row: centerRow, col: centerCol, direction: 'horizontal', number: 1 });
265
 
 
267
  return true;
268
  }
269
 
 
270
  this.removeWord(grid, originalState);
271
  placedWords.pop();
 
 
272
  }
273
  return false;
274
  }
 
276
  // For the second word, try to create a central cross with smart selection
277
  if (wordIndex === 1) {
278
  const firstWord = placedWords[0];
 
279
 
280
  // Try all remaining words to find one that can intersect
281
  const remainingWords = words.slice(1); // All words except the first
 
295
 
296
  // Sort by intersection count (more intersections = better)
297
  wordsWithIntersections.sort((a, b) => b.intersectionCount - a.intersectionCount);
 
 
298
 
299
  // Try each word with intersections
300
  for (const candidateInfo of wordsWithIntersections) {
301
  const candidateWord = candidateInfo.word;
 
302
 
303
  for (const intersection of candidateInfo.intersections) {
304
  const placement = this.calculateIntersectionPlacement(candidateWord, intersection.wordPos, firstWord, intersection.placedPos);
 
305
 
306
  if (placement && this.canPlaceWord(grid, candidateWord, placement.row, placement.col, placement.direction)) {
 
307
  const originalState = this.placeWord(grid, candidateWord, placement.row, placement.col, placement.direction);
308
  placedWords.push({ word: candidateWord, ...placement, number: 2 });
309
 
310
  // Create new word order with the selected second word
311
  const newWordOrder = [words[0], candidateWord, ...words.slice(1).filter(w => w !== candidateWord)];
 
312
 
313
  if (this.backtrackPlacement(grid, newWordOrder, wordIndex + 1, placedWords, startTime, timeout, callCount + 1)) {
314
  return true;
315
  }
316
 
 
317
  this.removeWord(grid, originalState);
318
  placedWords.pop();
 
 
319
  }
320
  }
321
  }
322
 
 
323
  return false;
324
  }
325
 
 
858
  }
859
 
860
  createSimpleCross(words) {
 
 
861
  // Find the best intersection between the two words
862
  const word1 = words[0];
863
  const word2 = words[1];
864
  const intersections = this.findWordIntersections(word1, word2);
865
 
866
  if (intersections.length === 0) {
867
+ console.error(`❌ No intersections found between ${word1} and ${word2}`);
868
  return null;
869
  }
870
 
 
895
  ];
896
 
897
  const trimmed = this.trimGrid(grid, placedWords);
 
898
  return { grid: trimmed.grid, size: trimmed.grid.length, placedWords: trimmed.placedWords };
899
  }
900