Mastodon Verification Link Blog – Sam Seltzer-Johnston

Jun 23, 2017

What if Git Issue Trackers used Git?

On my drive home today I was thinking about how there’s some dissonance between DVCS’s and the issue-tracking systems that accompany them more and more these days. GitHub has one. GitLab has one. Atlassian sorta has two if you count JIRA and Bitbucket separately. And… *drum roll* they’re non-transferrable. What the heck? The repositories are! Why not the issues? Are they not important as well? There’s a lot of project history and meta-documentation to be found in issue-tracking systems.

A Git repository can live anywhere. You can clone it and push it anywhere. It seems strange that issues don’t come along for the journey. I routinely utilize multiple remotes, and where they’re hosted is arbitrary. I have some respositories that exist on Bitbucket, GitHub, and a private GitLab instance. Why? Not important. What’s important here is that each of these have incompatible issue tracking systems.

One emergent pattern I’ve seen is the formation of Wiki repositories: a collection of markdown documents that are loosely coupled to a main repository. They have their own repository history and exist as a sibling to the main repository. Why not do something similar with issue tracking? They have history. They could be coupled to one repository or used across many. Each issue is essentially a document.

There’s a project out there called gaskit that aims to do this, though it seems to still be in its infancy. There’s another one called cil that looks like it’s meant to be used for managing the issues as documents via command line. Neither quite seem to be what I’m imagining, but at least I’m not alone in being frustrated with non-transferrable issue tracking.

Jun 20, 2017

In the Radio Silence

Things have been quiet on here for a while1. I have some reasons for that, but those are less important. This is a brief update on what I’ve been up to.

  1. Apartment hunting and moving ate up a great deal of my time and energy.
  2. Been working on my first contribution to the Orx Project. Super excited to get it wrapped up. I’ll write more about it when it’s done.
  3. Made some major strides in my Japanese immersion learning. This is including but not limited to getting AJATT IMX on a wicked sale2, covering my first Japanese song (which somehow catapulted me to starting a sentences deck for realsies this time), and I even found some Japanese people who were willing/enthusiastic to help me translate interesting content.
  4. On a similar note, discovered my own life-hack3 that has completely altered the trajectory of my immersion and general productivity, for better or for worse.
  5. Briefly explored the fascinating world of online dating services only to find I enjoyed breaking them down mechanically as if they were games4 more than actually using them. We’ll just say it was… “research” - right, research…
  6. Lastly, I’ve been a bit torn about what to write here. I have lots of interesting ideas swirling around in my head, but this site is the most public interface I have between my identity and the rest of the world. It’s probably in my best interest to blog about games and programming and staying away from anything that a potential employer might find off-topic or objectionable, which frankly could be anything. However, I have other interests close to my heart that I’d like to share. I’ve considered running multiple blogs to keep things segregated, or customizing my Jekyll theme to break this blog up into a few primary threads, but that also sounds like a lot of work deciding how to split things up. There are also some worthwhile ideas that are better shared under a veil of anonymity. I just do not know where to put this content.
  1. I originally authored this very post in late April, and here I am finishing it in June… 

  2. Which quickly paid for itself, in a manner or speaking. It spams my inbox with random Japanese stuff every 90 minutes. All day. Every day. Though it does show some diminishing returns. For now it has earned its keep. 

  3. I’ve had several people tell me that it needs to be packaged into one easy-to-use program and sold because it’s so wild that people don’t even know they want it yet. Perhaps I’ll make a post about that later. 

  4. Spoiler Alert: They are designed like games. Freemium games. ~Total shocker, I know~ 

May 19, 2017

SRS Revelations

I’ve started my Japanese Sentence deck. Again. Again. Again.

That is to say, I tried SRSing vocab, sentences, and even MCDs. Couldn’t ever bring myself to enjoy any of them… Until now. This time it’s here to stay. I’m gravitating more to my sentences now than to my RTK deck.

In short, I accidentally figured out

  1. that I always quit sentences because it wasn’t fun to add or rep them, and
  2. a process that made adding/repping sentence cards easy and fun.

When I figured out a process that worked for me, I found I had some tension points where it became mildly tedious/error-prone. Thank goodness I’m a programmer! I decided to make an Anki addon.1 And lo did he open-uppeth Spacemacs and starteth typing.

I’d never made an Anki mod before. I’d never used Python before. What could go wrong? Well, everything, if I were a less bright individual. Good thing I’m smart or I might’ve programmatically deleted all my decks. Man, wouldn’t that’ve been a riot?2

