Created
July 4, 2012 01:05
-
-
Save mediaupstream/3044471 to your computer and use it in GitHub Desktop.
HackerRank.com Challenge #2 (SpaceX) Solution in NodeJS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// HackerRank.com Challenge #2 (SpaceX Challenge) | |
// @hacker mediaupstream, aka Derek Anderson | |
// | |
// How to Win: | |
// 1. Install required npm packages: `npm install request sugar` | |
// 2. Modify the `username` and `password` variables below to match what you used to signup on | |
// hackerrank.com with | |
// 3. Run the script: `$ node hackerchallenge2-spacex.js` | |
// 4. Wait for about 40 minutes... | |
// | |
// The script can be optimized to run each game in parallel, probably. | |
// | |
var request = require('request'), | |
username = 'USERNAME HERE', | |
password = 'PASSWORD HERE', | |
gameStart = 1, | |
gameEnd = 10000; | |
// I am lazy, and sugar is sweet. | |
require('sugar'); | |
// | |
// Quick lookup for our ghetto lexical parser | |
// | |
var tokens = { | |
zero: 0, one: 1, two: 2, three: 3, four: 4, five: 5, six: 6, seven: 7, eight: 8, nine: 9, | |
ten: 10, eleven: 11, twelve: 12, thirteen: 13, fourteen: 14, fifteen: 15, sixteen: 16, | |
seventeen: 17, eighteen: 18, nineteen: 19, twenty: 20, thirty: 30, fourty: 40, fifty: 50, | |
sixty:60, seventy: 70, eighty: 80, ninety:90 | |
}; | |
// | |
// Ghetto lexical parser, transforms phrases into numbers | |
// eg: "one thousand two hundred and six" becomes "1206" | |
// | |
var parseWords = function(str){ | |
str = str.replace(',', '').replace('-', ' ').replace(' and', '').split(' '); | |
var results = []; | |
for(var i=0; i<str.length; i++){ | |
var cur = str[i]; | |
if(cur.has(/hundred|thousand|million|billion|trillion/)) continue; | |
var num = tokens[cur]; | |
var next = str[i+1]; | |
if(next){ | |
if(next.has('hundred')) num += '00'; | |
if(next.has('thousand')) num += '000'; | |
if(next.has('million')) num += '00000'; | |
if(next.has('billion')) num += '00000000'; | |
} | |
results.push(num); | |
} | |
var sum = results.sum(function(n){ | |
return parseInt(n, 10); | |
}); | |
return sum; | |
}; | |
// | |
// Decrypts a `string`, using ROT13 style encryption, given the `num` number offset | |
// | |
// @param {string} string The encrypted string, eg: 'lbhe zbz' | |
// @param {number} num The number offset to "shift" the `string` by | |
// @param {boolean} enc (optional) true to encrypt rather than decrypt | |
// | |
var decrypt = function(string, num, enc){ | |
// dictionary of character codes, the numeric values of our ascii characters. | |
var dict = [ | |
'abcdefghijklmnopqrstuvwxyz'.codes(), | |
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.codes() | |
]; | |
var buf = ''; // our encrypted / decrypted message will go here | |
// loop over our string, one character at a time | |
for(var i=0; i<string.length; i++){ | |
var c = string.charCodeAt(i); // the current character code | |
var o = (c >= 97) ? dict[0] : dict[1]; // which dict to use | |
var index = o.findIndex(c); // index of the character in dict | |
var n = c; // encrypted / decrypted character code | |
if(c != 32 && c != 45 && c != 44){ // skip "space" "," and "-" chars | |
index = (!enc) ? (index-num) : (index+num); // shift up or down based on `enc` | |
// get the new character from our dictionary, given the shifted index from above | |
n = o.at(index, true); | |
} | |
// transform the number (n) into the character code (eg, "A") | |
// and append it to our buffer, which in turn will start to spell out the | |
// decrypted / encrypted message | |
buf += (n).chr(); | |
} | |
return buf; | |
}; | |
// | |
// Wrapper to decrypt with the `enc` flag set to true | |
// | |
var encrypt = function(string, num){ | |
return decrypt(string, num, true); | |
}; | |
// | |
// Log into HackerRank.com using the Request module | |
// | |
var login = function(username, pass, cb){ | |
request({ | |
url: 'https://www.hackerrank.com/users/sign_in', | |
form: { | |
'commit': 'Sign in', | |
'user[login]': username, | |
'user[password]': pass, | |
'user[remember_me]': 1 | |
}, | |
method: 'POST' | |
}, cb); | |
}; | |
// | |
// Ask HackerRank.com for a game | |
// @param {number} n The number of game you want in the range 1-10000 | |
// @param {function} cb A callback with response data | |
// | |
var getGame = function(n, cb){ | |
request({ | |
url: 'https://www.hackerrank.com/game.json', | |
method: 'POST', | |
form: { | |
n: n, | |
remote: true | |
} | |
}, cb); | |
}; | |
// | |
// Helper method to submit an answer to HackerRank.com | |
// @param {object} d This object should be in the format: | |
// { | |
// id: 12943, // the game ID, not the same as the game number, eg 1-10000 | |
// answer: 102, // your answer | |
// remote: true | |
// } | |
// | |
var submitAnswer = function(d, cb){ | |
request({ | |
url: 'https://www.hackerrank.com/game.json', | |
method: 'PUT', | |
form: d | |
}, cb); | |
}; | |
// | |
// Crack an encrypted string | |
// | |
// we are taking advantage of the fact that all decrypted messages | |
// will contain lexical numbers, eg: "twelve", in essence a brute-force rot13 | |
// | |
// @param {string} str The encrypted string to decrypt | |
// @returns {string} a decrypted string | |
// | |
var crack = function(str){ | |
var result = ''; | |
for(var i=0; i<35; i++){ | |
var test = decrypt(str, i); | |
// console.log(test); | |
if(test.has(/thousand|hundred|and|zero|one|two|three|four|five|six|seven|eight|nine|ten/) || | |
test.has(/eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen/) || | |
test.has(/twenty|thirty|fourty|fifty|sixty|seventy|eighty|ninety/) ){ | |
result = test; | |
break; | |
} | |
} | |
return result; | |
}; | |
// | |
// Crack the whole game, from range `start` to `stop` | |
// Also submits the answer, so you'll want to login first before calling this... | |
// | |
var crackAll = function(start, stop){ | |
start = start || 1; | |
stop = stop || 10000; | |
console.log('cracking '+ start +' to '+ stop +'... this will take awhile.'); | |
for(var i=start; i<=stop; i++){ | |
(function(level){ | |
getGame(level, function(x,y, body){ | |
// something went wrong... probably a server request limiting issue | |
if(body.has('<')) return console.log('Something went wrong on game #'+ level); | |
// all is well... process | |
body = JSON.parse(body); | |
if(body && body.game && body.game.cph_number){ | |
var answer = crack(body.game.cph_number); | |
var number = parseWords( answer ); | |
console.log('Message: '+ answer +', Number: '+ number); | |
if(isNaN( number )){ | |
console.log('- Error'); | |
console.log(level); | |
console.log(body.game.cph_number); | |
console.log(answer); | |
console.log(number); | |
} else { | |
submitAnswer({ | |
id: body.game.id, | |
answer: number, | |
remote: true | |
}, function(x,y, resp){ | |
if(!resp.has('Congrats')){ | |
console.log('==='); | |
console.log('incorrect answer: '+ level); | |
console.log(body.game.cph_number); | |
console.log(answer); | |
console.log(number); | |
} | |
}); | |
} | |
} | |
}); | |
})(i); | |
} | |
}; | |
// | |
// login to hackerrank.com and start crackin' | |
// | |
login(username, password, function(err, res, body){ | |
crackAll(gameStart, gameEnd); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment