Cuttlepress

Blogging about Hacker School?

Not Graduating

My time at Hacker School is over. So, what have I learned?

Hacker School’s stated result for students is to become a “dramatically better” programmer, and I think I’ve been pretty successful in that direction. Here’s a partial list of things I can do now that I hadn’t done before September:

At the end of the recent Ludum Dare weekend, Lindsey asked me: “do you think you were better at LD as a result of having done HS?” To which I could only answer with, “Yes, definitely!” because there’s basically nothing in that game that I could have done three months ago (or at least, certainly not that quickly). I see my code skills as an important subset of my overall ability to Make Cool Things, and these new shiny tools in my toolbox will help me make different kinds of cool things than I’ve been able to make so far.

And, of course, I’ve talked to a whole bunch of new people. Without diminishing the splendor of 455 Broadway: the people are really the point of Hacker School, and they’re all utterly brilliant, with both a high degree of awesomeness and a willingness to share that awesomeness freely. I’m very fortunate to have been able to spend three months with the HS facilitators, residents, and my fellow students, and I look forward to continuing the many friendships and hopefully even some of the collaborations that began for me at Hacker School.

In short, A++, strong recommend, etc. This also seems like an appropriate place to publicly thank Etsy for making Hacker School possible for me.

What’s next, Lea?

It just started being 2014 today. For the first couple of weeks of January, I’ll be working on housework and personal projects and visiting my family in California; as soon as I get back to Pittsburgh after that, I’ll be delighted/terrified/delighted to begin my approximately 2.5-month residency at the STUDIO for Creative Inquiry, Carnegie Mellon’s “laboratory for atypical, anti-disciplinary, and inter-institutional research at the intersections of arts, science, technology and culture.” I’m hoping to continue to blog regularly as part of that process, so stay tuned for details.

Day 46

The end-of-batch party is still going, so: today I got teleportations working in my maps, and, with Adam, worked through a bunch of gotchas in the way one interacts with HTML forms via JavaScript. (For example: element.setAttribute(attributename, attribute) can’t be used to alter an existing atttribute; instead, you want element.attriutename = attribute. Similarly confusing is that, while readonly = true prevents an input element from being written to, readonly = false does not reverse the effect; you have to element.removeAttribute(attributename).

Green line is magic.

Day 45

Even more messing about with maps today. I added the ability to add more steps to a journey (and remove them again!), and made the code more MVC-ish — it’s still very much a work in progress, though. So many div.

Day 44

More work with Google Maps today. Arbitrary numbers of maps can now be added to the code, each with their own input devices and path renderers. In the afternoon, Mary helped me refactor the code for tidyness/modularity.

Now there are two of them

Day 43

Spent today recovering a bit from Ludum Dare. I poked some more at the Ultimate Tic Tac Toe board with Fei very briefly, then moved on to learning about the Google Maps API. Jeff and I used the Directions API to get walking times between locations. It then turned out that Google prefers you not to use the data from the Directions API without displaying a map, so I also started looking at the more general-purpose Javascript API. Did you know there’s a fun widget for generating JSON to style Maps with?

Here’s a quick labels-only map, which I just tried unsuccessfully for over an hour to embed in this page.

Project Writeup: Twitter Heist

This weekend was Ludum Dare, the thrice-yearly 48-hour gamemaking competition, and the theme was “you only get one.” I liked the idea of enforcing just one playthrough per player, but not enough to implement cookies/IP detection/whatever else (and have those all be insufficient enforcement anyway), especially because other folks would almost certainly do that anyway. Then on the LD homepage, I saw in the embedded Twitter sidebar that folks were using “#YOGO” for their LD-related tweets, which I thought was hilarious. Since one of my goals for this LD was to showcase some of the skills I’ve learned at Hacker School, it was decided: I’d do some sort of game on Twitter.

Twitter text adventures are a thing I’ve thought about before, even to the extent of encouraging gwillen to make a Twitter interface to Parchment, and I definitely did not want to go to that length for this; I like to take Ludum Dare at a leisurely pace. I chatted with Sumana about various formats a Twitter game could take; for example, I could have a clearly-demarcated list of one- or two-letter commands that players could enter all in one tweet to play the game (like this but with a gameplay aspect). I also thought about single-move games, like Rematch. In the end, I decided to go with the simplest option: a choice-based game with one tweet per state change. I would prototype in Twine and store the game states in JSON.

Much of the tech is similar to that of Curated Dannel, in that I used ntwitter and mongodb. Incoming tweets are grabbed through a User Stream. The user id is checked against a database of player’s states, and the text is searched for words associated with a change from that state (as stored in game.json). The new state is looked up in another JSON file, and a response tweet is sent back to user. Properly threading the messages was one challenge, because although most API requests have matching “whatever_id” and “whatever_id_str” parameters (to handle the fact that many IDs on Twitter are too large for JS ints), there is no “in_reply_to_id_str” parameter on a request to the Twitter status update endpoint; you just have to use the “in_reply_to_id” parameter even though you’re sending a string id.

I didn’t get to the actual storytelling until Sunday afternoon, which I think is unfortunately very obvious in the game. I’d been batting around various ideas, but none seemed approachable in the amount of time I had. I ended up going with a very brief heist plot. (Did you know I love heist stories?) I tried to keep it goofy as a default fit with its context, because I didn’t have a lot of time to go beyond that.

Spoiler after the cut:

Day 42

I went to Julia’s Unix executables presentation, and then I did a bit more poking at D3. My expenses chart now shows each expense’s category when it is hovered over:

1
2
3
.on("mouseover", function(d) {
  d3.select("#label").text(d.Category).attr("class", d.Category);
})

Fancy.

Then I wanted to get back into the swing of JS/canvas stuff to warm up for this weekend’s Ludum Dare. Fei and I worked on a board for playing Ultimate Tic Tac Toe (implementing the base set of rules, but not yet the “clarifying rules” at that link): Ultimatttt

Day 41

Spent today mostly on other people’s projects. I had some great conversation with Katie about the structure of her interactive fiction engine. I’m very trained to the Inform 7 way of doing things, so it was a fun exercise to think about the strengths and weaknesses of the various parts of that system; of course, Katie’s project is a lot smaller in scope than Inform, so a lot of simplification is called for. I always love an opportunity to pull out the Inform 7 rules chart.

I also “helped” Lyndsey create a horrifying process hydra in supervisord. (You’d kill one process, and two would spring up!)

Day 40

I started the day without a lot of direction, and Julia suggested that I take the day to learn D3. Good idea! I worked through this tutorial and generated this exponentially-scaled bar chart of all my monetary transactions (not including rent/utilities) while I’ve been in NY: Color-coded!

Here’s the entirety of the JavaScript (colors were styled in the CSS):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var width = document.documentElement.clientWidth-10;
var height = 500;
var svg =  d3.select("body").append("svg").attr("width",width).attr("height",height);

d3.csv("new_york_money.csv", function(error, rows) {
  makeChart(rows);
});



function makeChart(dataset) {
  var yScale = d3.scale.pow()
      .domain([0, d3.max(dataset, function(d) { return +d.Amount; })])
      .range([0, height])
      .exponent(0.4);

  svg.selectAll("rect")
      .data(dataset)
      .enter()
      .append("rect")
      .attr("x", function(d, i) {
          return (i-1)*(width/(dataset.length));
      })
      .attr("width", function(d, i) {
          return width/dataset.length - dataset.length/100;
      })
      .attr("y", function(d) { return height-yScale(+d.Amount); })
      .attr("height", function(d) { return height;})
      .attr("class", function(d) { return d.Category; });
}