Open Source

Checking the Open Source work of a developer is a great way to assess technical ability; however, this process can be very time consuming. To save you time I’ve analyzed some of my work here and explained how I approached and solved problems.

If you’re more interested in a business performance and past client projects overview, check the Work Examples analysis.

You can use the keyword navigation below if you’re looking for a particular skill, or just read from top to bottom. The repositories are in chronological order (more recent first).

mcdlr-2016 thumbnail

Intro

This is my personal site where I show different Front–End experiments and explorations. It’s a Middleman app with custom external pipeline using Gulp.

Highlights

I wanted each article to be identified by their own background color, which changes when scrolling over the article. To find colors that have the proper contrast for readability and are perceived as belonging to the same family, you can either choose them by hand or find an algorithmic way to choose them. I thought the latter was a better choice since I already had several articles to get colors for.

After assessing several color spaces I ended up choosing CubeHelix since it takes into account human perception of saturation and brightness excellently.

After choosing the right color for each article and transforming the color to CubeHelix with the right saturation and lightness, all I had left was doing the actual color changing via JS...

[...]

setArticlesPositions() {
  this.articles = Array.from(this.document.querySelectorAll('article'))
    .map(article =>
      ({
        id: article.id,
        offset: article.offsetTop,
        color: article.dataset.color,
      })
    )
    .concat({
      id: 'bung',
      offset: this.document.body.scrollHeight,
      color: '#fff',
    });
}

[...]

changeColor() {
  const viewportYPosition = this.window.scrollY;
  for (const [i, article] of this.articles.entries()) {
    if (this.isScrollingOnTheArticle(i, viewportYPosition)) {
      this.lastArticle = article.id;
      this.document.body.setAttribute('style', `background-color: ${article.color}`);
      break;
    }
  }
}

updateLayoutData() {
  this.setArticlesPositions();
  this.setDelta();
  this.changeColor();
}

[...]

... and letting the transition be handled by the CSS.

[...]

body {
  margin: 0;
  transition: background-color cubic-bezier(0, 0, .2, 1) 777ms;
  background-color: $body-default-bg-color;
}

[...]

Skills used

  • UX
  • Graphic design
  • Logo design
  • Color theory
  • ES6 JavaScript
  • Object Oriented JS
  • Ruby
  • SCSS
  • Gulp
  • Browserify
  • Middleman
  • RWD
  • CSS Animations
  • Content creation
any-aspect-ratio-carousel thumbnail

Intro

This node package is a lightweight responsive carousel to show any number of images with any aspect ratio.

Highlights

I wanted to make a carousel where the image fills the entire container, and still allows the user to view it in its entirety by panning (either up to down or right to left, according to the relationship of aspect ratios). You can better visualize it with an example.

This effect was achieved surprisingly easily, by combining object-fit: cover with animating object-position:

[...]

.aar-carousel--image-panning img {
  object-position: 0% 0%;
  object-fit: cover;
}

[...]

.aar-carousel--pan {
  animation-name: aar-carousel-pan;
  animation-timing-function: cubic-bezier(.45, .05, .55, .95);
  animation-duration: 10000ms;
  animation-fill-mode: both;
}


@keyframes aar-carousel-pan {
  0% {
    object-position: 0% 0%;
  }

  50% {
    object-position: 100% 100%;
  }

  100% {
    object-position: 50% 50%;
  }
}

I also wanted to have touch event support, for which I was going to use Hammer.JS, but for the sake of being lightweight and not importing features I didn’t need, I implemented a SwipeEvent class that lets me add “swiperight” and “swipeleft” event listeners to any DOM element:

class SwipeEvent {
  constructor(swipeElement) {
    this.swipeElement = swipeElement;
    this.touchstartX = 0;
    this.touchmoveX = 0;
    this.isNewSwipe = true;

    this.swipeRightEvent = new window.Event('swiperight');
    this.swipeLeftEvent = new window.Event('swipeleft');
  }

  determineDirection() {
    if (this.isNewSwipe) {
      if (this.touchstartX < this.touchmoveX) {
        this.swipeElement.dispatchEvent(this.swipeRightEvent);
      } else {
        this.swipeElement.dispatchEvent(this.swipeLeftEvent);
      }
      this.isNewSwipe = false;
    }
  }

