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.
Godot Engine is a game engine that has made me rediscover the fun of making games. As a hobby, I’ve been creating games and apps for mobile for 10 years, and I only recently started using Godot.
Although I tried using game engines before, I always ended up programming things from “scratch” (using Apple’s Xcode and their Kits). Don’t get me wrong, game engines are good and a lot of hard work has been put in all of them. But they can be overwhelming for one-man projects.
In this article I’m going to compare some of the aspects I find important for my projects, and why I ended up using Godot. Every project is different, so I’m sure you will have your reasons for a different choice.
A bit of self-introduction
I’m not going to do a thorough comparison of current game engines. There are plenty of resources online talking about their renderers and their performance. Instead, I’m going to speak from my own experience, focused on some aspects that I will describe in the next section.
Professionally, I worked for 4 years implementing graphics techniques for Fox Engine, an in-house engine for Kojima Productions (when it was still part of Konami). The biggest amount of work I did was on deferred rendering and rendering of indirect lighting through spherical harmonics (check my Mac app Harmonikr), but I also implemented basic stuff like the sky simulator, 2D sprites, the VFX system, shadows, depth-of-field, motion blur, damage shaders, and all kinds of graphics stuff.
After that, I worked a couple of years using Unity in 2.5D games for mobile, in a different company. The scale was much smaller, so I worked in all aspects of the game, even the backend. But the greatest amount of work was on optimization (see my Unite Japan 2013 presentation).
Then, I moved to the UK and started working again making console games. I used Unreal Engine 4, and also an in-house game engine used in Disney’s Infinity. I worked mostly in optimizing graphics and fixing UE4 lighting bugs.
Currently, I’m out of the games industry, but I still do rendering work, mostly server side, at Metail. We had our own renderer, but more recently we use V-Ray cloud rendering.
In my free time, I’ve been making small games and apps. Mostly word puzzle games for iOS, using Apple’s SpriteKit. But I also started a pet graphics engine, Vid Engine, using Apple’s Metal graphics API. I started it mostly to learn about Metal, but I also released an app made with it, Palettist.
This year I started using Godot and I ported one of my word puzzle games to Android with it, Syllabits (on the cover image).
Reasons for not using a game engine
For me, the main reason to use a game engine is the editor and all the libraries you get with it (graphics, physics, sound, etc.). But if you are working on a simple 2D game, perhaps the overhead of adding an engine is too much for what you are getting from it.
These are some of the issues I see with game engines:
Time to get started. Do you have to register and get a license? Do you have to download lots of stuff? How long does it take to install? If it’s source-based, you may also need to compile the editor yourself.
Space. Does it fit in my laptop? Do I still have space for the build tools? In my case, I need Xcode to build for iOS, and that takes a lot of space already if you add simulators. Using Xcode directly is the most space-efficient solution.
Binary size. How much overhead does the engine add? If you are creating a 2D game, you can try creating a simple stage with a few sprites and compare its final binary size in iOS with respect to the same project created in SpriteKit with Xcode.
Optimization. Are there proper tools to measure performance? And how easy it is to optimize? When I worked with Unity, I used Unity tools to evaluate CPU and memory performance, but there were memory leaks on Android once compiled. The reason was a bug in Mono very difficult to find. I had to reverse-compile the C# bytecode to find out that static variables were created by the compiler inside lambda functions. And for graphics, instead of the editor, the best thing was to make the iOS build and use Xcode frame capture tool. There I could see there were unnecessary textures from the engine, for instance, although there was no easy way to get rid of those from the editor.
Deprecation problems and rejection issues. How easy is it to update to the latest release guidelines for the platform you are targeting? If you try to release some feature that has been deprecated, Apple may reject it. More often than not, you get hundreds of warnings in the iOS build which tend to be a symptom that the Engine is not up-to-date with the latest build tools. But it’s very difficult to address those by yourself. Whereas if I had written the game directly in Xcode, it would have been really easy to update.
Access to platform native features and latest API. If you want to try the new things Apple announces in the WWDC, your best option is to develop natively on the platform. For instance, in Palettist I was exploring the display-P3 color space, but many engines still do not have explicit support for it (not even Apple’s own SpriteKit).
The editor doesn’t really make things easier. The game editor may be useful to create a prototype of a game, but for the final game you may want to script things out, like the stage creation. Specially for puzzle games, it will save you many clicks if you create the stages from code. If my stages are created from a script, I may as well create them in Xcode with Swift.
Not source-control friendly. Are the project files readable? Can you diff the changes? Unity has a source-control friendly mode, but still the files are quite big and it’s difficult to make much sense out from them.
Scripting doesn’t really save time. If I have to choose between C++ and C#, I would rather choose C# for scripting, because there’s nothing as difficult to debug as C++ build errors from templated functions from an obscure engine code you haven’t created. However, often C# can’t be debugged properly because of poor editors for Mac that fail to connect with the editor. On the other hand, Swift is built natively in Xcode, so debugging and optimizing Swift code is much smoother.
For all those reasons, I ended up programming my apps and games natively in Xcode. It can also be summarized with just 2 words: less frustration.
Why I like Godot
Godot doesn’t address all the issues I mentioned above. For instance, you can’t create any iOS native UI with it. It comes with its own 2D UI elements that may not abide by Apple’s design rules or guidelines. There are modules to give you access to certain features like leaderboards, but you are usually better off working natively for those.
However, some other aspects are really a joy:
a very small download,
no install,
an easy-to-use editor with properties that can be easily added from script,
tiny readable project files that can be even edited by hand without breaking things,
a very smart scene object inheritance system,
And a python-like scripting language, GDScript, which is very well integrated with the editor, making debugging and prototyping fun at last.
I will compare some of these aspects with Unity, because it’s a big player for mobile games and because it’s pre-compiled like Godot, and with Open 3D Engine (O3DE, branched from Amazon Lumberyard). O3DE is source-based like Unreal Engine, but it’s also Open Source like Godot. O3DE is currently not available for macOS (a major blocker!), so I’ll be using one of Lumberyard’s latest builds with macOS support. Also, I will add some notes about Lens Studio, an engine to create AR lenses for Snapchat, which can also be used to create small games. Lens Studio is quite compact and therefore comparable to Godot in a couple of aspects.
Godot vs Unity vs O3DE vs Lens Studio
See the table above for a quick summary.
Let’s look first at the space that you need for installation, ignoring other development tools that you may need for building the actual game (Xcode or Android Studio). The worst offenders are source-based engines like O3DE/Lumberyard: more than 40GB! The minimal install was so big it wouldn’t fit in my laptop and I had to carry an external drive to use it.
The download time is of course bad when the space it requires is so big… Lumberyard has to download many dependencies which also creates many files on disk. Handling that many files is also slow. If we add the installation and build times, the total time before you can actually start using it can be several hours. The build may contain errors as well and you have to figure out how to resolve them.
The project files are mostly source-control friendly for most engines, except for Lens Studio, which uses big binary data files. The main project file is in plain JSON, but it is not very readable so if you modify it by hand it may break easily. Lumberyard uses JSON for the project files, and XML for the prefabs. Unity uses YAML for their config files, metadata, and prefabs. And Godot uses GDScript-looking plain text files for project files and scene files, which are very human-readable.
I found it very easy to resolve conflicts in Godot or to modify the scene files directly in a text editor. In fact, I edited all the properties for the stages of my game Syllabits directly in Visual Studio Code (there are plugins for it, with syntax highlighting and connection to the editor). I created a template stage in the Godot editor, and then created copies that inherit from it and typed in the changes in VS Code. For instance, in the game I have a panel with a set of syllables. The scene file for a given stage looks like the one below, where you can immediately see that I’ve picked a different set of syllables for this stage:
This segues nicely into the topic of object reusability and prefabs. Except for Godot, all engines use prefabs for reusability. The idea of a prefab is that you create a template asset, configured with everything you need, and then you create instances of that prefab in your game. It is a great concept, but if the prefab breaks after an update, I find it quite hard to figure out what happened. If the prefab points to some component that doesn’t exists anymore, it just appears as “unknown”, and you are left clueless. Also, after you fix the prefab, you will probably need to recreate all the instances, and that means remembering all the settings you had for all of them. In Unity you may need to install a previous version of the engine, open the prefab, and take screenshots of the interesting properties. In Lens Studio I also end up taking screenshots of all the property panels. It is really time-consuming. Having the prefabs in a human-readable form helps a bit, but unless you are comfortable by editing it by hand, you still have to go to the editor to do the dirty job.
In comparison, Godot scene files are based on inheritance. As with the Syllabits example, I can have a “template” scene file where all my stages inherit from, and then I just override whatever needs to be customized in that stage. It can be just 4 lines, as above. Those tiny files are very readable and I’m confident I won’t break anything if changing it by hand. Also, if the scene I’m inheriting from has gone missing, I can’t simply create a new one with the same name and I don’t need to recreate all the other scenes that inherit from it.
The way you create custom properties for your objects and make them appear in the editor is quite straightforward for all engines. But I find Godot to be really responsive and it covers all kinds of datatypes. Below there’s another example from Syllabits, with the list of properties as defined in GDScript, as they appear in the editor, and as they appear in the scene file. It’s so neat that it blows my mind 🤯
Publishing the game
Godot, as other engines, have separate modules that add support for different builds. O3DE doesn’t support iOS at the moment, but all of them support Android. Publishing with Lens Studio is the easiest of all. It creates lenses that run on top of Snapchat, so they can run on whatever Snapchat runs in, and you don’t have to deal with Apple or Google.
If you are publishing a native iOS or Android app, once you have a build, it is supposedly straightforward to upload it to Apple’s App Store Connect or to Google Play Console. However, as I mentioned earlier, some issues may arise that you are not in control of. For instance, in the automatic tests that Google runs for your game, I found that Syllabits was crashing in some devices due to the audio. I had no idea how audio was implemented in Godot and why it may be crashing. Also, there was no way for me to reproduce the bug in the editor, or in my Android device to get more clues. After a couple of months, I saw there was an updated that fixed that crash, but I felt a bit helpless in the meantime.
I had other troubles implementing features like Google Play Leaderboards. Someone has created a nice plugin for Godot, but it’s the usual thing in Open-Source where projects can get outdated quite quickly. Then, you have to search for forks that may have updated it, or fix the problem yourself. But to fix the problem yourself, you need to learn a bit about Android development, which was what I was trying to avoid by using Godot in the first place.
Getting started with Godot
I found many nice tutorials on YouTube and it’s really easy to get started. There are some template projects as well, so you can start with any of those.
In my case, because I already had several games programmed in Xcode, I thought a good way to learn would be to port one of those games. That way, you can focus on learning the engine, since you don’t have to think about designing the game itself. I got the main loop of Syllabits working within one week. Then, the whole game with all its stages took me just 1 month (only working a few nights and weekends). As with other engines or with Xcode, I spent most of the time with UI things, making sure they resize nicely in different locales, and things like that. (The actual release took several months due to my struggles with Google Play Leaderboards…)
The refactoring taught me a few things I could do in the Swift version of the game as well. I find having every scene as an object that you can instance anywhere and inherit from it makes everything not only very reusable, but easy to test. The testing becomes easier because you can test each scene separately, and make changes that will be immediately reflected everywhere.
Conclusion
You can get Godot running in less than a minute and it has an editor fun to use. But not only that, the way scenes are based around inheritance and the use of Python-looking GDScript makes the whole experience very programmer-friendly.
For the type of one-man projects I create is a perfect choice. However, there are still certain limitations, common to all engines, when accessing native features of a platform and debugging on the device. For that reason, I’ve only used Godot so far to port my games to Android. But I keep the iOS version separate, programmed directly in Swift using Xcode.
Godot’s future looks exciting, with the release of Godot 4 around the corner. I want to start prototyping new games directly on it, instead of simply porting the games I already created in the past. But I have a couple of more ports to go! Porting and refactoring is a good learning exercise that I recommend as well.
I often get asked why don't I release my indie iOS games for Android as well. The short answer I give recently is simply "I only have one life". I hope they can infer from my answer that releasing for another platform is not as easy as pressing a button. And between starting a new project, or keep working endlessly in something that you've already released (once), the former is obviously more fun.
I program my games natively for iOS using Swift, because I love Swift, but also because in my professional experience using Unity things weren't as smooth as one would expect. Unity is a game engine that makes multiplatform development easier. But even when we were targeting Android in my old job, many times I ended up exporting the project to iOS and use the Xcode debugging tools because those tools either worked better, or simply because they worked (GPU profiling for different mobile vendors is not fun, and sometimes the tools don't work at all).
But even if I had made my games in a game engine and I just needed to "press a button" to get the Android build, there are other things that are quite cumbersome: reading legal agreements, creating accounts, handling different ways of integrating things into the respective ecosystems, taking screenshots, creating video materials, debugging, and testing. Today I'm going to touch just one of the integration aspects: creating leaderboards for Google Play.
Godot changed my mind
I wasn't planning to port any of my games to Android because, as I said, it's not fun. The game is finished. I just want to move on. But I started playing with Godot and I found it quite fun to work with.
That motivated me to create something with Godot. But instead of starting with something new, I got one of my word puzzle games and started porting it to Godot. I'll write my impressions in a separate article, but the main conclusion is that it was fun! 😃
But the game is already released for iOS, so what could I do with the Godot version? Oh... that... 😅 Let's attempt to release it for Android.
Then the fun died... 😅
Leaderboards hell
The client code for Game Center leaderboards, the leaderboards from Apple, is a bit complicated, but I reuse the same code in all my games. So I usually don't spend too much time adding leaderboards. Perhaps a couple of hours, because you still have to prepare icons for each leaderboards and also translate it to all the languages you support (I usually do the 4 languages I speak).
Suppose that you've created a game and that you are new to Apple, and you are now preparing things to release it for iOS. The first thing you want to do is to add some leaderboards. Roughly, these will be the steps from scratch:
Create Apple ID. If you are making games on your own, you will use this Apple ID for everything.
Sign to Apple Developer with your Apple ID.
Add App entry for your game in App Store Connect.
Add Leaderboard in Game Center section of your game. You will need some icons and translations for this.
Implement Game Center and leaderboard access in game. You only need to add your Apple ID to Xcode and use the leaderboard ID from the code.
Let's now do the same for Google Play:
Create Google account (A)
Sign to Google Play with your Google account (A)
Add Game entry for your game in Google Play.
Sign to Google Cloud Platform with your Google account (A)
Go to Create OAuth consent screen (A)
You get prompted to register your company. By following the registration process, a new Google Cloud account (B) gets created.
Authorize your domain by adding a TXT entry to the DNS server of your website.
Go to Google Search console to check that your domain is verified.
Go to Create OAuth consent screen (B). Not sure what happened with (A).
Write Privacy Policy and Terms of service documents for your game.
Add "scopes" games, games_lite, and drive.appdata (no idea what I'm doing... just reading tons of guides online...)
Deal with error, The following scope(s) were not added because they are invalid. Please update or remove these scopes: games, games_lite, and drive.appdata.. Go to APIs, and enable Google Play Game Services. Now you should be able to find the 3 scopes.
Add test user.
Go back to Google Play Console (A), to the dashboard of your game. Click Refresh on Credentials in the Play Games Services configuration. Nothing happens. I suspect I need to link account (B) to (A).
Repeat steps 7 to 13 for account (A) -- and good luck deleting account (B)... (I haven't managed to yet...)
Now Google Play Console lets you continue. You now have to create Android and Game Center credentials (I'm starting to get lost, in case you didn't notice 🙈).
Create OAuth client for Android credential, and for web (not sure why I need the web one, but that's the one I need to use to initialize the services from the game... 🤷♂️)
Use the Keytool utility to get the SHA1 fingerprints you need for the credentials.
Create upload and keystore in Android Studio (consult "known issues" doc to resolve multiple errors).
Upload APK signed with the upload key -- only to find out that APK are no longer supported and you need an AAB, with target SDK 30. That means that the only Android device I own, a Samsung Galaxy S7 from 2017, is no longer supported... (on iOS I still release games compatible with iPhone 4s, from 2011...)
Once you have the right AAB file, Google creates and signs the app for you (how nice and easy! thanks! 😂), with the app signing key. Get the fingerprint for that app signing key from the Google Play Console and create a new OAuth client in the Cloud console. That will be used by the final release of the game.
After a couple of days, you have 1 leaderboard with half the functionality that you get in Apple's Game Center (although that could be partially the fault of the Godot plugin I'm using... A disadvantage of not programming natively).
Wrapping up
Making games in Godot may be fun, but releasing a game is never fun. It's not fun for Apple's App Store, but even less fun for Android. I haven't released it yet, although I've readied the final release build and all the marketing and legal materials. Several things are under review now. Whatever that means 🤷♂️
In App Store Connect everything needed for the release is in one place, but in Google Play Console you have to click many menus and checklist-type of navigation to do things. I expected Google Play to be as painful as Apple's, and that was a reason to avoid it. I didn't expect it to be even worse...
If you are interested in using the latest technology in a particular platform, you better use the native tools for that platform. That was my main motivation when I started with Xcode and Swift. But if you've created a game in a multiplatform engine like Godot, you may as well try to release it in different platforms. But be prepared for the pain. The leaderboards are just the beginning... 🙈
I hope this sheds some light as to why I haven't released anything for Android (yet).
On day 18 of Advent of Code 2021 there was an interesting problem about something the author called "snailfish numbers". I immediately thought that, since I was solving these problems in Swift, a recursive enum would be ideal to represent these numbers. But the fanciest data structure is not always the best solution... 😅 Let's see why.
Snailfish numbers
The problem basically describes a snailfish number as a binary tree, where leafs of the tree are integer numbers. These trees are written down as lists of 2 elements, where each element can either be a number, or another list of two elements. Examples given in AoC:
Enumerations in Swift can have associated values, and these enumerations can be defined recursively (you need to add the indirect keyword to the definition of your enum). I thought this would be a very neat declaration of a snailfish number (I call it SnailNumber, a bit shorter):
indirect enum SnailNumber {
case number(Int)
case pair(SnailNumber, SnailNumber)
}
It's pretty straightforward. This defines a binary tree. To understand how to use it, let's implement the CustomStringConvertible protocol to provide a textual description of a SnailNumber,
indirect enum SnailNumber: CustomStringConvertible {
var description: String {
get {
switch(self){
case .number(let number):
return "\(number)"
case .pair(let left, let right):
return "[\(left),\(right)]"
}
}
}
case number(Int)
case pair(SnailNumber, SnailNumber)
}
If you pay attention, that description is recursive, because "[\(left),\(right)]" invokes the description of left and the description of right, both SnailNumbers. It will stop when you find an integer number. Some example output:
let a = SnailNumber.pair(.number(1), .pair(.number(4), .number(5)))
print("\(a)")
// produces: [1,[4,5]]
The problem defines addition of snailfish numbers as simply the creation of a new pair, which it's very easy with these enums:
Parsing a string with a snailfish number is a bit trickier. I'll paste here my code for reference:
static func parse(_ s: String) -> SnailNumber {
var rest = s
var numbers: [SnailNumber] = []
while !rest.isEmpty {
if rest.first == "[" {
// new pair
let pair = SnailNumber.pair(.number(0), .number(0))
numbers.append(pair)
rest = rest.substring(from: 1)
} else if rest.first == "]" {
let right = numbers.removeLast()
let left = numbers.removeLast()
rest = rest.substring(from: 1)
numbers[numbers.count - 1] = .pair(left, right)
} else if rest.first == "," {
rest = rest.substring(from: 1)
} else {
let groups = getGroupsFromRegex(pattern: #"^(\d+)(.*)"#, in: rest)
if let pair = groups.first {
let n = SnailNumber.number(Int(pair[0])!)
numbers.append(n)
rest = pair[1]
} else {
print("parse error: \(rest)")
}
}
}
return numbers.first!
}
Exploding nightmares
Let's not celebrate yet. If you've read the AoC website, these numbers need to be reduced after every addition. The reduction process is an iterative process of explosions and splits. Read AoC2021 day 18 for details.
The splitting process keeps all the integer numbers smaller than 10, by splitting every number which it's 10 or greater into a new pair. For instance, 10 becomes [5,5], and 11 becomes [5,6]. Although this operation is easy with the enum I've defined, the problem says you need to apply one operation at a time, so I can't recursively split all the tree. It needs to be explored from left to right, and stop at the first split. I used Swift optionals (the ?) to check whether a split has happened, like this:
static func split(_ n: SnailNumber) -> SnailNumber? {
switch(n){
case .number(let a):
if a >= 10 {
let r = a % 2
return .pair(.number(a/2), .number((a+r)/2))
}
case .pair(let left, let right):
if let sLeft = split(left) {
// one split at a time at most
return .pair(sLeft, right)
}
if let sRight = split(right) {
return .pair(left, sRight)
}
}
return nil
}
The explosion operator is trickier. What it does is keeping the depth of the tree at 4 at maximum. If you find any node that is deeper, you take its left number and add it to the first regular number to its left, and its right number is added to the first regular number to its right. Again, you do such operations one at a time. When you look at snailfish numbers as strings, that sounds trivial. But with this recursive structure, things become complicated. Also, note that I can't modify a number in place, since enums are passed by value. I tried something similar to what I did for the split operator, but it ended being a nightmare. Here's the whole function for reference. Feel free to skip to the next paragraph.
static func explode(_ n: SnailNumber, depth: Int) -> (n: SnailNumber?, left: Int?, right: Int?) {
switch(n){
case .number(_):
return (nil, nil, nil)
case .pair(let left, let right):
switch(left, right) {
case (let .number(a), let .number(b)):
if depth >= 4 {
return (.number(0), a, b)
}
return (nil, nil, nil)
case (.pair(_, _), let .number(c)):
let e = explode(left, depth: depth + 1)
if let en = e.n {
if let r = e.right {
let newPair = SnailNumber.pair(en, .number(c + r))
return (newPair, e.left, nil)
} else {
return (SnailNumber.pair(en, .number(c)), e.left, e.right)
}
}
return (nil, nil, nil)
case (let .number(a), .pair(_, _)):
let e = explode(right, depth: depth + 1)
if let en = e.n {
if let l = e.left {
let newPair = SnailNumber.pair(.number(a + l), en)
return (newPair, nil, e.right)
} else {
return (SnailNumber.pair(.number(a), en), e.left, e.right)
}
}
return (nil, nil, nil)
case (.pair(_, _), .pair(_, _)):
let eLeft = explode(left, depth: depth + 1)
var leftPair: SnailNumber?
var rightPair: SnailNumber?
var propagateLeft: Int?
var propagateRight: Int?
if let en = eLeft.n {
leftPair = en
rightPair = right
if let er = eLeft.right {
rightPair = SnailNumber.addLeft(right, value: er)
}
propagateLeft = eLeft.left
}
let eRight = explode(rightPair ?? right, depth: depth + 1)
if let en = eRight.n {
rightPair = en
if leftPair == nil {
leftPair = left
}
if let el = eRight.left {
leftPair = SnailNumber.addLeft(leftPair!, value: el)
}
propagateRight = eRight.right
}
if let el = leftPair, let er = rightPair {
return (.pair(el, er), propagateLeft, propagateRight)
}
return (nil, nil, nil)
}
}
}
This recursive function returns optional values that I use to know whether I need to propagate a number to the left or to the right. Because the tree is explored depth-first and from left to right, the numbers can be propagated to the right correctly, but they are only propagated correctly to the left if the number it's in an immediate parent of the exploded leaf. The algorithm worked for the examples given in the AoC website, but it didn't work for the general case.
While debugging this I realized this data structure looked cool on paper, but it had complicated things unnecessarily. 😣
Turing machines and flat snails
Compared to that binary tree, finding the left number in string format sounds more straightforward: simply move the cursor to the left until you find a number. If you've studied Turing machines at uni, that would probably sound familiar. So what if I stored the snailfish number as a string?
I decided that I would do some minimal parsing instead, and store it as a list of strings. Each string can either be a number, or a square bracket or a comma. The string description of such a number is just a join of all the elements in the list. Here's my flat snailfish number:
struct FlatSnail: CustomStringConvertible, Equatable {
static let zero = FlatSnail("[0,0]")
let number: [String]
var description: String {
get {
return number.joined()
}
}
init(number: [String]) {
self.number = number
}
init(_ s: String) {
var data: [String] = []
var rest = s
while !rest.isEmpty {
if rest.first == "[" || rest.first == "]" || rest.first == "," {
data.append(String(rest.first!))
rest = rest.substring(from: 1)
} else {
// getGroupsFromRegex returns all the regex matches in [[String]]
let groups = getGroupsFromRegex(pattern: #"^(\d+)(.*)"#, in: rest)
if let pair = groups.first {
data.append(pair[0])
rest = pair[1]
} else {
print("parse error: \(rest)")
}
}
}
number = data
}
}
Addition with this structure is not as cool as with enums, but still quite simple:
func + (lhs: FlatSnail, rhs: FlatSnail) -> FlatSnail {
let number = ["["] + lhs.number + [","] + rhs.number + ["]"]
let snail = FlatSnail(number: number)
return snail.reduce()
}
Notice that I'm already calling the reduce function because the problem specifies that numbers always need to be reduced after addition. The reduction process consists of iterating through explosions and splits until the number doesn't change. I wrote it like this:
func reduce() -> FlatSnail {
var out = self
var hasChanged = true
while hasChanged {
if let next = out.explode() {
out = next
continue
}
if let next = out.split() {
out = next
continue
}
hasChanged = false
}
return out
}
And the explosion operator, although still a bit long, is now easier to implement because it's just moving the "cursor" from left to right. Every time you encounter an open square bracket, you are going down one level. A closing bracket means you go back one level. Here's the whole function:
func explode() -> FlatSnail? {
var out: [String] = []
var depth = 0
for i in 0..<number.count {
switch(number[i]) {
case "[":
depth += 1
if depth < 5 {
out.append("[")
}
case "]":
depth -= 1
out.append("]")
case ",":
out.append(",")
default:
if depth < 5 {
out.append(number[i])
} else {
// assume number is correct, i.e. it has been exploded
// before further nesting can happen
// i: number, i+1: comma, i+2: number, i+3: ]
let left = Int(number[i])!
let right = Int(number[i+2])!
for j in (0..<out.count).reversed() {
if let n = Int(out[j]) {
out[j] = String(n + left)
break
}
}
var rest = number[(i+4)...].map { $0 }
for j in 0..<rest.count {
if let n = Int(rest[j]) {
rest[j] = String(n + right)
break
}
}
out = out + ["0"] + rest
return FlatSnail(number: out)
}
}
}
return nil
}
That function returns nil if the number didn't change. The exploded pair is replaced by a 0, and the numbers of the pair are added to the closest number to the left (if any), and to the right (if any), respectively. This can be done by using indices (or "moving the cursor").
For completeness, here's the split function as well, which also returns an optional (nil if the number does not change):
func split() -> FlatSnail? {
var out: [String] = []
for i in 0..<number.count {
switch(number[i]) {
case "[":
out.append("[")
case "]":
out.append("]")
case ",":
out.append(",")
default:
let n = Int(number[i])!
if n >= 10 {
let r = n % 2
let left = String(n/2)
let right = String((n+r)/2)
let rest = number[(i+1)...].map { $0 }
out = out + ["[", left, ",", right, "]"] + rest
return FlatSnail(number: out)
}
out.append(number[i])
}
}
return nil
}
And with this, it's now quite simple to complete the last part of the AoC exercise, which it's to compute the magnitude of these snailfish numbers. But I'll leave that to you as an exercise.
Conclusion
Swift enumerations are very powerful and can be used to represent even recursive structures like binary trees in a very compact manner. However, you won't have references to the elements of the tree, so you will need to recreate the tree every time you operate on it.
For the given problem, it turns out that it was easier to search for numbers linearly. We could flatten the tree (i.e. print the description), alter it, and parse it as a tree again. But we can simply work with a flattened tree all the time. Think on the operations you need to do on the data, and don't just simply buy the fanciest data structure in your language of choice. 🙈
P.S. Because of my bad choice, I couldn't solve the problem on the day, the 18th of December. I came back to it on Christmas day, and I redid it from scratch in an hour or so. In the Leaderboards, the fastest person has solved it in 17 minutes. 😳 I think I need at least 15 minutes to read the problem properly. 😅
Tweet
Already in 2022, but I finally found some time to think about 2021. I only did half of the goals that I set for 2021, but it's not bad.
This year I didn't do as much indie development as in previous years, but I wrote lots of "geeky" blog posts instead. Check the blogging section below.
I haven't worked in any major new game or app this year. I needed a bit of a break. However, I did release a few updates. Release summary:
In February, I released The Therapy RPG, a game to celebrate my sister's birthday.
Coloroscope was updated in April, with dark mode support, and new readings.
In May, they removed Snake on a Sphere from the App Store because of lack of updates, so I had to make an emergency update. I also added a new Hard Mode to the game.
Complex Feelings was updated in June, with a new NVC flow, to help you track observations and requests.
Also in June, I updated Sil·labetes and Silabitas, just with a bug fix for newer phones.
Sopilabitas was updated in July, to help my mum recover her hi-score, because she deleted the game by accident.
In August I made the first public release of Harmonikr, a macos App to compute Spherical Harmonics and HDR cubemaps. I made this app in 2015, and it was available as a direct download, but because it was useful for something for work, I tidied up and released it in the App Store.
Blogging
I've been a bit lazy with technical articles at work. However, I did write a couple of technical posts in my spare time:
and a small introspective article about anxiety: FOHWT: Fear Of Having Wasted Time.
All of that has been syndicated to Medium @endavid.
This year I've been quite active in my "otaku" blog, a blog in Spanish where I talk mainly about anime and games. I wrote a total of 19 posts. I'll highlight a few articles worth mentioning:
El declive de Clubhouse. I got into Clubhouse in Spring, and I translated a quite interesting article from Japanese Yahoo News. I'm mostly happy about how the translation turned out. My own impressions are in another post I wrote in July, Lecciones de Clubhouse.
"Mujer", la culebronización turca de un J-drama 🇯🇵🐍🇹🇷. I found out that a very popular Turkish drama that became a hit last year in Spain, was originally a J-drama. I watched the Japanese series and compared it with the Turkish.
Violet Evergarden y las chicas robot. Violet Evergarden is a short anime series that's in Netflix. I made an analysis of the personality of the main character and compared it to some similar characters in other anime (Spock-like girls, perhaps in the autistic spectrum).
Violet Evergarden, Hideo Kojima, "mono no aware" y la crisis de la mediana edad. I'm quite happy with this article. I extensively searched Kojima's twitter timeline, selected interesting tweets, pasted in slides, put them in order, and made a story out of it. I discovered the Japanese concept of "mono no aware", which it's a kind of nostalgia you experience by observing how time passes. Finally I talked about his mid-life crisis.
I recorded a few development stories about Syllabits, Sil·labetes, Silabitas, and Sopilabitas. You can find the videos here: David Gavilan Ruiz videos (2 in Spanish, 1 in Catalan, 1 in Japanese, and 1 in English).
Back to "otaku" stuff, I posted also several videos and shorts (a couple about Final Fantasy XV, game I cleared last year, and one where I compare different keyboards and how they perform when typing multiple languages).
Power up
Between work and my spare time indie development, I keep using these 4 programming languages: Python, Swift, C++, and Javascript. I didn't use much Go this year.
I read 3 of the latest Michio Kaku's books this year, about physics and the mind. I also read a Final Fantasy XV novel in Japanese, to practice reading Japanese (see Readings). I've also read several manga in Japanese, like the whole collection of 鬼滅の刃 / Kimetsu no Yaiba / Demon Slayer, and one of the seasons of Uramiya Honpo. But the novel was definitely more challenging than the comic books.
In Clubhouse I mostly join rooms in Japanese, but I also joined other rooms in Portuguese, Italian, and French. And I got a bit better at understanding conversations in those romance languages. Unfortunately, I still can't speak any of those 3... 😅 But I bought a book of French grammar and another of Italian grammar, and I've been studying a little of both.
Power down
I tried picking up piano again, but I gave up quite quickly... 😅
Leisure
I managed to take a 3-week holiday and go to Spain. I was in a small town in the sea-side with my mum for 2 weeks. With no Internet, and going to swim at least 1 Km every morning. So very relaxed and healthy. It's still a bit difficult to travel with the pandemic, but it's a matter of avoiding peak seasons. Similarly, I went to Barcelona in early December, to avoid the Christmas rush. And we even managed to do one short trip to Porto, and another to Madrid, both off-season (October and November).
Unfortunately, I haven't managed to go to Japan this year either... Perhaps in 2022... Depending on what happens with the pandemic and provided that the Japanese government doesn't make things difficult to enter, of course.
After clearing Final Fantasy XV, I played a bit of "La Mulana" on PS Vita, but I got frustrated by the difficulty, and I stopped playing games completely for a while. Only last month I started playing "Bioshock". It's an old game I hadn't played yet. I had no idea of what it was about, so I was quite surprised by its noir themes. It's good. I cleared it last week.
I got subscribed to Apple Music and I found lots of interesting channels. I'm currently hooked to an Attack on Titan playlist, but I can't pick a favorite song from 2021 right now... Let's say One Last Kiss, by Utada Hikaru, from the last movie of Evangelion. Because 2021 is also the year when Evangelion finally ended (I also wrote something about it, Sayonara a todos los EVAs).
Anyway, happy new year to everyone! The year of the tiger 🐯 Here's my new year greetings (年賀状): endavidg (happy 2022).
Let's hope we can go back to normality in 2022 ❤️
Tweet