2011-01-04

Game comparison chart

As I explained in a previous post, once I start uploading reviewes, I'll refrain from scoring them. Instead, I'll publish a chart with similar games, showing their quality difference with the reviwee.
The last two nights I've been awake for much longer that it is healthy, programming the tool to automatically generate the game charts, based on simple better/worse pairs. Once the ideas were established, it was only a matter of coding, learning how SVG works, remembering how to generate XMLs with Linq's XDocument -never again without it-, and using some advanced XML features.
So, I'll explain in a few words what I have done.

First of all, is the data I work with. I keep a simple XML document with games and comparisons. Games are specified as:
    <games>
        <game id="Dragon Age" year="2009" url="http://social.bioware.com">
            <names>
                <name value="Dragon Age: Origins"/>
            </names>
        </game>
        <game id="Mass Effect 2" year="2010"/>
        <game id="Mass Effect" year="2008"/>
        <game id="Baldur's Gate" year="1996" url="http://www.gog.com"/>
        <game id="Baldur's Gate 2" year="1997">
            <names>
                <name value="Baldur's Gate II"/>
                <name value="Baldur's Gate II: Shadows of Amn"/>
            </names>
        </game>
        <game id="Neverwinter Nights" year="2000"/>
        <game id="Icewind Dale" year="1997"/>
        <game id="KotOR" year="1999">
            <names>
                <name value="Knights of the Old Republic"/>
            </names>
        </game>
        <game id="Oblivion" year="2006">
            <names>
                <name value="The Elder Scrolls IV: Oblivion"/>
            </names>
        </game>
        <game id="Gothic" year="2001"/>
        <game id="Fallout" year="1997"/>
        <game id="Planescape: Torment" year="2000"/>
    </games>
The ID is the name the game is usually known for, the URL would point to that game's review, if I have one up, and the year... I have not verified them for this test file, so they are most probably not the year the game was released.

Then, I have the comparisons, which are much simpler:
    <comparisons>
        <comparison better="Fallout" worse="Baldur's Gate" difference="Little"/>
        <comparison better="Baldur's Gate 2" worse="Baldur's Gate" difference="Little"/>
        <comparison better="Mass Effect" worse="Mass Effect 2" difference="Lots"/>
        <comparison better="Mass effect" worse="Dragon Age" difference="Quite"/>
        <comparison better="Planescape: Torment" worse="Baldur's Gate" difference="Quite"/>
        <comparison better="Dragon age" worse="Neverwinter Nights" difference="Quite"/>
        <comparison better="Dragon Age" worse="Kotor" difference="None"/>
        <comparison better="Baldur's Gate" worse="Icewind Dale" difference="Lots"/>
        <comparison better="Baldur's Gate" worse="Mass Effect" difference="None"/>
        <comparison better="Kotor" worse="Gothic" difference="Lots"/>
        <comparison better="Gothic" worse="Oblivion" difference="None"/>
    </comparisons>
 Basically, a game is better than other by a difference margin. I have defined "Little", "Quite", "Lots" and "None". The latter is a little dirty trick =).

With this info, I just request the chart for a game, with a given number of entries and how far to explore related games (4 steps could return Mass Effect -> Dragon Age -> Kotor -> Gothic). It is possible that, with time, I'll add incorrect cycles or incongruent relationships into the pairs, so I have built a safeguard which warns me when a game would be re-added far from its current position.
This operation explores all games related to the original, and places them in levels (0 is the reference level; games in level -1 are a little worse, those in level 3 are a lot better), repeats with the games just added to the chart, and so on. For my sanity's sake, if a game is already in the chart, it is not added.

Once the chart is complete, it is turned into a nice SVG, with code as simple as this:
            XDocument svgDoc = new XDocument (
                new XDeclaration ("1.0", "UTF-8", "yes"),
                new XDocumentType ("svg", @"-//W3C//DTD SVG 1.1//EN", @"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd", null),
                new XElement (this.svgns + "svg",
                    new XAttribute ("version", 1.1),
                    new XAttribute (XNamespace.Xmlns + "xlink", this.xlinkns),
                    new XAttribute ("width", this.ImageWidth),
                    new XAttribute ("height", this.ImageHeight),
                    new XAttribute ("viewBox", "0 0 " + this.ImageWidth + " " + this.ImageHeight),
                    new XElement (this.svgns + "defs",
                        this.CreateGradientElement ()),
                    this.CreateBackgroundElement (),
                    new XElement (this.svgns + "titles",
                        keys.Select (key =>
                            this.Chart.GetLevelGames (key).Select (game =>
                                this.CreateTitleElement (game, ++titleCount * titleDistance))))));
 New elements are added with things like:
        private XElement CreateLinkedTextElement (string text, string url, float yOffset, string colour, int size)
        {
            XElement linkedText = new XElement (this.svgns + "a",
                new XAttribute (this.xlinkns + "href", url),
                this.CreateTextElement (text, yOffset, colour, size));

            return linkedText;
        }
 or
        private XElement CreateTextElement (string text, float yOffset, string colour, int size)
        {
            XElement textElem = new XElement (this.svgns + "text",
                                    new XAttribute ("x", "50%"),
                                    new XAttribute ("y", yOffset + "%"),
                                    new XAttribute ("font-family", this.FontName),
                                    new XAttribute ("font-size", size),
                                    new XAttribute ("fill", colour),
                                    new XAttribute ("text-anchor", "middle"),
                                    text);

            return textElem;
        }

And the final result is something as pretty as this (note: Blogspot does not support SVG, nor does Internet Explorer, so the image is a PNG imported from the SVG; I'll try to get the SVGs to work, with links and all):
And the SVG code:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="400" viewBox="0 0 200 400" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <linearGradient id="gradient" x1="0%" y1="0%" x2="0%" y2="100%">
      <stop offset="0%" stop-color="Green" />
      <stop offset="50%" stop-color="Blue" />
      <stop offset="100%" stop-color="Red" />
    </linearGradient>
  </defs>
  <rect x="0%" y="0%" width="100%" height="100%" fill="url(#gradient)" />
  <titles>
    <text x="50%" y="9.090909%" font-family="Arial" font-size="14" fill="Black" text-anchor="middle">Planescape: Torment</text>
    <text x="50%" y="18.18182%" font-family="Arial" font-size="14" fill="Black" text-anchor="middle">Fallout</text>
    <text x="50%" y="27.27273%" font-family="Arial" font-size="14" fill="Black" text-anchor="middle">Baldur's Gate 2</text>
    <text x="50%" y="36.36364%" font-family="Arial" font-size="16" fill="White" text-anchor="middle">Mass Effect</text>
    <a xlink:href="http://www.gog.com">
      <text x="50%" y="45.45454%" font-family="Arial" font-size="14" fill="Gold" text-anchor="middle">Baldur's Gate</text>
    </a>
    <a xlink:href="http://social.bioware.com">
      <text x="50%" y="54.54546%" font-family="Arial" font-size="14" fill="Gold" text-anchor="middle">Dragon Age</text>
    </a>
    <text x="50%" y="63.63636%" font-family="Arial" font-size="14" fill="Black" text-anchor="middle">KotOR</text>
    <text x="50%" y="72.72727%" font-family="Arial" font-size="14" fill="Black" text-anchor="middle">Mass Effect 2</text>
    <text x="50%" y="81.81818%" font-family="Arial" font-size="14" fill="Black" text-anchor="middle">Icewind Dale</text>
    <text x="50%" y="90.90909%" font-family="Arial" font-size="14" fill="Black" text-anchor="middle">Neverwinter Nights</text>
  </titles>
</svg>
I hope I'll put all this to some use pretty soon.

3 comments:

  1. Oh my... Kawaii!!!

    ReplyDelete
  2. The previous post link is not working... (should I have used quotes?? only you can answer that >_<)

    xml is so ugly :_(

    ReplyDelete
  3. "Previous post" fixed. Copy/Paste is uglier =)

    As a matter of fact, XML is like a rose: pluck it with your bare hands and hate it; use a cute pair of gardening gloves and admire the beauty. Humans were never really intended to get too dirty with XML.

    ReplyDelete