Tuesday, January 28, 2014

Dropping out of High School to Join a Startup in San Francisco

TL;DR

I dropped out of high school and got a job at Nomic as a software developer in San Francisco, after turning down an on-site interview with Google. This is my story.

Do what you love

Coding is my passion, and I can't think of anything else in the world I would rather do. Fortunately, I found my passion early in life. I was into computers by age 10; I installed Ubuntu 6.10 at age 11. At 14 I built my first computer from parts off of newegg. By age 15 I was writing XSS exploits for Google (Google Security Hall of Fame - Q2 2011) and had created my GitHub profile as I started to get into coding. The summer after my freshman year in High School I got my first internship at Indeed.com (at 15). At 16 I started blogging about my technical projects on zolmeister.com. That summer I interned at Indeed again, and then the following summer interned at Balderdash, the company behind Sails.js. During these internships I was writing real code and contributing to the growth of the companies I worked with. I wouldn't be where I am today if not for the opportunity these companies gave me.

Why I Dropped out of School

Most individuals in my position have gone to college (at least, before dropping out). The problem was, I hated school. Every day I would sit in a classroom and lose 7+ hours of my time, looking forward to coming home every day to code and be productive. I was definitely not excited about the prospect of doing the same thing for 4 more years. But then again, college is different.

I asked a few developers about their college experience and what it was like for them. What they said was that college provided a great way to meet people who were interested in the same topics as they were, and these people helped foster ideas and creativity, and build strong relationships for the future. (That, and also a degree)

The developers I spoke to also expressed that college was a great way to develop their skills in fundamental computer science concepts (data structures, algorithms, etc.). However through my previous experience I already had a strong grasp on these fundamental concepts, so for me the experience would not be as valuable in that area.

Taking what they said into consideration, and also how much I enjoyed working at my summer internships, I decided that I would try find an alternative to college which would be the best of both worlds. So I decided to move to San Francisco.

(There are some things that are difficult to learn without a strong academic background, such as Machine Learning and Cryptography. My hope is that I will be able to teach myself those topics through free online courses and research.)

For many years, my view of San Francisco had been shaped by Hacker News, and especially Paul Graham. San Francisco was this amazing hub, full of incredible entrepreneurs and developers. I knew that I wanted to be a part of that. So far I can say that SF is not exactly what HN made it out to be, but it's definitely close, and I think my experience here will be a great one.


After deciding not to go to college, it was obvious to me that I didn't need to finish high school to start doing what I loved. From my experience in applying for jobs I can say that not a single employer asked if I had a high school diploma (though they did ask about a college degree). And, if I ever really needed it, I could always take the GED (equivalent to a high school diploma). I did however promise my parents that I would only drop out once I got a job offer of at least what top CS college graduates were receiving.


Requirements

While looking for a job, I had specific attributes that I was looking for:
  • The company must have a product that fits my interests and have a great culture
  • The company must be in San Francisco
  • The company must use Node.js
  • The compensation must be at least the level of a college graduate
  • Ideally the company would be small (< 20 people)
  • Ideally the company would use MongoDB/PostgreSQL, and/or BackboneJS/AngularJS

These requirements were based partially on my skill-set, and also on what tools I think I would enjoy working with most as a developer.


Prep

Here are most of the resources/topics I reviewed before my interviews:

Additionally by working on side projects and writing blog posts I had plenty to talk about during interviews. This was especially important because I didn't have much work experience outside of my internships, and having these projects helped show that I knew what I was talking about.

What follows is my journal of events that lead up to getting the job I have today.


Prologue (before I turned 18)

I accidentally ignore a Google recruiter on Aug 21. They contacted me because of my participation in Google Code Jam, a coding competition that Google sponsored (in which I did poorly might I add).

The same Google recruiter tries again Oct 16 (props to them) and we schedule a phone chat for Friday Oct 17. I then update my resume and email it over.

The chat goes well, and we schedule a technical phone interview for Nov 6 (I needed lots of time to prepare for the interview). Also I will be coding in python (the interviewer let me pick). Python is my preferred language for short programming challenges and is a great language to work with (compared to Java, which is what most of Google uses).


Oct 19

I turn 18 - Time to start looking for more job opportunities.

Hired / Developer Auction
I had signed up for Developer Auction (now Hired) a long time ago, but now I can actually use it. They provide a service where developers can write up a profile and have companies contact them during the auction period (which lasts a week). I update my profile, and submit it for approval.

Oct 21

Hired emails me, letting me know I’m in the next auction (starting Oct 28). They ask that I block off their auction week for them, and not look for other job opportunities during the auction. That's fine with me, but I'm already interviewing with Google. I let Hired know, and it's not a problem.

Google gets back to me with confirmation about my technical phone interview on Nov 6th. They also provide resources for preparing for the interview.

Oct 25

