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
|
import { OpenAI } from "openai";
const basePrompt = `
You are the backend logic for a game like the New York Times’ *Connections*.
A player gives you four words. Your job is to return the **most clever, satisfying category** that all four words fit into — as if you were designing a high-quality puzzle.
🎯 Output must be JSON:
{"categoryName": "Short Title (≤5 words)", "reason": "Brief, clear explanation why each word fits"}
🧠 Your answer should feel:
- **Clever and insightful** (not generic)
- **Tight and specific** (all 4 words must fit cleanly)
- **Surprising but satisfying** (think lateral thinking, not just surface meaning)
🧩 Great connections are often based on:
1. **Grammar or structure** (e.g., homonyms, stress-shifting words)
2. **Wordplay** (prefixes, rhymes, common idioms)
3. **Cultural patterns** (slang, media, jokes, Jeopardy-style trivia)
4. **Domain-specific themes** (tech terms, sports slang, myth references)
💡 Think like a puzzle maker. Test your idea:
- Would a smart, skeptical puzzle fan say “Ohhh, nice”?
- Does **each word** clearly belong?
- If not, scrap it and try another approach.
Avoid weak categories like:
- “Verbs” ❌ (too broad)
- “Things you can flip” ❌ (tenuous logic)
- “Nice things” ❌ (vague)
✔ Examples of great answers:
[record, permit, insult, reject] → "Stress-Shifting Words"
[day, head, toe, man] → "___ to ___"
[brew, java, mud, rocketfuel] → "Slang for Coffee"
[duck, bank, mail, plant] → "Nouns That Are Verbs"
[transexual, muslims, media, taxes] → "Fox News Scapegoats"
⚠️ Don’t overreach. Do not invent connections — if it’s not clean, try a new angle.
Mandatory check before submitting:
- Word 1: does it clearly fit?
- Word 2: does it clearly fit?
- Word 3: does it clearly fit?
- Word 4: does it clearly fit?
If even one doesn’t, the category is wrong. Start over.
Keep it **fun**, **tight**, and **clever**. Never lazy. Never vague.
🎯 Output must be JSON:
{"categoryName": "Short Title (≤5 words)", "reason": "Brief, clear explanation why each word fits"}
`;
let client: OpenAI;
const getCompletion = async ({
messages,
}: {
messages: string[];
}): Promise<string> => {
if (!client) {
client = new OpenAI({ apiKey: process.env.OPENAI_API! });
}
const completion = await client.chat.completions.create({
model: "gpt-4.1",
messages: messages.map((message) => ({
role: "developer",
content: message,
})),
});
return completion.choices[0].message.content!;
};
export const getGroupName = async (
words: string[]
): Promise<{
categoryName: string;
reason: string;
}> => {
let candidate: { categoryName: string; reason: string };
const messages = [
basePrompt,
`Now, given these four words: ${[words.join(", ")]}`,
];
candidate = JSON.parse(await getCompletion({ messages }));
if (!candidate.categoryName || !candidate.reason) {
throw new Error(`Got invalid response!`);
}
console.log(`Got candidate: ${JSON.stringify(candidate)}`);
return candidate!;
};
|