  touchHandler(event) {
    event.preventDefault();
    if (typeof event.touches !== 'undefined') {
      const touch = event.touches[0];

      switch (event.type) {
        case 'touchstart':
          this.touchstartX = touch.pageX;
          break;
        case 'touchmove':
          this.touchmoveX = touch.pageX;
          this.determineDirection();
          break;
        case 'touchend':
          this.isNewSwipe = true;

        // no default
      }
    }
  }

  init() {
    this.swipeElement.addEventListener('touchstart', event => this.touchHandler(event), false);
    this.swipeElement.addEventListener('touchmove', event => this.touchHandler(event), false);
    this.swipeElement.addEventListener('touchend', event => this.touchHandler(event), false);
  }
}

export default SwipeEvent;

Skills used

  • UX
  • ES6 JavaScript
  • Object Oriented JS
  • SCSS
  • Gulp
  • Browserify
  • RWD
  • CSS Animations
  • Graceful Degradation
  • Proper documentation
youtube-search-by-duration thumbnail

Intro

This is a ruby script that scrapes specific YouTube searches, gets videos of a specific length range and creates a site where you can browse them. Very useful if you have an activity of a specific time (in my case it was indoor biking for 20 minutes).

Highlights

After saving all of your search queries and video length range in the config yaml, you run a script that collects all search results filtered by your criteria. Thanks to Nokogiri and a recursive search function, the task is pretty straightforward:

#!/usr/bin/env ruby

require 'nokogiri'
require 'open-uri'
require 'yaml'
require 'json'

settings = YAML.load_file('search-config.yml')

[...]

def search(query, min_time="16:00", max_time="18:00", pages=40, sp='', results=[])
  query_page = Nokogiri::HTML(open("https://www.youtube.com/results?sp=#{sp}&q=#{query}"), nil, 'utf-8')

  videos = query_page.css('.yt-lockup-video:not([data-ad-impressions])')
  next_page_sp = get_next_page_sp(query_page)

  min_time = min_time.is_a?(String) ? get_seconds(min_time) : min_time
  max_time = max_time.is_a?(String) ? get_seconds(max_time) : max_time

  videos.each do |video|
    video_info = get_video_info(video, query)
    video_duration = get_seconds(video_info[:duration])

    if min_time <= video_duration && video_duration <= max_time
      results << video_info
    end
  end

  if pages > 1 && !next_page_sp.nil?
    print '.'
    search(query, min_time, max_time, pages - 1, next_page_sp, results)
  else
    results
  end
end


final_result = []

settings["keywords"].each do |keyword|
  print "\nsearching #{keyword}"
  result = search(keyword, settings["min_time"], settings["max_time"])
  puts "\n#{result.length} videos found\n"
  final_result += result
end

sorted_final_result = final_result.sort do |a, b|
  b[:views] <=> a[:views]
end

[...]
Excerpt from get-links

Skills used

  • Rapid prototyping
  • Ruby
  • Gulp
  • Nokogiri
  • Proper documentation
spelunky-roi thumbnail

Intro

I was playing a lot of Spelunky and wanted to calculate the return on investment of spending X resources for Y treasure. I wanted a responsive site I could use on my iPad while playing, that could show me the results instantly.

Highlights

The Accountant class deals with managing prices, updating item’s prices according to the level you’re at, calculating ROI and displaying the results on the DOM.

[...]

/**
 * Search this[category] for specific price according to type of item
 *
 * @param {string} category
 * @param {string} id
 * @returns {object|undefined} reference to found object
 * @see https://lodash.com/docs#find
 */
Accountant.prototype.findNumber = function(category, id){
  return _.find(this[category], function(previous){
    return previous ? previous.id === id : undefined;
  }, this);
};

/**
 * Returns function used by invest and profit
 *
 * @param {string} category - Subject domain from where the returned function will search
 * @returns {function} Function that takes data as param
 */
var moneyExchange = function(category){
  return function(data){
    var previousData = this.findNumber(category, data.id);
    if(previousData){
      previousData.number = data.number;
      previousData.amount += 1;
    }
    else{
      this[category].push(
        {
          id: data.id,
          number: data.number,
          amount: 1
        }
      );
    }
    this.render();
  };
};