Anyway, here’s how this works:

  1. I use Clozes in a way similar to MCDs, but with one but major difference: Clozes don’t hide text - they highlight it. To accomplish this, I construct the Cloze c1::ClozeText::HintText such that ClozeText == HintText. This is quite tedious, so I made an addon that automates this for me.

    This gets rid of that intimidating [...] text on the front of my cards, and replaces it with the actual clozed content I’m testing myself on.

    There are 2 reasons for this.

    1. It’s way less intimidating, which keeps me motivated to do reps. I’m using these to learn readings and grammar the easy way, not train myself on linguistic fill-in-the-blanks.
    2. Anki represents clozes as <span class="cloze">CLOZED TEXT</span> when it generates the front side of the card. This allows me to use CSS to highlight the cloze text and remove the brackets. I can also programmatically extract the text I’m testing myself on using javascript and do fancy things like add dictionary links for it on the back of the card. The result is quite appealing.
  2. I made a new Cloze card type with 4 fields: Sentence, Context, Meaning, Extra. I essentially split the Text/Extra fields of the normal Cloze card into two extra fields. This is a preference on my part so that I can have more fine-grained control over my card templates. Sentence and Context go on the front, Meaning and Extra go on the back. Sentence is the textual content I’m trying to learn. Context is often some form of media for the Sentence to make the card more compelling. The Meaning is a general overall meaning of the Sentence. The Extra is reserved for readings, definitions, and misc notes. I fill that section out as I go.
  3. Before I add add a card, I always capture context. I import some form of media to the Context field; no more, no less. No need to fill out the Sentence text or Meaning/Extra just yet. GifCam + VClip + OfficeCam = media ripping excellence. Whenever I see a scene in an anime I’m watching that really grabs me, I use GifCam or VClip to capture it. Whenever there’s a page in the manga I’m attempting to read that piques my curiosity, I scan it with OfficeCam or take a screenshot in cases where it’s a digital comic that can’t be downloaded. Sometimes context is just a short description or a link to the content, such as Twitter posts. In the case of songs, I usually just put the title of the song as Context.
  4. If I feel like it, I’ll add content to one or more of the other fields, in whole or in part. No rush. Maybe I don’t know how to write all of the sentence. That’s okay. I just fill out what (I think) I know and leave some blanks where I’m not sure. I can always fill it out later when I know more. Maybe I only cloze one word at the end of the day without actually knowing the full meaning. Maybe I only have the word. Whatever. Just do one.
  5. I only Cloze things that I immediately want to learn, and only in the order that seems most fun. I eat my desert first. When I feel like I need some extra cards generated later in a pinch, I cloze the less-interesting content for the sake of adding cards. Perhaps days or weeks later, I’ll get around to really fleshing out the other content like Meaning and Extra. I usually fill out the Extra field as I go. The Meaning field is the most time consuming one if I don’t have some help. Thankfully I know some Japanese people who can help with that when I’m stuck. Most of the time I just do my best guess, though. Is it sometimes wrong? Sure, but being wrong is okay. Wrong action is easier to correct than no action. I’m still gaining valuable contextual exposure, and that’s arguably the most important thing.

So far this has been working well. I’m generating 1-3 new cards every day with this process, which is significantly better than before.

One pattern I’ve noticed in myself is that I learn best through use, even if it’s slow and takes some healthy indifference to sucking. It’s how I learned Kana, it’s how I’ve maintained interest in Kanji, and it’s what has finally gotten me into the swing of sentences. When I say use, I mean that in a very casual sense. For instance, I (pretend to) read manga every day. This equates to looking at a few pages and occasionally looking up a word. It sounds silly, but it has made a serious difference. It’s one of those black magic things you gradually get from immersion. It’s also a crazy good feeling when I make a connection between an RTK card and a compound in the manga.

All that to say, my “study” of Japanese is going well. I’m also approaching a whole year since I decided to start. That means ~10 months of immersion and SRSing. Lazily, mind you. I’ve really half-assed it. RTK can be cleared in 3-6 months if you really put your mind to it, and I’m only about 2/3rds through it in about twice that time. So what? I spend maybe 10-60 minutes per day doing active learning, and it’s completely at my leisure. I think that really drives home the point that any fool can do this if they take their time with it and stop trying so hard.

  1. And what a trainwreck Anki is when it comes to modding. 

  2. No, not in a good way. A few sequentially wrong moves and I’d have irreversibly deleted all of my progress. That could have been soul crushing. Thankfully, I’d need to be a particularly stupid person to make that mistake. :) 

Mar 10, 2017

Orx Narrative System Draft

