Feb 17

Introducing Yokosuka 2k

y2kPlease go check out this JavaScript-based beat ’em up game engine demo I made.¬†Yes, I used artwork from River City Ransom. ūüėõ Once I’ve made my own sprites, I’ll replace them.

I’ll be posting some articles here about the design and history of this. All I’ll say for now is that this current version (written in JavaScript) was written¬†over the course of a three or four¬†evenings.

The current demo is pretty basic, but its goal was to stand up the game engine’s framework. The next step will be to add these features:

  • Add “regions” where you must clear out all the enemies before the game allows you to move forward onto the next region.
  • Add a “level complete” screen when you’ve finished the last region.
  • Add more enemies on the screen at once, and coordinate the enemies’ movement so that only one attacks at a time.
  • Add health bars for your character and for the enemies.
  • Add combos.

More details to come!

Feb 17

Re-capping a Commodore 1702 monitor

I’ve got a Commodore 1702 monitor and it’s been flickering a lot lately. It’s high time for a re-cap. I’ve got a cap kit and am ready to go.

I opened it up and this is what it looks like on the inside. Note: it has a lot of capacitors, so I’ll have to do this over several sessions as soldering is a laborious process. ūüôĀ


If you’re doing a re-cap of a 1702, here’s a good diagram of all the capacitors. I bought my cap kit from Console5.

Some quick notes about opening up the Commodore 1702:

  • There a bunch of screws in the back. After unscrewing all of them (including the ones near the rear inputs), store them all together somewhere. They’re all the same.
  • There are three screws in the front. Store these together. They’re different from the back ones.
  • Pull apart the front and back plastic frame carefully.
  • Next, discharge the cathode ray tube for safety. Please look up how to do this online. It’s very important as you don’t want to zap yourself. The CRT can store thousands of volts even when unplugged!
  • After discharging the CRT, slowly pull the CRT neck PCB off the neck tube. You’ll need to slowly wiggle it backwards to get it off.
  • Remove the flyback transformer “suction cup” from the top of the CRT.
  • Unplug the ground¬†wire¬†from the neck PCB.
  • Unplug the speaker wires.
  • Unplug the three-wire input (sorry, don’t know the term for this) coming from the CRT into the main¬†chassis PCB.
  • Unplug the two-wire input input (don’t know this term either) from the CRT going into the main chassis PCB.

After doing the above, you can separate the chassis PCB from the CRT itself. This is what you see in the picture above.

For me, the next step is to de-solder, remove, and replace each capacitor from all the boards. I’ll do this one by one so I don’t get lost. (Removing all of them at once would¬†lead to confusion when replacing them. I’d have to constantly look at the diagram!) I’m not sure yet if it’s better to unscrew the chassis PCB from the frame or if I can just work on it directly. We’ll see.

More updates coming soon!

Jan 17

How Lil Todo Syncs Tasks Across Multiple Devices Just Using Dropbox

Lil Todo is a to-do app that I wrote for the Mac. (An iOS version of the app is coming soon!) One of the great things about it is you can run it on multiple Macs and use a single Dropbox account to save and sync all of your tasks. Mark a task completed on one device and the change shows up on all the other devices.

Since the app just uses Dropbox and doesn’t use its own sync service, you don’t need to create a new account to sync (assuming you already have a Dropbox account). This is great for me too because it means I don’t have to write and maintain a sync service. ūüôā

So how does that work? From the user perspective, it sounds so simple, but behind the scenes it’s not as straightforward¬†as you’d think.

Dropbox == Simple File Store

Dropbox is just a file store¬†and doesn’t really handle multiple edits to the same file very well. If¬†two people edit the same file and both attempt to save it, Dropbox will detect a conflict and save two copies of the file, letting you pick which of the two to keep. If¬†we used a single file to¬†represent our database of tasks in Dropbox and had multiple Macs editing¬†and saving that file, we’d run into conflicts¬†all the time.

If that’s the case,¬†how¬†can we use Dropbox to represent our database of tasks across multiple devices?

The Solution: One Weird Trick

The answer is simple but not necessarily intuitive: have each device running Lil Todo¬†write to its own file in Dropbox. If each instance of the app has its own file to write to, we’d never have any conflicts since each file has a single owner. (Multiple devices are okay to read that same file, of course.)

That’s great, but how do you represent a¬†database of tasks¬†using multiple files, one for each device?

