WURFL Capability Browser = Mobile Device Browser (dot com)

April 12th, 2011

I’m moving the WURFL Capability Browser to its own domain with a slightly different name (and look):  http://www.mobiledevicebrowser.com

So there it is.

 

Spoofing Those Bitly Alpha-Numeric Keys in MySQL

March 26th, 2011

Recently I was tasked with making an internal “link shortener”, a la Bit.ly’s.  For those that are unfamiliar with the concept, if you have an exceedingly long url that’s difficult to remember i.e.

http://us.rd.yahoo.com/dailynews/external/ap_av/av_ap3_wl/92a15245e85f7ca01a3a65b88ede5075/39997189;_ylt=AvuGi5zx_KMlvJdMHmFncMoUewgF;_ylu=X3oDMTFhanRoYjYwBHBvcwM5BHNlYwN5bl9yXzNzbG90X3ZpZGVvBHNsawN2aWQtZWQtbGluaw–/*http://news.yahoo.com/video/world-15749633/24062366

You can use a shortener to make it more manageable.  Bitly compressed the  example URL above to: http://bit.ly/g25djH much easier to remember, much easier to distribute.

So I wanted similar functionality.  I want to be able to generate a short, unique key to represent a long url.  Most relational databases already use some form of base-ten integers (i.e. 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13… so forth)  for keys on their tables.  This is pretty well established, there are either auto increment support or sequences depending on the database that will keep track of these values for you. I could use these keys for shortening, but in the offshoot chance this feature took off it wouldn’t be too long before the integer numbers would get pretty long themselves, plus its generally advisable to mask the real table ids from the public eye.

So back to that url that bit.ly generated for me: http://bit.ly/g25djH.  “g25djH” is obviously a key that translates to a row in a database somewhere that holds the original long url. Given that databases prefer numeric keys, what’s to make of bit.ly’s alpha numeric version?  By using upper and lower case letters in their keys, they save a ton of space.

To wit:  with a decimal representation of a number, each digit represents 10^x.  If you add lowercase letters (a-z), each digit represents 36^x.  Much larger values can be represented with fewer digits. So the standard ASCII character set provides for a couple hundred or so characters with which to construct an alpha numeric key, but the ten digits and twenty six lower-case letters are enough for my needs, and this example.

But still, how should you leverage a database to store these keys?  You could make a varchar column a primary key and store these alpha-numeric values, but that wouldn’t be practical as the dataset grew not to mention if you wanted to use this value as a foreign key elsewhere.  You could have a standard int-type primary key and a second key for the alpha-numeric representation, this is closer to ideal but the extra column is wasted space.

The best solution, in my opinion, is to make a function that will convert the standard base 10 integer into a base 36 value.  This is essentially the same process as converting a decimal to hexidecimal value, but with a whole slew of characters.  Therefore you get your condensed alpha-numeric keys while only using the standard int-type primary key.

This is functionality I’d like the db to have so I can translate keys directly in the sql.  To accomplish this I’ve written two functions, one to convert an int to an alpha numeric key, and the other to convert an alpha numeric key to an int:

DELIMITER $$
DROP FUNCTION IF EXISTS ConvertIntToKey$$
CREATE FUNCTION ConvertIntToKey(intval INT )
RETURNS VARCHAR(100)
DETERMINISTIC
BEGIN
DECLARE i INT;
DECLARE d INT;
DECLARE r INT;
DECLARE dMoinsR INT;
DECLARE ch CHAR(1);
DECLARE result VARCHAR(200);

SET result = '';
SET d = intval;
REPEAT
SET r = MOD(d, 36);
SET ch = SUBSTR("0123456789abcdefghijklmnopqrstuvwxyz", (r+1), 1);
SET result = CONCAT( ch, result);
SET dMoinsR = d - r;
SET d = (dMoinsR / 36);
UNTIL (dMoinsR = 0)
END REPEAT;

RETURN result;
END$$

DELIMITER ;

DELIMITER $$
DROP FUNCTION IF EXISTS ConvertKeyToInt$$
CREATE FUNCTION ConvertKeyToInt( keyval VARCHAR(100) )
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE backwards VARCHAR(100);
DECLARE keylen INT;
DECLARE intval INT;
DECLARE i INT;
DECLARE rootval INT;

SET backwards = REVERSE(keyval);
SET keylen = CHAR_LENGTH(keyval);
SET i = 0;
SET intval = 0;
SET rootval = 0;

WHILE i < keylen DO
SET rootval = POW(36, i) * (LOCATE(SUBSTR(backwards, (i+1), 1), "0123456789abcdefghijklmnopqrstuvwxyz") - 1);
SET intval = intval + rootval;
SET i = i + 1;
END WHILE;

RETURN intval;
END$$
DELIMITER ;

Because these are functions–not procedures–you can slot these right into a SQL query.

So the user types in http://your.bitly.clone/aba1

“aba1” is the key.

Your SQL query would look like:

SELECT su.id, ConvertIntToKey(su.id), su.full_url
FROM stored_urls su
WHERE su.id = ConvertKeyToInt(“aba1”);

Also, credit where credit’s due, I basically modified the hex to decimal conversion up on wikipedia: http://en.wikipedia.org/wiki/Hexadecimal#Converting_from_other_bases

Python vs the 7th Guest

March 12th, 2011

 

