An Algorithmic Lucidity

a blog

Tag: Friendship Is Magic

An Element Which Is Nameless

I had always thought Twilight Sparkle was the pony that best exemplified the spirit of epistemic rationality. If anypony should possess the truth, it must be the ones with high p (p being the letter used to represent the pony intelligence factor first proposed by Charles Spearpony and whose existence was confirmed by later psychometric research by such ponies as Arthur Jenfoal) who devote their lives to tireless scholarship!

After this year, however, I think I'm going to have to go with Applejack. Sometimes, all a pony needs to do to possess the truth is simply to stop lying.

Just—stop fucking lying!

Ideas Have Expirations

One often-overlooked aspect of the crime of not-writing is that the harm isn't just about the things that deserve to be said that you never get around to saying because you don't put in the time and effort. It's also about the things that you can't say anymore even if you suddenly had the will, because the opportunity to say it was bound to a particular time, and trying to recapitulate the thoughts months or years after the fact would be irrelevant, or impossible.

This phenomenon comes in degrees. Start with irrelevance. Often the inadmissibility of tardy words isn't absolute: you could say things late, but the product would be less valuable than if it were timely—especially in a medium like blogging, where the posts being dated and displayed reverse-chronologically creates an expectation that the entries are associated with a particular point in time—at least, that they were written not too long before their publication date, even if the actual content isn't about the ephemera of the day or season. This has contributed to An Algorithmic Lucidity not being as good of a blog as it could be.

BABSCon swag

Like—each of the last two Aprils, I attended (one day of) BABSCon, the San Francisco Bay Area's premier convention for fans of the animated series My Little Pony: Friendship Is Magic, both times—even the second!—with the thought that the experience would make good fodder for a blog post in the "autobiographical account of my day at this timely Special Event" genre. I was going to tell you about how the first time, I made a couple of social faux pas while meeting Tara Strong and Nicole Oliver, only one of which was intentional; I was going to tell you about how the second time, I hadn't been planning to buy anything in vendor hall, but couldn't help but say "Shut up and take my money" in response to the demonstration of the Twilight Sparkle's Secret Shipfic Folder card game, the game-opening card of which I later got autographed by both members of Sherclop Pones, whose Friendship Is Witchcraft fandub series had clearly inspired some of the cards and probably the game itself, as well as being the source of some of my favorite music. I was going to tell you about how the first time, I was considering buying a coffee cup depicting the Mane Six (because I frequently bought medicinals at the outpost of the American coffee hegemon, and felt guilty about the wastefulness of accepting the default disposable cup every time like every other American), but hesitated, explaining to the vendor that I wasn't sure whether I wanted to use that cup in public, whereupon she said she could throw in a free button, to which I replied, "Sold!" And I was going to do a careful sociological analysis of curious observations like how I hesitated to buy that cup and why H. liked it so much in terms of gender ideology and signaling contrarianism.

But I didn't, and those Aprils were nine and twenty-one months ago, respectively. Not timely. My being motivated to write now ("That my past does not define me, 'cause my past is not today") can push a 350-word counterfactual "postview" of what I thought about saying over the threshold into existence, but it would be a bit unseemly to try to construct (I almost wrote reconstruct, but the re- prefix should be reserved for things that ever existed in the first place) a 2000-word personal account of the timely Special Event that happened last year or the year before that.

screenshot of the Leafline web client mid-game, showing the chess board and a scrolling move log

