Saturday, September 27, 2014

Oh snake

As I am spending more time learning Javascript, the inevitable has occurred: building a Snake game (a part of the Odin Project).

Posting here some fruits of my labor (css and html are here). The game is fairly basic, though I tried to make it more fun by using little sprites.

$(function(){

    prepareGrid();

    $('#submitter').click(function(){
        initiateGame();
    });
});

function prepareGrid(){
    $(".grid").remove();
    $("#points").text(0);
    for (var i = 0; i < 30; i++){
        for (var g = 0; g < 40; g++){
            createDivs(18, i, g);
        }
    }
}

function createDivs(size,i,j){
    d = document.createElement('div');
    $(d).addClass("grid");
    $divgrid = $(d);
    var h = size.toString() + 'px';
    $divgrid.height(h);
    $divgrid.width(h);
    $divgrid.data("value", { row: i, col: j } );
    $divgrid.attr("col", j );
    $divgrid.attr("row", i );
    $('.wrapper').append($divgrid);
}

function initiateGame(){
    prepareGrid();
    snake = new Snake(20,20);
    $("div").find("[row="+snake.xx+"]"+"[col="+snake.yy+"]").addClass('snake');
    generateFood();
    runGame();
}

function Food(){
    this.xx = Math.floor(Math.random() * 29);
    this.yy = Math.floor(Math.random() * 39);
}

function generateFood(){
    $('div.food > img').remove();
    $("div").removeClass('food');
    food = new Food();
    $("div").find("[row="+food.xx+"]"+"[col="+food.yy+"]")
            .addClass('food').prepend('<img src="images/star.gif"/>');
}

function runGame(){
    var snake_head = [snake.xx, snake.yy];
    var snake_tail = snake.sbody.pop();
    var new_coords = snake.changeDirection(snake_head);

    snake.eatFood();
    snake.moveToXY(new_coords[0], new_coords[1]);

    if (new_coords[0] < 0 || new_coords[0] > 29 || new_coords[1] < 0 || new_coords[1] > 39 || snake.collision())
    {
        alert("Kaboom! You just crashed into a wall or\n\nYOURSELF. Eek. \n\nGame over!");
        location.reload();
        return;
    }

    repaintSnakeHead(new_coords[0], new_coords[1]);
    repaintSnakeBody(snake_tail);
    setTimeout(function(){ runGame() }, 50);
}

function Snake(xx,yy){
    this.xx = xx;
    this.yy = yy;
    this.sbody = [];
    this.direction = "up";
    this.sbody.push([this.xx, this.yy]);
}

Snake.prototype.changeDirection = function(snake_head){
    switch(this.direction){
        case 'up':
            var new_x = snake_head[0] - 1;
            var new_y = snake_head[1];
            break;
        case 'down':
            var new_x = snake_head[0] + 1;
            var new_y = snake_head[1];
            break;
        case 'right':
            var new_x = snake_head[0];
            var new_y = snake_head[1] + 1;
            break;
        case 'left':
            var new_x = snake_head[0];
            var new_y = snake_head[1] - 1;
            break;
    }
    return [new_x, new_y];
}

Snake.prototype.collision = function () {
    for(var i = 1; i < this.sbody.length; i++){
        if (this.xx === this.sbody[i][0] && this.yy === this.sbody[i][1]){
            return true;
        }
    }
    return false;
};

Snake.prototype.eatFood = function(){
    if (food.xx === this.xx && food.yy === this.yy){
        this.sbody.unshift([food.xx, food.yy]);
        $("#points").text(Number($("#points").text())+1);
        generateFood();
    }
}

Snake.prototype.moveToXY = function(xx,yy){
    this.xx = xx;
    this.yy = yy;
    this.sbody.unshift([this.xx,this.yy]);
}

function repaintSnakeHead(new_x, new_y){
    switch(snake.direction){
        case 'up':
            $("div").find("[row="+new_x+"]"+"[col="+new_y+"]").addClass('snake head_up');
            break;
        case 'down':
            $("div").find("[row="+new_x+"]"+"[col="+new_y+"]").addClass('snake head_down');
            break;
        case 'right':
            $("div").find("[row="+new_x+"]"+"[col="+new_y+"]").addClass('snake head_right');
            break;
        case 'left':
            $("div").find("[row="+new_x+"]"+"[col="+new_y+"]").addClass('snake head_left');
            break;
    }
}