/**
 * Log investment
 *
 * @function
 * @param {object} data
 * @param {string} data.id consumable id
 * @param {number} data.calculatedPrice consumable price
 */
Accountant.prototype.invest = moneyExchange('investments');

/**
 * Log profit
 *
 * @function
 * @param {object} data
 * @param {string} data.id treasure id
 * @param {number} data.calculatedPrice treasure price
 */
Accountant.prototype.profit = moneyExchange('profits');

/**
 * Get all members of a category ('investments' or 'profits'), multiply them by
 * their price and sum them
 *
 * @param {string} category - 'investments' or 'profits'
 * @returns {number} Price of the multiplication and sum
 */
var sumNumbers = function(category){
  if(this[category].length){
    return this[category].map(function(item){
      return item.number * item.amount;
    }).reduce(function(prev, curr){
      return prev + curr;
    });
  }
  else {
    return 0;
  }
};

[...]
Excerpt from accountant-class.js

For highlighting a selected element, I used filter: drop-shadow instead of box-shadow since drop-shadow follows the contour of non-rectangular elements:


$selected-color: #fff200;

[...]

.item-selected img,
.item-selected span {
  filter: drop-shadow(0 0 0.85em rgba($selected-color, .5)) drop-shadow(0 0 0.25em lighten($selected-color, 5%));
}
Excerpt from modules/_item.scss

And I put the seldom used property border-image to work, since it’s basically a native CSS implementation of 9-Slice Scaling which is very useful if you want to resize graphically complex elements and keep their visual consistency, without any aspect ratio distortions:

$button-text-color: #ffd36e;