Or like—I made a thing not too long ago. I didn't mean to. It was an accident, really. It's a sort of oppositional strategy game engine—like a board game you play against the computer. The core move-scoring application is written in Rust, but there's also a web-application GUI (the "web client") in Clojure and ECMAScript 6 that's much more ergonomic for playing a game against. The game is—okay, well, it's chess: the endeavor snowballed out of my desire to participate in my coworkers' friendly office chess games combined with my reluctance to spend effort learning to be good at a task whose essential nature is so obviously suitable for automation. But writing a chess engine is just so cliché, and I enjoy naming things, so I quickly settled upon the conceit that actually I was writing an engine for a game that just happened to be exactly like chess, except that everything has different names: for example, instead of the pieces being black and white pawn, knight, bishop, rook, queen, and king, the figurines, or agents (never "pieces"), in my game are blue and orange servant, pony, scholar, cop, princess, and figurehead. (I kept rank and file, though.) This "adorable idiosyncratic names for everything" convention permeates the codebase. Despite the fact that unnecessarily gendering things isn't normally my style, I decided that ponies, scholars, and (inevitably) princesses were female, and that servants, cops, and figureheads were male, specifically in order to have an excuse to leave // ♀ and // ♂ comments in the enum declaration, because I feel like too many of my code comments are restricted to the ASCII or maybe Latin-1 subsets of the Basic Multilingual Plane, such that I don't want to pass up an opportunity to legitimately ("legitimately") use \u2640 FEMALE SIGN and \u2642 MALE SIGN. There are comments with ludicrous rationalizations for using the standard Forsyth–Edwards notation abbreviations ('P' is for peon, 'N' is for neigh, 'B' is for book, 'R' is for the Rule of law; the blue team's runes are lowercase because lowercase characters have higher ASCII codepoints, just as blue light has a higher frequency than orange light). Instead of pawn promotion, it's servant ascension, and a different verb is used to describe the process depending on the target figurine type (the servant moving the to the final rank can transform into a pony, be brevetted to cop, or transition into a princess or scholar). Or, like, Rust is quite conservative in the Yeggean sense, as manifested in features like the compiler forcing you to explicitly handle all possible variants of an enum; in cases where you know an instance of the enum can only be one of a strict subset of the possible variants, but the type system doesn't know that, you have to fill in those branches or supply a default case anyway, probably with a panic, which is like an exception that you can't catch, to abort the program with a message indicating that something entirely unanticipated has happened and it makes no sense to carry on. So I had been ending all of my panic messages with a stock phrase about how the unexpected thing was contrary to the operation of the moral law ("non-princesslike agent passed to princesslike\_lookahead, which is contrary to the operation of the moral law", and so forth), because this isn't the kind of codebase where you just say something like "assertion failed," as if the program should die because of some human authority's mere assertion, rather than only as a matter of justice when its behavior is contrary to the operation of the moral law. And then when my coworker Alexander Corwin started contributing, he abstracted away the boilerplate in these panic messages into this moral_panic! macro, which I thought was brilliant.

The web client is configured to run on port 2882. It's easy to remember because 2882 is Magnus Carlsen's peak ELO score.

terminal output from a Leafline console session, listing move alternatives with scores and principal variations

Software, like poetry, is never really finished (only abandoned), but getting the project to the point that I felt it was a minimal viable "product" took about eight glorious weekends (plus a few weekday nights, and with some help from Alexander). Near the end, watching the program utterly trounce me in web client play and feeling for all the world like a lieutenant junior grade, I started looking ahead to what came next for me. Over the past two years, the first two years of my life (that I feel comfortable admitting to), I had spent many, many night and weekend hours hacking on various side projects and coding exercises out of genuine enthusiasm and curiosity and desire to improve my craft—and, honestly, a feeling of insecurity, sensing that I needed to prove my worth as a hacker ("I threw myself into my studies, to have the world in my control"). But decent chess AI in Rust as an impulsive throwaway project seemed like a sufficiently strong signal of my programming prowess that maybe it was time to tie off this project, write the obligatory exciting blog post about it, and start allocating night and weekend hours towards some of the non-programming (!) interests I remember having in the before-time (assuming those memories are not fake). I could do some math! Write some fiction! Maybe even meet new friends ("I will not be shy; I'm going to try; I bet I'll find the reason why so many people, like me—")?!—all while feeling secure in the knowledge that my technology skills are clearly adequate for my continued existence to be economically viable. (For now.)

So ... about that end-of-project blog post. I was going to title it "Project Review: Leafline version 0.0.14-MVP; or, Lessons From Writing an It's-Not-Chess Engine in Eight Weekends", and I was going to explain everything to you—not just the silly names for everything that inexplicably amuse me (and only me), but the actually interesting substantive technical details of the implementation. Not that the AI is anything special—it's just textbook minimax search (in the negamax style) with α–β pruning and a position-evaluator that mostly counts material but also has bonuses for things like having both scholars or ponies and servants being in the center sixteen squares, plus a transposition (hash)table to look up scores of game-states it's already seen before, and another hashtable for history-heuristic move ordering. It's multithreaded, to take advantage of multiple cores (although I confess that the specific threading strategy is kind of a questionable hack). It can search up to 7 plies if you have a decent machine and are willing to wait ten or twenty minutes for the answer.

But even if it's nothing special, I was going to tell you about all the deeply moving philosophical insights I got from doing it. It's one thing to know how to refute the classic anti-AI argument (or straw person) that "humans couldn't possibly build something smarter than themselves by definition," but it's another thing to have the personal experience of having written something smarter than yourself in some particular domain, to the extent of feeling an acute sensation of futility while playtesting it, thinking: I've already coded my understanding of what it means to be good at this game; obviously I'm not going to do any better thinking about it with my slow meat-brain. (Although this is partially explained by my knowing almost nothing about chess strategy that I didn't learn in the course of this project; I've seen one of my coworkers beat the program at 4-ply search very quickly.) I was going to explain to you how α–β pruning works, with hand-drawn diagrams, and how you can interpret it as disregarding possible worlds that are too good or too terrible to be true (given rational play on both sides). I was going to tell you about the exciting surprise where the program seems to behave as if it understands the concepts of pinning and forking, even though those ideas aren't represented anywhere in the code!—but that when you think about it, that shouldn't actually be surprising: to the extent that we think pinning an opposing piece is a good idea that will lead to us picking up material, then pins should naturally appear in the results of searching the game tree for moves that are predicted to pick up material. And the reason we search for game-states where we hold a material advantage is because we expect that we're more likely to mate from those positions (in future nodes beyond our current planning horizon). Things that are, in all philosophical strictness, mere instrumental values, might profitably be treated as if they were terminal values by some algorithm that can't see far enough ahead to the actual goal, and this is a quantitative phenomenon: the shorter your lookahead, the more you want to rely on near-term approximating rewards. That's why I threw in a bonus for servants advancing to far ranks; I suspected that the existing code searching at the not-greater-than-7 plies that it could get in a reasonable amount of time, wouldn't adequately appreciate the true value of servant ascension, even though I expect that value would be naturally emergent in a deeper search, just as the value of some forks and pins emerged from mere 4- or 5-ply searches. (This was just a suspicion, though; I didn't take the time to actually test ascension-seeking behavior.)

