Friday, April 26, 2013

FlexBox made awesome

  Today I had to work with a "flexbox" jQuery plugin(FlexBox) - a combination of a text box and a drop-down list.

  Quick background of the plugin:

  • simple data-cache with list of items that just have an "id" and a "value"
  • hybrid use of either supplied object or AJAX requests for data. AJAX data requests just pass "query" (the text box value), page number and page size.
  • does not store $.data() - so you can't grab an instance of the plugin


Shortly after adding it to the solution I found out that my particular use would require tweaking of the source.
Right there we can see that there are two instances of the plugin and "Frame" is dependent on "Roll", so outright we have several shortfalls of the flexbox:

  • Frame's flexbox must know Roll value
  • Frame's JavaScript cache must be Roll-dependent (in this case)
  • AJAX request for Roll/Frames list should also have other parameters, in my case it was an EventId.
  • Plugin works only with simple AJAX enabled "sources" that just return a string, and I would have like to use ASP.NET WebMethods and the ScriptManager generated methods that are already on page.
  • minor : no option to add a "placeholder" attribute to the input field
The one thing I did not want to do is inject any kind of static code or if statements that would make this plugin unusable outside of this page. Firstly, I created C# classes for working with flexbox - data and AJAX request objects.

[Serializable]
public class FlexBoxValue {
    public int id { get; set; }
    public string name { get; set; }
}
[Serializable]
public class FlexBoxReturnResult {
    public int total { get; set; }
    public FlexBoxValue[] results { get; set; }
}
[Serializable]
public class FlexBoxGetPostObject {
    public string query { get; set; }
    public int pageNumber { get; set; }
    public int pageSize { get; set; }
}
[Serializable]
public class FlexBoxGetRollDataPostObject : FlexBoxGetPostObject {
    public string roll { get; set; }
    public long eventId { get; set; }
}

The "FlexboxGetPostObject" is a clone of the existing simple functionality that just has the three listed parameters, which I expanded for my new WebMethod to have two extra parameters.
The Rolls flexbox was the easier out of the two because I could load the list of rolls on Page Load and just using what was already in the plugin bind the data.
$("#txtRoll").flexbox(QOE.EventInfo.Rolls, {
    "width": 90,
    "placeholder": "Roll",
    queryDelay: 200
});

QOE.EventInfo.Rolls is a serialized "FlexBoxReturnResult" C# object, same object I will use for fetching list of Frames per roll. So far, so good.

It must be mentioned that above code that instances flexbox has "static"(?) parameters, meaning once flexbox is initialized none of the options could have varying values - like Roll dependency for the Frame's flexbox. Also as you would imagine, the internal JS cache for Frames would not depend on anything but its own value, so once "0" search for Roll 001 is cached, you're out of luck for other roll values. So that's why we end up with :
$("#txtFrame").flexbox("custom", {
    method: "PageMethod",
    pageMethod: PageMethods.GetRollData,
    extraProperties: {
        roll: (function () { return $("#txtRoll_input").val(); }),
        eventId: QOE.EventInfo.EventID
    },
    cacheKeyPrefix: (function () { return $("#txtRoll_input").val(); }),
    width: 90,
    placeholder: "Frame",
    queryDelay: 200
});


There are several things going on here. method - previously only supported GET or POST. pageMethod is where I am just passing a function out of my ASP.NET generated JavaScript framework. If you recall the C# classes, there's a class for "basic" flexbox usage and one with "extra parameters" for my WebMethod to find frames. Here I'm making an object to send to ASP.NET, assuming (and you should) that you know the object you WebMethod is expecting. Also notice that "roll" and "cacheKeyPrefix" are actually functions -- this is how we get dynamic values into the flexbox (awesome).

"But how can you just pass functions and values as part of the object for a WebMethod consumption, that shouldn't work", is what you're probably thinking. Plugin code changes:
//o are the plugin's options
if (o.method.toUpperCase() == 'PAGEMETHOD') {
    var postObject = {
        query: q,
        pageNumber: p,
        pageSize: pageSize
    };

    if (o.extraProperties !== null) {
        postObject = $.extend(postObject, o.extraProperties);
        for (prop in postObject) {
            if (typeof (postObject[prop]) == "function") {
                postObject[prop] = postObject[prop]();
            }
        }
    }

    o.pageMethod(postObject,
        function (msg) {
            callback(msg);
        },
        function (msg) {
        });
}