function repaintSnakeBody(snake_tail){
    $('div.snake').each(function()
    {
        var cols = $(this).data("value").col;
        var rows = $(this).data("value").row;

        if (snake.xx === rows && snake.yy === cols){
            // do nothing
        }else{
            $(this).removeClass('head_up head_left head_right head_down');
            $(this).addClass('body_piece');
        }
    });

    $("div").find("[row="+snake_tail[0]+"]"+"[col="+snake_tail[1]+"]").removeClass('snake body_piece head_up head_left head_right head_down');
}

$(document).bind("keydown", function(key)
{
    switch(parseInt(key.which,10))
    {
            // Left arrow key pressed
        case 37:
            snake.direction = 'left';
            break;
            // Up Arrow Pressed
        case 38:
            snake.direction = 'up';
            break;
            // Right Arrow Pressed
        case 39:
            snake.direction = 'right';
            break;
            // Down Arrow Pressed
        case 40:
            snake.direction = 'down';
            break;
    }

});




Wednesday, September 24, 2014

Symbols and strings

There is a lot of discussion around symbols and strings in Ruby. I put together a little flashcard explaining the difference. So: a symbol sort of looks like a string, except it is not. A symbol is immutable and reusable; unlike strings, it references one object in memory. For this reason, it makes the lookup of symbols faster. However, symbols cannot be manipulated like strings (hence immutability), which makes them less useful in certain situations. Another problem is that symbols cause memory leaks, since they are not released from the memory. However, the good news is since Ruby 2.2 symbols will be garbage collectible. See this article here.

Saturday, September 20, 2014

On coding linguistics

This is an older post by Sarah Mei about the relationship between programming and human languages, and I still love it.

Not to boast, but I've been what they call "natural" when it came to picking up grammar and spelling rules in my native language. In fact, I seemed to have had an innate knowledge of grammar (frankly, it must have come from my love of reading). Learning a foreign language was also easy - I would see similarities in how to apply grammar rules. I liked languages - when I was little, I created my own language inspired by Tolkien's Hobbit. I created the alphabet (the letters looked like ancient hieroglyphs) and the grammar. My friend and I then used this language to write notes to each other, so that no one else could understand them. That was the first code I created.

When I was learning the basics of programming, drawing on linguistics concepts helped me tremendously. There is a structure, and a 'sentence' is formed by following a set of rules. Yes, a programming language has a much more rigid structure than a human language. But in a sense, I think it makes it easier because in simple terms, it is easier to follow one set of rules rather than 55 sets plus 155 exceptions.

Just a note for today. As I said, the article was great.

Wednesday, September 17, 2014

Good reads

A couple of books have been sitting on my bookshelf for a some time, and I can't wait to go through them thoroughly.

The first one that I recently started reading is a well-known Practical Object-Oriented Design in Ruby by Sandi Metz. I am looking forward to improving my understanding of design, and this book comes highly recommended. Even though a lot of design principles were (re)introduced during the edX SaaS course, it was not enough. I fully expect to take as much as possible of Metz's book.

One more book that will have to wait a little is Metaprogramming Ruby by Paolo Perrotta. I can't say much about it yet but it appears to be recommended.

Finally, I've discovered a golden mine of Javascript-related resources here. I have started digging more deeply into Javascript, and the first one on the list is Eloquent Javascript by Marijn Haverbeke.

Lots of good reads, and many more are coming.

Monday, September 15, 2014

Find a unique character

It never hurts to get a little more practice with solving problems in Ruby. A simple problem that I came across is how to find the first unique element in a string.

This can be done using hashes and counting how many times an element appears in the string.

example = "Hhello, Mr. Doglet."

def find_first_unique(example)
    hsh = Hash.new(0)
    example.downcase.split("").each { |i| hsh[i] +=1 }
    hsh.each { |key, value| return key if value == 1 }
end

puts find_first_unique(example) #=> ,


Friday, September 12, 2014

Fixup Git

Looking back at my Git commit history, I realize it looks quite ugly. I frequently make very small commits - like fixing a typo, then fixing another typo (since the first one was not actually a typo), fixing it again etc etc. It really looks ugly.