So—though I've managed to just now haphazardly summarize some of the things about this project, this isn't really a careful project-review post. And now the distance between today and 27 September's 0.0.14-MVP tag is longer than that between that tag and the start of the project. And I ended up making a few more commits in subsequent weeks. So, no longer timely, at least not the way I originally imagined publishing a grand just-finished-project review post as a symbolic milestone marking a transition in how I'm going to start spending my precious non-dayjob time.

Or like—in late October, I saw the recent Jem and the Holograms film, because the previews (accurately) made it look like it was going to be really bad, and I thought I'd write a post combining a negative review of the new film with praise for the original Jem cartoon that this awful film mendaciously desecrated the name of. And then ... I didn't get around to finishing the post. Maybe my notes and memory from that night at the theater are accurate enough such that it's not too late—but the potential impact departed with the timeliness; I can't warn you not to go see it, because it's not in theaters anymore. (And good riddance.)

But beyond these sad cases of ideas that didn't get written up properly while they were still maximally relevant, there's an even worse way to fail to communicate, which is when the would-have-been-author has changed so much since first having the idea, that there's no way they could plausibly do justice to the idea as it was first had. You can't write a Diary entry about the day five years ago that you don't remember, and—more poignantly—you can't write a grand ideologically-driven novel in favor of the ideas you don't believe anymore. Some would argue it's just as well—if it's something you wouldn't write and couldn't stand by today, aren't you relieved that it doesn't exist to sully the name that represents who you are today? Even so, I would still favor intertemporal solidarity among past and future selves against the common enemy of our illiteracy.