.button {
  display: inline-block;
  padding: .3em .5em .4em;
  border-style: solid;
  border-width: 7px 9px 9px;
  border-image: url('/images/textures/wood-button.png') 7 9 9 fill stretch;
  margin: 0 1rem 1rem 0;
  font-size: 1em;
  line-height: 1;
  vertical-align: middle;
  text-align: center;
  border-radius: 0;
  font-family: inherit;
  color: $button-text-color;
  background-color: transparent;
  cursor: pointer;
  appearance: none;
  transition: color 250ms ease-out;
  @include text-border(#150c06);
}
.button:hover,
.button:focus {
  color: lighten($button-text-color, 15);
}

Skills used

  • UX
  • Graphic design
  • ES5 JavaScript
  • Object Oriented JS
  • SCSS
  • Gulp
  • Browserify
  • JSDoc
  • Proper documentation
meetup-flyer-mdl thumbnail

Intro

Simple flyer for meetups, using Material Design Lite.

Highlights

For my talk about Material Design Lite on the Google Developer Groups DevFest Uruguay, I first explained the rapid prototyping benefits of using the framework and then I live coded this meetup flyer in front of an audience of around 300 people.

[...]

<div class='mdl-layout mdl-js-layout mdl-color--grey-200'>
  <main class='mdl-layout__content'>
    <div class='flyer-paper mdl-shadow--2dp'>
      <div class='mdl-grid mdl-color--primary mdl-color-text--white'>
        <div class='mdl-cell mdl-cell--12-col'>
          <h1 class='mdl-typography--display-2'>GDG Uruguay · Title of meetup</h1>
        </div>
      </div>
      <div class='mdl-grid mdl-color--primary mdl-color-text--white'>
        <div class='mdl-cell mdl-cell--3-col'>
          <i class='material-icons'>event</i>
          <span class='flyer-label'>Aug 19</span>
        </div>
        <div class='mdl-cell mdl-cell--3-col'>
          <i class='material-icons'>schedule</i>
          <span class='flyer-label'>19:30</span>
        </div>
        <div class='mdl-cell mdl-cell--6-col'>
          <i class='material-icons'>place</i>
          <span class='flyer-label'>Tiburcio Gómez 1330 apto 3</span>
        </div>
      </div>

      <div class='mdl-grid mdl-color--white'>
        <div class='mdl-cell mdl-cell--2-col'>
          <img class='flyer-avatar' src='http://placehold.it/77x77' alt='speaker avatar'>
        </div>
        <div class='mdl-cell mdl-cell--10-col flyer-speaker-data'>
          <strong> A talk about the universe and cats </strong>
          <em>Robert Robertson <a href='http://twitter.com/jojoooo'>@jojoooo</a></em>
        </div>
      </div>
      [...]

      <div class='mdl-grid mdl-color--grey-600 mdl-color-text--white flyer-footer'>
        <div class='mdl-cell mdl-cell--col-4'>
          <p>Register at</p>
          <p><a href='http://meetup.com/GDGuruguay/' class='mdl-color-text--white'>meetup.com/GDGuruguay</a></p>
        </div>
        <div class='mdl-cell mdl-cell--col-4'>
          <p>Space sponsor</p>
          <img src='https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png' alt='Google logo'>
        </div>
        <div class='mdl-cell mdl-cell--col-4'>
          <p>Drinks sponsor</p>
          <img src='http://aprend.io/images/aprendio-logo.png' alt='aprend.io logo'>
        </div>
      </div>
    </div>
  </main>
</div>

[...]
Excerpt from app/index.html

[...]

.flyer-paper {
  max-width: 44em;
  margin: 2em auto;
}

.flyer-paper h1 {
  display: block;
  margin: .25em 0 0;
}

.flyer-label {
  vertical-align: calc(.5em);
}

.flyer-avatar {
  border-radius: 50%;
}

.flyer-speaker-data {
  align-self: center;
}
.flyer-speaker-data strong {
  display: block;
}

[...]
Excerpt from styles/main.scss

Skills used

  • Rapid prototyping
  • Material Design Lite
  • Live coding
deminer thumbnail

Intro

Algorithm to solve all the non-ambiguous cases on Minesweeper. Check this example, make an opening and press the letter “j” on your keyboard to solve for all of the deterministically solvable situations.

Highlights

First of all, the implementation of the game itself is courtesy of minesweeperonline.com.

The algorithm gets all discovered squares and for each it counts their undiscovered neighbors. If the number on the square matches the undiscovered neighbors, it flags the neighbors as bombs. After this it does a pass of double clicking squares and it repeats the process with the new result until the pool of available squares is stagnant (no more changes). Then the player can make a decision and press “j” again.

There is also memoization of squares that are not “fertile” anymore, i.e. there is no chance of discovering anything from that square because all neighbors are discovered.

# In the comments $ means jQuery object that represents a set of DOM elements


minefield = $("#game")
openRegex = /open(\d)/


# Takes $ with several squares
# Returns $ with only open squares
#
getOpen = (minefield) ->
  minefield.find('[class*="open"]:not(.open0)').filter(":visible")


# Takes $ with a single square
# Returns $ with all surrounding squares (neighbors)
#
getNeighbors = (square) ->
  xy = square.attr("id").split(",").map (num) -> parseInt(num, 10)
  neighbors = $()
  for x in [xy[0]-1..xy[0]+1] by 1
    for y in [xy[1]-1..xy[1]+1] by 1
      neighbors = neighbors.add minefield.find("[id='#{x},#{y}']").filter(":visible") unless x == xy[0] && y == xy[1]
  return neighbors


# Takes $ with a single square
# Returns integer representing the number of mines in neighbors
#
getMyNum = (square) ->
  parseInt(square.attr('class').match(openRegex)[1], 10)


# Takes $ with several squares
# Returns integer representing amount of those squares that are closed (not yet
# discovered what they hide)
#
countClosed = (squares) ->
  length = 0
  squares.each ->
    if $(this).hasClass('blank') || $(this).hasClass('bombflagged')
      length += 1
  length


# Takes $ with a single square
# Emulates a secondary click therefore flagging the square (has a bomb)
#
flagBomb = (square) ->
  unless square.hasClass('bombflagged')
    square
      .trigger({type: 'mousedown', button: 2})
      .trigger({type: 'mouseup', button: 2})


# Takes $ with a single square
# Emulates a two button click therefore automatically clicking all neighbors
# if amount of flagged bombs is equal to self number
#
research = (square) ->
  square
    .trigger({type: 'mousedown', button: 0})
    .trigger({type: 'mousedown', button: 2})
    .trigger({type: 'mouseup', button: 0})
    .trigger({type: 'mouseup', button: 2})


# Takes $ with a single square
# Counts neighbors and square number and if they equal, neighbors get flagged
#
flagNeighbors = (square) ->
  neighbors = getNeighbors(square)
  closedNeighborsLength = countClosed(neighbors)

  if closedNeighborsLength != 0 && closedNeighborsLength == getMyNum(square)
    neighbors.each ->
      flagBomb($(this))


# Takes $ with a single square
# If there are no more possible moves for this square and its neighbors, the
# square is marked as "infertile" to avoid unnecessary calculations. If the
# square still has open neighbors its marked as "fertile" and further
# calculations are done on the square. Whether it's fertile or not gets
# memoized on the square to avoid even doing this check on squares already
# marked as infertile
#
isFertile = (square) ->
  if !(square.data("fertile") == false)
    if not square.data("fertile")?
      square.data("fertile", false)

    _isFertile = false
    getNeighbors(square).each ->
      if $(this).hasClass("blank")
        _isFertile = true
        square.data("fertile", _isFertile)
    return _isFertile

  else
    false


# Takes $ with a single square
# If the square is "fertile" executes flagNeighbors and research on it
#
swipe = (square) ->
  if isFertile square
    # square.css {"outline":"green 2px solid"}
    flagNeighbors square
    research square


# Pressing j starts the research loop and stops when there are no more changes
# after two ticks
#
$(window).keypress (event) ->
  if event.which == 106 # j
    minefield.css {"outline":"1em solid red"}

    previousOpenMinefieldLength = 0
    previousPreviousOpenMinefieldLength = 0
    openMinefield = getOpen(minefield)

    tick = setInterval ->
      if openMinefield.length != previousPreviousOpenMinefieldLength
          openMinefield.each ->
            swipe $(this)

          previousPreviousOpenMinefieldLength = previousOpenMinefieldLength
          previousOpenMinefieldLength = openMinefield.length
          openMinefield = getOpen(minefield)
      else
        minefield.css {"outline":"1em solid green"}
        clearInterval tick
    , 16


# Binds pressing the smiley to setting fertility to null on all squares
# (it's a new game)
#
setTimeout ->
  console.log $("#face")
  $("#face").on "click", ->
    minefield.find(".square").each ->
      $(this).data("fertile", null)
, 500

Skills used

  • Algorithm design
  • Coffeescript
  • Grunt
aprend.io thumbnail

Intro

In aprend.io you can find Front–End lessons in Spanish for people with no background on the subject. It’s a Middleman app that combines YouTube videos, Disqus threads and GitHub code examples for each of the lessons.

Highlights

I wanted to create a series of Front–End lessons that started from the fundamentals, ramping up progressively. I first started outlining the curricula, defining all of the different areas and analogies (I think analogies are very effective for explaining new concepts). After this, I started recording in the morning, editing in the afternoon, and getting all the materials ready for the next lesson at night.

When I had all of the video content ready, I designed and built the Middleman app that would structure the lessons and add comments & relevant links.

Thanks to this project many people who don’t know English were able to get junior Front–End dev jobs and get their foot in the door. I also know that teachers at ORT University in Uruguay use my materials for their Front–End classes.

- content_for(:subject_title, subject_title)
- content_for(:page_id, page_id)
- content_for(:current_url, current_page.url)
- if page_title != nil
  - content_for(:page_title, page_title)

%article
  %header
    %h1
      = "#{subject_title}"
      - if page_title != nil
        = "▸ #{page_title}"
      - if number
        %small.nice-number= number
  %section#video
    %iframe{ src: "//www.youtube.com/embed/#{video_url}", allowfullscreen: true }
    %small
      1080 y audifonos recomendado
  %section#details
    %p= description

    - if code_url
      %h2 Código
      %ul
        - code_url.each do |code_link|
          %li
            = link_to code_link, code_link

    - if links
      %h2 Links relacionados
      %ul
        - links.each do |link|
          %li
            = link_to link, link, { rel: "external" }

    - if keywords
      %h2 Keywords
      %ul.keywords
        - keywords.each do |keyword|
          %li= keyword

    %ul.pagination
      - if previous_page
        %li.prev
          = link_to "← Anterior", previous_page, { class: 'button', rel: 'prev'}
      - if next_page
        %li.next
          = link_to "Siguiente →", next_page, { class: 'button', rel: 'next'}

  %section#comments
    %h2 Comentarios
    %p Ahora es cuando podes “levantar la mano” y hacer preguntas. Sin miedo!
    #disqus_thread

Skills used

  • UX
  • Graphic design
  • Logo design
  • ES5 JavaScript
  • Ruby
  • SCSS
  • Haml
  • Middleman
  • RWD
  • Content creation
  • Teaching
  • Video Edition
font-viewer thumbnail

Intro

Font Viewer is an Angular app that allows you to view your installed fonts and test & compare specific text.

Highlights

On the web, you can’t count on the user having the fonts that you want to use installed. That’s why we use web fonts. But what if I could actually know what fonts I have installed? Could I just use the exact font-family name as font-face and render it faithfully on my browser? After some experimentation I discovered that I actually could, and this lead to the idea of building a font-viewer webapp.

By using the font-manager NPM package I can get access to a list of fonts installed in the OS, and with this data I built an Angular SPA to quickly browse, check, filter and compare the fonts that I favorited.

By leveraging localStorage I can also make my selections and come back to them later without losing the state of the application.

I think that the UX and visual design of the app is more impressive than the code, and I invite you to check an example online or run it on your machine.

Skills used

  • UX
  • Graphic design
  • ES5 JavaScript
  • Angular
  • Proper documentation
meme thumbnail

Intro

Responsive Meme/Advice Animal generator in Angular that separates the image from the text.

By not embedding the text in the image you can use the same binary with different content, and have the text information be actual text, which is screen readable (accessible), crawlable and modifiable.

Highlights

The content of each meme (image source and text) gets encoded in the URL, giving us the benefit of not needing a backend. And since the image is a completely separate entity from the text, this means that we can have animated advice animals.

For the display of the iconic meme text, I researched on Impact like fonts installed on several devices and OSs to reach a font stack that would evoke the meme feeling across the board. And with copious amount of text-shadow, we can emulate a thick border on the text that, apart from being part of the format, helps with readability.

[...]

#first-line,
#second-line {
  width: 100%;
  padding: .5em;
  position: absolute;
  left: 0;
  margin: 0;
  font-size: 2em;
  line-height: 1.07;
  letter-spacing: 1px;
  text-align: center;
  font-family: Impact,
               HelveticaNeue-CondensedBlack,
               Futura-CondensedExtraBold,
               Nimbus Sans L,
               Garuda,
               FreeSans,
               sans-serif;
  font-weight: bold;
  font-stretch: condensed;
  text-transform: uppercase;
  word-wrap: break-word;
  color: #fff;
  text-shadow:  0     -.050em 0 #000,
               .025em -.050em 0 #000,
               .050em -.050em 0 #000,
               .050em -.025em 0 #000,
               .050em   0     0 #000,
               .050em  .025em 0 #000,
               .050em  .050em 0 #000,
               .025em  .050em 0 #000,
                0      .050em 0 #000,
              -.025em  .050em 0 #000,
              -.050em  .050em 0 #000,
              -.050em  .025em 0 #000,
              -.050em   0     0 #000,
              -.050em -.025em 0 #000,
              -.050em -.050em 0 #000,
              -.025em -.050em 0 #000;
}