The magic happens in the block where we look for extra properties. Firstly, using jQuery's $.extend, we merge our basic postObject with always-present parameters with WebMethod-specific parameters we defined back on the aspx page (no hardcoding of anything). Secondly, we go through each property and see if it's a "function" and if so we call it and grab a value assigning the value back into the same property. If you look above there are two extra parameters for a frame search : a static EventId that does not change, and a value of the roll flexbox that does change. The last thing is to just call the pageMethod and you're done. Not going to paste the cache-key update because it's very similar function-to-value conversion so that the Frame flexbox can cache Frame values per roll dependency.

So in summary, this modified plugin can work with ASP.NET WebServices, send custom objects, have dynamic parameters, and dynamic value dependent internal cache. Awesome.

Friday, January 27, 2012

ORC

How does one write an OCR ?

Sunday, December 13, 2009

Step 2


Step 2
Originally uploaded by dima.barashkov
Set up simple solving logic of setting Cell's values and eliminating row/column candidates when a value is set.

Onto group logic.
First step would be to make a simple algorithm for generating possible group solutions. Ex. a 2-count group of "5+" would be either 1+4, or 2+3, or a 3-count group of "7+" would be 1+2+4, or 1+3+3.

  • since the number of parts to split a sum(or other action) is variable - have a List of possibles initialized to 1
  • the order is irrelevant so far, so a 1,3,2 solution values is same as 3,2,1
  • for addition, while the sum of parts is less than needed value
  • increment last most value by one, and check if it fits
  • when value has reached to highest board allowed values' transitions should follow like so: On a board size 4 when going from (1,2,4) next should be (1,3,3)

Wednesday, December 9, 2009

Preliminary algorithm/brainstorming.
A main Board class consisting of
  • Array of Cells
  • List of CellGroups
Array of Cells
  • each item in this 2D array would represent each cell of the game
  • has a solution value (or blank if one is not found yet)
  • has a list of possible solution values
List of CellGroups
  • A cell group represents a collection of cells that amount to the group's value based on the action described. ex. 5+ would mean that this Cell-Group individual values should add up to 5
  • a list of Cell references. ex. a particular CellGroup is including Cells[1,1] and [1,2]
  • this class would most likely include most of the game-solving logic.
Preliminary solution logic. I think I will employ something that resembles a "queue" here (//toexplain). Rough logic steps for first few hundred puzzle boards (that are granted to be very simple) in priority order from highest to lowest...
  • Cell's possible-solution contains only one item or Cell text value contains a "=" then set the Cell's solution value
  • Cell's possible-solution contains only one item from the Cell-Groups possible solution values then set the Cell's solution value
Hah! pretty simple to start out with, I think.
Keeping in mind the most general rule of this number game - one unique number per row and column no matter the cell-group rules -- the "queue" I mentioned will work something like this
  • Anytime a Cell's solution value is set, queue up a possible solution value eliminating method from all cells in the same row and column
  • From the bullet above any time a Cell is down to one possible solution value queue up a Cell's solution value to be set... naturally resolving in a recursive loop.

Overlay


Overlay
Originally uploaded by dima.barashkov
In white are the drawn-recognized characters from within a cell.

Now I am ready to actually write the game-solving logic with the cells, underlying groups and math functions that each group has to satisfy.

Everyday Genius



Originally uploaded by dima.barashkov
Progress: Cells are parsed out of the screenshot into their own images. Next up to convert them to b/w image and run the connected shapes algorithm (which has already proven to work). The results of the connected-shapes would be saved for each new unknown character to server as a baseline for recognition.
After that onto the actual game-solving logic.

Tuesday, December 8, 2009

Writing a solver for Everyday Genius : Square logic (http://store.steampowered.com/app/32150). First things first -- image recognition of the game board to figure out what numbers are being used.
I am using a 3rd party connected components c# class that gives me a "connected component" in a list of vertical "spanned" lines that it appears on.

CodeBlocks