startshape realStart rule realStart { start { x 2 } } background { b -1 } rule start { terminal4 { r 12 } } rule start { terminal1 { r 12 } } rule start { terminal4 { r 12 } } rule start { terminal4 { r 12 } } rule start .05 { terminal2 { r 12 } } rule terminal4 { SQUARE { b 0.50 s .055 sat 0.40 hue 95 } SQUARE { b 1 s .055 a -.8 } train { x .06 } train { r 90 y .06 } train { r 180 x -.06 } train { r 270 y -.06 } } rule terminal2 2 { SQUARE { b 1 s .055 a -.85 } train { r 90 y .06 } train { r 270 y -.06 } } rule terminal2 1 { SQUARE { b 1 s .055 a -.8 } train { x .06 } train { r 90 y .06 } } rule terminal2 1 { SQUARE { b 1 s .055 a -.85 } train { x.06 } train { r 270 y -.06 } } rule terminal1 { CIRCLE { b 1 s .055 a -.9 } train { r 90 y .06 } } rule terminal1 { CIRCLE { b 1 s .055 a -.9 } train { r -90 y -.06 } } rule train 1 { CIRCLE { b 1 s .045 a -.9 } train { x .06 } } rule train 2 { terminal1 { } } rule train .02 { terminal2 { } } rule train .005 { terminal4 { } } rule train .03 { CIRCLE { b 1 s .03 a -.9 r -95 } }

Keith's Blog

A teen's .net musings

F# Random Projects

Posted on November 28, 2011 at 9:25 PM

So… Its been a while…

In the time since I last posted (April, ouch) I have done many a programming project including a core wars implementation, a raycaster on the windows phone, an AI, and a port of the famous processing library to f#. I will try to post about them soon, but with school and copious amounts of homework, the likely hood of anything getting finished is very low. You may have noticed that I now have the new theme up and that, yes, it has crazy stuff going on in the background. What you are seeing is a fully working implementation of the context free js project in the background. It took forever and its cool, but I am still wondering if it was worth it. I had to lower the opacity of the text in the main portion of the blog to get the background to poke through and so I almost feel that it’s a bit busy (FIXED!!! I had to use rgba background instead of opacity, but now it works fine) Another interesting addition is the fact that my theme is now created using the razor render engine and asp.net 4.0 with blogengine.net 2.5.

The first topic that I want to cover is what f# is itself. F# is without a doubt the most amazing and complete programming language that I have ever had the pleasure to work with. It has the ability of c#, the brevity of ruby, and the power of lisp. It is a statically typed, functional, object oriented beast of a language with type inference, sequence expressions, and a plethora of interesting new data types. With all of this power and speed comes a new REPL command line that gives it the responsiveness and exploratory capabilities of the dynamic scripting languages. I believe without a doubt that this relatively new functional language is the salvation of the .net community and just what the doctor ordered.

Unfortunately I am one of the few who are willing and completely happy with completely switching cold turkey. Along with the age and functional nature of this gem of a language comes some, ahem, setbacks the biggest of which is a lack of tooling support. Although F# does have native .net support and the ability to run on silverlight and the phone, it does not have any of the xaml creation utilities that are a given in a native c# environment. The only project types that come out of the box for F# are a console app, .net library, and the tutorial. This alone is a great setback (or chance to learn how stuff works, depending on your point of view).

In my opinion although there are these difficulties, there is one feature that completely redeems the entire language: Discriminated Unions. These data types allow one to create a complete tree structure with a few lines. They are also useful with embedded dsls. This particular example is in my  AI script engine.

type Line =
    | Group of string * string list
    | Resp of int * string * string

let g name list = Group(name,list)
let r level pattern reply = Resp(level,pattern,reply)
let c level sentence = Resp(level,".+",sentence)

Here you can see that I create one type called line which has two subtypes: group and resp. The part after the initial declaration says what each type can contain. In the case of a group it contains a tuple with a string and a list of strings and with a response an int and two strings. What this allows me to do is create functions that take or return an object of type line, so the signature for g is:

image

So from the point of view of any other function it can treat the return value as a line and all that that implies. Now that is cool, but the true power of the discriminated union shows its ugly mug only when combined with the second most powerful construct in the F# language: Pattern Matching. The basic idea of pattern matching is that it breaks objects down by first type, and then internal parts. Let me explain, Say I have recursive data structure like a tree.

type Tree =
    | Branch of int * Tree * Tree
    | Leaf of int

This structure can then be summed via a simple recursive function

type Tree =
    | Branch of int * Tree * Tree
    | Leaf of int

let rec traverse tree =
    match tree with
    | Branch(value,left,right) -> value + traverse left + traverse right
    | Leaf(value) -> value

If this hasn’t blown your mind, then nobody can! Just to put this in perspective, this is the equivalent c# code. Brace yourself

public class TreeNode
{
    public int Value { get; private set; }
    public TreeNode Left { get; private set; }
    public TreeNode Right { get; private set; }
    public TreeNode(int value)
    {
        Value = value;
    }
    public TreeNode(int value, TreeNode left, TreeNode right)
        :this(value)
    {
        Left = left;
        Right = right;
    }

    public static int Traverse(TreeNode node)
    {
        if (node.Left != null && node.Right != null)
        {
            return node.Value + Traverse(node.Left) + Traverse(node.Right);
        }
        else
        {
            return node.Value;
        }
    }
}

That leaves us with a grand total of 28 to 8 lines. Even if you only count substantial lines, that gives 12 to 8. That is not to say that the F# is any worse to read like other succinct tar pits. It is simply a better and more dense way of representing the same information. One feature I did not describe with the unions is that they can have static and instance members which act just the same as objects and methods on normal c# classes. This allows you to have fundamentally different objects with similar methods on them and type checking.

Did I mention that I liked f#?

Algorithm Ink and Zoomooz

Posted on April 3, 2011 at 8:21 PM

The other day I went to my site and realized that it looks awful. Ok, maybe not awful, but it definitely needed some improvements. So, I have decided to completely rewrite it in my own code. This means learning html again, using cool libraries and finding what the heck this blog engine does to my posts and how to make that presentable. I hope I have success, but I do know that it will be fun and I will learn some things regardless ( maybe not to mess with html ). Wish me luck.

One of the many libraries that I am thinking about using is called Contextfree.js. This little gem was written by Aza Raskin as a port for context free to javascript. The idea is that you write code in a special language which is then translated into recursive drawing algorithms for the system to use in the new html 5 canvas object. I hope to use this as my background, but I have not completely decided yet. I was looking into this and found that the main place that has used it effectively was the Algorithm Ink site made by Aza. He is the kind of guy that figures samples are the apps he makes, so I had to go into the code of his applet and try to figure out how to use it. It was a bit gnarly, but I think I will be able to grasp as much as I need.

Another library is the Zoomooz library. This little script makes every dom object zoomable.

Let me explain. The idea is that the internet is becoming a playground of many hundreds of different devices and the pages need to get updated to fit the bill for a more tactile experience. The guys that created Zoomooz decided that the solution to this problem was to make the page visually zoom in to the objects on the page, making it so that the page can hold more and be usable in many different places. Brilliant right! Anyway I have decided to use it in my new theme as the main nav system. Hope it works.

JSAdventure

Posted on May 11, 2010 at 4:50 PM

Copy this code into an html file and then run it and you will get the saving on your computer. All this does is make it so that you get the saved cookies on your computer instead of the site which makes it so that they don’t get saved to this domain but yours which for some reason seems to work better.

<html>
<head>
    <title>Text Adventure</title>