[...]
Excerpt from css/stijl.css

Skills used

  • ES5 JavaScript
  • Angular
  • RWD
project-euler-ruby thumbnail

Intro

I started solving Project Euler’s problems in Ruby to get better at algorithm design, practice more Ruby and just have fun thinking.

I also made it a point not to research the problems beforehand in any way, and see what I came up with.

Highlights

Let’s analyze problem 15: I have to count all the possible ways to traverse an N by N grid from top-left to down-right, given that you can only move right or down.

My first solution involved a recursive function that kept branching until finding “row” cases (an N by 1 grid) whose calculation is straightforward:

# Starting in the top left corner of a 2×2 grid, and only being able to move to
# the right and down, there are exactly 6 routes to the bottom right corner.
#
# http://projecteuler.net/project/images/p_015.gif
#
# How many such routes are there through a 20×20 grid?


def reduce_grid(x, y, m)
  if x == 1 || y == 1
    ([x, y].max + 1) * m

  elsif x == y
    reduce_grid(x -1, y, m * 2)

  else
    reduce_grid(x -1, y, m) + reduce_grid(x, y -1, m)
  end
end

result = reduce_grid(20, 20, 1)
puts result

# Slow solution for 015 (up to 20 minutes)
# Check 015_cache.rb for fastest solution

