Skip to content

A carriage return almost broke every Alfazy guess

I added a real dictionary to Alfazy, then realised an invisible \r would reject every valid word on a Windows checkout.

SD
Shubham Datarkar
· 2 min read

I added a real dictionary to Alfazy this weekend so the game stops accepting gibberish. Wired it up, felt good. Then it hit me that on a fresh Windows checkout the whole thing would reject every valid word -- and I would not have seen it until a real player did.

Alfazy is my Wordle-style game on the site. Until yesterday it let you guess any five letters -- 'qwrtz' counted as a turn. Cheap to ship, annoying to play. So I dropped in a ~15,000 word list as one big raw string and built a Set to check membership: split the text on newlines, done in a single line.

The one-liner was new Set(RAW.split(backslash-n)). Reads fine. Passes on my machine. Ships the bug anyway.

The bug I could not see

Here is the trap. I develop on Windows with git autocrlf on. When the file lands on disk every line ends with carriage-return plus newline, not just newline. Split that raw string on the newline alone and every word keeps a trailing carriage return. So the Set holds 'apple\r', 'table\r', 'sloth\r'. A player types 'apple', I look it up, it is not there. Every legal word silently rejected. The game becomes unwinnable and it looks like my dictionary is just wrong.

The nasty part is invisibility. A carriage return does not print. In a console log 'apple\r' looks exactly like 'apple'. You can stare at the two strings side by side and swear they match. I only caught it because the validation felt too strict when I played, and line endings are the first thing I check now after years of getting burned by exactly this on Windows.

The fix, and the lesson

The fix is boring: split on a regex that eats an optional carriage return before the newline, then filter out empty strings. Four lines instead of one.

Split on \r?\n so a CRLF checkout does not leave a carriage return on each word.

the comment I left so future-me does not repeat this

The comment matters more than the code. The code looks obviously fine, so the next person -- me, in six months -- will 'simplify' it back to a plain newline split unless a note is screaming why not. Half of defensive code is really a message to the future.

The real lesson is not about line endings. It is that 'works on my machine' and 'works on a fresh checkout' are different claims, and the gap between them is where Windows quietly eats you. Newline handling, path separators, case-sensitive filenames -- none of it bites until the code runs somewhere that is not your laptop. Dictionary is live now, guesses are validated, and a stray blank line can never sneak an empty string in either. Small fix, but the kind that would have turned into a confusing bug report from a real player. I would rather kill those before they are typed.

Reactions

How was this article?

Newsletter

Never miss the next one

Get the latest playbooks, build logs, and the occasional unpublished idea straight to your inbox. One signal a week — no noise.