Welcome to EnDavid.com. You can find here a compendium of things that I have published and other stuff I made during my spare time.If you get lost, try visiting the Site Map.In this main page, you can find my main blog, where I keep track of the updates of this site, and post some technical articles from time to time. If you are interested, just subscribe to the RSS feed.
I have several word puzzle games in the App Store, and 3 of them started acting funny in recent versions of iOS: Silabitas, Sil·labetes, and Sopilabitas. The English one, Syllabits, still works fine.
What happened is that some words could not be made, as if they were not in the dictionary. For instance, there’s a stage in Silabitas where you have to construct the word “chaqueta” (jacket), but the game doesn’t recognize it, so you can’t clear the stage.
Screenshot of the “chaqueta” stage in Silabitas
The bug appeared in recent version of iOS because the way strings are sorted has changed. Continue reading for more details.
Alphabetical order across languages
In my word games I do string comparison with the compare function in Swift string class to check the order of letters and words. Strings are made of Unicode characters.
In Unicode, each character is assigned a unique code point. For example:
“ñ” (LATIN SMALL LETTER N WITH TILDE) has a Unicode scalar value of U+00F1.
“o” (LATIN SMALL LETTER O) has a Unicode scalar value of U+006F.
When comparing strings or characters in Swift, the result depends on the collation algorithm being used. Collation determines how characters are ordered, taking into account locale-specific rules.
It seems that by default the string comparison was using the locale before, but that seems to have changed. If no locale is explicitly provided, Swift must be using binary Unicode ordering now, instead of a specific linguistic locale. That means that the “ñ” will appear at the end of the alphabet, after the “z”, instead of appearing after the “n”, and before the “o”, because its numerical value is greater than “z”.
To fix that I simply needed to specify the locale when calling compare. See the unit test below:
func testSpecialSymbolsOrder() {
// "z" has a Unicode scalar value of U+007A.
// "ñ" has a Unicode scalar value of U+00F1.
XCTAssertFalse("ñ" < "o")
XCTAssertFalse("ñ" < "z")
// to properly sort,
let loc = Locale(identifier: "es")
XCTAssertEqual(ComparisonResult.orderedAscending, "ñ".compare("o", locale: loc))
XCTAssertEqual(ComparisonResult.orderedDescending, "ñ".compare("n", locale: loc))
}
Issues with binary search
The reason why that change in iOS prevented the game from functioning correctly was that I sorted the dictionary in advance using the correct collation for the given language. And then, I used binary search to find whether the word existed in the dictionary. If the sorting changes, binary search will fail.
Imagine that you have the words “mono, moño, mote, mozo, mula”. If you are looking for “moño” with binary search and you start in the middle with “mote”, if the sorting is wrong it will believe that “moño” should appear after “mote” (because the Unicode value of “ñ” is greater than “t”), and we will fail to find the word in the dictionary.
Diacritics and other symbols
The solution wasn’t that easy in Catalan. The character “ç” was treated before as appearing after “c” and before “d”. That is correct, but when appearing in a word, the Catalan collation rules say that the “ç” should be treated as a a diacritic, that is, the “ç” is equivalent to a “c” and only placed after it when there are 2 equivalent occurrences. That means that the order should be “caca, caça, cacatua, cada”, and not “caca, cacatua, caça, cada” as the older version of iOS had given me.
Another example of such diacritic symbol in Catalan is the interpunct between els (to make a longer “l” sound): “l·l”. An example of sorting words using the correct collation would be: “filla, fil·lastomàtid, fillastra“.
But the collation algorithm is not ignoring other symbols such as the hyphen or the apostrophe, that appear in some words of the dictionary, such as “pèl-blanc”, or “d'amagatons”. In order to ignore these, I remove them from the string before doing any comparisons.
Here’s some reference code in Swift to do the search with the correct sorting. The binary search function is from Stackoverflow.
extension String {
func noSymbols() -> String {
return self
// e.g. d'amagat
.replacingOccurrences(of: "'", with: "")
// e.g. pèl-blanc
.replacingOccurrences(of: "-", with: "")
}
}
class WordDictionary {
let words: [String]
let locale = Locale(identifier: "ca")
func getWordIndex(_ word: String) -> Int {
// the diacritic insensitive search treats ñ as n, and ç with c,
// but we want to avoid that, so use the locale and only remove symbols
let ref = words.noSymbols()
let i = words.binarySearch({
let w = $0.noSymbols()
let order = w.compare(ref, options: [.caseInsensitive], locale: locale)
return order == .orderedAscending
})
return i
}
}
Allow bad spelling
The compare function has also an option to ignore diacritics. When comparing words, I can’t use that option because there are certain things I don’t want to ignore. In particular, the “ç” is a letter that can be input in the game, so “caca” (poo) is different from “caça” (hunt). But the player can’t input accents or interpuncts, so in those cases I do want to ignore the differences.
So instead of relying on the compare function, I convert every string to a “potentially badly written” one where I removed everything but the “ç” (in the case of Catalan):
If I find a match in the dictionary, I return the entry from the dictionary so the player sees the correct spelling.
Conclusion
Nothing apparently simple is really that simple. The evil is always in the details. It turns out that alphabetical order is not universal, and system functions change with time. Be sure you write unit tests for the system functions that are required by the core functionality of your app. In this instance the unit tests I already had in place saved me a lot of time.
And strings will always be the last boss in computer science! 😂
Happy new year 2025 🐍 & a visual summary of my 2024
Overview of my 2024
2024 has been another year of rollercoasters. I started 2024 without a job, and I did lots of job interviews and studied to prepare for them. I released a macOS app, Mantis Shrimp, and I was busy for a while doing the Genuary challenge using my own app, writing shaders for practice.
I found a job in a video games company, Lucid Games, so I’m back in the games industry after a gap of 8 years working for fashion. As soon as I secured the job, I flew to Tokyo to take a short break, for 10 days. It was great to meet ex-colleagues and friends again.
I started work in April and I’ve been quite busy since, so I didn’t write all the blog posts I had planned, but no one was expecting me to, so I should relax 😅
I’ve done more exercise than usual, but it’s not lots. At the beginning of the year I started badminton and I was playing every week. However, I stopped in summer and I haven’t resumed it yet… I also started yoga 4 months ago, and I’m doing it every week so far 🧘🏻👍
I’ve also been checking my health. All of a sudden one GP wanted me to have all kinds of checks and I said yes to all, because it’s usually hard to get any attention from NHS and they usually ignore any mild conditions, even if chronic. So I’ve been a bit busy with GP appointments for the past 2 months, but for a good reason.
In November I heard the bad news that a friend I talked a lot to for the past 5 years had passed away. It wasn’t a surprise because I was aware of his deteriorated health, but it was still very sad. I wrote about him in here: Nuku-Nuku / Future Brain.
In the new year I’m going to start in a new project (at Lucid), so I start 2025 again with more changes.
Let’s see how 2025 goes. Let me list some of the stuff I did during 2024 first.
Work during 2024
I did tons of interviews before landing at Lucid Games. I've been learning Unreal Engine 5, touching some of the code, and doing cool things. The codebase is huge, so I'm quite overwhelmed at times, but people in the team have been very helpful and I think I've levelled up a bit.
I can't show anything of what we are doing, but I did some test with Unreal on my spare time using Unreal's Mass system and storing skinning weights on textures to do the animation on the GPU, and I recorded a funny clip of that. I used it in this music video: Programmer's affliction (vocaloid Japanese voice).
Indie development and GenAI music
I haven't made any games this year. I've only done some development of my shader app, Mantis Shrimp. I just released an update last month. I also released some maintenance updates to Sil·labetes, Silabitas, and Sopilabitas. Those 3 had a bug introduced in recent versions of iOS. It's quite an interesting bug, so I've written a blog post about it that I'll publish soon.
On a down note, I removed Syllabits from the Google Play store. It was too stressful to maintain. Google is like that last boss monster that you know you could beat with enough grinding, but you can't be bothered anymore, so you decide to stop playing the game. If you understand Spanish, I recorded about it here: "Rajando de Google Play y adiós a Syllabits".
I also managed to clear the Advent of Code challenge again, although I struggled with a couple of exercises... I only cleared Part 2 of Day 24 on the 31st of December (I was home alone with my cat, on New Year's eve) 😅 -- But apparently I finished second in our office ranking, so I may get a small trophy 🥳
And I created a font with my handwriting. You can find it in some places of EnDavid.com. See some of the writing on the figure on top of this article. You can download it from here: EnDavid Handwritten.otf.
What I've spent lots of time in was writing lyrics 😅 I got a bit addicted to Suno AI. I started by adding music to some old poems I had written, and it is quite satisfying to listen to something you've written with a nice melody. Then, I started writing new lyrics and generating more AI music. The AI can also generate the lyrics for you, but that's not quite fun for me. I like writing lyrics. I have a playlist in YouTube with some of the music I created: Focotaku Music. I've done some interesting experiments like mixing Catalan and Japanese in the same song, by creating the song at intervals. Most recently, Suno released a feature (still in beta) that lets you cover your own songs, or even clips you upload. So I started singing some of my lyrics and then covering them to make them sound good (because I sing horribly, but the notes are more or less recognisable). For instance, I wrote, sung, and covered with AI this Christmas Carol about Castlevania: Last Castlevania 🏰🧛♂️ (Christmas song). I know Generative AI can be scary and some people hate it, but I've had so much fun with this 😅 Please forgive me 🙏
Some of the drawings I did for #inktober, copying videogame covers with seals 🦭
I also did some drawings manually to disconnect from the computer. I managed to completely the Inktober challenge for the first time. I drew lots of videogame covers with seals 🦭😂. And I also completed Genuary, a generative art / programmer art challenge. All the code is here: endavid (github) / Genuary2024.
Blogging
I had some things in my backlog that I didn't write about in the end, and I didn't write lots, but I'm quite happy with what I wrote. Some of these articles took months, or even years to write, if you think of the extend of what they cover. I can list them all because it's not much:
8+ years programming for fashion. It didn't take me 8 years to write this, but it's a summary of those 8 years, so I think it's a nice glimpse of the tech we used and the tech that is coming (lots of GenAI) -- It also happens to be the 100th entry in EnDavid.com! 🥳
Concurrency recipes in Swift and C++. This was a fun exercise in concurrent programming, using one of the exercises of Advent of Code 2023. Quite technical.
Can Generative AI create original art?. After a few months without writing anything, I came back with this. It's a very lengthy philosophical discussion on what art is. It's a culmination of months of me playing with Generative AI, mixed with some ideas I explored in my PhD years ago. I defined something I called “fiascart”, but in retrospective, I think everything can be considered art. But I wanted to draw some line between good "human" art, and Gen AI, and art fiascos.
Vlogging
I've recorded lots of videos in 2024 for my “otaku” YouTube channel, mostly about videogames and Japanese culture. For a few months, I was releasing one video weekly! I don't have many visits, but I just make these to make a “brain dump” of things that I'm curious about and I want to share a bit more widely. For instance, I read the book “Nagomi” by prof. Ken Mogi, and I recorded a video about it (in Spanish), with slides and all: NAGOMI: Filosofía japonesa del equilibrio (at the moment, the video has 450 views, but most videos in that channel have just around 10 to 30 views 😅).
I also recorded a couple of technical rants, that I've uploaded to another channel. Like the rant I mentioned earlier about Google Play.
Leisure
I played many great games during this year. My backlog of games to play is getting too long, so I tried to clear it up a bit 😅 I played:
Final Fantasy XVI - I had already cleared it, but I had so much fun that I played it again. And I also cleared the DLCs
Final Fantasy VII Remake / Intergrade. This was so much fun and nostalgic! ❤️ And the orchestral remake of the soundtrack is amazing.
The Last Of Us: Left Behind. This was a DLC I bought more than 10 years ago and I hadn't played it yet 😅 It was good but my heart may not tolerate scary games anymore...
Silent Hill: The Short Message... Who said no horror games? 😅 I'm very fond of the Silent Hill series (one of the reasons why I wanted to work at Konami at the time!), so I had to play this. It's quite short, and it's free, so I had to try it. I enjoyed the story. I read bad reviews, but I liked it. And the soundtrack by Akira Yamaoka is very good.
Fantasian. I played this for 3 months or so, but I didn't clear it. It was fun at the beginning, but at some point it got too hard so I gave up. I don't think I've ever given up on a game after playing for that long (56 hours), but it wasn't fun anymore. I wouldn't recommend it, at least not the Apple Arcade version.
And I also read the 3 NieR novels, in Japanese. So good practice. See: Readings.
As I said in the introduction, I also managed to visit Tokyo again, and I did lots of catching up with friends. But I also travelled to Madrid and saw a couple of good friends I hadn't seen in a while. And we attended a beautiful wedding of friends in Barcelona. Friends are a treasure ❤️
Wishes for 2025
I'm currently playing Final Fantasy VII Rebirth and it's lots of fun. But I also have some coding (perhaps some new game) I want to start this year. I also have some blog posts, technical and not so technical, in my backlog. A couple of them are almost finished. I just need to add some pictures.
I'll continue doing yoga and hopefully I will resume playing badminton. And I have a short trip planned to Tokyo. And several trips to Barcelona to see family and friends.
Peace ☮️🕊 to the world 🙏 and Happy New Year 2025 🐍