The m parameter stands for “multiplier”, a way to optimize for cases where x == y, since the result of the branching will be the same either going right or down. Notice that whether the path is going right or down is not important for the purposes of finding the amount of paths, that’s why all paths are treated as “vertical” since the result of calculating a 5 by 2 path is the same as a 2 by 5 path.

Even though this solution was short and succinct, it was lacking in the performance department. Many calculations were exactly the same and were repeated unnecessarily. That’s why in my second attempt I memoized the results:

# Starting in the top left corner of a 2×2 grid, and only being able to move to
# the right and down, there are exactly 6 routes to the bottom right corner.
#
# http://projecteuler.net/project/images/p_015.gif
#
# How many such routes are there through a 20×20 grid?


@cache = {}

def reduce_grid(x, y, m)
  cache_index = x > y ? :"#{x}_#{y}_#{m}" : :"#{y}_#{x}_#{m}"

  if @cache[cache_index]
    @cache[cache_index]

  elsif x == 1 || y == 1
    @cache[cache_index] = ([x, y].max + 1) * m

  elsif x == y
    @cache[cache_index] = reduce_grid(x -1, y, m * 2)

  else
    @cache[cache_index] = reduce_grid(x -1, y, m) + reduce_grid(x, y -1, m)
  end
end

result = reduce_grid(20, 20, 1)

