Syndles

My Dad’s just turned 60 so I’ve made his app idea https://syndles.com/.

He envisioned a game similar to Wordle that’ll help you learn new words each day. He liked the idea of guessing words that were all synonyms, so when you manage to get one you have a good shot at the others.

I created this to shut him up ?. He’s still upset about a previous app idea he had, which I dismissed, only for Adobe to go ahead and create something similar a couple of years later. I often hear, ‘We could be on a yacht if you had just listened to me,’ from him.

So let me know what you think, has Bernard had a stroke of brilliance here?

How to play?

  • A Syndle contains 3 words you need to guess
  • All words in a Syndle are synonyms*
  • You get 5 lives per Syndle
  • You get 1 go per day

*A synonym is a word or phrase that means exactly or nearly the same as another word or phrase in the same language, for example shut is a synonym of close.

The tech

Frontend

I originally wrote the frontend using plain old JavaScript, but quickly realised how much more code I was having to write. I stuck to Vue 3.

Backend

The backend is written using Azure functions and CosmosDB for persistence. This should handle the scaling requirements, as of course It’s bound to go viral.

How it works

I’m using WordNet‘s thesaurus and gathering all the possible synonyms that fit together in the 7×7 grid. I’m running this script once and persisting the combinations:

for (const [word, synonyms] of Object.entries(synonymDictionary)) {
    if (synonyms.length > 1) {
        const potentialMiddleWords = new Set();
        for (const synonym of synonyms) {
            if (word.includes(synonym) || synonym.includes(word) || !isSynonym(word, synonym)) continue;
            potentialMiddleWords.add(synonym);
        }

        for (let middleWord of potentialMiddleWords) {
            const otherSynonyms = synonyms.filter(s => s !== middleWord);
            for (let otherSynonym of otherSynonyms) {
                if (isSynonym(middleWord, otherSynonym) && !middleWord.includes(otherSynonym) && !otherSynonym.includes(middleWord) && !word.includes(otherSynonym) && !otherSynonym.includes(word)) {
                    games.push([word, middleWord, otherSynonym]);
                }
            }
        }
    }
}

Then each day I’m running a timer job to create the puzzle for the next day:

    [Function("CreateTomorrowsPuzzle")]
    public async Task Run(
        [TimerTrigger("0 0 17 * * *")] TimerInfo myTimer, FunctionContext context)
    {
        try
        {
            await CreatePuzzle(1);
        }
        catch (Exception e)
        {
            _logger.LogError(e, "There was an issue creating puzzle");
            await CreatePuzzle(1);
        }
    }

I’ve got a very basic retry mechanism there ha! That’s one for later.

There’s currently no login mechanism, it uses local storage to keep track of your user id and reloads the session when you come back to it. This should hopefully encourage people to come back to the puzzle if they get stuck, rather than banging their heads against the wall.

It’s been a fun little project coding up Syndles, my family are enjoying competing against each other on the leaderboard each day. So I hope other people get some enjoyment out of it too. I potentially might need to add a family leaderboard feature as it might get a little disheartening seeing people scoring really high!

2 Comments

  1. Looks great. Looking forward to playing tomorrow’s game. Your dad picking out Yachts?

    • Thanks mate! Today’s Syndles were really difficult. Hahaha, with the revenue this is gonna pull in I might be able to get him one from Temu.

Leave a Reply

Your email address will not be published. Required fields are marked *