My programming practice of the week (http://codingforinterviews.com/) email comes in. The problem is to write an algorithm which prints out elements from an NxM matrix in spiral order. The question is similar to this one except reversed. I write up a solution on my whiteboard, and then transfer it to the computer to test it. (This was extremely useful!)


Oct 27

The Hackerrank Oct 20/20 competition begins. Basically a few difficult algorithmic coding problems to do over the next few weeks. (I placed 129 out of 3404 scoring participants. The questions are quite difficult.)

I update my resume, Hired profile, and LinkedIn Profile. Here is what my Hired profile looked like:


Oct 28

I get a great job offer from Balderdash based on my internship (thanks guys!). However I decline because I’m looking to move to San Francisco.


Oct 29 - Nov 3

Bids from Hired:
I receive 10 bids from Hired, ranging from $85,000 to $130,000 per year,
with a median bid of $100,000.

Upstart: I request a revision because the salary is a bit low (and also they seem to be in Palo Alto, which is not San Francisco).

The Minerva Project: I schedule a call to chat for Friday, Nov 1st.

Nomic: I schedule a call for Monday afternoon.

BookFresh: I decline because I am uninterested in the product (calendar appointments).


Yahoo! (Sunnyvale, CA): I schedule a call for Monday (Nov 4).


ShopRunner (San Mateo, CA): I schedule a call for Monday (Nov 4).


Klout: I schedule a call for Thursday (Nov 7).


Everlane: I decline because I am not interested in industry (clothing).


Silicon Valley Biosystems: I decline because it feels too much like enterprise software.


Grouper: I decline because they deal a lot with clubs/bars, with which I have no experience.

I have my call with The Minerva Project. I feel nervous and as a result rush through the call. I am asked to talk about my projects, and I give a general overview of them, though was not really prepared and it definitely showed. (I learned a lot from this experience.)

As a result of my interview, I create a document outlining my best projects and what I should say about them for future interviews: Google Doc

The Hackerrank Oct 101 Competition takes place. Unlike the 20/20, this competition is strictly timed to a few hours. (I placed 82 out of 490 scoring participants.)


Nov 4

I have my call with Yahoo! (which lasts exactly 4 minutes). There was a miscommunication (not sure how) where they thought I was looking for an internship (and were about to offer it to me). After straightening that out (and the caller mentioning they were quite impressed with my resume), she says “We don’t hire people without a degree”. And the conversation ends there. (I do realize that this recruiter does not necessarily represent the views of Yahoo! and may have been misinformed.)

I get an email via LinkedIn about a Senior Front End Development position in Austin, however I decline because again I am looking to move to San Francisco.

Nomic reschedules our call for Friday (Nov 8)

I have my call with ShopRunner, and while the caller seems quite impressed with my resume and experience, says he is worried that the founder (an MIT graduate) will disapprove of my lacking a degree. He says he will get back to me before continuing the process.

Nov 5

ShopRunner gets back to me, and unfortunately they are only looking for candidates with a degree.

I get an email via LinkedIn about a Web Developer position in Austin, and again decline as I'm looking to move to San Francisco.

I update my LinkedIn profile location to say San Francisco, so hopefully future LinkedIn contacts will be for opportunities there.

I skip school to prepare for my Google Technical Interview tomorrow.


Nov 6


Google interview (1):

(tl;dr - This interview consisted of 3 programming problems and lasted 45 minutes. They didn't ask about any previous experience.)

Question 1: This question is quite difficult, as I have never seen it (or anything similar) before. Without giving it away, the question is about implementing a fundamental mathematical concept. I start with the naive solution, which for this problem has an O(n) run-time complexity. However I am prompted to try to get a better solution. This means I am looking for an O(log(n)) solution, assuming it can be done using conventional methods (log(n) is quite common, see Binary Search). O(log(n)) implies a divide and conquer approach, where each step reduces the number of subsequent steps by half. This is usually done with recursion, which is the approach that I take and eventually use to solve the problem. (My biggest help here was to write out examples and talk through them to try and break the problem down.)

Question 2: This question is extremely easy for me because I have seen it before. The only issue is that I never actually coded the solution and was not sure if it would work. However I stayed confident (confidence is important during these interviews) and type out what I think the solution is. The key to the problem is to use pythons amazing built-in 'zip()' function which essentially solves the problem itself. (The interviewer was unfamiliar with it, so I explained it to them and walked through a few examples.)

Question 3: This problem is similar to one that I have done previously, and only requires a slight modification. We don’t have enough time to code this one, so I explain a high-level overview of my approach. The interviewer isn’t sure of my answer, and asks me to prove it. I have never done that before, but staying confident, I explain step by step how my algorithm reduces the problem space from N to 1, which proves that it works for any sized N.

Afterwards the interviewer asks if I had any questions. I ask about their testing methodologies (they practice test driven development), as well as version control systems (team dependent), and their code review process (their answer was unclear).

Overall I feel good about it. Now we play the waiting game.

Google gets back to me (the same day!), I will be having my second technical phone interview within the next 2-3 weeks (they couldn't fit me in sooner, and will get back to me soon).


Nov 7

I have my call with Klout. Overall it goes well, I mostly talk about my tanzania slideshow project, and how I overcame many different obstacles while creating it. They let me know that I will get an email soon with time for a technical phone interview.


Nov 8

Google gets back to me, and we schedule my 2nd technical phone interview for Monday Nov 25.

I have my call with Nomic, we talk a bit about my background and what I'm looking for in a company. I let them know that I care about making an impact and really contributing to the growth of a great company. Overall I think the call seems to go well.


Nov 10

Klout emails me, and we schedule a technical phone interview for Wednesday (11/13)


Nov 12

Nomic gets back to me, and we schedule a coding interview (2 hour ‘workout’) for Friday (11/15).


Nov 13

My technical interview with Klout is suppose to happen today. Instead I receive an email from Klout letting me know that won't be happening. They let me know that unfortunately Human Resources has a minimum age requirement which I do not meet. (Age discrimination is legal for young workers).


Nov 15

Nomic 2hr Technical 'workout'
At the scheduled time I get an email with the coding problem. The problem itself is a simple command line implementation of a common coding problem. I easily solve the problem in python and email over my solution within the first hour. In the email I ask if they would like to go over and talk about my solution.

I do not get a response until nearly the end of the 2hrs, and it seems as though they had no intention of actually speaking to me over the phone at all. Later that evening I receive an email letting me know that everything looks good (the code), and that they will get back to me soon.


Nov 17

I apply for a job at Optimizely via their website. This consisted of simply writing a cover letter and sending over my resume.

I get a response from Nomic, giving a bit of feedback about my code and mentioning an optimization that I missed relating to a 'mathematical function'. I immediately recognize that they were talking about the modulus operator '%' and respond with some of my deeper knowledge about it (relating to the way python differs from JavaScript when handling modulo). We then schedule a technical phone interview for Tuesday afternoon (11/19).


Nov 18

Optimizely responds, and is interested in chatting. However they don't provide any specific details.


Nov 19

Nomic second technical interview
(tl;dr - This interview was my favorite. We spoke for over an hour and a half about tons of topics. We didn't actually write any code, but instead talked about high-level problems and approaches to solve them.)

Problem 1: This problem is a fairly straight forward array traversal problem. My first answer is O(n^2), but the interviewer says I can do better. To me, this means that I am looking for an  O(n * log(n)). An O(n * log(n)) run-time means that I can sort the data (e.g. merge-sort) before working with it. After sorting the data, I can then take advantage of a binary search which runs in O(log(n)) and combine it with a linear traversal O(n), to get a run time of O(2 * n * log(n)) which is equivalent to O(n * log(n)).

After getting that solution, I am told that there is an even better solution which is non-trivial but involves having sorted data to begin with. They say there is no need to get the solution, which is O(n) after sorting, and were about to move on to the next problem. I think for a second, and immediately give the correct answer, which involves starting at opposing ends of the array, and moving pointers inwards towards the middle. (This outward-in approach came up while reviewing programming practice problems).

Problem 2: This problem is a pretty standard scaling problem using basic algorithms. In addition to a run time constraint, there is a memory constraint where I need to optimize around having a very limited amount of memory. I come up with a solution which only requires storing ~2 variables in memory at any given time, however it isn't a stream-able solution. I am prompted to try to get the streaming answer, and after a bit of thinking out loud I am able to get it.

The second part of this problem deals with putting the problem in a real-world context where developer time is a concern. The key to solving this part of the problem is to use a built-in Linux command line tool. I don’t know one off the top of my head that would be efficient, but I mention that what I am after probably exists as a Linux command.

Problem 3: This problem relates to test-driven development, where I basically explain that it is important to write tests for code to create a level of confidence that it runs as expected. I also need to generate some test data for the tests themselves, and mentioned the Linux 'head' and 'tail' commands are great for getting a subset of data from a larger file. He mentions that at the test level I may want to do something instead of using actual files. I disagreed about testing code using a different input type than would be used in production because then the test would give a lower confidence. I do however mention the possibility of using a python generator function to generate test data, which I think is probably one of the best possible answers to the problem.

After these three problems, we talk about a plethora of other topics, such as the differences between dynamically typed languages, such as JavaScript and Python, as compared to statically typed languages such as Java and C++. I also bring up the difference between JavaScript and Python where JavaScript uses weak types and Python uses strong types. We then talk about prototypical inheritance, and compare it to classical inheritance, and I mention JavaScript prototypical function performance.

Finally we move to talking about my previous projects, and I mention some of the projects I worked on at Balderdash.


Nov 20

Nomic emails me back, letting me know that they want to fly me out to Las Vegas and San Francisco to meet the team and do an on-site technical interview. As thanksgiving is approaching, I have to postpone the trip until at least the week after.


Nov 21

A recruiter contacts me via LinkedIn about applying for a position at Apple. I decline, because I realize that the only large company I am willing to work for is probably Google. Through my interviews, I have realized that I really want to make an impact, and I realize that to do that I need to be part of a small team where I can contribute heavily to the companies growth.


Nov 22

Optimizely gets back to me, and we schedule a phone call for Monday, Nov 25.


Nov 25

Google Technical Phone Interview #2
This interview starts off by having me talk about my previous projects, of which I focus on The Pond. After talking about The Pond for a few minutes, we get into the coding problem.

Initially the coding problem that the interviewer proposes is the exact same problem that Nomic gave me just the other day. I let him know, and he decides to use another one, which he says he uses for on-site interviews.

Problem: This problem is about architecting a game, which means coming up with how the game data and state will be represented in code. I opt to take advantage of python's tuples, which allow me easily model the game information. My representation however has a little bit of potential duplication, so we talk about the impact (which turns out to be non-significant) by calculating a large sample case and the expected memory usage.

After setting up the game state, the interviewer then has me implement the basic physics for the game. This was trivial thanks to the way I modeled my data.

Finally, as we are running out of time, the interviewer asks me to talk about how I would code a certain part of the game. My answer is simply that I would use a stack-based depth first search (as opposed to a recursive solution, which doesn't scale unless you have tail call recursion).

Optimizely calls, and we spend the time talking about my background. I mention my passion for code, and ask a few questions about their back-end. Overall the call goes well and they say they will get back to me before thanksgiving.

Google calls back (again same day!), and they want to fly me out to Mountain View for an onsite interview. Ideally I would like to overlap the scheduling with my Nomic interview, but we’ll see how it works out.


Dec 4

I fly out to Las Vegas, and prepare to meet the co-founder of Nomic the following day.

Google has been slow to get back to me about the on-site interview, so it probably won’t happen this trip.


Dec 5

Nomic onsite interview - Las Vegas
I meet the co-founder of Nomic at The Beat in downtown Las Vegas. I have never been to downtown Las Vegas before, but it seems pretty interesting. He shows up, and we chat a bit about Google Glass (which I’m wearing), and a bit about android. Then another Nomic employee shows up, we get introduced, and have a walk out to the shipping container place which Zappos is funding. We walk around, talk to a few random people, at the same time talking about what Nomic is and does (a surprising amount considering what the app is). The subject of dropping out of high school comes up, and we talk about that for a good while. Then we go out to lunch and have an interesting conversation on the lean business development strategy. We spend a long time diving deep into what Nomic is about and how it has evolved over the years, which gives me a better idea of what the future will look like if I were to join. After that we walk around Downtown, and finally he takes me to the airport at 3pm for my flight to San Francisco.

I get in to San Francisco at 5:30pm, and take a taxi to where I’m staying. An engineer from Nomic shows up, and we walk to the office, get the rest of the team, and head out to dinner. Dinner goes great, we talk a lot about technology in a lot of different areas (Information Security, Hardware, coding methodologies). For me, this is an amazing opportunity to get to meet the team and interact with them on a personal level. Everyone is extremely smart and shares many similar interests, and I get a great feeling that working with these people would be an amazing experience. This is especially important because Nomic is a smaller company, (similar to Balderdash), so being able to connect on a personal level is important.


Dec 6


Nomic onsite technical interview
I arrive at 10am, not knowing what to expect. It turns out to be a whole day interview with each of the team members.

In my first interview we start by talking about what I’m looking for in a company. I emphasize that I want to make an impact and really contribute to the growth of the company. I am then asked to reverse-engineer a feature of their application and describe how I would implement it. I take the problem head on, focusing on getting the most performant solution possible, which involved using merge-sort and then a modified binary search (which was bit overkill for the problem).

After that the next engineer comes in, and asks me to implement a mathematical concept, which at first seems similar to the one that Google gave me during my first phone screen, but then turned out to have a completely different implementation. I don’t get the answer right away, but by talking through it out loud and breaking the problem down I am able to simplify the problem to a simple algorithm.

The next question they give me is similar to one I received in my first technical phone interview with Nomic, however it has a small twist which makes it much more difficult and hard to optimize for efficiency. Eventually we get through the problem, but I definitely did not ace it. This is where I definitely regret not spending more time with the python itertools source code.

After that we go out to lunch, and when we get back I get together with the last engineer to write an app in a few hours.
Basically it aggregates a person’s information using the GitHub API and displays it in a useful way. (I will say though that this part of the interview, while being the hardest, felt potentially the least meaningful. It seemed like it was too easy to get stuck and end up with nothing, especially after such an exhausting morning.)

Lastly we talk about MVC and architecture patterns, and I showed them how Sails works and what approaches it takes to solving problems. The other co-founder then comes in (unexpectedly) and I get to chat with him. My leaving high school has been the topic of discussion ad nauseum for the whole trip, but oh well. The co-founder doesn't seem to have any issue with it though.

Things go well, and I feel good about the interview.


Dec 7

I spend the day hanging out in the Mission area in San Francisco. One of the Nomic engineers meets me at noisebridge (not for work), and we talk for a bit. Then the co-founder calls and gives me some good news. Everyone is on board with me coming on, and he would like 3 references from previous work to talk to.


Dec 9

Optimizely emails me back, apologizing for the delay. They let me know that they will get in touch with me soon (which they never do).


Dec 13

Nomic calls and says everything went smoothly with my references, and then officially make me an offer (which is a bit low). I explain that my Google interview has been postponed until two weeks into January, and that if they can improve their offer then I would potentially be willing to accept before then. He then raises his offer, making it an exploding offer which would expire on Tuesday. I tell him I will get back to him as soon as possible. (A note about expiring or 'exploding' offers is that in general they are not set in stone. Just because you take an extra week to decide won't mean that they won’t still hire you).


Dec 16

Google emails, trying to schedule an on-site interview for Jan 16.

I accept Nomic's offer.

I let Google know my decision (they mention trying to get me in earlier if possible, but I was committed to joining Nomic).

Time to start start hunting for an apartment in San Francisco!

Note: For those who have gotten this far, there is one piece of the puzzle that I left out, and that is culture fit. It's difficult for me to put into words how to know when a team has a culture where you'll fit in well, and all I can say is that you can definitely feel it (at least I know I have).


Why did I turn down Google?

I would love to work at Google someday, I really would. However one of my goals is to start my own company, and while Google would be a great experience, I believe that working at a startup in San Francisco will help me to better achieve that goal. Additionally, after getting to know the people behind Nomic, I was sure that working there would be an amazing and worthwhile experience.


Today

As of today I have worked with Nomic for an amazing two weeks. I started on January 13th, and have had a great time getting to know the team better and start digging into the code (I pushed code into production in my first week). Working and living in San Francisco makes every day an adventure; the city is beautiful (and has lots of great food), and the people are friendly and interesting.


This marks a new chapter in my life, and I can’t wait to see what lies in the pages ahead,

- Zoli

Thursday, January 16, 2014

N Queens Problem - Backtracking


The N Queens problem is a simple yet effective way of assessing a developer's ability to use recursion. For that reason, it may show up during technical interviews, so I wrote a quick little example using the chessboard.js library for visualization.

Link: http://queens.zolmeister.com/
Source: https://github.com/Zolmeister/queens

Here is a simple solution in JavaScript:
function solve(n) {
  var ans = []
  solver([])
  return ans
  function solver(current) {
    if (current.length === n)
      ans.push(current)
    else
      for (var i=0; i < n; i++) {
        for (var j=0, l=current.length; j < l; j++) {
          var prev = current[j]
          if (prev === i)
            break
          if (prev-(l-j) === i)
            break
          if (prev+(l-j) === i)
            break
        }
        if (j === l)
          solver(current.concat([i]))
      }
  }
}


My solution uses a technique called backtracking, which is basically depth-first search without an end condition. Note that I do not recommend the pattern above with a 'return' above a function, but it provides an interesting exercise for those who have never seen it before to understand JavaScript hoisting.

The algorithm moves left to right, placing a queen in the next available column. The key to the above code is the inner loop. This is what checks to see if a move is legal, such that no other queen on the board conflicts with the one to be placed. After that, it is simple recursion, with a condition that if the board is full then add it to the solution set.

Monday, January 13, 2014

Promiz Micro - Promises in 228 bytes (min+gzip)


Promiz.js (original post) is a (now) completely Promises/A+ spec compliant library that fits in 590 bytes (min+gzip). In addition, the new Promiz Micro comes in at 228 bytes (min+gzip) and provides an amazingly clean API.
Promiz Micro:
!function(){function a(b,c){function d(a,b,c,d){if("object"==typeof h&&"function"==typeof a)try{var e=0;a.call(h,function(a){e++||(h=a,b())},function(a){e++||(h=a,c())})}catch(f){h=f,c()}else d()}function e(){var a;try{a=h&&h.then}catch(i){return h=i,g=2,e()}d(a,function(){g=1,e()},function(){g=2,e()},function(){try{1==g&&"function"==typeof b?h=b(h):2==g&&"function"==typeof c&&(h=c(h),g=1)}catch(e){return h=e,j()}h==f?(h=TypeError(),j()):d(a,function(){j(3)},j,function(){j(1==g&&3)})})}var f=this,g=0,h=0,i=[];f.promise=f,f.resolve=function(a){return g||(h=a,g=1,setTimeout(e)),this},f.reject=function(a){return g||(h=a,g=2,setTimeout(e)),this},f.then=function(b,c){var d=new a(b,c);return 3==g?d.resolve(h):4==g?d.reject(h):i.push(d),d};var j=function(a){g=a||4,i.map(function(a){3==g&&a.resolve(h)||a.reject(h)})}}"undefined"!=typeof module?module.exports=a:this.Promiz=a}();

Recently I came across Promiscuous, a tiny spec compliant library which was written about in this blog post. I commented that Promiz was nearly as small and provided more features, however as Ruben pointed out, Promiz was not completely spec compliant. In fact, it had some major issues with regard to one particular use case where it failed completely. Here is an example of what would not have worked in the previous version of Promiz.js:
var promise = Promiz.defer()
promise.resolve(42)

promise.then(function(fortyTwo) {
  console.assert(fortyTwo === 42)
  return 43
})

promise.then(function(fortyTwo) {

  // this fails, as it becomes 43
  console.assert(fortyTwo === 42)
})

Thanks to Ruben for pointing out my mistake, I then decided to make Promiz fully compliant. However I had a big issue, which was that the entire model of Promiz was based on stack based execution which would have been a nightmare to alter to be able to support the above feature. So instead I re-wrote the whole thing.

Basic Implementation

In the process or re-writing the library, I started out by creating a minimal spec compliant library which would pass all of the promise spec tests. This is what became Promiz Micro, and I'll go over some of the concepts behind it's implementation.

Promise State

If you notice the above code (and read the spec), it specifies that once a promise has been resolved or rejected, it's state must not change. This meant that I decided to chain objects together, similar to a Tree/Linked List, by tracking pointers to each promise in the chain. Because of this, we need variables to track state:
var self = this

// .promise is required by the testing library, but not spec
self.promise = self

// once set, state is immutable
self.state = 'pending'
self.val = null

// success and error functions
self.fn = fn || null
self.er = er || null

// array of pointers to chained promises
self.next = [];

Resolve and Then

The implementation of the Resolve and Then functions is similar to the original implementation, except with Then it returns a new promise instead of itself:
self.resolve = function (v) {
  if (self.state === 'pending') {
    self.val = v
    self.state = 'resolving'
    setImmediate(function () {
      self.fire()
    })
  }
  return this
}

self.then = function (fn, er) {
  var self = this
  var p = new promise(fn, er)
  
  if (self.state === 'resolved') {
    p.resolve(self.val)
  } else if (self.state === 'rejected') {
    p.reject(self.val)
  } else {
    self.next.push(p)
  }
  return p
}

2.3.3.1 - Let then be x.then

One of the more frustrating parts of the spec is the requirement that the x.then function on a thenable (promise) is only accessed once. This means that when we check to see if an object has '.then', we have to save that value to a variable if we want to call it later. Not only that, when we try accessing that value, it may throw an exception (2.3.3.3.4).
self.fire = function () {
  var self = this
  // check if it's a thenable
  var ref;
  try {
    ref = self.val && self.val.then
  } catch (e) {
    self.val = e
    self.state = 'rejecting'
    return self.fire()
  }
  ...

Resolve input promises, then apply functions, then resolve output promises

Part of the way I implemented the .resolve function meant that an input value (the current self.val) could potentially be a promise (this is part of the spec), so we have to resolve that promise before we do anything else. I created a helper function for this step, because we will want to re-use this code again for the last step of resolving the output value. We also have to protect the functions passed in because they are 'abused' in the testing code (not part of spec).
// ref : reference to 'then' function
// cb, ec, cn : successCallback, failureCallback, notThennableCallback
self.thennable = function (ref, cb, ec, cn, val) {
  val = val || self.val
  if (typeof val === 'object' && typeof ref === 'function') {
    try {
      // cnt protects against abuse calls from spec checker
      var cnt = 0
      ref.call(val, function(v) {
        if (cnt++ !== 0) return
        cb(v)
      }, function (v) {
        if (cnt++ !== 0) return
        ec(v)
      })
    } catch (e) {
      ec(e)
    }
  } else {
    cn(val)
  }
}

Apply functions

This step is fairly straight forward. We need to apply the given functions to our current value, and watch out for errors.
if (self.state === 'resolving' && typeof self.fn === 'function') {
  try {
    self.val = self.fn.call(undefined, self.val)
  } catch (e) {
    self.val = e
    return self.finish('rejected')
  }
}

if (self.state === 'rejecting' && typeof self.er === 'function') {
  try {
    self.val = self.er.call(undefined, self.val)
    self.state = 'resolving'
  } catch (e) {
    self.val = e
    return self.finish('rejected')
  }
}

2.3.1 - TypeError

Part of the spec specifies that we should avoid direct circular loops, where we return our own promise as a return value from a function. If someone tries to do this, we need to throw a TypeError exception:
if (self.val === self) {
  self.val = TypeError()
  return self.finish('rejected')
}

Finish

Finally, we finish up our call by finalizing our state and calling the next promise in the chain:
self.finish = function (type) {
  self.state = type || 'rejected'
  self.next.map(function (p) {
    self.state == 'resolved' && p.resolve(self.val) || p.reject(self.val)
  })
}

Performance

This re-write, being spec compliant, no longer resolves synchronously. This means it takes a huge performance hit, but not more that other Promises/A+ compliant libraries already have to deal with. That being said, if you're after a fast (but heavy) Promises/A+ implementation, I recommend checking out bluebird.

Tuesday, January 7, 2014

Zoggle - Rewritten Using AngularJS


Zoggle, the ultimate word game!
Play Zoggle (source) FREE on:





A Long Time Ago...

Zoggle was my very first app on Google Play, and also happened to be the topic of my first blog post over a year ago: http://www.zolmeister.com/2012/04/zoggle.html. I finally got around to giving it a proper upgrade by rebuilding from the ground up. Zoggle is now better than ever, with a new theme, less bugs, and an illustriously impeccable code-base.

Round 2

Originally Zoggle was built using many javascript and css hacks (not maintainable) and depended zero external libraries (not even jQuery). As a result, it quickly became messy spaghetti code, making even simple changes take hours to implement and test.

Now, after 6 days and 74 commits (and many hours of lost sleep), I finished re-writing Zoggle using AngularJS, jQuery, Lo-Dash, and Grunt to create a highly maintainable and scalable application. So, without further ado, let's go over some of the key aspects of the re-write.



Boggle Library

While searching NPM for a boggle library (before writing my own), I came across Boggle which leverages tries (mentioned in my original blog post) for optimal performance. However the library had some issues (no 'Qu' single tile support), and a poor dictionary list. I contacted the developer bahmutov, got the code moved to GitHub, and added the features. (He was also awesome enough to let me push to npm and the core repo, so you may want to check out his blog).

AngularJS

What can I say, except that AngularJS is absolutely amazing! It has a few bugs (you've been warned), but it is incredible to work with and makes webdev even more fun and exciting than before. If you're new to Angular, I recommend 25 days of AngularJS, and checking out the Angular-Seed project. I won't be writing a tutorial, but some things to watch out for.

AngularJS Gotchas

  • Data-binding on primitives (string, num, bool) doesn't always work properly. Update: updating primitives from views doesn't work as expected (more info).
    • Either nest data inside an object {} or call $scope.$apply()
  • Directives can be called before the DOM is ready
    • If you have a directive which depends on an ng-repeat to be completed, you need to add custom events. I used $rootScope.$emit and $rootScope.$on
    • Sometimes if you need the DOM to load you can run:
      • $timeout(function() { $timeout(theDomisReadyFn, 0) }, 0) 
      • or alternatively use $scope.$watch(function() {  })

Boggle Board

I could have rendered the board using either the DOM or Canvas (or WebGL) but I opted for using the DOM. One issue I ran across when writing the original Zoggle was that touch-move events do not cross over elements. This meat that I had to use the document.elementFromPoint() function in the original Zoggle. Unfortunately, it does not perform well on mobile, so this time around I decided to overlay a div over the whole board and calculate the position of the mouse over the elements manually. Unlike the non-performant code in the stackoverflow answer, I cached the position of the elements so that detection would be much faster.
var positions = [];

function cachePos() {
  var targets = $el.parent().find('.' + attrs.targets)
  positions = []
  targets.each(function (i, $el) {
    $el = $($el)
    positions.push({
      x: $el.offset().left,
      y: $el.offset().top,
      w: $el.outerWidth(),
      h: $el.outerHeight()
    })
  })
}

function collide(x1, y1, w1, h1, x2, y2, w2, h2) {
  if (y1 + h1 < y2 || y1 > y2 + h2 || x1 + w1 < x2 || x1 > x2 + w2) return false;
  return true
}

Letter Interpolation

The boggle board is represented internally as a single list (of 16 characters) because it made rendering easy. The logical thing (which I didn't do) would be to clone the data (as it's static per-game) and represent it as a 2D array. Anyways, one great feature I added to Zoggle was the ability to interpolate between two tiles. This means if you touch one tile (mobile device) and move too quickly and end up 2 tiles over, the algorithm will add the missing tile(s). The algorithm is pretty simple (see bonus for explanation of touching function):

function touching(x1, y1, x2, y2) {
  return _.some(_.filter(_.flatten(_.map(_.range(-1,2),_.compose(_.partial(_.zip,_.range(-1,2)),_.partial(_.compose(_.partial(_.map,_.range(3)),_.partial),_.identity))),!0),_.compose(_.first,_.compact)), function(dir) {
    return x1+dir[0] === x2 && y1+dir[0] === y2
  })
}

while (!touching(x1, y1, x2, y2)) {
  // interpolate position
  if(x1 < x2) x1++
  if(x1 > x2) x1--
  if(y1 < y2) y1++
  if(y1 > y2) y1--
  // add new y1 x1 to selected
}

Real-Time Word Highlighting

Another amazing new feature I added was highlighting as you type. The code for this is a simple depth-first search, except that instead of searching it adds every valid visited node as selected.

function depthFirstSearch(grid, word, pos, index, past) {
  index = index || 0
  past = past || []
  if (!word[index]) return past
  var currentLetter = grid[pos]
  // visiting a previous node or out of bounds
  if (pos < 0 || pos > grid.length ||
    _.contains(past, pos) ||
    word[index] !== currentLetter)
    return []

  // cardinal directions (N,S,E,W) + diagonals (NW, NE, SW, SE) in 1D
  var dirs = _.filter([-5, -4, -3, -1, 1, 3, 4, 5], function (dir) {
    var col = pos % 4
    if (col === 0 && _.contains([-1, 3, -5], dir)
      return false
    if (col === 3 && _.contains([-3, 1, 5], dir)
      return false
    return true
  })

  // recurse
  return _.map(dirs, function (dir) {
    return depthFirstSearch(grid, word, pos + dir, index + 1, past.concat(pos))
  })
}

Minification and Concatenation

For any production application, minifying and concatenating JS source is essential for an optimal user experience. To automate the process, I used Grunt which is an amazingly powerful tool for running a multitude of tasks.
module.exports = function(grunt) {

  grunt.initConfig({
    concat: {
      lib: {
        // order matters because jQuery needs to come before angular
        src: ['public/lib/*.js', 'public/lib/angular/**/*.js'],
        dest: 'public/dist/lib.js'
      },
      zoggle: {
        src: ['public/js/**/*.js'],
        dest: 'public/dist/zoggle.js'
      }
    },
    ngmin: {
      zoggle: {
        src: ['<%= concat.zoggle.dest %>'],
        dest: 'public/dist/zoggle.ngmin.js'
      }
    },
    uglify: {
      lib: {
        options: {
          sourceMap: 'public/dist/lib.js.map',
          sourceMappingURL: 'dist/lib.js.map',
          sourceMapPrefix: 1
        },
        files: {
          'public/dist/lib.min.js': ['<%= concat.lib.dest %>']
        }
      },
      zoggle: {
        options: {
          sourceMap: 'public/dist/zoggle.js.map',
          sourceMappingURL: 'dist/zoggle.js.map',
          sourceMapPrefix: 1
        },
        files: {
          'public/dist/zoggle.min.js': ['<%= ngmin.zoggle.dest %>']
        }
      }
    }
  });

  
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-ngmin');

  grunt.registerTask('default', ['concat', 'ngmin', 'uglify']);

};

And set this in package.json to use grunt on Heroku:
"scripts": {
  "postinstall": "echo postinstall time; ./node_modules/grunt-cli/bin/grunt"
}

A few things to note though. AngularJS injection relies on the variable names in functions. These change during compression, which is why it is necessary to use ngmin to prepare AngularJS code for compression. Additionally, the Angular-Seed project adds unnecessary files under /lib which must be deleted. Minified code (.min.js) (and source maps) should never be committed to source (add them to .gitignore), and should always be generated at compile time (by Grunt). Oh, and AngularJS is picky about what order you need to include jQuery (before Angular), or you may get unexpected errors.

Android application

Originally the mobile application was simply a web-view which pointed to the website, but I wanted to add a message in case there was no internet access. I figured that using Cordova (PhoneGap) would make it simple. I was wrong, but in the future I will be able to do it in mere minutes. Read my previous post on how to take a webapp and turn it into a native android mobile application in 5 minutes. http://www.zolmeister.com/2014/01/how-to-turn-webapp-into-native-android.html

Bonus

Recall the touching function from above:
function touching(x1, y1, x2, y2) {
  return _.some(_.filter(_.flatten(_.map(_.range(-1,2),_.compose(_.partial(_.zip,_.range(-1,2)),_.partial(_.compose(_.partial(_.map,_.range(3)),_.partial),_.identity))),!0),_.compose(_.first,_.compact)), function(dir) {
    return x1+dir[0] === x2 && y1+dir[0] === y2
  })
}

For fun, I decided to write a functional-only (using only LoDash functions) generator for the 8 cardinal directions (2D) - N, S, E, W, NW, NE, SW, SE. That list ([[0,1],[0,-1],[1,0],[-1,0],[-1,1]...]) is what populates 'dir' in the function call. Let's see how I composed it:
// Here is the original
var dirs = _.filter(_.flatten(_.map(_.range(-1,2),_.compose(_.partial(_.zip,_.range(-1,2)),_.partial(_.compose(_.partial(_.map,_.range(3)),_.partial),_.identity))),!0),_.compose(_.first,_.compact))

// Now let's decompose it

// [-1, 0, 1]
var oneDimention = _.range(-1,2)

// this first one is probably the most difficult to understand
// n -> [n, n, n]
var repeat3 = _.partial(_.compose(_.partial(_.map, oneDimention),_.partial),_.identity)

// n -> [[n, -1], [n, 0], [n, 1]]
var addToEachDimention = _.compose(_.partial(_.zip, oneDimention), repeat3)

// create a 3d matrix by joining 3x1 * 1x3 matrix
// [ [[-1, -1], [-1, 0], [-1, 1]], [[0, -1], [0, 0], [0, 1]], [[1, -1], [1, 0], [1, 1]] ]
var created3D = _.map(oneDimention, addToEachDimention)

// flatten
// [ [-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 0], [0, 1], [1, -1], [1, 0], [1, 1] ]
var created2D = _.flatten(created3D, !0)

// removes [0, 0] from list
//[ [-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1] ]
var removedCenter = _.filter(created2D, _.compose(_.first,_.compact))

Saturday, January 4, 2014

How to turn a WebApp into a native Android application in 5 minutes









While re-writing my game Zoggle (Blog post coming soon) I decided to use Cordova (PhoneGap) to create a new mobile application for the game.
So without further ado, here is how to turn a website into a native mobile Android application in less than 5 minutes.




Step 0 - Prerequisite: Installing Cordova CLI, and the Android SDK tools

To install the Cordova CLI, you first need to install Node.js version 0.10+.
It is imperative that the version is 0.10+ or you will be unhappy.
http://nodejs.org/

Next run this command to install the Cordova CLI
sudo npm install -g cordova

Alright, now let's install the Android SDK tools
http://developer.android.com/sdk/installing/index.html

Step 1 - Hello World application

# go into your project
cd myapp
# create a mobileapp folder for your app
cordova create mobileapp com.whatever.appname AppName
cd mobileapp
# compile the app
cordova build
# now, plug in your testing device, and let's run our test app on it
cordova run android
# if you don't have a device handy you can use an emulator (much slower)
## cordova emulate android

# install plugins for alerts and network information
# used to alert the user if they are not connected to the internet
cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-dialogs.git
cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-network-information.git

Step 2 - Portrait mode only

Now let's edit our android manifest to force the app to stay in portrait mode, edit:
platforms/android/AndroidManifest.xml

and add this config to it:
android:screenOrientation="portrait"

Step 3 - Content

Finally we get to adding our website. Edit your index.html to look similar to mine:
www/index.html

<!doctype html>
<html lang="en">
<head>

  <title>Zoggle</title>
  <script type="text/javascript" charset="utf-8" src="cordova.js"></script>
  <script>
  document.addEventListener("deviceready", onDeviceReady, false);
  function onDeviceReady() {
    //navigator.splashscreen.hide();
    if (navigator.network.connection.type == Connection.NONE) {
      networkError()
    } else {
      loadApp()
    }
  }

  function loadApp() {
    window.location="http://zoggle.zolmeister.com";
  }

  function networkError() {
    navigator.notification.alert('Zoggle requires an internet connection')
    var $net = document.createElement('div')
    $net.innerHTML = 'Zoggle requires an internet connection'
    document.body.appendChild($net)
  }
  </script>

  <style>
  body {
    padding: 15px;
    background: #23252e;
    color: #01ced3;
    text-align: center;
  }
  div {
    font-size: 20px;
  }
  </style>
</head>
<body>
</body>
</html>

Done! (well, almost)
Go ahead and test your app using the run command:
cordova run android

Step 3 - Icons

Lastly we need to add icons for our application.
You will find all icons here:
platforms/android/res
Just replace them with your icon (of the correct size).
And that's it. Now lets look into compiling for release on the app store.

Step 4 - Publishing!

First, we need to remove debug mode (and in my case update the app version).
Open up the Android Manifest
platforms/android/AndroidManifest.xml
and change the line from
android:debuggable="true"
to
android:debuggable="false"

Now we can generate a release version of the APK
cordova build --release

Your APK file should be located here:
platforms/android/bin/MyApp-release-unsigned.apk
To submit it to the app store, we need to sign it (cryptographically). This page details how to do that: http://developer.android.com/tools/publishing/app-signing.html
but the short version is this:
(Note, these commands reference tools which come with the android  SDK)

# generate a keystore
keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000
# sign the apk
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore MyApp-release-unsigned.apk alias_name
# zip-align the apk
zipalign -v 4 MyApp-release-unsigned.apk MyApp.apk

And that's it! You can now upload that APK to Google play and publish your application.

Bonus - Splash Screen

I created a splash screen for Zoggle, but the game loaded so quickly that it became unnecessary. However it was a bit tricky, so I'll go ahead and explain the process.

First install the cordova plugin
cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-splashscreen.git

Then edit your main activity Java file. Mine was here:
platforms/android/src/com/zolmeister/zoggle/Zoggle.java

And add this line:
super.setIntegerProperty("splashscreen", R.drawable.splash);

Then, in the app code above (www/index.html), uncomment the following line:
navigator.splashscreen.hide();

which will hide the splashscreen once the app loads.

Creating a splash screen image

Splash screen images are not regular images, instead they are 9-patch images. This allows them to stretch to meet different screen sizes.
Here is a great video introduction to how they work:
http://www.youtube.com/watch?v=MQRA9nwxU7g
The Android SDK tools come with a tool called draw9patch, which you can read about here:
http://developer.android.com/tools/help/draw9patch.html
It's not the easiest to use, but it works. Simply run it, open your image, and zoom in on an edge of your image. The 1 pixel transparent border on your image will be the guide which lets the device know how to stretch the image.
Here is mine:
The black lines mean the the content there WILL BE STRETCHED (so long to figure this out...). This means if you don't want your logo to be distorted, draw the sections around the outside of your logo.
Lastly, make sure your image ends in '.9.png' to let the device know that it is a 9-patch image. Then simply put it inside the folder next to your icon:
platforms/android/res/drawable/splash.9.png

Done!
Now go check out Zoggle!