I am finally learning the power of rebase and squash (fixup) in history management. While I have not applied it to my real projects yet (I've only experimented on a test project), I am really looking forward to using it in the future.

Basically, the rebase command allows you consolidate however many commits you want into one. To do that, one needs to call
git rebase -i HEAD~N, where N is the number of commits to consolidate, counting from the last one. For example, 4.

You then see a screen, which lists the commits to be rebased, for example:

pick aaaaa 'commit message1'
pick bbbb 'commit message2'
pick ccccc 'commit message3'
pick dddd 'commit message4'


There are also several options: pick, edit, squash and fixup. I tried squash and fixup - they are both the same, except that squash shows the commit logs and fixup doesn't. So I guess if it is necessary to hide some particularly bad commit logs, fixup is the choice.

Assuming we want to consolidate three commits with the first one, the command console will look like that:

pick aaaaa 'commit message1'
fixup bbbb 'commit message2'
fixup ccccc 'commit message3'
fixup dddd 'commit message4'


Once the changes have been applied, the project will only show one commit: that is, commit message1. And now the history looks much cleaner.

Now, I still need to learn how to fix any issues when the rebase command was applied incorrectly. So far, Git has been winning over in these situations (though not for long, I bet).
 
 

Wednesday, September 10, 2014

GeoRails

Yesterday I attended my first class at Railsschool. The topic of the discussion was how to create geospatial applications with Rails. As I've worked at lot with traditional GIS in the past (mainly ArcGIS), I was definitely interested in learning how to apply my geographic skills in the realm of web geography.

We covered the usage of the activerecord-postgis-adapter and rgeo gems. The rgeo gem relies of PostGIS extension for PostgreSQL, which is the recommended choice for working with spatial data.

I am not sure how many hiccups there would be along the way when working with complex datasets and applications (I am sure more than I can imagine), but creating a very simple tutorial-like application was fairly straightforward. The author of the gem wrote a series of blog posts back in 2011, which is still a great reference.

Some pain usually comes with installing PostGIS itself. However, I used a Postgres.app for OSX, which comes bundled with PostGIS and is very easy to install.

One thing to remember is that PostGIS extension needs to be enabled for each of the databases you intend to use. It is possible to do it either manually, that is connecting to the database via psql --dbname=yourdatabasename and running the following commands (taken from the website):

-- Enable PostGIS (includes raster)
CREATE EXTENSION postgis;
-- Enable Topology
CREATE EXTENSION postgis_topology;
-- fuzzy matching needed for Tiger
CREATE EXTENSION fuzzystrmatch;
-- Enable US Tiger Geocoder
CREATE EXTENSION postgis_tiger_geocoder;

However, in Rails environment it is easier to run the following command: rake db:gis:setup, which enables the PostGIS extension. As already mentioned, the blog referenced above as well as the  github pages for activerecord-postgis-adapter and rgeo gems provide a lot of instructions.
 

Monday, September 8, 2014

GoogleV3 geocoder

This weekend I was playing with GoogleV3 geocoder and Python, as I needed some geocoded data for one of the projects.

The script itself is essentially copy and paste of the example code. However, I added a timeout call to the function in order to prevent any crashes if the server is not responsive. Additionally, I added an if-else statement in order to check whether the geocoder returned a valid value before writing it to file (again, in order to prevent any crashes).

It was certainly pleasing to see that so hundreds of addresses can be geocoded to lat/long within seconds. The only limitation was that a public API allows only about 1,000 or so calls per 24 hours (presumably for a given IP address).

I also wonder how accurate the geocoder is - it would be nice to plot all the data on the map and check how well it corresponds to what's on the ground.

import os, sys,re, time, csv
from geopy.geocoders import GoogleV3

geolocator = GoogleV3()
resultFile = "output.txt"

fileWrite = open(resultFile,'w')
fileRead = open("input.csv")
fileRead.readline() # read the first line

for line in fileRead:
    theaddress = splitline[3] # array index of address in my sample data
    theaddress += (", San Francisco, CA")
    result = geolocator.geocode(theaddress, timeout=20)
    if result:
        address,(latitude, longitude) = result
    else:
        print("Error: geocode failed on input %s"%(theaddress))
    fileWrite.write(address,(latitude, longitude) + "\n")

fileRead.close()
fileWrite.close()
print("done")

Friday, September 5, 2014

Word cloud

D3 library provides a lot fun ways to visualize one's data. I came across a great word-cloud library by Jason Davies, and I wanted to implement this visualization in my Rails app.

In order to do that, you first need to add d3-rails gem to your Gemfile, and //=require d3 to application.js (I always forget to restart the server at this point, and the app complains it can't find D3). You should then add d3.layout.cloud.js (downloaded from here) to the vendor/assets/javascripts directory. Finally, don't forget to add //= require d3.layout.cloud in application.js as well.

If you copy and paste example code (provided with Davies' library), it should work straight away. However, I wanted to see if I could read a static JSON file with word data instead of using a word array like in the example.

This can be done by creating a file say 'word.json' and placing it in the public directory so Rails has access to it. D3.layout.cloud requires such attributes as text and size, so the JSON file should look like this:

[
  { "text":"word1", "size":1 },
  { "text":"word2", "size":2 },
  { "text":"word3", "size":3 },
   .....
]


The file and its data can then be accessed as follows:

d3.json("/word.json", function(data){

   d3.layout.cloud().size([200,200]).words(data)

   ..... 

});

Additionally, I implemented a function that would redraw the graph on window.resize event. However, getting JSON data like this resulted in a slow and delayed rendering. Not quite sure whether this behavior is to be expected or whether something should have been done differently. Until a better solution, I am placing my words in an array directly in the function similar to the example.

Wednesday, September 3, 2014

Ajax and partials

As I keep adding more jQuery options to Rails my app (renamed Speechy), I discover the need to pass parameters via Ajax requests. In the app, one of the pages has two tabs: a click on one of the tabs shows a list of emotions to be selected, and a click on the second tab shows the images selected accordingly. Data from the first click is supposed to be passed via Ajax click event, and the first tab is supposed to display selected images without reloading the entire page.

This is done as follows:

In show.html.erb I set up the tabs to be called. emotion-tabs renders contents_form partial with checkboxes for emotions. In test-tabs, blank_response partial will be replaced with selected images with the help of jQuery.

<div id="student-tabs">
    <ul>
        <li><a href="#test-tabs" class='test-tabs-button'>Test</a></li>
        <li> <a href ='#emotion-tabs'> Select emotions</a></li>
    </ul>

    <div id="test-tabs">
         <%= render 'blank_response' %>
    </div>

    <div id="emotion-tabs">
        <%= render 'contents_form' %>
    </div>
</div>

_contents_form partial with a remote:true link:

<h3> Selecting emotions </h3>
    <% tag_selection Content.select_tags(current_user.id) do |tag| %>
        <li>
            <%= check_box_tag "tag_ids[]", tag.id, false, class: 'submittable' %>
            <%= tag.tagname %>
        </li>
    <% end %>
    <br/>
    <%= link_to "Select", show_selected_path(@student),
          remote: true, class: "btn btn-primary btn-xs submitme" %>

show_selected_path(@student) is a custom route that belongs to a method in a controller (in my case Student controller). The method gets the passed params[:tag_ids]:

def show_selected
    @selected_contents = Content.joins(:tags).
        where(tags: { id: params[:tag_ids]}).belongs_to_user(current_user.id)
    respond_to do |format|
         format.js
    end
end

The submitme button collects data from selected checkboxes as an array of ids and passes it to the show_selected method in controller:

$('.submitme').on('click',function(){
    var tag_array = $('input:checked').valList().split(',');
    var pathname = window.location.pathname;
    $.ajax({
         type: "GET",
         url: pathname+'/show_selected',
         data: { tag_ids: tag_array },
         success: function()
         {
            // alert('Success occurred');
         },
         error: function(){
            alert('Error occurred');
         }
    });
    return false;
 }); 

Finally, the show_selected method takes a responds_to format.js block. Since format.js does not take any arguments, this means that the file to be called has the same name as the method:

show_selected.js.erb:

<% if @selected_contents.count <= 0 %>
    $("#test-tabs").html("<%= escape_javascript(render 'blank_response') %>");
<% else %>
    $("#test-tabs").html("<%= escape_javascript(render 'shared/selected_contents') %>");
<% end %>


Monday, September 1, 2014

Second RailsBridge event

On Saturday I attended another Front-End RailsBridge event. As always, it was a great event organized by great people.

My group first started working on implementing a calendar-like app with jQuery. The idea was to create a simple calendar (just the front end of it), where one would be able to choose a date and set an event for that date.

However, later a smaller group (including myself) has broken off and started working on a Wacky Painter app. Here, the idea was to create a bunch of grids and to change the grid color on a mouseenter event.

While I have done a similar application in the past, it was good to see other approaches. Additionally, I learned how to populate a dropdown menu with jQuery. Assuming you have an array of colors and a div named dropdowns, the function looks like so:

window.populateMenu = function(){
    $(".dropdowns").find('option').remove();
    $(".dropdowns").append('<option value="'+'random'+'">'+'random'+'</option>');
    for(i = 0; i < colors.length; i++){
        $(".dropdowns").append('<option value="'+colors[i]+'">'+colors[i]+'</option>');
    }
}