Here’s the novel bit: instead of writing to a database of tasks, have each device write the changes it would like to make to the database. In other words, each file for each device is a transaction log of all edits made from that device. (This is also known as Event Sourcing, although when I came up with my initial solution, I had no idea it was already a thing. I promise!)

Transaction Logs, Baby

When you launch Lil Todo for the first time on a device, the app is assigned a universally unique identifier (UUID). This identifier is guaranteed to be unique across all devices and is used to name the transaction log for that particular device.

When you create your first task, a “Create task” entry is added to the log with details of the new task. When you mark a task done, an “Edit task” entry is added to the log with the ‘done’ property set to true. In fact, all edits to a task (changing the title, due date, or notes) causes a new “Edit task” entry to be added to the log with a list of the properties that have changed. (Even deleting a task simply adds an “Edit task” entry with the¬†‘deleted’ property to true. Tasks aren’t actually deleted in the file.)

As you can see, we don’t actually write to a traditional database of tasks. Instead, we write a history of edits to all tasks.

This transaction log file grows¬†over time and represents all task creations and edits made from that device. When you sync to Dropbox, we upload the latest version of your device’s transaction log to the Lil Todo folder in Dropbox. We also download all the transaction logs from all other devices.¬†We then take all the transaction logs¬†and merge them in memory to form one large, universal transaction log¬†(which I call Voltron).

Every¬†entry¬†in the transaction logs has¬†a timestamp¬†to ensure we can sort all of the logs across all devices in¬†the order in which they occurred. It’s simply a matter of¬†“playing”¬†back all entries in¬†the universal log and reconstructing the entire database in memory. Once we have¬†the full database reconstructed, we’re ready to show it in the UI and let the user make edits (which, remember, simply cause new entries to be added to the log for that particular device).

Dealing with Conflicts

Note that¬†if you try to edit the same task on two different devices at about the same time, the most recent edit will “win”. In the unlikely¬†case that the edits actually happen at¬†exactly the same time, the device with the UUID that appears¬†later sorted alphabetically will “win”. The system is deterministic. It has to be to ensure consistency across all devices. Once all devices sync with the most recent version of all logs, their database contents will all match. (This is a form of¬†eventual consistency!)

It’s worth noting that the timestamps across all the devices aren’t¬†necessarily perfectly in sync. Each device uses UTC¬†for its timestamps, but¬†it’s possible they could be off by a¬†second or two. For the goals of this app, this is acceptable as the target scenario for this app is a single user jumping from device to device to make edits. I don’t expect the target audience to be editing the same task on two different devices within a second or two of each other. Most users will make a bunch of edits on a device and later move to another device later (at least not seconds) to make more edits.

This app works great for a single user, but if this app required multiple users¬†across multiple devices, it would not work as well. We’d need to have much more active syncing of the files to ensure each user has the most up to date version. At the moment, the app syncs to Dropbox every few minutes as¬†needed.

Performance Concerns

Replaying the transaction log does take longer and longer as it grows. At the moment, a cached version of the database is written to disk and loaded on launch to avoid the perf hit of regenerating the it, but the database does have to be regenerated if the app detects that there are new entries added to any of the transaction logs from other devices during sync.

It should be possible to alleviate some of these performance issues by occasionally creating snapshots of the database given the current state of the logs. When new transaction logs are downloaded, we’d simply re-play the universal transaction log¬†from the most recent database snapshot that did not contain the first new log entry. (This snapshot¬†feature has not yet been implemented in the interest of shipping the product fast! Once the author starts to feel the pain of long load-times, he promises to add this feature. 😁)

Closing Thoughts

Overall, I’m quite happy with¬†the implementation. It means I don’t have to create a new sync service. Creating a new service to host other people’s private tasks is a big deal. I’d have to worry about security, maintenance, and long-term plans (I’d have to keep the service up if people relied on it).

This sync system has been reliable thus far and there are no worries about race conditions or locking issues since each device is solely responsible for its own transaction log. The system can also scale up indefinitely (at the cost of perf) since each device gets a UUID and it’s simply a matter for all devices to “notice” the new transaction log in the shared app folder on Dropbox. There is no central registry of devices, just transaction logs representing each device. No locking of resources is ever needed.

In the future, I’d like to refactor some of the code I’ve written for this so it can be generalized to any file store (cloud or otherwise) and any type of database.¬†If I do that, I can post it on github so others can use it.