I’ve gotten distracted, once again, in making other systems. Since I haven’t hit a performance need for shader-based tilemaps, I’m putting it on the backburner. Bad habit, but my overall goal is to have a toolset for development with Orx. There are 2-3 other systems I have an immediate need for.

That is to say, I had an idea for a game that doesn’t have a tilemap. I’ll make a post about that later if it ends up taking off. For now I’d like to outline some systems I’d like to pursue, and draft out one of them here.

The most important ones are a narrative system and a turn-based gameplay system. Both are sorta intended to be used in an FSM which makes me wonder if they’ll both get generalized. The reason I’ve chosen these is because a lot of the game ideas I have require such tools. Particularly the narrative system since I’m interested in making games sorta like Sword & Sworcery or Lone Survivor. This also presents a 4th tangent of contributing some text-rendering features to the Orx Project. Oy.

Anyway, narrative system. A bit of the struggle has been figuring out usage code. I want a nice open-ended way of integrating this with any game-loop. Here are some qualities I’d like to satisfy.

  • Arbitrary Text sources (i.e. speaker)
  • Arbitrary Prompts, which allow branching narrative
  • Narrative Graph, in the sense that prompts could create cyclic patterns
  • Simple-to-read config
  • Control Flow is up to the user
  • Open to interpretation

Here’s one idea.

Player ObservesFlower          = "It's a flower." # "Hmm..."
Player PromptHello             = ? "Say Hello?"
                               # : Yes # -> Player GreetFlower
                               # : No  # "Pssh, flowers can't talk" # -> Exit
                               # : Maybe? # "I dunno about this..."
Flower ThinksToSelf            = "I guess I'll say hello first." -> Flower GreetPlayer 
Player GreetFlower             = "Hello flower"
Flower GreetPlayer             = "Hello human"

Here’s how I imagine the above example playing out.

  • Player says “It’s a flower.”
  • Player says “Hmm…”
  • Player is prompted with “Say Hello?”
  • If Player chooses Yes
    • Player says “Hello flower”
    • Flower says “Hello human”
    • No more properties are available, so dialogue terminates
  • If Player chooses No
    • Player says “Pssh, flowers can’t talk”
    • Jumps to Exit (which doesn’t exist) and causes dialogue to terminate
  • If Player chooses Maybe?
    • Player says “I dunno about this…”
    • Flower says “I guess I’ll say hello first.”
    • Flower says “Hello human”
    • No more properties are available, so dialogue terminates

Wow, that expressed a lot of behaviour with a relatively small configuration, didn’t it?

There are a number of concepts to note here.

  • Path - The resulting evaluation of series of items and properties. By default this is formed by concatenating the Items of a series of Properties, but can be modified by Jumps and Options.
  • Item - Text, Prompt, Option, or Jump.
  • Property - A line of a config section. Formed by a Source, Identifier, and a series of one or more Items. These properties translate to a handle for accessing these parts.

    Ex: Player ObservesFlower = Item1 # ... # ItemN

  • Source - Context idicating where the text is coming from.

    Ex: Player

  • Identifier - Something to differentiate Properties with the same Source. The user may wish to take advantage of this for additional context or debugging.

    Ex: ObservesFlower

  • Text - Available for the user to display as they wish.

    Ex: "It's a flower."

  • Prompt - Same as Text, but indicates that the remainder of this Property is made up of Options/sub-Paths.

    Ex: ? "Say Hello"

  • Option - Indicates a choice to be made in response to a Prompt. An Option is followed by a sub-Path formed by zero or more non-Prompt, non-Option items. An Option sub-Path ends when another Option begins, or when the end of the Property is reached. Each Option sub-Path is terminated by the next Option, end of Property, or a Jump. All Items after a Jump are ignored until the next Option. If an Option sub-Path is not terminated by a Jump, it will jump to the next Property by default when its sub-Path runs out of items.

    Ex: : Yes # Item1 # ... # ItemN

  • Jump - Specifies which property to go to next. They always point to a Property name. Unless it is part of an Option sub-Path, all remaining items on the current Property are ignored. If it is part of an Option sub-Path, all remaining items on the sub-Path are ignored. If left empty, or if an invalid Property is specified, it terminates the Path entirely. Jumps make it possible to end up in an infinite narrative loop.

    Ex: -> Player GreetFlower

There are a few features not yet represented here that would probably be nice.

  • Text Input as an alternative to Options
  • Flagging Options as disabled
  • Removing/replacing Jumps