puts result

# Fast solution for 015, by caching previous results
# Instant calculation
# This problem was the one that took me the longets to solve so far

The memoization made a calculation that took close to 20 minutes, almost instantaneous. I was informed later that I used a Dynamic Programming approach, unbeknownst to me.

Skills used

  • Algorithm design
  • Ruby
conway-game-of-life thumbnail

Intro

Responsive and web native version (no Java applet) of Conway’s Game of Life.

Highlights

At the time I wrote this, if you wanted to play Conway’s Game of Life you had to download an applet or program, so I decided to do a native implementation. It handles touch events, so it allows you to play GoL on a tablet or mobile phone.

The size of the grid will adapt to the size of your viewport, so the more pixel real estate you have, the bigger is your playing area, all the while retaining a minimum cell size suitable for touch devices. The grid is also connected to itself, meaning that life in disparate sides of the grid interacts with each other, or that a glider can travel indefinitely.

[...]

// calculating next generation
//
var nextGenCalc = function(array){

  var prevGen = array;
  var minefield = twoDArrayGenerate(poolCols, poolRows, false);

  // calculating influence of each cell towards its neighbours
  //
  for(var i = 0; i < prevGen.length; i++){
    for(var j = 0; j < prevGen[i].length; j++){
      if(prevGen[i][j]){
        for(var n = f(i)-1; n <= f(i)+1; n++){
          for(var m = f(j)-1; m <= f(j)+1; m++){
            var na = modulus(n, poolRows);
            var ma = modulus(m, poolCols);
            minefield[na][ma] += 1;
          }
        }
        minefield[i][j] -= 1;
      }
    }
  }

  // http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Rules
  // applying CGF rules, mapping influence to an array that holds the future generation
  //
  for(var k = 0; k < minefield.length; k++){
    for(var l = 0; l < minefield[k].length; l++){
      if(minefield[k][l] <= 1){
        nextGen[k][l] = false;
      }
      if(minefield[k][l] === 2){
        if(prevGen[k][l]){
          nextGen[k][l] = true;
        } else {
          nextGen[k][l] = false;
        }
      }
      if(minefield[k][l] === 3){
        nextGen[k][l] = true;
      }
      if(minefield[k][l] >= 4){
        nextGen[k][l] = false;
      }
    }
  }

  return nextGen;
};

[...]
Excerpt from js/gedrag.js

Skills used

  • Algorithm design
  • ES5 JavaScript
  • RWD
  • Proper documentation