milwright commited on
Commit
e38d8d4
·
1 Parent(s): dfcab2f

improve: differentiate prompts, fix passage boundaries, and avoid early blanks

Browse files

- Make each question type distinct with specific instructions and word limits
- Ensure passages end at complete sentences, not mid-sentence
- Prevent blanks from appearing in first 10 words of passage
- Extract longer passages (1000 chars) for better sentence completion
- Add retry logic if extracted passage is too short

src/clozeGameEngine.js CHANGED
@@ -70,11 +70,11 @@ class ClozeGame {
70
 
71
  // Random position in the middle section
72
  const availableLength = endAtThreeQuarters - startFromMiddle;
73
- const randomOffset = Math.floor(Math.random() * Math.max(0, availableLength - 800));
74
  const startIndex = startFromMiddle + randomOffset;
75
 
76
- // Extract passage
77
- let passage = text.substring(startIndex, startIndex + 800);
78
 
79
  // Clean up start - find first complete sentence
80
  const firstSentenceEnd = passage.search(/[.!?]\s+[A-Z]/);
@@ -82,10 +82,18 @@ class ClozeGame {
82
  passage = passage.substring(firstSentenceEnd + 2);
83
  }
84
 
85
- // Clean up end - end at complete sentence
86
- const lastSentenceEnd = passage.lastIndexOf('.');
87
- if (lastSentenceEnd > 300) {
88
- passage = passage.substring(0, lastSentenceEnd + 1);
 
 
 
 
 
 
 
 
89
  }
90
 
91
  return passage.trim();
@@ -143,18 +151,18 @@ class ClozeGame {
143
 
144
  let wordIndex = -1;
145
 
146
- // First try to find the word in the designated section
147
- for (let i = sectionStart; i < sectionEnd; i++) {
148
  if (wordsLower[i] === cleanSignificant && !selectedIndices.includes(i)) {
149
  wordIndex = i;
150
  break;
151
  }
152
  }
153
 
154
- // If not found in section, look globally
155
  if (wordIndex === -1) {
156
  wordIndex = wordsLower.findIndex((word, idx) =>
157
- word === cleanSignificant && !selectedIndices.includes(idx)
158
  );
159
  }
160
 
@@ -173,11 +181,11 @@ class ClozeGame {
173
  console.warn('No AI words matched in passage, using manual selection');
174
  const manualWords = this.selectWordsManually(words, numberOfBlanks);
175
 
176
- // Try to match manual words
177
  manualWords.forEach((manualWord, index) => {
178
  const cleanManual = manualWord.toLowerCase().replace(/[^\w]/g, '');
179
  const wordIndex = wordsLower.findIndex((word, idx) =>
180
- word === cleanManual && !selectedIndices.includes(idx)
181
  );
182
 
183
  if (wordIndex !== -1) {
@@ -191,11 +199,11 @@ class ClozeGame {
191
  // Sort indices for easier processing
192
  selectedIndices.sort((a, b) => a - b);
193
 
194
- // Final safety check - if still no words found, pick random content words
195
  if (selectedIndices.length === 0) {
196
  console.error('Critical: No words could be selected, using emergency fallback');
197
  const contentWords = words.map((word, idx) => ({ word: word.toLowerCase().replace(/[^\w]/g, ''), idx }))
198
- .filter(item => item.word.length > 3 && !['the', 'and', 'but', 'for', 'are', 'was'].includes(item.word))
199
  .slice(0, numberOfBlanks);
200
 
201
  selectedIndices.push(...contentWords.map(item => item.idx));
 
70
 
71
  // Random position in the middle section
72
  const availableLength = endAtThreeQuarters - startFromMiddle;
73
+ const randomOffset = Math.floor(Math.random() * Math.max(0, availableLength - 1000));
74
  const startIndex = startFromMiddle + randomOffset;
75
 
76
+ // Extract longer initial passage for better sentence completion
77
+ let passage = text.substring(startIndex, startIndex + 1000);
78
 
79
  // Clean up start - find first complete sentence
80
  const firstSentenceEnd = passage.search(/[.!?]\s+[A-Z]/);
 
82
  passage = passage.substring(firstSentenceEnd + 2);
83
  }
84
 
85
+ // Clean up end - ensure we end at a complete sentence
86
+ const sentences = passage.split(/(?<=[.!?])\s+/);
87
+ if (sentences.length > 1) {
88
+ // Remove the last sentence if it might be incomplete
89
+ sentences.pop();
90
+ passage = sentences.join(' ');
91
+ }
92
+
93
+ // Ensure minimum length
94
+ if (passage.length < 400) {
95
+ // Try again with different position if too short
96
+ return this.extractCoherentPassage(text);
97
  }
98
 
99
  return passage.trim();
 
151
 
152
  let wordIndex = -1;
153
 
154
+ // First try to find the word in the designated section (avoiding first 10 words)
155
+ for (let i = Math.max(10, sectionStart); i < sectionEnd; i++) {
156
  if (wordsLower[i] === cleanSignificant && !selectedIndices.includes(i)) {
157
  wordIndex = i;
158
  break;
159
  }
160
  }
161
 
162
+ // If not found in section, look globally (but still avoid first 10 words)
163
  if (wordIndex === -1) {
164
  wordIndex = wordsLower.findIndex((word, idx) =>
165
+ word === cleanSignificant && !selectedIndices.includes(idx) && idx >= 10
166
  );
167
  }
168
 
 
181
  console.warn('No AI words matched in passage, using manual selection');
182
  const manualWords = this.selectWordsManually(words, numberOfBlanks);
183
 
184
+ // Try to match manual words (avoiding first 10 words)
185
  manualWords.forEach((manualWord, index) => {
186
  const cleanManual = manualWord.toLowerCase().replace(/[^\w]/g, '');
187
  const wordIndex = wordsLower.findIndex((word, idx) =>
188
+ word === cleanManual && !selectedIndices.includes(idx) && idx >= 10
189
  );
190
 
191
  if (wordIndex !== -1) {
 
199
  // Sort indices for easier processing
200
  selectedIndices.sort((a, b) => a - b);
201
 
202
+ // Final safety check - if still no words found, pick random content words (avoiding first 10)
203
  if (selectedIndices.length === 0) {
204
  console.error('Critical: No words could be selected, using emergency fallback');
205
  const contentWords = words.map((word, idx) => ({ word: word.toLowerCase().replace(/[^\w]/g, ''), idx }))
206
+ .filter(item => item.word.length > 3 && !['the', 'and', 'but', 'for', 'are', 'was'].includes(item.word) && item.idx >= 10)
207
  .slice(0, numberOfBlanks);
208
 
209
  selectedIndices.push(...contentWords.map(item => item.idx));
src/conversationManager.js CHANGED
@@ -106,13 +106,13 @@ class ChatService {
106
  const wordInstruction = `The target word is "${targetWord}". NEVER mention or reveal this word in your response.`;
107
 
108
  const prompts = {
109
- part_of_speech: `${baseContext}\n\n${wordInstruction}\n\nGive the part of speech for "${targetWord}" and a grammatical hint. Say "This is a [noun/verb/adjective/adverb]" then explain its function. Maximum 20 words. Do not reveal the actual word.`,
110
 
111
- sentence_role: `${baseContext}\n\n${wordInstruction}\n\nDescribe the role "${targetWord}" plays in this sentence. Does it express action, emotion, description, or relationship? Maximum 20 words. Do not reveal the actual word.`,
112
 
113
- word_category: `${baseContext}\n\n${wordInstruction}\n\nWhat category does "${targetWord}" belong to? Say "This word describes [general category]" without giving specific examples. Maximum 20 words. Do not reveal the actual word.`,
114
 
115
- synonym: `${baseContext}\n\n${wordInstruction}\n\nGive a conceptual hint about "${targetWord}". Format: "Think of something that [general description]." Be indirect and conceptual. Maximum 20 words. Do not reveal the actual word.`
116
  };
117
 
118
  return prompts[questionType] || `${baseContext}\n\n${wordInstruction}\n\nProvide a helpful hint about "${targetWord}" without revealing it.`;
 
106
  const wordInstruction = `The target word is "${targetWord}". NEVER mention or reveal this word in your response.`;
107
 
108
  const prompts = {
109
+ part_of_speech: `${baseContext}\n\n${wordInstruction}\n\nState only the grammatical category: "This is a noun" or "This is a verb" etc. Then give ONE grammar rule about how this type of word works. Maximum 15 words total.`,
110
 
111
+ sentence_role: `${baseContext}\n\n${wordInstruction}\n\nExplain only what job "${targetWord}" does in this specific sentence. Focus on its function, not what it means. Start with "In this sentence, it..." Maximum 15 words.`,
112
 
113
+ word_category: `${baseContext}\n\n${wordInstruction}\n\nClassify "${targetWord}" into a broad category. Choose from: living thing, object, action, feeling, quality, place, or time. Say "This belongs to the category of..." Maximum 12 words.`,
114
 
115
+ synonym: `${baseContext}\n\n${wordInstruction}\n\nGive a different word that could replace "${targetWord}" in this sentence. Say "You could use the word..." Maximum 8 words. Choose a simple synonym.`
116
  };
117
 
118
  return prompts[questionType] || `${baseContext}\n\n${wordInstruction}\n\nProvide a helpful hint about "${targetWord}" without revealing it.`;