Thankfully, I might not need to. Config can be modified in memory. So long as I restrain myself in terms of how stateful this system is, the user could do all sorts of things to manipulate narrative flow. Adding, removing, and modifying Items would be the basic stuff. What’s really important is that the user could use their own special identifiers to interpret items in other ways. This opens the door to all sorts of dynamic behaviour. You could even procedurally generate entire dialogues. Being able to design things this way is one of the reasons I love Orx.

So with that, we come to drafting an API to navigate this information.

Here’s some pseudocode.

state = State("PlayerFlowerInteraction")

-- Function to be used in game loop
function handleSpeech()
  -- Make sure there's still a path to traverse
  handle = state:currentHandle() or state:nextHandle()
  if (handle == nil)
  item = state:currentItem() or state:nextItem()
  if (item == nil)

  debugPrint(handle.source, handle.identifier)

  -- Handle prompts/options
  if (state.markedInPrompt)
    if (not state.markedAsPrompted)
      output("Prompt: " + item.text)
      while (option = state:nextOptionItem())
        output("  Option: " + option.text)
      answer = input()
      if (not state:isValidOption(answer))
      debugPrint(handle.source + " chooses " + answer)

  -- Handle item types (option is not included here since that is handled by prompts above)
  if (item.isPrompt)
    debugPrint("Prompting with " + item.text)
  else if (item.isText)
    output(handle.source + ": " + item.text)
  else if (item.isJump)
    debugPrint("Following jump -> " + item.text)

Sort of Lua, but not.

It’s important to note that these functions do not do any displaying. They just form the backbone of an access API with which a user may choose how to display it. Nearly all of this is arbitrary, and control flow is up to the user. What they do with source, identifier, and items is entirely up to them. This is basically a loosely defined FSM that’s tuned for narrative flow.

Mar 10, 2017

GDC 2017 Retrospectives

I was at GDC last week and I’ve decided to blog about it this year. This is a review of how things went this year, what I plan to do next year, and some tips to myself (or anyone like myself) for getting the most out of GDC.

For Next Year

  • I am doing Trainjam next year. Period.
  • I’m booking my lodgings as soon as I get a ticket. Booking a high-end place early can be cheaper than booking a low-end one late. Also note that GDC-specific Hotel discounts can potentially be better than Airbnb, so check both. If at all possible, I’d like to ensure I’m in a hotel that has more game developers and is closer to the conference center. More convenient for networking.
  • I’m on the fence as to whether I’ll do All Access again. I might do something else and get Vault Access separate. Maybe. It certainly gave me the peace of mind that I didn’t have to make it to every interesting session, and it also seemed to add to my credibility as a developer.
  • As far as RSVPing for parties is concerned, I’ll try and stay a little more on top of it next year. I really wanted to go to GDCompression and missed out on reserving a spot. Totally forgot about it. I didn’t need to RSVP for it last year. I just needed a password. Oh well.
  • Help the GDC store staff pack up on Friday. I hear they give you free swag, like T-shirts and such, in exchange for the help. It’s not like they can sell it next year. I always go on Friday towards the end of the day to see what’s on sale, but I’d never considered getting stuff for free.
  • Go to more sponsored sessions for the food.
  • Take notes that on sessions that are more relavent to evaluating their quality as opposed to remembering their content, especially if you’re All Access.


I made a point to not go crazy about getting into parties this year. I think this was a good decision, and look forward to doing similarly next year.

I went to 4 parties of note: AMD’s party, a secret Roguelike Developers meetup, Unparty, and Japan Party.

  • AMD was the only high-profile event I went to. Got in by total chance. Unfortunately, it was pretty weak for networking. I mean, if you like being unable to hear what people are saying and feeling way out of your league, that’s great.
  • The Roguelike meetup was awesome. I felt like I was walking among giants. There were at least a few people there whose games you’ve either played or heard of. It was a very small gathering, so I felt honoured to be there.
  • Unparty was an event that I helped host/organize. It happened totally off the cuff last year. This year was a little more deliberate, but was still thrown together at the last minute. Charles Huang was the primary organizer - he did most of the footwork. Chase Bethea, Chris DeLeon1 and I were all ancillary crew members. The turnout was incredible, and we got lots of positive feedback. Super proud to be a part of it.
  • Japan Party helped me form a lot of eastern connections. They were all enthusiastic seeing westerners who were interested in working in Japan. I’m still on the fence about it, but I’m not completely opposed to the idea. It would really depend on the company. I know that a lot of companies don’t treat their programmers very well, and work-life balance can get pretty bad. I did learn that not all companies are like that.


Oh networking… What a bloody history we share. I took a different approach to networking this year.