</head>
<body onunload="save()">
    <p id="p0.3">
        You do not have javascript enabled currently. Please enable javascript to play my
        game.</p>
    <script type="text/javascript">
        "use strict";
        var versionNumber = 0.3;
        var paragraphElementID = "p" + versionNumber;
        var currentRoom;
        var rooms = {};
        var inventory = {};
        var output = "";
        var header = "<b>JSAdventure version " + versionNumber + "</b> <br/> <br/>";
        var setUp = false;
        var jsonizer = {};

        // implementation of JSON.stringify borrowed from http://www.sitepoint.com/blogs/2009/08/19/javascript-json-serialization/
        jsonizer.stringify = function (obj)
        {
            var type = typeof (obj);
            if (type !== "object" || obj === null)
            {
                // simple data type
                if (type === "string")
                {
                    obj = '"' + obj + '"';
                }
                return String(obj);
            }
            else
            {
                // recurse array or object
                var nameOfElement;
                var v;
                var json = [];
                var isArray = (obj && obj.constructor === Array);
                for (nameOfElement in obj)
                {
                    if (obj.hasOwnProperty(nameOfElement))
                    {
                        v = obj[nameOfElement];
                        type = typeof (v);

                        if (type === "string")
                        {
                            v = '"' + v + '"';
                        }
                        else if (type === "object" && v !== null)
                        {
                            v = jsonizer.stringify(v);
                        }

                        json.push((isArray ? "" : '"' + nameOfElement + '":') + String(v));
                    }
                }
                return (isArray ? "[" : "{") + String(json) + (isArray ? "]" : "}");
            }
        };

        // implementation JSON.parse borrowed from http://www.sitepoint.com/blogs/2009/08/19/javascript-json-serialization/
        jsonizer.parse = function (str)
        {
            if (str === "")
            {
                str = '""';
            }
            var func = new Function("var p =" + str + ";" +
    "return p;");
            return func();
        };

        function CreateCookie(name, value, days)
        {
            var expires = "";
            if (days)
            {
                var date = new Date();
                date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
                expires = "; expires=" + date.toGMTString();
            }
            value = value.replace(/\;/g, "#");
            document.cookie = name + "=" + value + expires + "; path=/";
        }

        function ReadCookie(name)
        {
            var nameEQ = name + "=";
            var ca = document.cookie.split(';');
            for (var i = 0; i < ca.length; i = i + 1)
            {
                var c = ca[i];
                while (c.charAt(0) === ' ')
                {
                    c = c.substring(1, c.length);
                }
                if (c.indexOf(nameEQ) === 0)
                {
                    var string = c.substring(nameEQ.length, c.length);
                    string = string.replace(/#/g, ";");
                    return string;
                }
            }
            return null;
        }

        function AddToOutPut(text)
        {
            output += text;
        }

        function AddLinkToOutput(text, functionName, functionParams)
        {
            AddToOutPut("<b onclick=\"" + functionName + "(" + functionParams + ")\">" + text + "</b>");
        }

        function AddToCurrentText(text)
        {
            document.getElementById(paragraphElementID).innerHTML += "<br/" + text;
        }

        function WriteStoredLine()
        {
            document.getElementById(paragraphElementID).innerHTML = header + output;
            output = "";
        }

        function WriteDiscription()
        {
            AddToOutPut("You are in " + currentRoom.name + "<br/><br/>");
            AddToOutPut(currentRoom.discription + "<br/><br/>");
            for (var direction in currentRoom.exits)
            {
                if (currentRoom.exits.hasOwnProperty(direction))
                {
                    AddLinkToOutput(direction + " ", "Go", "'" + direction + "'");
                    if (currentRoom.exits[direction].key)
                    {
                        AddToOutPut("there is a locked exit.<br/>");
                    }
                    else
                    {
                        AddToOutPut("there is an exit.<br/>");
                    }
                }
            }
            for (var x in currentRoom.items)
            {
                if (currentRoom.items.hasOwnProperty(x))
                {
                    AddToOutPut("There is a ");
                    AddLinkToOutput(x, "GetOrDrop", "'" + x + "'");
                    AddToOutPut(" in the room.<br/>");
                }
            }
            AddToOutPut("<br/>");
            for (var itemName in inventory)
            {
                AddToOutPut("You have a ");
                AddLinkToOutput(itemName, "GetOrDrop", "'" + itemName + "'");
                AddToOutPut(" in your inventory.<br/>");
                if (inventory[itemName].listOfUses)
                {
                    AddToOutPut("With it you can:<br/>");
                    for (var useName in inventory[itemName].listOfUses)
                    {
                        if (inventory[itemName].listOfUses.hasOwnProperty(useName))
                        {
                            AddToOutPut("&nbsp&nbsp&nbsp&nbsp&nbsp");
                            AddLinkToOutput(useName, "Use", "'" + itemName + "','" + useName + "'");
                        }
                    }
                }
            }
            WriteStoredLine();
        }

        function Room(name, discription)
        {
            this.name = name;
            this.discription = discription;
            this.actors = {};
            this.items = {};
            this.exits = {};
            this.visits = 0;
            this.events = [];
            rooms[name] = this;
        }

        function Exit(direction, room, targetRoom, key)
        {
            this.direction = direction;
            room.exits[direction] = this;
            this.targetRoom = targetRoom;
            this.key = key;
        }

        function Item(name, container, carryable, listOfUses)
        {
            this.name = name;
            if (container)
            {
                container.items[name] = this;
            }
            else
            {
                inventory[name] = this;
            }
            this.carryable = carryable;
            this.listOfUses = listOfUses;
        }

        function Event(room, action, timesInRoom)
        {
            rooms[room].events.push(this);
            this.action = action;
            this.timesInRoom = timesInRoom;
        }

        function GetOrDrop(item)
        {
            var found = false;
            for (var name in currentRoom.items)
            {
                if (name === item)
                {
                    if (currentRoom.items[name].carryable === true)
                    {
                        inventory[name] = currentRoom.items[name];
                        delete currentRoom.items[name];
                        WriteDiscription();
                        found = true;
                    }
                    else
                    {
                        AddToCurrentText("You can't carry that! It's too big!");
                    }
                }
            }
            if (!found)
            {
                for (var itemName in inventory)
                {
                    if (itemName === item)
                    {
                        currentRoom.items[itemName] = inventory[itemName];
                        delete inventory[itemName];
                        WriteDiscription();
                    }
                }
            }
        }

        function Go(direction)
        {
            for (var i in currentRoom.exits)
            {
                if (currentRoom.exits[i].direction === direction)
                {
                    var door = currentRoom.exits[i];
                    if (!door.key)
                    {
                        if (rooms[door.targetRoom] === currentRoom)
                        {
                            AddToOutPut("You go through the exit and somehow come right back to the room you were just in... Weird...<br/><br/>");
                        }
                        currentRoom = rooms[door.targetRoom];
                        currentRoom.visits += 1;
                        WriteDiscription();
                    }
                    else
                    {
                        for (var x in inventory)
                        {
                            if (x == door.key.name)
                            {
                                door.key = null;
                                currentRoom = rooms[door.targetRoom];
                                currentRoom.visits += 1;
                                WriteDiscription();
                                AddToCurrentText("You unlocked the door with your " + x + " and step through.");
                                break;
                            }
                        }
                        if (door.key)
                        {
                            AddToCurrentText("You can't go that way because it's locked with a " + door.key.name);
                        }
                    }
                    break;
                }
            }
        }

        function Use(nameofitem, nameofuse)
        {
            for (var i in inventory)
            {
                if (i === nameofitem)
                {
                    var tool = inventory[i];
                    for (var z in tool.listOfUses)
                    {
                        if (z === nameofuse)
                        {
                            var func = new Function(tool.listOfUses[z]);
                            func();
                        }
                    }
                }
            }
        }

        function save()
        {
            if (setUp === true)
            {
                var inventoryJSON = jsonizer.stringify(inventory);
                var roomsJSON = jsonizer.stringify(rooms);
                CreateCookie("roomsJSON" + versionNumber, roomsJSON, 50);
                CreateCookie("inventoryJSON" + versionNumber, inventoryJSON, 50);
                CreateCookie("currentRoomName" + versionNumber, currentRoom.name, 50);
                CreateCookie("currentText" + versionNumber, document.getElementById(paragraphElementID).innerHTML, 50);
                CreateCookie("saved" + versionNumber, "saved", 50);
            }
        }

        if (ReadCookie("saved" + versionNumber))
        {
            rooms = jsonizer.parse(ReadCookie("roomsJSON" + versionNumber));
            inventory = jsonizer.parse(ReadCookie("inventoryJSON" + versionNumber));
            currentRoom = rooms[ReadCookie("currentRoomName" + versionNumber)];
            var text = ReadCookie("currentText" + versionNumber);
            text = text.replace(/$/g, "");
            document.getElementById(paragraphElementID).innerHTML = text;
            setUp = true;
        }
        else
        {
            var room1 = new Room("Room1", "A plain room... what else?");
            var room2 = new Room("Room2", "Another plain room... sigh...");
            var room3 = new Room("Room3", "Yet a third plain room... I'll give you a guess what the next one is!");
            var room4 = new Room("Room4", "Yep! Another plain one.");
            var room5 = new Room("Room5", "This is a secret room!!!!");
            var room6 = new Room("Room6", "Congrats! you just got past the first puzzle.");

            currentRoom = room1;
            currentRoom.visits += 1;

            var bouncyBall = new Item("small bouncy ball", room1, true, null);
            var bolder = new Item("large rock", room3, false, null);
            var key = new Item("key", room3, true, null);
            var bombuses = {};
            bombuses["blow up rock"] = "" +
                "for (var i in currentRoom.items)" +
                "{" +
                    "if (currentRoom.items[i].name == 'large rock')" +
                    "{" +
                        "delete currentRoom.items[i];" +
                        "for (var x in inventory) " +
                        "{" +
                            "if (inventory[x].name == 'bomb') " +
                            "{" +
                                "delete inventory[x];" +
                            "}" +
                        "}" +
                        "for(var room in rooms)" +
                        "{" +
                            "if(room == 'Room3')" +
                            "{" +
                                "new Exit('Down', rooms[room], 'Room6', null);" +
                            "}" +
                            "else if(room == 'Room6')" +
                            "{" +
                                "new Exit('Up', rooms[room], 'Room3', null);" +
                            "}" +
                        "}" +
                        "WriteDiscription();" +
                        "AddToOutPut('<br/>You use your bomb to blow up the rock<br/> and it leaves a small opening you can crawl through.');" +
                        "AddToCurrentText(output);" +
                        "output = '';" +
                        "return;" +
                    "}" +
                "}" +
                "AddToCurrentText('<br/>There is nothing to blow up in here...');";
            var bomb = new Item("bomb", room5, true, bombuses);

            var room1exit1 = new Exit("South", room1, "Room2", null);
            var room2exit1 = new Exit("North", room2, "Room1", null);
            var room2exit2 = new Exit("East", room2, "Room3", null);
            var room3exit1 = new Exit("West", room3, "Room2", null);
            var room3exit2 = new Exit("South", room3, "Room4", null);
            var room4exit1 = new Exit("North", room4, "Room3", null);
            var room4exit2 = new Exit("South", room4, "Room5", key);
            var room5exit = new Exit("North", room5, "Room4", null);

            WriteDiscription();
            setUp = true;
        }
    </script>
</body>
</html>

JSAdventure Item Uses and Saving

Posted on May 11, 2010 at 1:30 PM

In this version I have added two major features. First is the ability to create uses for the items. For instance I made a boulder in the previous version that was not carryable. Now with the bomb in the new locked room you can blow up the boulder and reveal an exit leading down to a new room. This was all possible through JavaScript's eval. I take the string which is defined in creation of the item and call eval on it which will parse the string and execute it as though it were just plain JavaScript. I have found this somewhat useful and effective only because it allows my program to be easily extended, though there are some things that you will need to think about before using it every where. First is the fact that all eval code should only be taken from your own program and nowhere else. This is because if you were to gather it from some other source it might be liable to tampering and could cause data loss or some other hap hazard effect. The only places I have found that this was useful was in this case and in json which I have used in this update as well. Here is the code for the uses.

function Use(nameofitem, nameofuse)
{
    for (var i in inventory)
    {
        if (i === nameofitem)
        {
            var tool = inventory[i];
            for (var z in tool.listOfUses)
            {
                if (z === nameofuse)
                {
                    var func = new Function(tool.listOfUses[z]);
                    func();
                }
            }
        }
    }
}

As you can see I try not to have actual references to any of the objects and so I keep the names instead. And using the objects in JavaScript (or associative arrays, they are one in the same) I can access the item or exit or whatever that I need just from the name of the object and it’s container. Then once I have the item I iterate through the objects list of uses to find the one I want and then I execute it. It’s as simple as that, but now if I wanted I could create an admin console or something in c# to generate whatever I’d like and just convert it to json to be used as the map or objects that I can create.

The last thing that I added into this version was the use of cookies for saving state. I searched and searched all over the internet for a good serializer when I realized that it was all right under my nose! json when trying to serialize straight to and from JavaScript on the same client is amazing and it was quite straight forward too. I just take some code to serialize ( borrowed from here ) into json and then just call eval to bring it back.

jsonizer.stringify = function (obj)
{
    var type = typeof (obj);
    if (type !== "object" || obj === null)
    {
        // simple data type
        if (type === "string")
        {
            obj = '"' + obj + '"';
        }
        return String(obj);
    }
    else
    {
        // recurse array or object
        var nameOfElement;
        var v;
        var json = [];
        var isArray = (obj && obj.constructor === Array);
        for (nameOfElement in obj)
        {
            if (obj.hasOwnProperty(nameOfElement))
            {
                v = obj[nameOfElement];
                type = typeof (v);
                if (type === "string")
                {
                    v = '"' + v + '"';
                }
                else if (type === "object" && v !== null)
                {
                    v = jsonizer.stringify(v);
                }
                json.push((isArray ? "" : '"' + nameOfElement + '":') + String(v));
            }
        }
        return (isArray ? "[" : "{") + String(json) + (isArray ? "]" : "}");
    }
};

The code is a little complicated ( I still don’t exactly know all that v does, but I the basic idea is to iterate over all of the properties of the object (or associative array which allows us to for loop over them) and either return a hard coded value, or rinse and repeat on the new object. Then what you get back is all of the literals of the object that you wanted serialized. Then when I want any of it back I just eval the string and I get back the object with all of it’s literals and everything still intact.

Ok, so I lied about that being the very last thing but this one is minor. I was looking for a good source analysis program for JavaScript and stumbled over jslint. At first the site looks like some small side project that somebody just works on in their spare time, but don’t be fooled, it can pack a powerful punch! The premise is that you take your crappy JavaScript code and put it all into jslint and it will yell at you telling you that it’s crappy. It actually found over 1000 problems with my code and I fixed every single one. Now I'm not going to say that my program is perfect ( you have to realize that I started coding in JavaScript last week ) but it sure is a lot better than it was.

So if anyone cares… Here it is!

You do not have javascript enabled, please enable it and try again.

DPMud lives on! For now...

Posted on April 25, 2010 at 7:41 AM

We have finaly given up on adding security to the login for dpmud because it was just moving to slowely and the mixture o wcf and silverlight doesn't leave a good taste in my mouth. So we move on. No promises when we will finish though...