/* No Comments */

About a few hacks and the violin.

Google App Engine

Well ive been spending the last week and a bit with my first AppEngine project.  I had recieved my account on the day it was released (yes a shameless boast there :D)…  Unfortunately due to exams never got around to doing anything with it.

So as a simple project I figured Il write (yet another online) chess game - WizChess.  Note that there is already a Blitz Chess as part of the appengine samples page.  I wanted to write one just to get a feel for the engine and to largely to learn a lot of things myself.  There are a still a few things I have not implemented (eg on a castle, the server updates, but the client does not properly update UI.  en-passante-s are not implemented.  Timed game not hapening yet, chatting between players not yet happening and so on).

It was a very interesting experience.  The documentation was quite nice and full, but still I was not fully satisfied.  For some reason I found django a lot more satisfying as a web-app dev platform.  For the models for the game I had chosen to follow a more django style (relational) rather than harnessing the rich model extensions provided by appengine (mainly the Expando Models).  I had done this as I wasnt sure if I wanted to seal my app purely to the AppEngine platform.  And after finishing the project, I am still not sure that I do.

I had got a whole bunch of puzzle games from here.  Now I had needed a way to update the database with an initial data set.  So my way was to have a url on the webapp (something like /resetdb) that would load the initial board templates (and in the future games that are saved and so on).  However, since this loading would take some time, I had to resort to breaking this up into several parts and calling each of these individually.  eg:  Id nomally have:

class ResetDB(webapp.RequestHandler):
    def get(self):
        for board in my_boards:
            add_board_to_db(board)

Now as the number boards in my data set increased, the time to put them in the data store would naturally increase.  But there seems to a time limit on each request, which would make my request fail.  So I had to resort to writing a client side script that would call ResetDB with parameters (eg, firstboard and lastboard) and then also break em up as more saved games were restored etc.  Absolute Pain!  Its all good for appengine to be smart about things but to impose assumptions severely limits the usefulness of this platform and I hope in the future this is released.

Next thing was the model apis.  The Expando model is awesome, but once again I was not happy about the lockin.  But this was not my main concern.  The querying api is extremely poor.  Django has amazing querying facilities where one can specify pretty much arbitrary query filters (not a and c or a minus x and so on).  AppEngine does not support “not”s in its query.  Aaaaaaaaaaaaaaaaah.   Also I couldnt find an easy way of chaining queries.  Eg, to find a game, where either player0 or player1 (my way of saying white or black) was user, I had to do this:

games_with_user = []
games = models.Game.all()
games_with_user.extend([g for g in games if g['player0'] = current_user])
games_with_user.extend([g for g in games if g['player1'] = current_user])

Yuck!  Same thing in Django happens with:

games_with_user = models.Game.filter(Q(player0 = current_user) or Q(player1 = current_user))

(ok I did that syntax from memory so it may not be right, but it is something similar).

Now the final that REALLY annoyed me was lack of Comet (or any reverse ajax) support.  Essentially I poll the server every 5 seconds to get updates on whether the opponent has played.  Would have been great for the server to notify the client instead of having to keep polling and waste requests (I believe there is a limit of 6.5 million requests a day or is that 650K requests?)

But having said all this, the main (only?) advantage of the platform is the fact that we dont have to worry about scalability.  Unfortunately this is a HUGE advantage.  Normally I could write an app, and host it on my own boxes at home (if i want it to be free), but then il soon be hitting limits with 10 users and Il have to find a hoster and also handle scalability issues myself.  So migrating my own code to appengine at THAT point would be quite a nightmare.  By writing and prototyping it on appengine allows me to fail fast.  Looking at the pricing, app engine would charge about $40 per month per app (with reasonable traffic).  If my app ever got to that stage, I could always port it out of appengine (as long as I dont use Expando and stick to django style object modelling - with which I dont loose any power).

Please let me know if you would like the sources.  I am not sure how to upload files to wordpress here.

Also I would really appreciate it if you could give me feedback on my learning here.  As a python and web-app noob, Id be grateful for pointing any errors in my ways :D

June 1, 2008 Posted by Sri | Google App Engine, Internet, Python | , , | No Comments

Parur Style Techniques

Well I love Parur style violin.  Full-stop.  I would kill to master it.  It is extremely demanding and needs a kind of discipline that would make the marines look like amateurs at Paintball!

I have had the extreme good fortune of being pointed to a few clips on Youtube by Akella Mallikarjuna Sharma.  Here they are:

http://youtube.com/results?search_query=msakella

Hope you benefit from these.  Good luck.

May 25, 2008 Posted by Sri | Classical Music, General | | No Comments

Inbreeding - The Valley Style

So Marc Andreessen joins the Board of Facebook.  (Advance note of jealousy towards Mark Zuckerberg for getting someone of Marc’s stature.  Kudos).

What I find surprising is will there be any input from Marc on FB’s technical strategy?  I am specifically concerned about Marc’s Ning using OpenSocial and FB using its own FB API.  Will we see a merging of the two?  Surely it would be stupid (a quality I possess in abundance) to think FB would abandon FB API in favour of Ning for better tapping into Ning and its countless social networks hosted within.  However one cannot help see the allure in a wrapper to FB API to be OpenSocial compatible?

Or is this issue too puny and insignificant to deserve a mention?

May 7, 2008 Posted by Sri | Entrepreneurship, Internet, Startups | | No Comments

The Greatest Entrepreneurs

The 80s and 90s, saw a huge influx of Indian migrants to Australia (the trend still continues).  Now there is something different about these migrants.  Now what is odd about this class of migrants is most of them were in their mid 30s to mid 40s, with very comfortable lifestyles back home (by which I mean respectable jobs/positions resulting from years of hard work).  Why I think this is odd is that you would not expect people/couples with such low risk lifestyles to risk it all and jump ship to sail to the uncertain shores of a foreign land.

It is pretty easy (or comforting atleast) sitting in your parents’ basement coding or being sponsored to spend summers on startup ideas sponsored by THE man PG himself (an opportunity Id kill for), but leaving all you have with very little promise of great rewards, purely for the sake of your family, is what I consider true entrepreneurship (well children are the ultimate startups anyway).  Costs of failures are very high (hardship for entire family, loosing face, etc).  Rewards are not staggering except that life for one’s family is slightly better.

I would like to thank two such migrants who risked very comfortable jobs for the sake of their two sons.  Myself and my brother.  Mum was a very respected high-school teacher (she was my math teacher - which could explain my interest in maths).  Dad was a successful accountant.  Like others they had left it all for us.  The main reason was reverse-discrimination in the society gripped by the plague of the caste system, existing only to empower already corrupt politicians (redundant adjective). Times were hard for the first two years - time they had spent unemployed, living on government assistance.  But I am glad for those times.  It had instilled in us a passion and respect for hardwork.  Cant complain at all.  No room for becoming spoilt brats.  In fact I wonder if I can ever replicate their own dedication and values on my own kids (one day).  Atleast I know that I dont have to look far in search of role-models!