The overall lesson I wish to impart is that there’s no universal right or wrong way to network. Do whatever works for you. Take advice, not orders. I’m going to share some strategies that have been successful for me, despite many people telling me they’re “wrong”. Some of this is common sense, but some of it might seem unconventional. Use what works, discard the rest. I plan to write an entire Gamasutra article on this, but here’s the short version.

  • If a networking activity feels like pulling teeth, it’s probably a bad strategy for you. Not all of these will work for you. It is not an exact science. Experiment and find what works for you.
  • Prioritize acquiring friends before business cards.
  • Seek to make connections that offer mutual utility.
  • Find ways to have genuine conversations. Minimize smalltalk as much as possible.
  • Be Taoist about this: If you open a space to be filled by something specific, like job opportunities for instance, it will be fulfilled as a matter of course. I know this sounds lofty and strange, but try it. You might be surprised.
  • If you’re looking for work, try to keep an aloof mindset. Believe deep down that you want it, but you don’t need it. Really drill that into your head. People pick up on that confidence, and it gives you a professional air.
  • If you’re a student, please, please, please downplay that fact. You’re not a student looking to break into the industry; You’re a game developer who happens to be a student. Every time I see a nametag that says “student” on it, I cringe. Be honest, but prioritize putting forward a professional appearance. GDC is for professionals, so be professional.
  • Don’t feel obligated to exchange cards with everyone you meet.
  • Don’t feel obligated to follow up with everyone.
  • Don’t feel obligated to utilize career centers. They don’t work for everyone. Try it out. If it works for you, that’s great, but for some people (like me) it’s time that would be better spent making more personal connections.
  • Don’t feel obligated to go to every party and networking event. Try some of them out, but don’t feel like you have to stick around when you’re not making progress. I have no problem making a u-turn on a party that makes me uncomfortable. I’ve found that obnoxiously loud music, super crowded rooms, and excessive drinking makes for weak networking. If you feel like your networking game is weak in a certain environment, get out of that environment. That being said, there will be times where you’ll need to stretch yourself a little. Finding a balance is key.
  • Business cards are not trophies or connections; They are contact information. No more, no less. Be mindful of this.
  • Receiving business cards is more important than giving them. If someone doesn’t have a card on them, don’t simply give them your card. There’s a very real possibility they will never contact you. If you really want to follow up, get their email address or some form of contact info.
  • Those who you definitely want to follow up with are to be contacted ASAP. This includes new friends and connections who offer immediate value.
  • Unless you’re looking for collaborators, people who are in a similar situation as you are low-priority targets. If a friendship feels natural, by all means, but DO NOT force yourself. It’s a waste of time and energy.
  • When it comes to connnections, time is the most valuable quality. Both in terms of their experience, and how long the connection lasts. With the exception of those who offer immediate value (i.e. ask you to send a resume or something similar), the most valuable connections are the experienced ones that last. The reason is twofold. First, the longer they’ve been in the industry, the more likely they are to have useful connections for you to draw upon. Second, the easier it is to maintain a long-term relationship, the more likely an opportunity is to come your way over time. This is why prioritizing friendship is key.

Yup, that’s the short version. The rest is a bunch of context that brought me to these priciples. Granted, I think spending one GDC forming a broad Linkedin network was an important step to take. However, analyzing the critical path that got me here, it’s mostly about forming a few really solid connections.

Work Opportunities

I can say confidently that career centers have been, and always will be a waste of time for me. I neglected to go to the career center this year. I didn’t even bring a resume.

I was ridiculed by an industry professional for not going last year. Their impression was that I wasn’t serious about finding work in the game industry. Trial and error has shown me that career fairs are not a successful strategy for me, so I stuck to my convictions. The career fair approach works for some people, but not for me. I wasn’t dead set on getting a job, but I certainly wasn’t not looking for work either. I was pretty much open to anything that happened to come my way. It’s sorta Taoist in nature; If you open the space for something to exist (i.e. work opportunities) the space will fill itself as a matter of course. I tried a “I want it, but don’t need it” mindset.

So what happened? Well, I had employees from 3 major companies ask me to personally send them a resume and apply, plus I have 3 temp indie gigs to discuss in further detail this week. And that’s just what I got during the conference! Who knows what else will crop up in the following weeks! This too, was a result of abandoning the net cast wide and opting for something more strategic. All the people who have discouraged me from forging my own path can eat it.

It’s looking like I at least have some freelance work available to me. I won’t name names or get too into it here, but I will say that my networking was much stronger this year.

  1. That’s right - as in Gamkedo 

3 / 9