If you’re considering writing an app and using a third party file store like Dropbox to sync across devices for a single user, I recommend giving this technique a try.

If you got this far, thanks for reading! I’d love to hear your¬†thoughts below.

One last plug: give Lil Todo a try while you’re here! Please¬†do¬†check out my other projects as well.

Jan 17

Introducing This Way or That

During the Thanksgiving weekend of 2016, I released a new interactive fiction website: This Way or That.

If you follow me on Twitter, you know that I’ve already released two interactive fiction websites:

These two sites were very similar in that they allow you to write interactive fiction using a simple syntax language made up of braces. You could create branches in your story easily and even go so far as to make use of variables and conditional statements to change what the reader would see based on various decisions they had made in the story.

The downside to the above sites was that they require a singular author to write the story. Most people aren’t authors, and a fun site to create interactive fiction doesn’t work if there are no contributors.

Interestingly, whenever I described the above sites to people, they would assume that my site allowed you to write interactive fiction with other people, in a sort of exquisite corpse way. That is, you’d write one paragraph, then someone else would come along and write the second, then a third person would write the third, and so on. That’s not what those sites allowed you to do at all, but it got me thinking…

Well, after a little bit of work of forking the Diorama Club II site, I came up with This Way or That. It allows you to write a part of a story and let someone else write the next part. (Mind you, Diorama Club was the first site I’d ever written in Node.js, so it took extra effort to write. Plus, I was new to Javascript programming! I had much more experience in C, C++, and Objective-C.)

This Way or That makes use of some simple rules to help move the story along. For instance, as the author of a page, you can write the title (or titles, if you want to allow the user to make decisions, like a Choose Your Own Adventure book) for the next part of the story. Now when someone else comes along, they can click on the title of the next section and based on the title you wrote, they can be inspired and write that part of the story.

You can also keep your part of the story open so that other people can come along and write any arbitrary sections with arbitrary titles.

So far, I’ve had a challenge of finding users for this site. Most of the people who view the site, I believe, probably get how the site works, but they aren’t inspired to write anything. Let’s be honest, most people aren’t very creative, and are probably a little bit hesitant to start becoming creative on a strange website.

I have some ideas to help make the site more fun and engaging and spur people’s creativity, but I’ll wait a bit to see if more people bite. One of the ideas is to have random words pop up for each part of the story that you are forced to incorporate. This¬†would hopefully have the effect of giving people a challenge and ideally cause them to start writing. Another idea is to simplify things a bit of have a Madlibs style form that you’d fill in with details that would complete the story.

In any case, as with¬†most projects, it takes time and effort to try different things, so for now I’m going to leave the site in its current state and try to promote it some more before going wacky with my ideas.

If you think the idea for the site is a good one, please do promote it!

In closing, go and check out This Way or That.

Jan 17

Little Phrase Book v1 for Mac released

About a week ago, I released Little Phrase Book for macOS to the Mac App Store.

If you’re learning a new language and want a way to keep track of all the foreign phrases you’re learning, try out Little Phrase Book. You can easily search existing phrases by typing in the search field and seeing results displayed right away. It also has a built-in flash card feature so that you can review all of your phrases and commit them to memory over time.

Download Little Phrase Book today from the Mac App Store.

I’m quite proud of this release because unlike a lot of projects I’ve worked on in the past, I released this one as soon as it was good enough. Essentially, I had in mind a sort of minimum viable product in mind, and when it got to that stage, I cleaned up the UI, fixed as many bugs that prevented the golden scenarios from working, and then sent it off to Apple to review.

In all, it took less than a month to go from the first commit to it appearing in the store. With that in mind, I also didn’t work on it nearly that much. In total number of hours, I’d guess I spent maybe 20 hours working on this app before shipping.

I have more plans ahead for it, but it was important to get the basic features working so that I could dogfood it daily to see what worked and what didn’t. I added a very early flash card implementation. It’s quite na√Įve at the moment, but it’ll be full-featured in the future. I plan on implementing a spaced-repetition algorithm so that you can truly use this app daily to memorize things.

Another thing I’ve noticed is that the app is actually quite useful for just storing general notes that you need to recall occasionally. For instance, there are some command-line tools at work that I need to use, but their syntax is a little cryptic and hard to remember. I just created a phrase book for commands and whenever I need to “recall” the cryptic command, I just type in the search term and it immediately appears. This is much faster than putting things in a notebook or text file and having to do a cmd-F to find the entry.