So thanks mum and dad (hope this combined post suffices for mothers’ and fathers’ days).

April 30, 2008 Posted by Sri | Uncategorized | | No Comments

gDay Mate

Well, no exception. Google dazzles us all with their supreme Mate technology - short for Machine Assisted Temporal Extraction.  Its the beginning of Future searches!!!  Just put in the keywords and search for items that may appear tomorrow!!

I was a bit skeptical to see how it actually works but man its amazing!  Looks like I wont have to be working ever again!!

Here’s the link!!

http://www.google.com.au/intl/en/gday/index.html

March 31, 2008 Posted by Sri | Internet | | No Comments

Android Hack - Parcellable in eclipse

Long story short - I have been playing around with android.

wow. Amazing platform. Ok so the sdk is not all that well documented but it is getting better by the day. Ofcourse due to its young age, it is nowhere near the flex sdk in terms of the ease of use, documentation and ide, but hey a great start. And there are tons of samples out there ofcourse.

Anyhoo today is about one of the many problems and a fix I stumbled across - Including Parcelables in eclipse projects.

Background:

It is very easy to create remote services in android. A remote service runs in the corner handling requests by activities (or other processes in general) that need to use them (essentially server like processes). By following the tutorial above, you will find that “aidl” files come in 2 flavours.

1. Include the interface definition of an interface.

2. Contain a single statement “parcelable xxxx”, indicating that class xxxx can be marshalled and unmarshalled (into Parcel objects) so instances of xxxx can be communicated between processes.

With step 1, an aidl file YYYYY.aidl automatically gets built into YYYYY.java which contains a lot of stub and other helper functions to take care marshalling and remote method invocation and yada yada yada. This seems to be an automatic thing with eclipse.

With step 2, the .java already exists, and hence the need to use the “parcelable” keyword to indicate that “there exists a corresponding XXXX.java that is already parcelable so do NOT create a .java file again”, but eclipse does not understand this.

Worse still, it is intuitive to place the aidl files along with all your other sources. The fix involves NOT doing this.

Your project will have usually have the following structure:

<project>

—- .classpath

—- .project

—- .settings/

—- bin/

—- src/

——– package1

————file1.java —> This is parcellable

————file2.java

——– package2

————interface1.java

————class2.java —-> This is parcellable

and so on

the key is to add another folder at the same level and structure as the “src” folder(I called mine aidl).

So the new structure looks like this:

<project>

—- .classpath

—- .project

—- .settings/

—- bin/

—- src/

——– package1

————file1.java —> This is parcellable
————file2.java

——– package2

————interface1.java

————class2.java —-> This is parcellable

—- aidl/

——– package1

————file1.aidl —> This is parcellable
——– package2

———— class1.aidl —-> a sample interface definition with no corresponding .java files ANYWHERE

————class2.aidl —-> This is parcellable
the aidl files that exist in your src folder, have their corresponding aidl files in the aidl folder in the same package structure.

Now good news is since the aidl folder exists in your <project> folder, it will be picked up by eclipse (on a refresh). However there is still one minor issue. aidl file class1.aidl will not be automatically compiled to class1.java. To do this, we need to add the entire aidl folder as a “source” folder. Simple. do this:

add an entry like so in the .classpath file!! (ah ha)

<classpathentry kind=”src” path=”aidl”/>

This essentially says, “i have another folder called ‘aidl‘ that is a source folder”.. voila, being a source folder it will get included in auto-builds.

Beauty of this is that for class2.aidl and file1.aidl (in your aidl folder), no .java file will be created as they already exist, however class1.java will be built.

Saves a huge pain in the rear side (i couldnt spell “posterior”)!

good luck. please let me know if ive missed something.

PS <08/03/2008>

Please see Comment # 2 for further clarifications (and speculations too).  Thanks for trickybit (Comment #1) for inspring that!

March 7, 2008 Posted by Sri | Android | | 7 Comments

Google Captcha got my tongue!!

AAAAAAAAAAAAAAAAAAAAAARRRRRRRRGGHHHHH

Hate captcha in the google maps.  I can understand how it needs to protect itself against spam, but this is ludicrous.  I was playing with the google maps api writing a simple google maps mashup.  This meant editing my js file and reloading the pages a few times.  Somehow google flagged this as a sequence of spam requests.  So it shows me the dreaded captcha page.  I enter the damn captcha.  You would think a company like google would be smart enough to let you get on with your work.  Oh no, it brings up the captcha page again.  May be I got the captcha wrong originally so I tried again, no luck.  Now onto my 100th try in a row and still no luck.  Google still thinks my requests (from a FF3 browser) are all spam requests.

Is this the sign that gOoGle is finally going the MS way?

February 11, 2008 Posted by Sri | Uncategorized | | No Comments

gprobe - Json Parser

Here is the code for the json parser I had described in an earlier post.  It is still to be fully tested (works for our purpose right now).

Essentially data is fed into the parser continuosly (as there are no blocked reads in flex from the socket).

Usage is:


var parser:GPJsonParser = new GPJsonParser()
parser.processBytes(byteArray)
var nextObj:* = parser.next();
while (nextObj != null)
{
// do something with nextObj
nextObj = parser.next();
}

nextObj will only be non-null if there were no syntax errors and if enough bytes were consumed to produced a complete json fragment.

package gprobe.utils
{

import flash.utils.ByteArray;/*
* On-demand parser for parsing json from a stream
*/
public class GPJsonParser
{
private static var STATE_EOF:int                  = -1;
private static var STATE_START:int                = 0;
private static var STATE_READING_IDENTIFIER:int    = 1;
private static var STATE_READING_NUMBER:int       = 2;
private static var STATE_READING_STRING:int       = 3;

private static var OBJ_NULL:int         = 0;
private static var OBJ_BOOL:int         = 1;
private static var OBJ_NUMBER:int       = 2;
private static var OBJ_STRING:int       = 3;
private static var OBJ_LIST:int         = 4;
private static var OBJ_STRUCT:int       = 5;
private static var OBJ_COMA:int         = 6;
private static var OBJ_COLON:int        = 7;
private static var OBJ_OPEN_BRACE:int   = 8;
private static var OBJ_OPEN_SQUARE:int  = 9;
private static var OBJ_CLOSE_BRACE:int  = 10;
private static var OBJ_CLOSE_SQUARE:int = 11;

/*
* Current string delimiter only valid if we are
* currently reading a string
*/
private var currDelim:int;

/*
* Current string - only if we are reading a string
*/
private var currString:String;

/*
* State in reading current string
*/
private var currStringState:int;

/*
* The byte buffer
*/
private var byteBuffers: Array = new Array();

/*
*
*/
private var currBuffer: ByteArray;

/*
* Number of bytes read since start of object
*/
private var bytesRead: int;

/*
* Current parser state
*/
private var currState: int;

/*
* How many unmatched open square brackets have we encountered?
*/
private var squareCount: int = 0;

/*
* How many unmatched open braces have we encountered?
*/
private var braceCount: int = 0;

/*
* Current parent object
*/
private var objStack: Array = null;

private static const CHAR_BSLASH:int = ‘\/’.charCodeAt(0);
private static const CHAR_DQUOTE:int = ‘”‘.charCodeAt(0);
private static const CHAR_SQUOTE:int = ‘\”.charCodeAt(0);
private static const CHAR_PLUS:int = ‘+’.charCodeAt(0);
private static const CHAR_DOT:int = ‘.’.charCodeAt(0);
private static const CHAR_a:int = ‘a’.charCodeAt(0);
private static const CHAR_A:int = ‘A’.charCodeAt(0);
private static const CHAR_e:int = ‘e’.charCodeAt(0);
private static const CHAR_E:int = ‘E’.charCodeAt(0);
private static const CHAR_t:int = ‘t’.charCodeAt(0);
private static const CHAR_v:int = ‘v’.charCodeAt(0);
private static const CHAR_b:int = ‘b’.charCodeAt(0);
private static const CHAR_f:int = ‘f’.charCodeAt(0);
private static const CHAR_r:int = ‘r’.charCodeAt(0);
private static const CHAR_n:int = ‘n’.charCodeAt(0);
private static const CHAR_z:int = ‘z’.charCodeAt(0);
private static const CHAR_Z:int = ‘Z’.charCodeAt(0);
private static const CHAR_0:int = ‘0′.charCodeAt(0);
private static const CHAR_9:int = ‘9′.charCodeAt(0);
private static const CHAR_SLASH:int = ‘\\’.charCodeAt(0);
private static const CHAR_SPACE:int = ‘ ‘.charCodeAt(0);
private static const CHAR_MINUS:int = ‘-’.charCodeAt(0);
private static const CHAR_UND:int = ‘_’.charCodeAt(0);
private static const CHAR_TAB:int = ‘\t’.charCodeAt(0);
private static const CHAR_CR:int = ‘\r’.charCodeAt(0);
private static const CHAR_LF:int = ‘\n’.charCodeAt(0);
private static const CHAR_COMA:int = ‘,’.charCodeAt(0);
private static const CHAR_COLON:int = ‘:’.charCodeAt(0);
private static const CHAR_OSQ:int = ‘['.charCodeAt(0);
private static const CHAR_CSQ:int = ']‘.charCodeAt(0);
private static const CHAR_OBRACE:int = ‘{’.charCodeAt(0);
private static const CHAR_CBRACE:int = ‘}’.charCodeAt(0);

/*
* Constructor
*/
public function GPJsonParser()
{
reset();
}

/*
* Resets the parser
*/
public function reset(resetBytes:Boolean = true): void
{
objStack        = [];
currState       = STATE_START;
bytesRead       = 0;
currString      = “”;
currStringState = 0;
squareCount     = 0;
braceCount      = 0;
if (resetBytes)
{
byteBuffers = new Array();
currBuffer  = null;
}
}

/*
* Process more bytes.
*
* Returns nothing.  Call next() to retrieve the next object
* from the stream (if any).
*/
public function processBytes(data:ByteArray): void
{
byteBuffers.push(data);
}

/*
* Return the next object from the stream.
* null if none (this is not necessarily an error, just means we do
* not have enough bytes given to us to extract the next object).
*/
public function next(): *
{
var currCh:*;
var readChar:Boolean = true;

while ( ! (objStack.length == 1 && squareCount == 0 && braceCount == 0))
{
if (currBuffer == null || currBuffer.bytesAvailable == 0)
{
if (byteBuffers.length == 0)
{
return null;
}
currBuffer = byteBuffers.pop();
}
if (readChar)
{
if (currBuffer.bytesAvailable == 0)
return null;
bytesRead++;
currCh = currBuffer.readByte();
}
else
{
readChar = true;
}

if (currState == STATE_READING_STRING)
{
processStringBytes(currCh);
}
else if (currState == STATE_READING_IDENTIFIER)
{
if ((currCh >= CHAR_a && currCh <= CHAR_z)         ||
(currCh >= CHAR_A && currCh <= CHAR_Z)     ||
(currCh >= CHAR_0 && currCh <= CHAR_9)     ||
currCh == CHAR_UND)
{
currString += String.fromCharCode(currCh);
}
else
{
if (currString == “true”)
{
shift(OBJ_BOOL, true);
}
else if (currString == “false”)
{
shift(OBJ_BOOL, false);
}
else if (currString == “null”)
{
shift(OBJ_NULL, null);
}
else
{
shift(OBJ_STRING, currString);
//throw new Error(”Invalid identifier: ” + currString);
}

currState = STATE_START;
readChar = false;
}
}
else if (currState == STATE_READING_NUMBER)
{
if ( ! processNumberBytes(currCh))
{
readChar = false;
}
}
else if (currState == STATE_START)
{
if (currCh == CHAR_SPACE || currCh == CHAR_TAB ||
currCh == CHAR_CR || currCh == CHAR_LF)
{
// ignore white spaces
}
else if ((currCh >= CHAR_a && currCh <= CHAR_z)         ||
(currCh >= CHAR_A && currCh <= CHAR_Z)     ||
currCh == CHAR_UND)
{
currState = STATE_READING_IDENTIFIER;
currString = String.fromCharCode(currCh);
}
else if (currCh == CHAR_MINUS || (currCh >= CHAR_0 && currCh <= CHAR_9))
{
currState = STATE_READING_NUMBER;
currStringState = 0;
processNumberBytes(currCh);
}
else if (currCh == CHAR_SQUOTE || currCh == CHAR_DQUOTE)
{
currDelim = currCh;
currString = “”;
currState = STATE_READING_STRING;
}
else if (currCh == CHAR_COMA)
{
shift(OBJ_COMA);
}
else if (currCh == CHAR_COLON)
{
shift(OBJ_COLON);
}
else if (currCh == CHAR_OSQ)
{
squareCount++;
shift(OBJ_OPEN_SQUARE);
}
else if (currCh == CHAR_OBRACE)
{
braceCount++;
// shift(OBJ_STRUCT, new Object());
shift(OBJ_OPEN_BRACE);
}
else if (currCh == CHAR_CSQ)
{
squareCount–;
shift(OBJ_CLOSE_SQUARE);
}
else if (currCh == CHAR_CBRACE)
{
braceCount–;
shift(OBJ_CLOSE_BRACE);
}
}
}

if (objStack.length == 1)
{
var obj:Object = objStack.pop();
reset(false);
return obj['value'];
}

return null;
}

/*
* Shifts a token onto the stack and immediately reduces it (the
* stack)
*/
private function shift(tok:int, value: * = null): void
{
if (tok == OBJ_CLOSE_SQUARE || tok == OBJ_CLOSE_BRACE)
{
reduce(tok);
}
else
{
objStack.push({’type’: tok, ‘value’: value});
}
}

/*
* Reduces the parse stackby utilising the next token.
* Reduction can happen more than once.
*/
private function reduce(topType:int): void
{
var t1: *;
var t2: *;

if (topType == OBJ_CLOSE_SQUARE)
{
// we are building a list
var newArray: Array = new Array();
while (objStack.length > 0 &&
objStack[objStack.length - 1]['type'] != OBJ_OPEN_SQUARE)
{
t1 = objStack.pop();
if (t1['type'] == OBJ_COMA)
t1 = objStack.pop();
newArray.push(t1['value']);
}
if (objStack.length == 0 || objStack[objStack.length - 1]['type'] != OBJ_OPEN_SQUARE)
{
throw new Error(”Could not find ‘['");
}
objStack.pop();
newArray.reverse();
objStack.push({'type': OBJ_LIST, 'value': newArray});
}
else
{
// we are building a map
var newObj: Object = new Object();
while (objStack.length > 0 &&
objStack[objStack.length - 1]['type'] != OBJ_OPEN_BRACE)
{
t1 = objStack.pop();
if (t1['type'] == OBJ_COMA)
{
t1 = objStack.pop();
}

t2 = objStack.pop();
if (t2['type'] != OBJ_COLON)
{
throw new Error(”‘:’ expected but not found”);
}
t2 = objStack.pop();

newObj[t2['value']] = t1['value'];
}
if (objStack.length == 0 || objStack[objStack.length - 1]['type'] != OBJ_OPEN_BRACE)
{
throw new Error(”Could not find ‘{’”);
}
objStack.pop();
objStack.push({’type’: OBJ_STRUCT, ‘value’: newObj});
}
}

private function processNumberBytes(currCh: *): Boolean
{
var NUM_START:int               = 0;
var NUM_READING_INT:int         = 1;
var NUM_READING_DOT:int         = 2;
var NUM_READING_FRAC:int        = 3;
var NUM_READING_EXP:int         = 4;
var NUM_READING_EXP_DIGITS:int  = 5;

if (currStringState == NUM_START)
{
currString         = “”;
currStringState = NUM_READING_INT;
if (currCh == CHAR_MINUS)
{
currString     = ‘-’;
return true;
}
}

if (currStringState == NUM_READING_INT)
{
if (currCh >= CHAR_0 && currCh <= CHAR_9)
{
currString += String.fromCharCode(currCh);
return true;
}
else
{
currStringState = NUM_READING_DOT;
}
}

if (currStringState == NUM_READING_DOT)
{
if (currCh == CHAR_DOT)
{
currString += ‘.’;
return true;
}
else if (currCh == CHAR_e || currCh == CHAR_E)
{
currString += ‘e’;
currStringState = NUM_READING_EXP;
return true;
}

currStringState = NUM_READING_FRAC;
}

if (currStringState == NUM_READING_FRAC)
{
if (currCh >= CHAR_0 && currCh <= CHAR_9)
{
currString += String.fromCharCode(currCh);
return true;
}
else if (currCh == CHAR_e || currCh == CHAR_E)
{
currString += ‘e’;
currStringState = NUM_READING_EXP;
return true;
}
}

if (currStringState == NUM_READING_EXP)
{
currStringState = NUM_READING_EXP_DIGITS;
if (currCh == CHAR_PLUS)
{
currString += ‘+’;
return true;
}
else if (currCh == CHAR_MINUS)
{
currString += ‘-’;
return true;
}
}

if (currStringState == NUM_READING_EXP_DIGITS)
{
if (currCh >= CHAR_0 && currCh <= CHAR_9)
{
currString += String.fromCharCode(currCh);
return true;
}
}

currState = STATE_START;
shift(OBJ_NUMBER, Number(currString));

// invalid char, must be end of numeric
// value so let the caller take care of the
// rest by not consuming the character
return false;
}
private function processStringBytes(currCh: *): void
{
var STR_NORMAL:int = 0;
var STR_SLASH:int  = 1;

if (currStringState == STR_SLASH)
{
currStringState = STR_NORMAL
if (currCh == CHAR_SLASH)
{
currString += ‘\\’;
}
else if (currCh == CHAR_t)
{
currString += ‘\t’;
}
else if (currCh == CHAR_n)
{
currString += ‘\n’;
}
else if (currCh == CHAR_r)
{
currString += ‘\r’;
}
else if (currCh == CHAR_b)
{
currString += ‘\b’;
}
else if (currCh == CHAR_f)
{
currString += ‘\f’;
}
else if (currCh == CHAR_BSLASH)
{
currString += ‘\/’;
}
else if (currCh == CHAR_DQUOTE)
{
currString += ‘”‘;
}
else if (currCh == CHAR_SQUOTE)
{
currString += ‘\”;
}
else
{
currString += (”\\” + currCh);
}
}
else if (currCh == currDelim)
{
currState = STATE_START;
shift(OBJ_STRING, currString);
}
else
{
currString += String.fromCharCode(currCh);
}
}
}

}
Essentially, since data is read asynchronously, it needs to be “fed” data as it is recieved by calling

February 7, 2008 Posted by Sri | Internet | | No Comments

gprobe - Part 1 - Message Formats

Message transport (between the gprobe, the game server I had written for my work in my spare time and the front end client) was using json or  Action Message Format, AMF.  Reason for the duality, was that for quick testing, I could login into the server with simple telnet and send (textual) commands and recieve data in json format - Simple.  However, I could not find a built in JSON parser for Flex and writing one (again, as I had already written one in C++ for the server side) - didnt just seem too appealing.  Luckily, Flex is capable of serializing and deserializing objects in binary format over a stream - yep - the Action Message Format or AMF.  Adding the AMF serializer to the server was fairly simple (did take few hints on the integer encoding from pyamf - thanks guys).

Now the problem was that I could not find a way of debugging the decoding (on the flex side) of the AMF stream I had generated from my server.  Why do I need debugging you ask?  Well, I was having random errors in the AMF stream (no doubt due to my encoding) while encoding strings.  Due to time-constraints I had decided to switch to JSON.  This meant I had to write a json parser for flex (as I could not find one at the time).  Not too bad as it gave me a lot of insight into what was being sent and so on.  One day when I earn more free time at work I may decided to debug my AMF encoder.

February 4, 2008 Posted by Sri | C++, Internet | | No Comments

Happy 3rd of January

Happy new year and a happy 3rd of January to all.  No, nothing significant about that date except that it happens to be the date of my first blog (hopefully of hundreds, cough cough) this  year.

Been a great vacation.  Drove down to Melbourne to visit the parents.  Boy that was relaxing and tiring at the same time.  Cant believe time just dissapeared before I knew it!

Any hoo a happy 2008 to all.  Hope 2007 was a great year for all of you and hope 2008 is much better!

January 2, 2008 Posted by Sri | Uncategorized | | No Comments

Flex

At work I write games.  No not the cool ones like Xbox or PS3 or Wii, but pokie machines ones.  Enough said.  Anyway, each build of the game takes about 5-10 minutes and each start takes upto 3 minutes.  Needless to say, the bug-fixing and configuration is quite the pain in the posterior.

An obvious realisation was, why not write a server that could listen to commands from “somewhere” and make changes to the game (objects) at run time.  This would mean a build is only necessary on a critical improvent.  And a restart (of the the game) would only be required on a rebuild!  This would be useful for modifying Sprite locations and properties and just about anything which the custom server can get hold off.  Now such a tool did exist.  Unfortunatly it was a custom written http server, which printed out the entire Sprite tree on each request.  Worse still, each request opened a new connection and then closed it.  Did I mention that this server only allowed querying, not modifying or creation of sprites (or other game objects)?

So I embarked on a new probing utility for doing just that.  The server was written in C++ using pthreads for multi-threading.  No big deal.  That is not what I am going to talk about - better people have written and preached about the art of server writing.  Thing that got me tickled was I used this opportunity to learn flash, so I begain writing the gui in flash/flex.  Main reason was that I wanted a web based UI for the client-side.  The experience was amazing.  The API is fantastic and intuitive, the IDE (well painful was having to move from VIM to Flex Builder/Linux on Eclipse due to intellisense).  Conversely without intellisense, the experience is tortuous.

Il talk more about the features in future posts each dealing with a subchallenge in the project.

December 18, 2007 Posted by Sri | C++, Internet | | No Comments

/* No Comment */

I think the new title certainly reflects what I usually have to say about things and how often I say it.  So figured why not.  On a “deeper” level (not much more though),  this was our team name back in uni when we had participated in the ACM programming competitions.  Jees, I think I did everything to avoid actually going to classes.  Good times, those were.  The intense atmosphere in the comps..  ah dont think Id be doing justice describing it.  Those were the days.  And plus, did I mention the free food?

Any body interested in discussing some of the ACM problems?

December 5, 2007 Posted by Sri | Uncategorized | | No Comments

OpenSocial

Google’s announcement of OpenSocial is fairly old news (atleast by blog time-lines anyway).  It was amazing how many companies had been  briefed earlier on, while Facebook had been kept in the dark.  If it was intended as fair platforms for all social network sites out there, why had Google decided to work with MySpace “secretly” over the past year?

And am I the only one finding it odd (or just finding) that it is merely a glorified version of the Google Gadget API?

November 5, 2007 Posted by Sri | Internet | | No Comments

Harry Potter and the Sickly Realisation

Well, I am a huge fan. I owe HP, or JKR rather, for getting me into the reading habbit! Yes it is embarassing, but I am proud of it. By the way there are a couple of spoilers so beware.

Anyway, I had finished the final installment HP and the Deathly Hallows last night at 3 AM. Thanks to the flu and an assignment I couldnt get into it earlier. But it is done. What an absolutely fantastic finale to the series. This is testament to the fact that she deserves every penny of the couple of billion pounds she has amassed thanks to the magical world she has woven. While the ending itself is not as surprising or brilliant as that of the Prisoner of Azkaban or the Goblet of Fire, it still ranks as one of the best books of all times. I am so glad that my theory (well not uniquely mine ofcourse) about Snape’s innocence and Id even say greatness, came true.

Ofcourse the deaths of Dobby was I think unnecessary. I loved the poor guy, and to have him killed especially after all that… well no words… but I think his death is made up by the house-elves’ participation in the final battle. The other deaths I beleived unnecessary were Tonk’s and Fred’s. I mean Moody and Lupin’s departure was inevitable, but Tonks and Fred’s? Surely we could have done without Charlie (no offence), but to deprive George of his significant other, thats just too cruel.

But deaths aside, an absolutely magnificent plot and character development. One thing that was a bit dissapointing was the epilogue. I could have used a bit more on what happened to more of the characters. JKR only had briefly described how Harry, Ron, Hermione and Ginny end up (with a couple of others). Hopefully these will be cleared out in an interview.

The hardest part was however the sickly realisation (which began as I had started the first page), that once I had gone through the 600 pages, that would be the end of HP world for ever. And boy was that painful. Actually it still is. I suppose this is nothing new. All good things must come to an end, but one can still wish otherwise.

By the way who is the individual who discovers magic late in his life (as JKR had pointed out in her interview?)

July 24, 2007 Posted by Sri | All, Harry Potter | | 2 Comments

C++ - Python Bindings

Version 3.0 of omnifs saw the onset of python bindings for the omnifs library (written in C++ using Curl). In this post I am going to talk about what I had learnt in writing python bindings for C++. What this means essentially is that a library written in C++ can now be used from python. This is ideal for situations where the core modules can be written in a more efficient/lower level languages and can be controlled in a higher level scripting language. This, some (including myself) believe would assist in testing and development in general.

I will be referring to libomnifs, part of the omnifs package, in this posting for any examples. I wont be showing the details of libomnifs, and will focus on what additions were required to enable python bindings.

I have used the Boost.python library for creating the python bindings to omnifs. There other ways of doing this, but I wont go into them here. I had chosen Boost.python as it was fairly well documented and easy to follow (plus it looked cool).

Prerequistes

  1. First download and install the boost library. I had used version 1.34.0. Please follow the installation guide for Boost on how to do this.
  2. Then read the bjam tutorial. bjam is the build system created and used by Boost. It is very very powerful and can make life a lot simpler (once you get the hang of it).
  3. Read the boost.python tutorial - This is a must. In fact this highlights everything one would need to create the bindings. I will only document what “extras” I have learnt in the process and will try to keep the repititions to a minimum.
  4. Install python if you do not already have it.

ONCE AGAIN - The boost.python tutorial is fantastic so please have a look at it.

Step 1: First Make a list of Exportable Classes

In my case, one of the classes I had chosen to export was OMNI_Action. OMNI_Action class is superclass of all actions that can be taken agains/on the omnidrive server. For example “ListFolder”, “UploadFile” etc.

The public interface of the OMNI_Action is (protected, static and private members are not shown):

class OMNI_Action
{
public: // Virtual methods and constructors
OMNI_Action(OMNI_Session &p_Session);
virtual ~OMNI_Action();
virtual bool Perform() = 0;

public: // Non virtual methods
int Result();
const char * Message();
OMNI_Session * Session();
};

I have chosen the Action object as the example (rather than the Session object which is required by all Actions) as it is a class that is extended and thus requires more work to create the bindings:

  • every OMNI_Action derived class, needs to only implement the Perform method (to *surprise surprise* perform the method).
  • Each OMNI_Action object also needs a OMNI_Session object - the session in which omnifs is running. T his is another class that is exported but not shown.

Step 2: Include necessary Boost headers

Include the required boost headers as follows:

#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/class.hpp>
#include <boost/python/args.hpp>
#include <boost/python/overloads.hpp>
#include <boost/python/docstring_options.hpp>
#include <boost/python/enum.hpp>
#include <boost/python/pure_virtual.hpp>

#include <boost/python/return_internal_reference.hpp>
#include <boost/python/copy_const_reference.hpp>
#include <boost/python/copy_non_const_reference.hpp>
#include <boost/python/return_value_policy.hpp>

or alternatively include ALL python related headers by include just python.hpp as:

#include <boosts/python.hpp>

Also enable the use of the python namespace:

using namespace boost::python;

Note that this could be avoided if you prefer to explicitly qualify items with the python namespace each time.

Step 3: Create the module

All the generated python bindings happen inside a module definition. This is done as:

BOOST_PYTHON_MODULE(module_name)

{

// Put binding definitions here - ie class exports and method exports

}

This is it. no need for a main function or anything. Think of the BOOST_PYTHON_MODULE bit as the main entry point of the generated shared object.

Step 4: Export the Classes

As shown in the “Exposing Classes” section of the boost.python tutorial, exporting OMNI_Action is simply a matter of doing:

class_<OMNI_Action>(”Action”)

.def(”Perform”,
pure_virtual(&OMNI_ActionAuthenticate::Perform),
“The function that performs the specific action.”)
.def(”Result”, &OMNI_Action::Result,
“Returns the result of the action after \”Perform\” is called.”)
.def(”Message”, &OMNI_Action::Message,
“Returns the message associated with the return result \n”
“after \”Perform\” is called.”)
.def(”Session”, &OMNI_Action::Session, return_internal_reference<>(),
“Reference to the session object on which the action “
“object is valid.”)

;

Now there is on SMALL problem with the above. The Perform method is a pure virtual method. Which means that due to a “no implementation” the above class cannot actually be exported. In order to do this, a wrapper class (called ActionWrap) is created for OMNI_Action, and THIS wrapper class is exported instead as shown below:


struct ActionWrap : OMNI_Action, wrapper<OMNI_Action>
{
bool Perform()
{

#if BOOST_WORKAROUND(BOOST_MSVC, <= 1300) // Workaround for vc6/vc7
return call<int>(this->get_override(”Perform”).ptr());
#else
return this->get_override(”Perform”)();
#endif

}

};

This is it. Now we have provided a proxy implementation of the Perform method which essentially calls the derived Perform method when invoked and we have gotten ridden of the pure virtual method.

The exporting of this class is:

class_<ActionWrap, boost::noncopyable>(”Action”,
“A wrapper for classes deriving from the Action object.\n”
“The Action class is the super class of all classes that \n”
“perform work on the omnidrive server.”, no_init)
.def(”Perform”,
pure_virtual(&OMNI_ActionAuthenticate::Perform),
“The function that performs the specific action.”)
.def(”Result”, &OMNI_Action::Result,
“Returns the result of the action after \”Perform\” is called.”)
.def(”Message”, &OMNI_Action::Message,
“Returns the message associated with the return result \n”
“after \”Perform\” is called.”)
.def(”Session”, &OMNI_Action::Session, return_internal_reference<>(),
“Reference to the session object on which the action “
“object is valid.”)
;

Note

  • how all the methods of the original OMNI_Action class are also exposed (ie Perform, Result, Message, and Session)
  • The noncopyable specifies that the ActionWrap cannot be copied or instantiated
  • The “return_internal_reference<>” specifies that the return value of the “Session” object is infact a pointer to a class member, which means that the lifetime of the return value (a session object) is tied to the lifetime of the Action object. So unless the Action object is destroyed, the reference count of the return Session instance cannot be reduced! Please refer to Call Policies in the Boost.python tutorial for more information on this.
  • The string values are essentially documentation strings in exported python module (docstrings).

Once this is done, the derived classes can be exposed normally.
Step 5: The Build Process

Once you have exposed all the classes in the above fashion, it is time for setting up your builds!

The builds are performed using bjam - the Boost build system. bjam looks for the file Jamroot in the current directory the same way that make looks for a Makefile.

bjam is a lot more complex than make and the bjam tutorial is an excellent source of all the information you are going to need.

I will go through my Jamroot file line by line:

1. Specify the Boost directory: This is the location where boost was installed.

use-project boost
: /opt/boost_1_34_9 ;

2. Specify the requirements and default builds. To make debug the default build, simply specify that in the default-build line.

project
: requirements <library>/boost/python//boost_python
: default-build release
;

3. Specify a dependency on the libomnifs C++ library. The C++ library is infact built in ../bld/<build_mode>

The following two rules mention that in the debug mode the debug build of the libomnifs is to be used and similar in the release mode, the release build of the libomnifs is to be used, as indicated by the “variant” flags.

lib libomnifs
:
: <file><locateion of omnifs source tree>/bld/debug/libomnifs.so <variant>debug
;

lib libomnifs
:
: <file> <omnifs_build_dir_path>/bld/release/libomnifs.so <variant>release
;

4. This specifies the installation target. Unlike in “make” where installations are arbitrary actions, they have a bit more meaning in bjam. These (in this rule) simply specify the installation location (in this case the current directory “.”) and types (install a library instead of an executable):

install dist : omnipy : <location> . <install-type>LIB ;

5. Finally the following specifies that the object being built is a python-extension library (of the name omnipy). Many other types of projects are also possible. Please refer to the bjam tutorial for a full list of build types. The .cc files are the files in which I had divided the export declarations. It is recommended to break up the class export declarations into multiple files so that changes in one class will not require the compilation of the entire source - there by speeding up the build process.

python-extension omnipy
: omnipy.cc omnipy-logger.cc omnipy-utils.cc omnipy-session.cc
omnipy-connection.cc omnipy-memory.cc omnipy-actions.cc libomnifs
;

Step 6 - In Action

Finally to see a glimpse of the library in action, simply start python and run the following:

– Import required modules including the one we just created

>>> import omnipy, sys, os

– create a session object by calling the Session constructor (refer to the actual distribution)
>>> session = omnipy.Session()

– create an actual action object - the action for creating a folder:

>>> action = omnipy.ActionCreateFolder(session, “new_folder1″, “”)

– Perform the action

>>> action.Perform()

and so on and so forth.

Please refer to the example.py and utils.py in the omnifs source distribution for more details.

Step 7: Gotchas and Todos

There a still a few things I havent yet figured out. One of them is how to export FILE * and streams so that we could use Python file handles instead. Once Im done with that Il put that up in here as well.


Well that concludes my little tip on how to create python bindings (more of a self-learning set of notes more than anything else). I hope this really saves you a lot of time. Please let me know if you find anything I am missing or forgot or just plainly got wrong. Id be glad to learn from it myself.

July 10, 2007 Posted by Sri | C++, Python | | No Comments

OmniFS

Today marks the release of omnifs. I had started trying omnidrive - a storage drive on the web in mid April. I am still on the free service (which comes with 1Gb of Storage and 5Gb of transfers/month). It is still in beta mind you, but not bad at all. They even have a client for windows and mac. The windows client lets you mount omnidrive as a special folder which can be accessed from windows explorer.

However, for Linux, I had to install a Java client. Now I am not against Java, but wondered why couldnt this just be a mountable drive? So being the fidder (or is it twiddler?) that I am, I thought, “if you cant find it, make it”. I set about making a mountable drive for omnidrive. Luckily I had come across the omnidrive developer API. It is not bad but could really be better by being clearer. The result of this work is omnifs. This is small program that uses the Fuse (Filesystem over User Space) library to mount your omnidrive account as a directory in Linux! By doing so, normal file operations can be performed (eg cp, mv, ls and also developer functionility like fXXX functions in the C library). Please do try it out. Would love to here what you think about it and ofcourse to fix it up more.

The general goal of omnifs is to transition into a “webfs” so that it would eventually support WebDAV and other online storage accounts!

June 1, 2007 Posted by Sri | All, Internet | | No Comments

Back On

Well after a long long gap. Here I am back on. Had a few things going on. Had my exams going, bought a house, tried out OmniDrive and made a mountable drive for OmniDrive. Will post more about it soon.

June 1, 2007 Posted by Sri | Uncategorized | | No Comments

NextGen Travel - Saucer Style

I wonder when these flying saucer’s would be available for the public? Would love to see the M2 and the M7 Highways with their 3D/Altitude versions so we can manage suburbian air traffic as well!

http://www.dailymail.co.uk/pages/live/articles/technology/technology.html?in_article_id=447317

April 10, 2007 Posted by Sri | All, General | | No Comments

Wireless Power

How cool is this? Basically a dime-sized attachment that fits to any device (like mobile phones or pace makers and so on), and quietly converts all that static in the air into a constant DC voltage. Effectively makes batteries last for ever!

http://money.cnn.com/magazines/business2/business2_archive/2007/04/01/8403349/

At the moment this can only be used with low power devices.  So with laptops getting less power intensive, it would become easier to power them as well.  For higher-powered devices, I wonder if these can cascaded together to provide a higher power?

April 2, 2007 Posted by Sri | All, General | | No Comments

KabookiWater

STIRR Sydney was hosted again last night (Thu 29/03/2007) at the Ivory Restaurant 620 Harris St, by Tangler, Atlassian, Omnidrive and TVP. This was my first time at this event (well I had been to BarCamp earlier so I knew some of the gang here) and the event stood upto its reputation. Smaller than BC, it was again teeming with loads of entrepreneurs and VCs alike.

As usual, the game HalfBaked was played here. The idea is something like this:

  1. Come up with a whole bunch of words in random: Kabooki, bungy, vodka, lemon, wave, sequins, sheik, water, etc
  2. Split crowd into 5 groups (around 8-12 in people each).
  3. Each group comes with a two-letter word dot com name drawn from the words above (in step 1).
  4. Each team now has 10-15 mins to make a pitch to the VCs out there.

SO the fun begins. Our team came upon the company name KabookiWater like a pack of bums on Sydney’s Liverpool St upon a juicy steak. We had initially thought this was something to do with japanese beer, but soon Michael had pointed out that Kabooki actually stood for Japanese theatre (D’oh). After a bit of hair pulling (lucky for me), we decided it should be User-Generated Interactive Drama - aka Theatre On Tap (TOT).

Kaching!! Folks, this is big money. Imagine a soap, that is actually controlled by what users want on any particular day, instead of the usual Birth-Marriage-Death cycles their writers have to churn out till time immemorial? Audience demands drama, KabookiTheatre gives them crying and moaning. Audience demands sex, instead of crying and moaning, KT gives them, moaning and THEN crying. So possibilities are limitless. I dont want to spoil your fun by revealing all the possibilities to you.

So where is the money coming from? Simple, folks. Product Placement! Users are telling you what they want from the drama, and that should give an indication of what the kind of audience is. So bingo. Google, with your Ad-Sense, eat your corporate hearts out! Not to mention Spin-off markup languages including UGIDML and TOTML for representing and distributed user theatrical preferences. Here come those courses on Tafe’s and those TOTML and UGIDML for Dummies books - Kaching Kaching.

So if any of you out there (especially VCs with lots of money) are interested in the next best/super-duper thing since sliced-bread and Google combined, here it is. Il be waiting. I have nothing better to do :D.

March 30, 2007 Posted by Sri | All, Internet, Startups | | 3 Comments

Upcoming Concerts

Well. As you all know about my little obsession with the violin. Im accompanying in a couple of concerts.

Lingo: In Indian Classical Music, there are two roles for an instrument.

  • Solo Playing: Actual solo concert and you do your own thing.
  • Accompanying: This is the hard part. You play for someone as they sing (or they may be an instrument doing a solo). The challenge here is that now you have to play in a totally different style and try and at times replicate (as close as possible) what they are doing. Pros do this without even rehearsing!!!

Anyway, I am accompanying Dr Ramanathan. Ive also been learning vocal Indian Classical Music from him for last 3 years so that it would better help me with the violin (follow the logic?). Details are:

Time and Date:
To Be Seated by 5PM, Sunday 1st April 2007 (no kidding)

Venue:

Bowman Hall,
Campbell St,
Blacktown,
Sydney

Hope to see you all there.

March 30, 2007 Posted by Sri | All, Classical Music | | No Comments

Cybersquatting

I am sure many of you read the article last week about M$ going on a crusade against cyber squatters. Great finally someone doing something about all those useless (albeit rich) rodents of the society trying to make a quick buck (or a lot of it) by getting a free ride on hard work (and not to mention trademarks) of others. But as much of a problem as this is, ie these folks trying to peddle spam and all other garbage, should Microsoft’s reaction become the defacto standard in the industry?

First of all, the money generated on such bogus sites (eg micrsoft.com or microsof.com) can be monitored and as a result the loss to MicrOsoft can be clearly evaluated. However, if you are stupid enough to end up falling for material on a squat-site, may be you are not the kind of material to be visiting MS? (wait a minute, I may be wrong about that).  If cybersquatters are making money from typo-ed sites, then surely Microsof and Microsoft are completely different entities, so while this is unethical, clearly this is not legal (isnt that what we are all about these days?).  Ofcourse, the easiest and most obvious solution is allow rich companies decide on what constitutes as law and in which situations these laws are applicable.

A more serious argument could be that a typo-ed site could be “sponsored” into redirecting a visitor to a competitor’s site. But how is this different from internet explorer redirecting invalid addresses to MSN.com? This is just free-enterprise. The legal boundary is crossed, when the typo-ed site is actually being used for slander and misrepresentation. But is this what the cybersquatters were doing?

An argument in favour of cybersquatting is that of free-enterprise and capitalism. The argument of free-enterprise goes something like “well they got there and bought it first so they should do with it what they can”.  And to extend the argument, being a free market MS should have got in there first and bought out the typo domains as well. Surely microsof and micrsoft could not have existed before the real deal.  However, the argument does not hold for common words. In this situation, the counter argument that unlike in a free market, the domains were improperly priced initially actually makes sense. But if common words had been used for company names, then companies registering these words SHOULD have gotten there first. If they couldnt, then why rely on common english words for a company name?  How about being a bit creative about it for a change instead of opting for a easy hack?  Besides how cool would “www.cool.com” really be?1.

What determines a trademark infringement? How would MikeRowSoft and MyCrowSoft be treated? With speech recognition becoming common, a browser could direct a search to one of these sites rather than MS. Should MS have the authority to deem these as evil and try to get them closed (after extracting all compensation ofcourse).  At the end of the day, such battles will be one by the sides with the more expensive lawyers rather than being based on any sense of legal or ethical fairness.

Notes

1. Example taken from Paul Graham’s essay - “Why Smart people have bad ideas?

March 20, 2007 Posted by Sri | All, Internet | | No Comments

Light and Tangler

I had first met Marty at a lift.  After forcing myself on him (conversationally ofcourse), I had managed to get myself invited to weekly soccer matches (Luckily you can suck at soccer while still loving it!).  At one of these matches he had told me that he was working-on/starting Tangler.  At first I had assumed this was a flavour of chips for us geeks.  So I had decided to go to tangler.com and was presented with an option to signup for beta-testing their new product.  All I had known at this point was it was kind of a forum - “jees done before” I thought.

Soon after I had received my account from Mick.  So I logged in for the first time.  Big mistake.  Let me say I am still paying for it.  While forums themselves are not a new idea, Tangler’s approach to facilitating forums is in fact quite simple and yet very very addictive.  They have used a pure web 2.0 approach to their app.  No software installation is necessary, works on major browsers (so far I have only tried it on Firefox 1.5 and 2.0 on linux), and provides complete control to you.  Also Within moments, I was part of about 15 groups (mostly invited to by Mick) and managed to create a couple myself.  Ofcourse this is what  contributed to one of the biggest mistakes of my life.  With the ease of (instant) communication, lacking in many other forums, you are never out of communicado with your contacts.  Eagerness of new technology will have to give way to rudeness and introverted behaviour if you would like to get any work done!

Tangler also has a standalone desktop client for windows and linux.  However I have not got this to work in linux yet (complains about missing libfXfixes.so.3).  I would have preferred a plugin to existing IMs like everybuddy and/or Trillian rather than YASIM (Yet Another Standalone IM).

One thing I had found (possibly) buggy was time-zone management.  Even though my profile says I am from Australia, I found some of my posts to have the wrong date.  May be I havent set it up correctly.  Also being in beta, I cant expect color-scheme customizations which I think would be very useful.

But all in all a great approach to forums, and especially justifies the eccentricity of folks at Tangler.  Hope to see lots more of it in the future from this bloody awesome team (For non-ausies: “bloody” can be negative - ie “what a bloody f@#%” or positive like “what a bloody ripper”… and this definitely is the latter).

March 6, 2007 Posted by Sri | All, General | | 2 Comments

Are reeding and riting important?

Lately (well last five years anyway) there has been quite a bit of media frenzy regarding whether traditional penmanship and writing skills are now becoming obsolete in this day and age of computers, PDAs and styluses (stylii?). One article (Jason Fry, “Mightier than the Pen“, March 05 2007, Wall Street Journal), suggests that the said new forms of communication are sufficient to replace traditional methods (writing and penmanship). Reasoning is that throughout human history new forms of communications have replaced older forms of communication successfully and this is no exception. Also, as Jason suggests, these new forms break down the creative boundaries for child who has embarked in his/her educational journey thereby increasing his/her imagination in ways not envisaged before. Another common argument presented is that (the physical process of) writing, due to outsourcing of this task to computers, itself does not add to human competency (of which survivability in the woods was traditionally a measure of ).

As a high-school student, I absolutely abhorred the idea of writing in all forms (essays, analysis, creative writing, poetry for women and all that “boring” stuff) so I am not sure Id be the right person to comment on this issue. But I suppose that is what blogs are for.

Ignoring the issues of availability of computers and word processing tools, bad (font) typing, eye-strain, difficulty in entry, should a child with access to a computer and wordprocessing tools, first develop handwriting skills or bypass this process in their favour?

I am not so sure it is a good idea to dispense with the basic writing skill (cursive or not). While efficiency can be definitely gained by using computers (via touch typing, voice recognition, styluses etc), there are other benefits that only handwriting, if not a clear and neat one, can offer. Briefly, these include:

  • Motor Skills: Handwriting, that most adults take for granted, requires dedicated effort to master and maintain. Learning to type is a process of taping fingers at discreete locations on a keyboards; Handwriting reuqires careful and controlled motion to produce continuous and fluid patterns.
  • Orderliness of the mind: Wordprocessing facilitates the noting of ideas in a sequential manner. This forces organising thoughts in an orderly and flowing fashion.
  • Internalising Skills: This really falls into the “Terminator” line of thought about maintaining/improving skills internally rather than (unnecessarily) depending externally on machines for basic skills and ideas.

At the end of the day, where can we draw the line regarding dispensibility of core skills? Do we need reading abilities? Text can be synthesized into voice. Computers can solve some of the most complex mathemtical problems so why do we need algebra? If Matrix (the movie) has anything to say, we could all be feeding ourselves via tubes attached to our bodies. Talking about dispensible skills, how about this automatic beer dispenser:

http://www.trendhunter.com/trends/beer-launching-fridge-all-lazy-men-rejoice

Having said all this, perhaps a bit of organising my thoughts on paper would have helped?

March 6, 2007 Posted by Sri | All, General | | No Comments

On Startups (and Software and Violin)

I think I may be a bit presumptuous in incorporating “Startups” in my blog title.  Like the other two items in the title, this is something Id like to do well, not something I am an expert at or even know anything about.  At best, its an effort to documenting my efforts in creating a (successful) startup (which is probably more imaginary at this point in time than the lord of the rings trilogy).  Hope that clarifies it.  Same applies to Software and the Violin.

March 5, 2007 Posted by Sri | All, Startups | | No Comments