2012/08/30

minimalistic OXO

When you're up against a master it is very noble to let him/her pick both the battleground and the battle. You're going to get beaten though. And while you might learn some important lessons along the way (watch Karate Kid for full list) ... you're still going to get beaten.

Of course I could not refuse to pick up the gauntlet on the Tic-Tac-Toe (or noughts and crosses or oxo as we call it) challenge. But it was with a bit of despair. Sure, I might point out that it looks more like a matrix with elements than a set with cells and that there are in fact 5 diagonals and 5 antidiagonals (only two of those counting for a "Winning Set", that is true), but I might then get slapped around the head with my own weapons. I'm not a mathematician and I'm challenging a PhD in Quantum Physics.


And then I remembered something. I used to play a text MUD (try them, I haven't seen anything graphical that beats them for depth of involvement and gameplay ... once you get used to the interface). Inside the game you could play chess against other players. Yes, really. It was minimalistic. I mean, you had to know the shorthand codes for moving a piece and only newbies would occasionally request a visual (ascii art) view of the board (the rest of us did it all in the head). The only thing the game itself knew was what the valid moves were at a given state for the board.


Did the chess game know about the victory conditions ? Of course not. Only a computer needs to be told what the victory conditions are !


A smile crossed my face when I remembered that. For I can not beat Peter at his own game and I'll be devouring his upcoming newsletter(s) that explain his solution (and no doubt learn lots from them). But a minimalistic OXO in under two hours ? Yes, I can do that.


You find my solution here
  • It has no concept of rows, columns or diagonals other than is needed for the interface (which is REST, using most of the verbs to get the job done).
  • There is a little bit of code, the biggest part going to showing the board (like the ascii art for the chess game).
  • The game has no concept of a victory condition, the players can decide what that is.
  • It comes with documentation, search for "oxo" after you deploy the module. 
  • There is no computer player or any other AI (you can add those as a layer on top if you want them, it's easy to find the rules for a perfect game on wikipedia).
Let the games begin !

2012/08/29

second oreilly webcast

Yesterday I gave my second O'Reilly webcast this month. I used a much quieter laptop and it went a lot smoother (for me at least), but you'll understand that I'm quite happy it is now a thing of the past.

You can find the
nCode applications I demo'd here. I also noticed that the previous talk is now up on Youtube, so you should be able to find that one there. This one will follow in about a week. The slideshow itself can be viewed on http://netkernelbook.org/wink/wiki/webcast_20120828.

This officially ends my holiday. I'll now be moving on to some serious stuff such as ... tic-tac-toe.

2012/08/17

fine grained control

September and the return of the normal flow of things are just around the corner. For this holiday post I'm going to share another snippet of code that I've recently been working on. It gives you fine grained control over NetKernel's kernel properties :

// Author: Tom Geudens
// Date  : 2012/08/15

// The usual suspects for an accessor.
import org.netkernel.layer0.nkf.*;
import org.netkernel.layer0.meta.impl.SourcedArgumentMetaImpl;
import org.netkernel.module.standard.endpoint.StandardAccessorImpl;

// Processing.
import java.util.Properties;
import org.netkernel.container.IKernel;
import org.netkernel.layer0.boot.*;
import java.io.ByteArrayOutputStream;

import org.netkernel.layer0.representation.IBinaryStreamRepresentation;

// The accessor itself.
public class KernelPropertyAccessor extends StandardAccessorImpl {

    public KernelPropertyAccessor() {
        this.declareThreadSafe();
        this.declareArgument(new SourcedArgumentMetaImpl("key",null,null,new Class[] {String.class}));
        this.declareArgument(new SourcedArgumentMetaImpl("value",null,null,new Class[] {String.class}));
        this.declareSourceRepresentation(String.class);
        this.declareInhibitCheckForBadExpirationOnMutableResource(); // golden thread takes care of it
        this.declareSupportedVerbs(
                INKFRequestReadOnly.VERB_NEW|
                INKFRequestReadOnly.VERB_DELETE|
                INKFRequestReadOnly.VERB_EXISTS|
                INKFRequestReadOnly.VERB_SOURCE|
                INKFRequestReadOnly.VERB_SINK);
    }
   
    public void onSource(INKFRequestContext aContext) throws Exception {
        // SOURCE requires one argument, key, which is mandatory for all
        // verbs and checked by the grammar.
        String aKey = aContext.getThisRequest().getArgumentValue("key");
        
        // Check validity of arguments.
        if ( aKey.equals("") ) {
            throw new NKFException("request does not contain a valid key argument");
        }

        // getting the properties data
        Properties vKernelProperties = new Properties();
        IKernel aKernel = aContext.getKernelContext().getKernel();
        vKernelProperties.load(BootPersistence.sourceBootResource("kernel.properties",aKernel).getInputStream());

        if (! vKernelProperties.containsKey(aKey)) {
            throw new NKFException("property - " + aKey + " - does not exist in kernel.properties");
        }
        
        INKFRequest vGTrequest = aContext.createRequest("active:attachGoldenThread");
        vGTrequest.addArgument("id", "gt:kp:" + aKey);
        aContext.issueRequest(vGTrequest);

        // response (string)
        aContext.createResponseFrom(vKernelProperties.getProperty(aKey));
    }

    public void onExists(INKFRequestContext aContext) throws Exception {
        // EXISTS requires one argument, key, which is mandatory for all
        // verbs and checked by the grammar.
        String aKey = aContext.getThisRequest().getArgumentValue("key");

        // Check validity of arguments.
        if ( aKey.equals("") ) {
            throw new NKFException("request does not contain a valid key argument");
        }
        
        // getting the properties data
        Properties vKernelProperties = new Properties();
        IKernel aKernel = aContext.getKernelContext().getKernel();
        vKernelProperties.load(BootPersistence.sourceBootResource("kernel.properties",aKernel).getInputStream());

        INKFRequest vGTrequest = aContext.createRequest("active:attachGoldenThread");
        vGTrequest.addArgument("id", "gt:kp:" + aKey);
        aContext.issueRequest(vGTrequest);

        // response (boolean)
        aContext.createResponseFrom(vKernelProperties.containsKey(aKey));
    }
   
    public void onNew(INKFRequestContext aContext) throws Exception {
        // NEW requires two arguments, key, which is mandatory for all
        // verbs and value, which is only mandatory for NEW and SINK.
        String aKey = aContext.getThisRequest().getArgumentValue("key");
        String aValue = null;
        if (aContext.getThisRequest().argumentExists("value")) {
            aValue = aContext.getThisRequest().getArgumentValue("value");
        }

        // Check validity of arguments.
        if ( aKey.equals("") ) {
            throw new NKFException("request does not contain a valid - key - argument");
        }
        if ( aValue == null) {
            throw new NKFException("request does not contain a valid - value - argument");
        }

        // getting the properties data
        Properties vKernelProperties = new Properties();
        IKernel aKernel = aContext.getKernelContext().getKernel();
        vKernelProperties.load(BootPersistence.sourceBootResource("kernel.properties",aKernel).getInputStream());

        if (vKernelProperties.containsKey(aKey)) {
            throw new NKFException("property - " + aKey + " - already exists in kernel.properties");
        }
        
        // adding new key/value
        ByteArrayOutputStream vBAOS = new ByteArrayOutputStream();
        vKernelProperties.setProperty(aKey,aValue);
        vKernelProperties.store(vBAOS, null);
        
        IBinaryStreamRepresentation vBSR =
                aContext.transrept(vBAOS.toString().toString(), IBinaryStreamRepresentation.class);
        
        BootPersistence.sinkBootResource("kernel.properties",vBSR,aKernel);
        
        // response, the newly created property
        aContext.createResponseFrom(aKey + "=" + aValue);
    }

    public void onSink(INKFRequestContext aContext) throws Exception {
        // SINK requires two arguments, key, which is mandatory for all
        // verbs and value, which is only mandatory for NEW and SINK.
        String aKey = aContext.getThisRequest().getArgumentValue("key");
        String aValue = null;
        if (aContext.getThisRequest().argumentExists("value")) {
            aValue = aContext.getThisRequest().getArgumentValue("value");
        }

        // Check validity of arguments.
        if ( aKey.equals("") ) {
            throw new NKFException("request does not contain a valid - key - argument");
        }
        if ( aValue == null) {
            throw new NKFException("request does not contain a valid - value - argument");
        }

        // getting the properties data
        Properties vKernelProperties = new Properties();
        IKernel aKernel = aContext.getKernelContext().getKernel();
        vKernelProperties.load(BootPersistence.sourceBootResource("kernel.properties",aKernel).getInputStream());

        if (! vKernelProperties.containsKey(aKey)) {
            throw new NKFException("property - " + aKey + " - does not exist in kernel.properties");
        }
        
        // replacing the value of a key
        ByteArrayOutputStream vBAOS = new ByteArrayOutputStream();
        vKernelProperties.setProperty(aKey,aValue);
        vKernelProperties.store(vBAOS, null);
        
        IBinaryStreamRepresentation vBSR =
                aContext.transrept(vBAOS.toString().toString(), IBinaryStreamRepresentation.class);
        
        BootPersistence.sinkBootResource("kernel.properties",vBSR,aKernel);

        // SINK has no response but we do need to cut the golden thread
        INKFRequest vGTrequest = aContext.createRequest("active:cutGoldenThread");
        vGTrequest.addArgument("id", "gt:kp:" + aKey);
        aContext.issueRequest(vGTrequest);
    }

    public void onDelete(INKFRequestContext aContext) throws Exception {
        // DELETE requires one argument, key, which is mandatory for all
        // verbs and checked by the grammar.
        String aKey = aContext.getThisRequest().getArgumentValue("key");

        // Check validity of arguments.
        if ( aKey.equals("") ) {
            throw new NKFException("request does not contain a valid - key - argument");
        }

        // getting the properties data
        Properties vKernelProperties = new Properties();
        IKernel aKernel = aContext.getKernelContext().getKernel();
        vKernelProperties.load(BootPersistence.sourceBootResource("kernel.properties",aKernel).getInputStream());

        if (! vKernelProperties.containsKey(aKey)) {
            throw new NKFException("property - " + aKey + " - does not exist in kernel.properties");
        }
        
        // removing the key
        ByteArrayOutputStream vBAOS = new ByteArrayOutputStream();
        vKernelProperties.remove(aKey);
        vKernelProperties.store(vBAOS, null);
        
        IBinaryStreamRepresentation vBSR =
                aContext.transrept(vBAOS.toString(), IBinaryStreamRepresentation.class);
        
        BootPersistence.sinkBootResource("kernel.properties",vBSR,aKernel);

        INKFRequest vGTrequest = aContext.createRequest("active:cutGoldenThread");
        vGTrequest.addArgument("id", "gt:kp:" + aKey);
        aContext.issueRequest(vGTrequest);

        // response (boolean)
        INKFRequest vKPrequest = aContext.createRequest("active:kernelproperty");
        vKPrequest.addArgument("key", aKey);
        vKPrequest.setVerb(INKFRequestReadOnly.VERB_EXISTS);
        aContext.createResponseFrom(!(Boolean)(aContext.issueRequest(vKPrequest)));
    }
}
 

My code doesn't often come with warnings, but this time I have to state it is delivered "as is" and that changing kernel properties can potentially mess up your system (for which I take no responsibility).

I can also see some possible improvements on the above code, so by all means try it out and change it as you like.

Enjoy !

2012/08/10

oreilly webcast

Who was it that said holidays are for the children, not the parents ? Whoever it was, he/she is right ! And it is neither the parents, nor the children that are to blame for this, it is our system of outsourcing children (and ourselves) during the whole year and then being put together (both sides looking upon the other as alien) for several weeks, often in a strange situation.

That's quite enough psychology for one day ... last Tuesday I was honoured (honored too) to be able to give a Webcast for O'Reilly. I must say I was very nervous, my laptop made a horrible sound and I couldn't hear or see the audience (the latter two are normal), so I have no idea how it went, you can get the nCode example module I used on https://dl.dropbox.com/u/65770556/urn.org.elbeesee.demo.ncode-1.0.0.zip.


You can see the slides that accompanied the webcast on http://netkernelbook.org/wink/wiki/webcast_20120807.

A recording was made and as soon as that is available I'll add the link to this post.

Enjoy your holidays !

P.S. There is a slightly more advanced webcast on August 28th. Read about it and register on http://www.oreillynet.com/pub/e/2261. Since the nCode stuff went down well, I will only use nCode on that webcast too (the NetKernel equivalent of : Look ma, no hands !).

P.P.S. No, I haven't gone soft. Word reached me that the white on black hurt the eyes of some of my potential readers.