Yes, illiteracy! Some would call it writer's block, but I know better than to bother with the unobservable distinction—whether you choose to describe your hypothesis as "doesn't write because unmotivated" or "doesn't write because doesn't know how," the result is the same: death of the non-author's memetic lineage.

Monthly Favorites, September 2015

Favorite commit message fragment: "it turns out that it's \d that matches a digit, whereas, counterintuitively, d matches the letter 'd'."

Favorite line of code: a tie, between

    let mut time_radios: Vec<(Commit, mpsc::Receiver<(Option<Commit>, f32)>)> =
        Vec::new();

and

        for (previous, new), expected in zip(
                itertools.product(('foo', None), ('bar', None)),
                ("from foo to bar", "from foo", "to bar", "")):

(Though both of these contain at least one internal newline, it's only for PEP 8-like reasons; they're both what we would intuitively call one "logical" line of code.)

Favorite film: My Little Pony: Equestria Girls: Friendship Games. (Poor plotting even by Equestria Girls standards, and it could only have been because of magic that I didn't get semantically satiated on the word magic during the climax. Alternate-Twilight's idiotic decision to withdraw her application to the Everton independent study program in favor of transferring to the Canterlot School of Mediocrity and Friendship in order to be closer to the Humane 5+1 was as predictable as it was disappointing—though I do credit the writers for at least acknowledging the existence of alternatives to school. And what was up with that scene where we're momentarily led to believe that alternate-Spike got switched up with Equestria-Spike in a portal accident, but then it turns out that, no, alternate-Spike just magically learned how to talk? Is it that there was no time in the script to deal with the consequences of swapping sidekicks across worlds, but that Cathy Weseluck's contract guaranteed her a speaking role? Despite being the weakest film in the trilogy (far worse than its brilliant predecessor, My Little Pony: Equestria Girls: Rainbow Rocks), Friendship Games is still a fun watch, and an easy favorite during a month when I didn't see any other feature-length films.)

Friday Night Lies

"I am a practical man," I said calmly and confidently to no one in particular while sitting down to an easy-mode round of the new tower defense game where the bad ponies are the good ponies and the good ponies are the bad ponies, "I have created no less than X times 276 divided by 365 dollars of economic value this year, and I don't believe in karma, sin, or willpower depletion."

Pumpkin Spice!

It's September in the first year of my life (that I feel comfortable admitting to), and I feel great—the mounting success or arguably-not-yet-failure of my professional, intellectual, and—other goals is complemented splendidly by a muted but nonetheless genuine appreciation of the subset of nature's cyclic harmonies that I'm capable of perceiving: the air is getting slightly less warm, the sun is setting slightly earlier, and the hacks by which the retailers separate us from our money have changed completely.

In particular, the American coffee hegemon has started offering its "pumpkin spice" medicinals again, and my esteemed colleague Alexander Corwin has been blogging about drinking them despite/because hating them, so as a loyal client of the hegemon (measured by spending habits; the market gods only accept sacrifices of time and money, and don't care what you say or believe), of course I have to accompany him to the hegemon's outpost on fourth street that I go to frequently (typically bringing the personal cup I got at BABSCon, and the barista H. insists on giving me a brohoof every time), but the day before was no good, because Alexander apparently needed his sweetener/caffeine medicinal while I was busy pairing with our CEO on our new lead pipeline and bought his traditional Diet Coke instead.

So this morning, shortly after I arrive in the office, plans are quickly formed for me and Alexander and our esteemed colleague Tim to go to the outpost.

Sep 10 09:18:04 <acorwin>     zackmdavis: PSL?
Sep 10 09:18:39 <zackmdavis>  acorwin, I don't know what that is an initialism for
Sep 10 09:18:43 <zackmdavis>  oh
Sep 10 09:18:46 <acorwin>     zackmdavis: pump--
Sep 10 09:18:49 <zackmdavis>  Pumpkin Spice!

We stroll down Brannan street, chatting about the sorts of things San Francisco software engineers talk about when they're not talking about their work, like the code we've been writing not-for-work. Alexander has been making a dice game for the iPhone but has run into trouble that he doesn't know how to debug; I suggest going to an iOS meetup group and enthusiastically mention that I went to an exciting Clojure meetup the previous night (where Zach Tellman, who has more GitHub stars than me by like two and a half orders of magnitude, gave me some tips on how to improve my 3D 2048 clone).

H. is manning the left register today. When I get to the front of the line, we perform the obligatory fist-bump and I order a grande (the BABScon cup holds sixteen ounces) iced-coffee with pumpkin spice syrup, and a birthday cake pop. H. makes a faux-teasing remark about my wanting the pink one. (I think this, and the matter of my cup and the bumping of fists, is part of an interesting psycho-sociological phenomenon that I've been meaning to blog about sometime.) Alexander orders a Pumpkin Spice Frappuccino and something to eat at the right register, and I think Tim gets a pumpkin spice latte.

We wait and continue to chat. Alexander receives what I think is a blueberry scone but later learn was actually a blueberry yogurt muffin with honey. His Frappuccino arrives (with whipped cream, despite/because of how he doesn't like whipped cream); he tries it and I think says something about it being mercifully less terrible than the standard pumpkin spice drinks on account of being cold. I express regret that I neglected to bring writing materials to record what he said verbatim to quote on my blog. Tim's latte arrives. I'm surprised that my own medicinal is taking so long, and I'm just about to ask when we see it being poured.

During our walk back to the office for another glorious day of building the future of cloud storage (perhaps not as glamorous as cloud computing, but the glory isn't about the glamor), I confess that I had considered offering to pay for my esteemed colleagues' drinks with my ridiculous golden loyalty/payment anti-credit card (you loan money to the American coffee hegemon at zero interest in exchange for a free medicinal every twelfth visit starting with the thirtieth) but didn't do so because it would be socially awkward, and Alexander points out that now it's awkward of me to have mentioned it and he had to pay for his pumpkin spice monstrosity. Next time, I say. Tim points out that gingerbread season isn't far away.

And on this tenth day of the first September of my life (I will cough loudly and unconvincingly if you insist otherwise), I think things are going to be okay (for a while (for me)).

Twilight Sparkle Is a Disaster Waiting to Happen

I swear to Celestia, one of these days that mare is going to get somepony killed or worse. I almost suspect it's already happened and Hasbro is just sitting on the footage.

Seriously. Levitation or teleporting is fine, but you do not buck around with mind control or transformation spells unless you are absolutely sure that nothing will go wrong and that you can fix it when it does anyway. You'd think she'd learn after, I don't know, accidentally brainwashing pests into destroying the town, or unleashing a transmissible-by-sight plague of madness that wasn't stopped until Princess Celestia herself intervened, or accidentally swapping parts of her friends' souls, or accidentally transforming her friend into a horrible bat-pony abomination, or transforming herself and her friends into tiny vaguely-insectoid creatures and venturing through a closing portal to another world and only barely making it back in time.

Even plot armor isn't infinitely thick. Someday it's going to break, and I don't want to be watching when it happens.

Quicksort in FIM++

Dear reader, I have got to tell you, fandom is intense. One day last October Equestria Daily (internet clearinghouse for fans of the animated series My Little Pony: Friendship Is Magic) posts a joke proposal for a programming language (FIM++) based on the show, and within the week there's a working interpreter for it. What does it mean to model a programming language after a cartoon, you ask? Well, in the show, episodes typically end with our heroine Twilight Sparkle (or after Season Two, Episode Three "Lesson Zero", one of her friends) writing a letter about what she's learned about the magic of friendship to her mentor (and God-Empress of the sun) Princess Celestia. So, then, why not have an esoteric programming langauge where the source code reads like a letter to Princess Celestia? Adorable, right?

So, this gift having been provided to us courtesy of Karol S. and the brony community, let's do something with it! More specifically, how about we implement quicksort?—that is a classic. What's quicksort? Well, we want to sort a list, right? So—bear with me—we define this partitioning procedure that, given indices into an array, partitions the subarray between those indices into a subsubarray of elements less-than-or-equal-to a given element dubbed the pivot, then the pivot itself, then a subsubarray of elements greater than the pivot. How do we do that? Well, let's designate the last element in our subarray as the pivot. Then we're going to scan through all the other elements, and if any of them are less-than-or-equal-to the pivot, we swap it into our first subsubarray and increment a variable keeping track of where the first subsubarray ends. Then, we swap the pivot into place and return its index. In Ruby—

def partition(array, p, r)
  i = p-1
  for j in p..(r-1) do
      if array[j] <= array[r]
        i += 1
        array[i], array[j] = array[j], array[i]
      end
  end
  array[i+1], array[r] = array[r], array[i+1]
  i+1
end

Then we can sort an entire array with a bunch of recursive calls to our partitioning procedure:

def quicksort(array, p, r)
  if p < r
    q = partition(array, p, r)
    quicksort(array, p, q-1)
    quicksort(array, q+1, r)
  end
end

# Let's try it!
my_array = [9, 5, 4, 11, 2, 10, 6, 3, 8, 12, 1, 7]
quicksort(my_array, 0, my_array.length-1)
print my_array # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

So that's quicksort. With a little more effort, we can do the same thing in FIM++:

Dear Princess Celestia: Letter about Quicksort:

I learned about exchange with Applejack, Rainbow Dash, and Rarity.

  On the page numbered by Rarity of Applejack I read about Sweetie
  Belle.

  On the page numbered by Rainbow Dash of Applejack I read about
  Scootaloo.

  On the page numbered by Rarity of Applejack I wrote what I knew
  about Scootaloo.

  On the page numbered by Rainbow Dash of Applejack I wrote what I
  knew about Sweetie Belle.

That's about exchange.


I learned about partitioning with Applejack, Rainbow Dash, and Rarity.

  On the page numbered by Rarity of Applejack I read about Apple
  Bloom.

  Sweetie Belle made the difference of Rainbow Dash and the number one.

  Did you know Scootaloo likes Rainbow Dash?

  I did this while Scootaloo had less than Rarity:

    On the page numbered by Scootaloo of Applejack I read about
    Diamond Tiara.

      When Diamond Tiara had not more than Apple Bloom:

        Sweetie Belle got one more.

        I did exchange of Applejack, Sweetie Belle, and Scootaloo.

      That's what I did.

    Scootaloo got one more.

  That's what I did.

  Sweetie Belle got one more.

  I also caused exchange of Applejack, Sweetie Belle, and Rarity.

That's about partitioning with Sweetie Belle.


I learned about quicksort with Applejack, Rainbow Dash, and Rarity:

  When Rainbow Dash had less than Rarity:

    Fluttershy did partitioning of Applejack, Rainbow Dash, and Rarity.

    Fluttershy got one less.

    I caused quicksort of Applejack, Rainbow Dash, and Fluttershy.

    Fluttershy got two more.

    I caused quicksort of Applejack, Fluttershy, and Rarity.

  That's what I did.

That's about quicksort.


Today I learned:

  Did you know Applejack likes 9, 5, 4, 11, 2, 10, 6, 3, 8, 12, 1, and 7?

  Applejack did dictionary of Applejack.

  I said: Applejack!

  I did quicksort of Applejack, one, and twelve.

  I said: Applejack! 

Your Faithful Student,
Twilight Sparkle

Bibliography Cormen et al., Introduction to Algorithms, third ed'n, §7.1

On an Image Macro

For the haters are going to hate,
And the ponies are going to pwn,
As for me, I will bear all the burden and weight
And uncertainty of the alone.

Supermarket Notes I

I think I like the store-brand "sparkling water beverages"; they fill a similar niche as soda (which I never buy at the store, but have been known to occasionally consume at parties or restaurants), but seem like they ought to be less deadly.

I think the "More grains. Less you!" slogan on this box of cereal sounds sinister. I mean, they're probably just talking about weight loss, but still ...

I'm suspicious of processed food products shaped like cartoon characters, as if there are highly-placed cannibals at General Mills plotting to train children that it's okay to eat creatures that can talk. On the other hoof, these fruit-flavored snacks are delicious.