20 March 2013

Hamlet's Monkey - Part 3

I've previously blogged about my Hamlet's Monkey project.

Part 1 where I introduced the concept and did it in CFML:

Part 2 Where I ported the project into a java class with some JSON file read and writing:

OK So now this whole project is moving toward where I was really excited to take it. GOOGLE! hahah I want to put this project on Google App Engine (GAE) so it uses cloud computing. That way a GAE cloud instance is like an actual monkey, tapping away at the keyboard and trying to re-write Hamlet! Awesome! This straight away introduces two potential problems:
  1. I've got to turn the project into a java servlet
  2. Tracking progress - In part 2 we added file storage, this won't work in the cloud, so we'll need to find a better method.

OK So converting the main crux of the method to a servlet isn't that complicated. The first thing you need to do is install the GAE plugin for eclipse. https://developers.google.com/appengine/docs/java/tools/eclipse Then we'll convert the project we made in Part 2 to a servlet, the public class needs to extend HTTPServlet:
public class Shakespearemonkey extends HttpServlet {

and the main method becomes doGet, which is what is called when the servlet responds to a http get request:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException {
        int            intCount                 = 0;
        String        strSubString            = "";
        String        strShakespeare            = Shakespeare.replaceAll("[^a-zA-Z]", ""); //Shakespeare without spaces etc
        String        strMonkeyString            = "";
        String        strBestSoFar            = "";
        while (intCount < intNumLoops) {
            //generate the random guess, one keystroke at a time
            strMonkeyString    = strMonkeyString + Character.toString(generateRandomLetter());
            strSubString    =    strShakespeare.substring(0,strMonkeyString.length());
            //check if we've guessed correctly so far
                //Is this our best guess so far
                if(strSubString.length() > strBestSoFar.length()){
                    strBestSoFar    =    strSubString;
                //incorrect guess, start again
                strMonkeyString    = "";
        //purely for output, re-read the latest and update user on progress
        try {
            response.getWriter().println("Good Morning, I am your monkey! I will be trying to guess the string: " + strShakespeare + "<br />");
            MonkeyResults monkeyresults    = readResultsFromFile();
            response.getWriter().println("My best guess so far is: ");
            response.getWriter().println((String) monkeyresults.getBestGuess());
            response.getWriter().println("<br />I have made ");
            response.getWriter().println((int) monkeyresults.getKeyStrokes());
        } catch (IOException e) {

So what I've done is created an object for storing all our progress data. Possibly not necessary / overkill but its OO and it feels good. It's just two getters and setters, so I won't bore you with the code. What is different in the trackProgress function though is I've replaced the JSON code with this:
        MonkeyResults        monkeyResults            = readResultsFromFile();
        if(monkeyResults.getKeyStrokes() != 0 && monkeyResults.getBestGuess() != ""){
            strBestGuessFromFile    = (String) monkeyResults.getBestGuess();
            intKeyStrokesFromFile    = (int) monkeyResults.getKeyStrokes();

So I'm going to use the GAE datastore to keep track of our progress. The first step is writing to the datastore:

    static Key            theResultsKey    = KeyFactory.createKey("Results","tblMonkeyResults");
    DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

    private void writeResultsToFile(int intKeyStrokes, String strBestGuess){
        //I write the number of keystrokes and the best guess so far to the datastore
        Entity objDaoOut = new Entity("Results", "tblMonkeyResults");
        objDaoOut.setProperty("strBestGuess", strBestGuess);

The next of course is the new read function, which returns an implementation of our local object:
    private MonkeyResults readResultsFromFile() {
        //I read results from the datastore and return an instance of class MonkeyResults
        MonkeyResults monkeyResults    = new MonkeyResults();
        long intStrokes    = 0;
        String strGuess    = "";
            Entity objDaoIn = datastore.get(theResultsKey);
            intStrokes    = (long) objDaoIn.getProperty("intKeyStrokes");
            strGuess    = (String) objDaoIn.getProperty("strBestGuess");
        }catch(EntityNotFoundException e){
        return monkeyResults;
That's basically it. We have converted our java function to a Java Servlet and modified the file read and write to use the Google Datastore. Simples.

Once you're done, you should be able to test it locally, if it works you right click on the project and goto Google -> Deploy to App Engine. Hey presto google uploads it all for you and you should have a successfully running monkey!

Here's my monkey: http://shakespearmonkey.appspot.com/ http://pastebin.com/1etNP1ge

No comments: