Spaces:
Sleeping
Sleeping
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 +23 -15
- src/conversationManager.js +4 -4
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 -
|
74 |
const startIndex = startFromMiddle + randomOffset;
|
75 |
|
76 |
-
// Extract passage
|
77 |
-
let passage = text.substring(startIndex, startIndex +
|
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
|
87 |
-
if (
|
88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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\
|
110 |
|
111 |
-
sentence_role: `${baseContext}\n\n${wordInstruction}\n\
|
112 |
|
113 |
-
word_category: `${baseContext}\n\n${wordInstruction}\n\
|
114 |
|
115 |
-
synonym: `${baseContext}\n\n${wordInstruction}\n\nGive a
|
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.`;
|