Recently I’ve been catching up on all the games I missed back in the late eighties and nineties.  Thanks to Steam, the Mac Games Store (http://www.macgamestore.com/), Good Old Games (http://gog.com), iTunes, and of course the random altruistic blog poster. So I’ve finally started on the 7th guest.   I won’t lie, I’ve never been very good at pure puzzlers, but the main difference between myself today and fifteen odd years ago is that I’m more comfortable with letting computers solve these damn things for me. I’ve also been looking for excuses to practice python, so its two bird with one stone…

 

The puzzle in question is the queens problem:  arrange eight queens on a chessboard such that none would “attack” each other in chess rules.  That’s to say that one queen cannot be on the same vertical, horizontal, or diagonal path as another.

 

So first off what are a few rules:

  • There can be only one queen per row, per column.  Because of this I only need a one dimensional array to store the values. My array will track the column of a queen, each index will correspond to a different row. A chessboard is 8×8, so the array will have eight indexes, counted 0-7
  • A vertical mismatch can then be determined if any of the “column” values occur more than once in the array.
  • A diagonal mismatch is a little trickier.  For my purposes, two points (xi, yi and xj, yj) are diagonal to each other if |xi – xj| == |yi -yj|

So those rules being determined, there’s a matter of how to feed in the various board configurations to test.  I could nest eight loops and run through each, but that’d take forever.

Since I’m using a one dimensional array to track this, a sample line would look like:

[0,1,2,3,4,5,6,7] or [0,2,1,3,5,4,6,7]

Every index could only hold a value from zero to seven. The starting array would look like [0,0,0,0,0,0,0,0] and the ending array would look like [7,7,7,7,7,7,7,7].   Well I want this to be in a single loop, I thought what if I just looped through a set number from 0 to the final value.  My math’s a bit shaky, and statistics were never a strong suit, but looking at these numbers again, the question became very simple.  Finding the maximum number of loops is pretty tricky in decimal (base ten) logic, but all these indexes could only go up to eight.  Fortunately Python supports base eight (aka octal) arithmetic right out of the box.  If counted in octal, converted the counter to a string and then sliced the resulting characters into an array, I could find all possible combinations in one loop and then process accordingly.

 

def printBoard( board ):
    for row in range(0, 8):
        rowstring = ''

        for col in range(0, 8):
            if(board[row] == col):
                rowstring += 'q'
            else :
                rowstring += '_'
            rowstring += ' '
        
        print rowstring
        
    print '\n\n'
    return


success_count = 0

for i in range (0, 0100000000):
    octa = int( oct(i) )
    mask = '%(#)09d' % { "#" : octa }
    
    board = [ int(mask[1]), int(mask[2]), int(mask[3]), int(mask[4]), int(mask[5]), int(mask[6]), int(mask[7]), int(mask[8]) ];
    
    works = 1
    
    for rowindex in range(0, 8):

        for testindex in range(0, 8):
            
            if(testindex != rowindex):
                
                #check for horizontal
                if( board[testindex] == board[rowindex] ):                    
                    works = 0
                    break
                
                # check for diagonal
                if( abs(testindex - rowindex) == abs(board[testindex] - board[rowindex]) ):
                    works = 0
                    break
        
        if(works == 0):
            break
        
    if(works == 1) :
        success_count = success_count + 1
        print "Board ", success_count
        printBoard( board )

 

As it turns out, there’s ninety two solutions to this puzzle, meaning I probably would’ve guessed the answer faster than coding had I stuck with it but c’est la vie.

And not to keep you in suspense, here’s one of the valid solutions to the puzzle:

_ _ _ _ _ q _ _ 
_ _ _ q _ _ _ _ 
q _ _ _ _ _ _ _ 
_ _ _ _ q _ _ _ 
_ _ _ _ _ _ _ q 
_ q _ _ _ _ _ _ 
_ _ _ _ _ _ q _ 
_ _ q _ _ _ _ _ 

Wurfl Capability Browser: Major Update

February 24th, 2011

So today marks the release of the newest version of the Wurfl browser (http://wurfl.ditherandbicker.com).  In brief the changes encapsulate:

  • Moved db to MySQL from Postgress.  I’ve written about the issues with Postgres before (http://www.ditherandbicker.com/wurfl-capability-browser/postgres-initial-impressions/) and finally decided it was in my best interests to make the switch.
  • Added a fancy dhtmlx (http://www.dhtmlx.com) grid to allow dynamic sorting and filtering of result sets.
  • Limited searches to devices released in the past five years (all in the name of improving speed, I assure you).
  • Updated to the Wurfl file released Feb 3, 2011
  • Added a group/capability tree browser.
  • Capabilities will generally have some descriptive info about what they represent.  These were ripped from: http://wurfl.sourceforge.net/help_doc.php
  • You won’t have to specify search criteria for a capability to see it in the final output.
  • The capabilities that can be searched against or returned are now limited to five, down from ten.
  • The “describe device” ain’t there anymore.
  • XML exporting is also gone.

As before, I haven’t put much focus on IE compatibility, given that I don’t have easy access to a Windows computer outside of the workplace.  IE fixes will probably be forthcoming.

This will probably be the last “major” revision to the tool.  Here on out I’ll try to update the underlying db a few times a year and may play with the styling some more.

Inhouse vs Outsourced Code

November 6th, 2010

A previous employer entrusted the redesign of a major product to an Indian offshore firm.  After about seven months, they lost total confidence in this team and severed all ties.  It quickly fell on me to salvage the project and I believe it took about three months to getting it working “passably”.  Nevertheless I still got constant questions about why it was taking so long.  Rather than try to explain all of the issues with the offshore code to management and the business owners of the product, I printed out a before and after of a single function in a single file (of which there were hundreds of others in a similar state).

Inhouse vs Outsource Code

So important to note here: my revised code was about two pages long when spaced for readability.  The original code was about eight pages long, basically single-spaced, and didn’t work at all.

I taped this over my workstation and whenever someone asked me about the delays I just pointed.  It always did the trick.