Thursday, 28 March 2019

Ekam: Core Model Improvements

Finally got some Ekam work done again. As always, code can be found at:

http://code.google.com/p/ekam/

This weekend and last were spent slightly re-designing the basic model used to track dependencies between actions. I think it is simpler and more versatile now.

Ekam build model

I haven't really discussed Ekam's core logic before, only the features built on top of it. Let's do that now. Here are the basic objects that Ekam works with:

  • Files: Obviously, there are some set of input (source) files, some intermediate files, and some output files. Actually, at the moment, there is no real support for "outputs" -- everything goes to the "intermediate files" directory (tmp) and you have to dig out the one you're interested in.
  • Tags: Each file has a set of tags. Each tag is just a text string (or rather, a hash of a text string, for efficiency). Tags can mean anything, but usually each tag indicates something that the file provides which something else might depend on.
  • Actions: An action takes some set of input files and produces some set of output files. The inputs may be source files, or they may be the outputs of other actions. An action is specific to a particular set of files -- e.g. each C++ source file has a separate "compile" action. An action may search for inputs by tag, and may add new tags to any file (inputs and outputs). Note that applying tags to inputs is useful for implementing actions which simply scan existing files to see what they provide.
  • Rules: A rule describes how to construct a particular action given a particular input file with a particular tag. In fact, currently the class representing a rule in Ekam is called ActionFactory. Each rule defines some set of "trigger" tags in which it is interested, and whenever Ekam encounters one of those tags, the rule is asked to generate an action based on the file that defined the tag.

Example

There is one rule which defines how to compile C++ source files. This rule triggers on the tag "filetype:.cpp", so Ekam calls it whenever it sees a file name with the .cpp extension. The rule compiles the file to produce an object file, and adds a tag to the object file for every C++ symbol defined within it.

Meanwhile, another rule defines how to link object files into a binary. This rule triggers on the tag "c++symbol:main" to pick up object files which define a main() function. When it finds one, it checks that object file to see what external symbols it references, and then asks Ekam to find other object files with the corresponding tags. It does this recursively until it can't find any more objects, then attempts to link (even if some symbols weren't found).

If not all objects needed by the binary have been compiled yet, then this link will fail. That's fine, because Ekam remembers what tags the action asked for. If, later on, one of the missing tags shows up, Ekam will retry the link action that failed before, to see if the new tag makes a difference. Assuming the source code is complete, the link should eventually succeed. If not, once Ekam has nothing left to do, it will report to the user whatever errors remain.

Note that Ekam will retry an action any time any of the tags it asked for change. So, for example, say the binary calls malloc(). The link action may have searched for "c++symbol:malloc" and found nothing. But, the link may have succeeded despite this, because malloc() is defined by the C runtime. Later on, Ekam might find some other definition of malloc() elsewhere. When it does, it will re-run the link action from before to make sure it gets a chance to link in this new malloc() implementation instead.

Say Ekam then finds yet another malloc() implementation somewhere else. Ekam will then try to decide which malloc() is preferred by the link action. By default, the file which is closest to the action's trigger file will be used. So when linking foo/bar/main.o, Ekam will prefer foo/mem/myMalloc.cpp over bar/anotherMalloc.cpp -- the file name with the longest common prefix is preferred. Ties are broken by alphabetical ordering. In the future, it will be possible for a package to explicitly specify preferences when the default is not good enough.

The work I did over the last two weekends made Ekam able to handle and choose between multiple instances of the same tag.

Up next

  • I still have the code laying around to intercept open() calls from arbitrary processes. I intend to use this to intercept the compiler's attempts to search for included headers, and translate those into Ekam tag lookups. Once Ekam responds with a file name, the intercepted open() will open that file instead. Thus the compile action will not need to know ahead of time what the dependencies are, in order to construct an include path.
  • I would like to implement the mechanism by which preferences are specified sometime soon. I think the mechanism should also provide a way to define visibility of files and/or tags defined within a package, in order to prevent others from depending on your internal implementation details.
  • I need to make Ekam into a daemon that runs continuously in the background, detecting when source files change and immediately rebuilding. This will allow Ekam to perform incremental builds, which currently it does not do. Longer term, Ekam should persist its state somehow, but I think simply running as a daemon should be good enough for most users. Rebuilding from scratch once a day or so is not so bad, right?
  • Rules, rules, rules! Implement fully-featured C++ rules (supporting libraries and such), Java rules, Protobufs, etc.
  • Documentation. Including user documentation, implementation documentation, and code comments.

After the above, I think Ekam will be useful enough to start actually using.

What Is ‘Adaptive’ Learning?

Personalised 'adaptive' learning came top of this 2019 survey in L&D. Having spent a few years involved with an adaptive learning company, delivering real adaption to real learners, on scale, I thought I'd try to explain what it is, a taxonomy of adaptive learning. The problem is that the word has been applied to many things from simple pre-test assessment to full-blown algorithmic and machine learning adaption, and lots in-between. 
In essence it means adapting the online experience to the individual's needs as they learn, in the way a personal tutor would adapt. The aim is to provide, what many teachers provide, a learning experience that is tailored to the needs of you as an individual learner. 
Benjamin Bloom, best know for his taxonomy of learning, wrote a now famous paper, The 2 Sigma Problem, which compared the lecture, formative feedback lecture and one-to-one tuition. It is a landmark in adaptive learning. Taking the 'straight lecture' as the mean, he found an 84% increase in mastery above the mean for a 'formative feedback' approach to teaching and an astonishing 98% increase in mastery for 'one-to-one tuition'. Google's Peter Norvig famously said that if you only have to read one paper to support  online learning, this is it. In other words, the increase in efficacy for tailored  one-to-one, because of the increase in on-task learning, is huge. This paper deserves to be read by anyone looking at improving the efficacy of learning as it shows hugely significant improvements by simply altering the way teachers interact with learners. Online learning has to date mostly delivered fairly linear and non-adaptive experiences, whether it's through self-paced structured learning, scenario-based learning, simulations or informal learning. But we are now in the position of having technology, especially AI, that can deliver what Bloom called 'one-to-one learning'.
Adaption can be many things but at the heart of the process is a decision to present something to the learner based on what the system knows about the learners, learning or context.

Pre-course adaptive
Macro-decisions
You can adapt a learning journey at the macro level, recommending skills, courses, even careers based on your individual needs.
Pre-test
'Pre-test' the learner, to create a prior profile, before staring the course, then present relevant content. The adaptive software makes a decision based on data specific to that individual. You may start with personal data, such as educational background, competence in previous courses and so on. This is a highly deterministic approach that has limited personalisation and learning benefits but may prevent many from taking unnecessary courses.
Test-out
Allow learners to 'test-out' at points in the course to save them time on progression. This short-circuits unnecessary work but has limited benefits in terms of varied learning for individuals.
Preference
Ask or test the learner for their learning style or media preference. Unfortunately, research has shown that false constructs such as learning styles, which do not exist, make no difference on learning outcomes. Personality type is another, although one must be careful with poorly validated outputs from the likes of Myers-Briggs. The OCEAN model is much better validated. One can also use learner opinions, although this is also fraught with danger. Learners are often quite mistaken, not only about what they have learnt but also optimal strategies for learning. So, it is possible to use all sorts of personal data to determine how and what someone should be taught but one has to be very, very careful.

Within-course adaptive
Micro-adaptive courses adjust frequently during a course to determine different routes based on their preferences, what the learner has done or based on specially designed algorithms. A lot of adaptive software within courses uses re-sequencing. The idea is that most learning goes wrong when things are presented that are either too easy, too hard or not relevant for the learner at that moment. One can us the idea of desirable difficulty here to determine a learning experience that is challenging enough to keep the learner driving forward.
Preference
Decision within a course are determined by user choices or assessed preferences. There is little evidence that this works.
Rule-based
Decisions are based on a rule or set of rules, at its simplest a conditional if… then… decision but I often a sequence of rules that determine the learner's progress.
Algorithm-based
It is worth introducing AI at this point, as it is having a profound effect on all areas of human endeavour. It is inevitable, in my view, that this will also happen in the learning game. Adaptive learning is how the large tech companies deliver to your timeline on Facebook/Twitter, sell to you on Amazon, get you to watch stuff on Netflix. They use an array of techniques based on data they gather, statistics, data mining and AI techniques to improve the delivery of their service to you as an individual. Evidence that AI and adaptive techniques will work in learning, especially in adaption, is there on every device on almost every service we use online. Education is just a bit of a slow learner.
Decisions may be based simply on what the system thinks your level of capability is at that moment, based on formative assessment and other factors. The regular testing of learners, not only improves retention, it gathers useful data about what the system knows about the learner. Failure is not a problem here. Indeed, evidence suggests that making mistakes may be critical to good learning strategies.
Decisions within a course use an algorithm with complex data needs. This provides a much more powerful method for dynamic decision making. At this more fine-grained level, every screen can be regarded as a fresh adaption at that specific point in the course.
Machine learning adaption
AI techniques can, of course, be used in systems that learn and improve as they go. Such systems are often trained using data at the start and then use data as they go to improve the system. The more learners use the system, the better it becomes.
Confidence adaption
Another measure, common in adaptive systems, is the measurement of confidence. You may be asked a question then also asked how confident you are of your answer.
Learning theory 
Good learning theory can also be baked into the algorithms, such as retrieval, interleaving and spaced practice. Care can be taken over cognitive load and even personalised performance support provided adapting to an individuals availability and schedule. Duolingo is sensitive to these needs and provides spaced-practice, aware of the fact that you may have not done anything recently and forgotten stuff. Embodying good learning theory and practice may be what is needed to introduce often counterintuitive methods into teaching, that are resisted by human teachers.

Across courses adaptive
Aggregated data
Aggregated data from a learner' performance on a previous or previous courses can be used. As can aggregated data of all students who have taken the course. One has to be careful here, as one cohort may have started at a different level of competence than another cohort. There may also be differences on other skills, such as reading comprehension, background knowledge, English as a second language and so on.
Adaptive across curricula
Adaptive software can be applied within a course, across a set of courses but also across an entire curriculum. The idea is that personalisation becomes more targeted, the more you use the system and that competences identified earlier may help determine later sequencing.

Post-course adaptive
Adaptive assessment systems
There's also adaptive assessment, where test items are presented, based on your performance on previous questions. They often start with a mean test item then select harder or easier items as the learner progresses.
Memory retention systems
Some adaptive systems focus on memory retrieval, retention and recall. They present content, often in a spaced-practice pattern and repeat, remediate and retest to increase retention. These can be powerful systems for the consolidation of learning.
Performance support adaption
Moving beyond courses to performance support, delivering learning when you need it, is another form of adaptive delivery that can be sensitive to your individual needs as well as context. These have been delivered within the workflow, often embedded in social communications systems, sometimes as chatbots.

Conclusion
There are many forms of adaptive learning, in terms of the points of intervention, basis of adaption, technology and purpose. If you want to experience one that is accessible and free, try Duolingo, with 200 million registered users, where structured topics are introduced, alongside basic grammar 

THE AMAZING SPIDERMAN 400MB GAME ON ANDROID !

THE AMAZING SPIDERMAN 400MB GAME ON ANDROID



Get ready for intense web-slinging action with The Amazing Spider-Man! Join Spidey in the official game app of this highly anticipated 2012 blockbuster! Play through the movie storyline as Spider-Man faces off against the Lizard and rampaging gangs. Web-sling and crawl your way through an open, fully 3D New York while using your amazing skills to save the city.

** Note that The Amazing Spider-Man needs 2GB of free memory to install **

THE OFFICIAL GAME OF 2012's HIGHLY AWAITED SUPER HERO BLOCKBUSTER

FREE NEW YORK CITY
• Explore the city through its five distinctive districts (Central Park, Business, Downtown, Pier and Residential)
• An exciting and enjoyable fighting system with melee, ranged, combo attacks and much more
• A wide selection of upgrades to customize your style, attacks and skills.

DOWNLOAD GAME FILES APK+DATA: DOWNLOAD APK+DATA (Via Drive)


LINK 2: DOWNLOAD APK+DATA



Minimum hardware requirements to play The Amazing Spider-Man:


- 1 GHz CPU
- 512 MB RAM
- PowerVR SGX540 GPU or equivalent
- 1.5 GB of free space on the device

For optimal performance, we recommend restarting your device and closing other applications before playing The Amazing Spider-Man.

Wednesday, 27 March 2019

Elopement Packages Bring Out Some Raw Emotions

By David Morgan


Traditionally, romance has had a way of breaking all the rules. Who writes the rules anyway, right? But how do you cut out the burdens that are placed on you and your future forever? How do you celebrate your union in a private setting that truly makes you the center of attention and share your raw emotions? Is there really even a rule that stipulates that friends, family and even strangers have to bear witness to this particular moment in time? I m thinking: Elopement packages.

We are, in all honesty, living in exciting times where companies specifically design escapes for lovebirds. There are an array of escapes that will fit your budget perfectly. From seaside destinations to mountainous pastures in remote regions. The negative stigma has lost its gravitas.

Who wants the hassle of planning a wedding that must meet expectations that aren t even fully yours? How about sifting through a guest list of people deemed worthy of how much you are able to spend before breaking the bank? Do you even care about assigning roles and holding people accountable for their deliverable? None of that sounds romantic and worse still it can come across as uncontrollable.

At its core, marriage is about the coming together of two people, sharing a common goal and following through. Those in support of your union will not be there behind closed doors where the real work happens. It is in no way a betrayal. Rather it is an affirmation of your merger which can never be taken away.

Organizing this isn t that hard a job if you have all your particulars, witnesses and someone who can legally marry you. Getting married is so easy today that if you wanted you to ask you best friend get officiated. This means you could sneak off to a beautiful spot where and get married by someone you both love. After all that you can take all that money you were going to spend, and go on an awesome honeymoon.

With accommodation covered for you and your loved one, complimentary breakfast with champagne, a complimentary minibar with an array of drinks and a catered ceremony. Does all of that now sound like the ideal situation? An additional plus is that the honeymoon comes included in a beautiful and exotic location.

Think of it as an adventure. A chance to share a crazy story with your friends, family, and colleagues. Being the author of your own story has never been easier. Avoiding certain family members also comes as a benefit especially with the family members that clash. The last thing you want on your big day is to be stressing about all the things that are going wrong.

To put it plainly, you can feel free to throw the rulebook out the window. The fact that people are resistant to change because of a lack of understanding is far from an applicable reason not to go for it. You are deserving of all the things you want out of life, settling for less will only leave you regretting the opportunities missed.




About the Author:



Guest Post: Student Andrew Lipian Attends GDC On A GANG Scholarship

I'm delighted to have my first guest post on my blog!  The below is written by my first ever one-on-one game audio student, Andrew Lipian.  Andrew won a student scholarship from the Game Audio Network Guild to attend GDC in March and I ask him to document his experience.  I thought it'd be cool to hear about the conference from the viewpoint an attendee who is both very interested in the field, a young up-comer in the area, and who went to GDC on scholarship.  Also, a great chance for him to synthesize all the notes he took there and his overall experience.  Andrew will have a second post upcoming soon as well where he describes his recent experience at NYU Steinhardt's Video Game Scoring Workshop.  



Four months have passed since the Game Developers Conference (GDC) in San Fransisco, where droves of video game industry elites gather annually to discuss the mechanics and business of gaming. I recall a large, imposing map of the world in one of the conference halls with the words "where are you from," scribed above it. The map was bathed in little red dots indicating where attendees hailed from; not even Siberia was without a few. As I squinted between the chicken pox markers to find my home in Ohio, I began to reflect on the awesome conditions that brought me to this remarkable conference; how, exactly, did I get here?

Why, studying video game music with Matthew Thompson, of course! His guidance helped make my secret passion for game audio a not-so-secret passion by having me apply for a longshot scholarship to the Game Audio Network Guild. This award included an All Access pass to GDC with a personal industry mentor in game audio. I submitted a 1-minute RPG-style battle track I wrote under Thompson's supervision, a narrative with some letters of recommendation, and I was elated to see I was selected for the award! The University of Michigan School of Music Theater and Dance (SMTD) even paid for my flight! 

What would follow? A whirlwind of corporate convention constructs the size of circus tents, endless panels and seminars on all aspects of game development; industry titans roaming about like average Joes, and a bevy of indie video game stations ready for play. 

The Moscone Center, host of GDC, was a veritable sea of people. The complex is broken into three massive buildings (North, East, and West), the former two with sprawling convention expos in each basement (if you can call something the size of a NASA Space Silo a basement). Throngs of video game journalists, voice actors, narrative writers, graphics artists, directors, CEOs, programmers, and game designers painted the halls and courtyards. While I enjoyed these diverse people and their ideas, what I was really there for was the Game Audio. 





I would soon be greeted by my assigned mentor, Adam Gubman. CEO and founder of Moonwalk Audio, who has written music for hundreds of clients such as Disney, Zynga, Storm8, Sony, PlayFirst, GSN, GameHouse, NBC Today, and Warner-Chappell – to name a few. We met at one of the many meet-and-greet tables on the third floor of Moscone West, where I would get acquainted with one of the most motivated people I have ever met. With a forward, engaged posture and a surveying glance, Gubman was a dodecahedra-tattooed, spikey haired mensch; intense and cool, with a quick wit and boundless passion for music. He also had a no-nonsense approach to success: if you want this, work hard every day, don't burn bridges, absorb all you can, and persist. I've seen men of his intensity in successful musicians like Tommy Tallarico and Tom Salta and have come to identify it as the flagship trait that makes these men so successful. Their time is precious, they waste precious little of it, and tackle every task with speed and abandon. 

Adam would prove an impactful mentor, spending a great deal of time with me despite a very busy schedule of his own. Explaining a personal story of how a demo song of his won a Golden Globe, Gubman said you never know what each opportunity could bring. Demonstrating loyalty and compassion, he tells me, creates a "halo effect," building rapport and camaraderie with potential clients. Trust and Loyalty, Adam believes, set you apart from other composers and earn you respect. He advised I take on GDC as a sponge, absorbing all I could, and give my time to every opportunity, even if the upshot for involvement wasn't clear yet; I decided to run with his advice.

There was no shortage of sessions to enjoy in game audio. From a seminar in VR audio, featuring Winnifred Phillips as lecturer, we analyzed how special positioning for music can be more immersive than stereo in this medium, using 3D elements to implement a 2D score into the VR world. Music could even transition from 2D to 3D for dramatic effect, citing how she used 3D sound effects in the game "fail factory," to accent the 2D musical score, creating several sounds in the "VR space." One example was the loud "clang," of a factory mallet dropping as the downbeat to a soundtrack for a stage. Analyzing "Shadows of Mordor," with Nathan Grigg and Garry Schyman, they discussed how the tribal identity of various tribes in the game informed the musical themes. Using a tribe's unique armor types, appearance and function of forts, enabled them to use the music to accent these properties. For example, the "Machine Tribe Fort Theme," is comprised of billowing smokestacks, so he created a "non-melodic, plodding rhythmic theme with odd sets of industrial sounds to blend together and bring the orchestra in, underneath. Also, in a post-mortem on the "Call of Duty WWII," sound track, Will Roget –who took home almost every award at the 2018 G.A.N.G. awards – described his embrace of a "modern," sound through expanding on tradition and not limiting oneself to "genre expectation." For example, to create the "WW II vibe," he decided upon string quartet and solo cello over big drums and high winds or overt brass. This enabled him to focus on a modern presentation, with an early focus on the "in-game mix," such as using trumpets only for doubling horns (instrumental EQ), and expanded low winds and brass into a "synth tuba." He peppered his music with signature sounds, like the "echo horns," in the piece "Memory of War," or air raid sounds in the piece, "Haze of War." Roget even used extended playing techniques, such as aleatoric orchestral techniques and "overpressure" in the strings. 

There was so much to absorb, I haven't space in this post to include it all! 

When not at the many seminars, I met developers seeking music for their games, attended a G.A.N.G town hall where I pitched an idea to head up a student committee, volunteered at an IASIG meetup to run their slack channel, and got to present an award at the 2018 G.A.N.G Audio Awards ceremony as one of the 4 scholars at GDC. To top it off, I even got to meet "The Fat Man."



GDC was an unforgettable experience, where endless paths crisscross into an intricate network to produce the pixelated art and sonic beauty keeping our hands glued to a controller. Whether I was examining the music of "Middle Earth: Shadow of War," having my music played and critiqued before a panel of game composers at the "Demo Derby," (where it was well received), or making new friends and colleagues, GDC provided an invaluable foot in the door for what I love to do. 

As I left my friends, boarded my flight, and scribbled notes on contacts from the handfulls of cards I obtained, the words of Adam Gubman pushed me forward faster than the jet I sat on. "You gotta be fast, you gotta work hard to deliver for your client; you have to push and persist every day." 

Tuesday, 26 March 2019

Explore Simple Game Algorithms With Color Walk: Part 10

We're back for another round of exploring game algorithms using the simple game Color Walk. We finally reached the point of evaluating Dijkstra's algorithm—the classic, efficient graph algorithm for finding shortest paths—in the last post. It performed pretty well against the top dogs: Greedy Look-Ahead (GLA) and the GLA-BFS hybrid, especially when it came to consistently finding the best moves. However, it failed to find the best moves when a board could be solved in under 29 moves, so we're going to see if we can squeeze out any more performance by modifying Dijkstra's algorithm further. To do that, we're going to try combining Dijkstra's algorithm with GLA, running Dijkstra's algorithm in more than one pass, and changing the heuristic we use to guide the search.

Dijkstra and an Assist


We saw in the last post that we couldn't use Dijkstra's algorithm in its purest form because that would still require searching the entire move graph to find the true shortest path from the source vertex (the start-of-game) to the sink vertex (the end-of-game). In fact, Dijkstra's algorithm, when run to completion, will find the shortest path from the source vertex to every other vertex in the graph. Since the move graph is actually a tree, we don't care what the shortest path is to most of the vertices, save one, the end-of-game vertex. Because of that restriction, we restricted the algorithm to only search until the end-of-game vertex was found, and then try to balance the search heuristic so that we reached that vertex on as short of a path as possible.

This tactic of using a heuristic search and stopping once a goal is reached is actually a variant of Dijkstra's algorithm by another name, called A* search. This algorithm is a popular way to do path finding in games where computer-controlled characters are moving around in a 2D or 3D space. The natural heuristic in that application is the straight-line distance from the current position of the character to the target position, and A* search is pretty effective at this task.

In using a heuristic for the Color Walk move graph search, we have given up the guarantee of finding the true shortest path because the heuristic is not perfect, but we gain a huge benefit in efficiency and tractability. Without the heuristic, the search would go on forever (or at least until it ran out of memory) in such a large graph. Even with the current performance of the algorithm, we want to try to tighten up the heuristic to find a shorter path, but to do that, we want to do something to shrink the size of the graph that it needs to search. To do that, we can add in our old friend GLA to make fast headway into the move graph before switching to Dijkstra's algorithm.

Adding this hybrid GLA-Dijkstra's algorithm is straightforward. We start with the normal task of adding the algorithm to the list of choices in the algorithm pull-down list and to the switch statement that lives behind the list:
  function Solver() {
// ...

this.init = function() {
// ...

$('#solver_type').change(function () {
switch (this.value) {
// ...
case 'greedy-dijkstra':
that.solverType = that.dijkstraWithGla;
that.metric = areaCount;
break;
default:
that.solverType = that.roundRobin;
break;
}

// ...
});

// ...
};
}
The implementation of the hybrid algorithm is about as simple as the other hybrid algorithms:
    this.dijkstraWithGla = function() {
if (moves < 15) this.greedyLookAhead();
else this.dijkstra();
}
It seemed like running GLA for 15 moves was reasonable, considering most boards are not solved in less than 30 moves, and then Dijkstra's algorithm is run to the end-of-game condition. Now we have a problem, though. We have two knobs to turn—one for the maximum number of moves to look ahead in GLA and one for the scale factor used in Dijkstra's algorithm, but only one text box in the UI. (Another knob would be the number of moves to run GLA for, but we'll just keep that at 15 to reduce the number of combinations to look at.) We'll want to separate those two knobs out by adding another text box for the scale factor to the UI. Let's call it solver_scale_factor and add it as another parameter in the code:
  function Solver() {
var that = this;
var iterations = 0;
var max_moves = 2;
var scale_factor = 25;
var time = 0;
var start_time = 0;

this.index = 0;
this.metric = nullMetric;

this.init = function() {
this.solver = $('<div>', {
id: 'solver',
class: 'control btn',
style: 'background-color:' + colors[this.index]
}).on('click', function (e) {
max_moves = $('#solver_max_moves').val();
scale_factor = $('#solver_scale_factor').val();
that.runAlgorithm();
}).appendTo('#solver_container');

// ...

$('#solver_play').on('click', function (e) {
_block_inspect_counter = 0;
_block_filter_counter = 0;
iterations = $('#solver_iterations').val();
max_moves = $('#solver_max_moves').val();
scale_factor = $('#solver_scale_factor').val();
start_time = performance.now();
time = start_time;
that.run();
});
};

// ...

function addVertices(vertices, depth, prev_control, prev_cleared) {
var stop = false;
_.each(controls, function (control) {
if (control !== prev_control && !stop) {
var removed_blocks = control.checkGameBoard(depth, markedBlockCount);
if (endOfGame()) {
doMarkedMoves();
vertices.clear();
stop = true;
} else if (removed_blocks - prev_cleared > 0) {
var markers_dup = markers.slice();
var cost = scale_factor*depth - removed_blocks;
if (removed_blocks > 590 ||
removed_blocks > 560 && vertices.length > 200000) {
cost -= (scale_factor - 5)*depth;
}
vertices.queue({markers: markers_dup,
depth: depth,
control: control,
cost: cost,
cleared: removed_blocks});
}
}
});

return vertices;
}
Inside addVertices() we simply replace max_moves with the new parameter scale_factor. Now we can independently control both parameters and more easily explore variations on this hybrid algorithm. After much experimentation with the max moves in the range of 4-7 and the scale factor in the range of 25-30 using ten iterations, I found that a max moves of 7 and a scale factor of 28 performed well. Then, running for 100 iterations produced the following results.

Color Walk results for 100 iterations of GLA-Dijkstra hybrid

This is quite good performance, meeting or exceeding the best algorithms in every metric except for the standard deviation as compared to Dijkstra's algorithm alone. But Dijkstra's algorithm didn't do as well on the min, mean, or max statistics, so in absolute terms the hybrid algorithm found better move sequences for nearly every board.

Before looking at the table of algorithm performance, let's add in another quick algorithm by reversing GLA and Dijkstra's algorithm to create the Dijkstra-GLA hybrid algorithm. We can add it to the algorithm list:
  function Solver() {
// ...

this.init = function() {
// ...

$('#solver_type').change(function () {
switch (this.value) {
// ...
case 'dijkstra-greedy':
that.solverType = that.glaWithDijkstra;
that.metric = areaCount;
break;
default:
that.solverType = that.roundRobin;
break;
}

// ...
});

// ...
};
}
And add another simple algorithm function that calls both of the base algorithms in the hybrid algorithm:
    this.glaWithDijkstra = function() {
if (moves < 5) this.dijkstra(300);
else this.greedyLookAhead();
}
Notice that the call to Dijkstra's algorithm now includes an argument of 300. This argument is the number of blocks that should be cleared before Dijkstra's algorithm stops. It's pretty easy to limit the algorithm by adding a condition to the if statement where the algorithm is stopped before it runs out of memory:
    this.dijkstra = function(blocks_to_clear = 600) {
var vertices = new PriorityQueue({ comparator: function(a, b) { return a.cost - b.cost } });
vertices = addVertices(vertices, 1, null, blocks[0].cluster.blocks.length);
this.max_depth = 0;
while (vertices.length > 0) {
var vertex = vertices.dequeue();
markers = null;
markers = vertex.markers;

if (vertices.length > 250000 ||
vertex.cleared >= blocks_to_clear) {
doMarkedMoves();
vertices.clear();
} else {
vertices = addVertices(vertices, vertex.depth + 1, vertex.control, vertex.cleared);
}

vertex.markers = null;
}
this.index = null;
}
By the default parameter, all blocks are cleared when the algorithm is run so the other two calls to dijkstra() still work like they did before. For this run the max moves was still set at 7, but the scale factor had to be rolled back to 25, like it was for Dijkstra's algorithm alone because otherwise it would stall on some boards. The performance of this hybrid algorithm comes out surprisingly worse:

Color Walk run with Dijkstra-GLA algorithm of 100 iterations

I didn't expect that just swapping the order of the two algorithms would have such a marked difference in performance. The slightly smaller scale factor doesn't account for the difference, either, because if it's set to 28, as it was in the GLA-Dijkstra algorithm, the performance is even worse. Let's look at how these two hybrid algorithms stack up to the rest of the algorithms we've looked at so far:

AlgorithmMinMeanMaxStdev
RR with Skipping 37 46.9 59 4.1
Random with Skipping 43 53.1 64 4.5
Greedy 31 39.8 48 3.5
Greedy Look-Ahead-2 28 37.0 45 3.1
Greedy Look-Ahead-5 25 33.1 41 2.8
Max Perimeter 29 37.4 44 3.2
Max Perimeter Look-Ahead-2 27 35.0 44 2.8
Perimeter-Area Hybrid 31 39.0 49 3.8
Deep-Path 51 74.8 104 9.4
Path-Area Hybrid 35 44.2 54 3.5
Path-Area Hybrid Look-Ahead-4 32 38.7 45 2.7
BFS with Greedy Look-Ahead-5 26 32.7 40 2.8
DFS with Greedy Look-Ahead-5 25 34.8 43 3.9
Dijkstra's Algorithm 29 33.1 40 1.9
GLA-Dijkstra Hybrid 25 31.8 37 2.2
Dijkstra-GLA Hybrid 28 36.3 44 3.1

While the GLA-Dijkstra hybrid performs better than any other algorithm we've seen so far, and seems to combine all of the best characteristics of its constituent algorithms, Dijkstra-GLA doesn't even perform as well as Dijkstra's algorithm alone. It's more on the level of the max perimeter heuristic, which is decidedly middle-of-the-road as far as these algorithms go. Looking at the boards from a high level, this disparity makes some sense. It looks like at the beginning of a game it's more important to figure out how to remove as many blocks as possible on each move. As the game progresses and gets closer to the end, where the graph search algorithms can "see" more easily to the end of the game, their ability to find the shortest path becomes more effective, and that benefit is especially true for Dijkstra's algorithm because it's more efficient than the other graph search algorithms. Swapping Dijkstra's algorithm and GLA ends up crippling both of them.

Self-Assist


A curious idea comes out of these hybrid algorithms by thinking about the difference between Dijkstra's algorithm and GLA. GLA operates on a per move basis, meaning for each move under consideration, the algorithm looks some number of moves ahead and then commits to a move before going on to consider the next move. If we string one GLA algorithm together with another GLA, it wouldn't look any different than running GLA all the way through in one pass.

In contrast, Dijkstra's algorithm looks as far forward as it's allowed to try to find the shortest path to the end-of-game condition, and once a path is found, it does all of the moves in that path at once. If we string Dijkstra's algorithm together with another Dijkstra's algorithm, running the first one to the halfway point, it looks different than running Dijkstra's algorithm once for the entire board. The combination of the first run to the halfway point and the second run to the end may find quite a different path than a single run does. It should also run faster because the paths it needs to search are shorter by half. Let's give this idea a try by running Dijkstra's algorithm with itself. First, we add the new hybrid algorithm to the list of choices again:
  function Solver() {
// ...

this.init = function() {
// ...

$('#solver_type').change(function () {
switch (this.value) {
// ...
case 'dijkstra-dijkstra':
that.solverType = that.dijkstraDijkstra;
that.metric = areaCount;
break;
default:
that.solverType = that.roundRobin;
break;
}

// ...
});

// ...
};
}
And then we can simply call Dijkstra's algorithm twice for the implementation of dijkstraDijkstra() (it's so fun to say, isn't it?):
    this.dijkstraDijkstra = function() {
if (moves < 5) this.dijkstra(300);
else {
scale_factor = 28;
this.dijkstra();
}
}
The first call to dijkstra() specifies the number of blocks to remove to get to the halfway point. The second call changes the scale_factor to the optimal value for when Dijkstra's algorithm is run for the later moves, as we found in the GLA-Dijkstra algorithm. The scale_factor for the first run can be set through the UI, so we can experiment a little. We could add another UI element so that two scale factors could be specified, but this should demonstrate the idea without adding that complication. With this simple addition to the algorithms, we can see how it performs:

Color Walk run with Dijkstra-Dijkstra hybrid algorithm for 100 iterations

This version of the hybrid Dijkstra's algorithm performs better than Dijkstra-GLA, but worse than GLA-Dijkstra, adding more evidence to the idea that Dijkstra's algorithm does better in the second half of the game than the first half. The first run of Dijkstra's algorithm to remove 300 blocks probably does not do as well as GLA, but the second run does do better than GLA, giving this hybrid a performance result that lands it squarely in between the other two hybrid approaches.

An Assist from the Perimeter


One more option to explore for amping up Dijkstra's algorithm is using other heuristics with the GLA part of the hybrid algorithm. We've continued to use the heuristic of maximizing blocks removed with areaCount(), but we did look at a number of other options for heuristics. Even though they didn't improve over the super-strong area-maximizing heuristic, the other heuristics are potentially interesting for use in paring down the move graph before running Dijkstra's algorithm. They're quite easy to add to our list of algorithms, so let's look at one of them, the perimeterCount() heuristic for maximizing the cleared perimeter:
  function Solver() {
// ...

this.init = function() {
// ...

$('#solver_type').change(function () {
switch (this.value) {
// ...
case 'max-perimeter-dijkstra':
that.solverType = that.dijkstraWithGla;
that.metric = perimeterCount;
break;
default:
that.solverType = that.roundRobin;
break;
}

// ...
});

// ...
};
}
It's so simple that all we had to do was add another choice to the algorithm list and add another case to the switch statement that uses the dijkstraWithGla() algorithm and the perimeterCount() heuristic. Everything else is already available and ready to go. So how does it perform?

Color Walk run with Max-Perimeter-Dijkstra hybrid algorithm for 100 iterations

It looks like another decent algorithm—slightly better than Dijkstra's algorithm alone, but not quite as good as GLA-Dijkstra. Here's the updated table of all the algorithms tried so far:

AlgorithmMinMeanMaxStdev
RR with Skipping 37 46.9 59 4.1
Random with Skipping 43 53.1 64 4.5
Greedy 31 39.8 48 3.5
Greedy Look-Ahead-2 28 37.0 45 3.1
Greedy Look-Ahead-5 25 33.1 41 2.8
Max Perimeter 29 37.4 44 3.2
Max Perimeter Look-Ahead-2 27 35.0 44 2.8
Perimeter-Area Hybrid 31 39.0 49 3.8
Deep-Path 51 74.8 104 9.4
Path-Area Hybrid 35 44.2 54 3.5
Path-Area Hybrid Look-Ahead-4 32 38.7 45 2.7
BFS with Greedy Look-Ahead-5 26 32.7 40 2.8
DFS with Greedy Look-Ahead-5 25 34.8 43 3.9
Dijkstra's Algorithm 29 33.1 40 1.9
GLA-Dijkstra Hybrid 25 31.8 37 2.2
Dijkstra-GLA Hybrid 28 36.3 44 3.1
Max-Perimeter-Dijkstra Hybrid 27 32.8 38 2.3

We have built up quite a list of algorithms, with some of the best performing ones at the very end finally overcoming the surprisingly solid performance of one of the earlier algorithms, GLA-5. If we're looking only at average performance, the GLA-Dijkstra hybrid is the clear winner, with BFS+GLA-5 and Max-Perimeter-Dijkstra hybrid coming in second and third with an average of one extra move per game. However, that higher performance in number of moves comes at a cost. Those algorithms take significantly longer to search for their results than GLA-5 does. If we ordered these top four algorithms based on search speed, the order would be reversed to GLA-5, Max-Perimeter-Dijkstra hybrid, BFS+GLA-5, and GLA-Dijkstra hybrid. At the top of the leaderboard there is a clear trade-off between average performance and search time.

While we've looked at graph algorithms in general and Dijkstra's algorithm in particular fairly extensively now, one thing that was somewhat glossed over was the workings of the priority queue that is the key to making Dijkstra's algorithm work so well. Next time we'll take a closer look at this essential data structure and see how it enables Dijkstra's algorithm to quickly choose each vertex to look at next.


Article Index
Part 1: Introduction & Setup
Part 2: Tooling & Round-Robin
Part 3: Random & Skipping
Part 4: The Greedy Algorithm
Part 5: Greedy Look Ahead
Part 6: Heuristics & Hybrids
Part 7: Breadth-First Search
Part 8: Depth-First Search
Part 9: Dijkstra's Algorithm
Part 10: Dijkstra's Hybrids
Part 11: Priority Queues
Part 12: Summary

GaryCon 2019 (Part 1)


What a weekend!

Yes, I got back from GaryCon in one piece.  Was there all Thursday and Friday, but had to leave about 1pm on Saturday, after my last game.

There's almost too much to say, so I'm going to keep this kind of brief and condense it all into one blog post.  Wish me luck!  [edit: Nope, just couldn't do it.  This is going to be in two parts.  First, Cha'alt, and then Alpha Blue.]

Session 4

The final session of Cha'alt concerned exploring and looting the frozen insides of the gargantuan purple demon-worm Kra'adumek.  My family and I pre-rolled nearly 100 ability scores (strength, dexterity, etc.), all 3d6 in order, ahead of time.  That's going to come in real handy in the years to come... and will appear in Cha'alt.

As that game was "D&D 5e," there were numerous expectations.  When I told everyone that I wanted to keep it standard races, only one racial ability per character, just the main 4 classes, and you roll a % and what your ability scores turn out to be is what they are, save vs. death... well, some jaws dropped. 

The players were more than a little dismayed (one player's suggestion of pre-gens was solid and I probably should have done that, but also wanted to use my d100 ability score table), but kept their composure (no table flipping), which I appreciated.  After all, if a PC died, he could simply roll up a new one and keep going.  With this method, it wouldn't have taken more than 5 minutes to come up with a whole new character.

We played it old school - distance, movement, and perception checks were all but hand-waved.  And sure enough, just as I told them, once the adventure got underway, how strong or wise their characters were didn't matter as much as thinking things through, tactics, diplomacy, knowing when to step on the gas and when to break, and, of course, luck.

I didn't open any D&D books the entire game... in fact, I neglected to bring any D&D books to the convention - aside from my own scenarios and guidebooks.  To me, that's a sign of a qualified old school GM who's comfortable with his rulings.

This being a con game, they came up with an overarching desire (kill the demon-worm), and I presented them with a way to achieve that... the unexploded photon torpedo.

Several party members almost died, but no one actually bit the big one.  Some nice treasure was had, NPCs interacted with, weirdness encountered, and juicy combat to get the blood pumping... as well as spilling out all over the demon-worm floor.

By the game's end, everyone thanked me for the experience.  I appreciated their indulgence of my old school ways, and a few of them said they were glad that I stuck to my guns.  Overall, I think everyone had been temporarily transported to another world.  That kind of immersion is all the thanks I'll ever need. 


Session 2

The second session of GaryCon was Beneath Kra'adumek.  Since I designated that particular game Swords & Wizardry, the occasional shitty ability scores didn't raise an eyebrow.  This crowd was primed for vintage D&D escapades. 

While I unfortunately forgot to bring my copy of How To Game Master like a Fucking Boss to session 4, I had it with me during session 2.  So, each player rolled % to see what past experience they had. 

The wildest result was a gnome (complete with ridiculous hat) trying to get the PC and others to invest in some financial scheme, offering a transparent cube as some sort of proof-of-concept. 

That could have just been a throw-away idea forgotten as soon as it was said, but no.  The player who rolled that proudly talked about his cube and attempted to seek investors in his uncanny enterprise.  After swapping realities with a nearly identical party, that player decided his cube was now a sphere... and the roleplaying began anew.  By the session's end, his business' tagline was "It never ends."  Appropriate.  ;)

As has usually been the case, the PCs decided to convince several of the purple priests to join a new faction - one instituted by the PCs themselves.  After killing the Ipsissimus and mid-wife-ing a newly hatched demon-worm spawn, it wasn't too difficult to persuade some drugged-out cultists to worship Kra'adumek in an entirely new way. 

They played around with the fissure in time and space, experimented with mutated xoth, ate 67 omelets, and foiled the priest's plans to sacrifice a trio of virgins. 

Due to the freewheeling nature of this dungeon, the loose rules of early D&D, and player personalities, a lot of jokes were told and the table erupted in laughter about a dozen times in those 3 hours of weird retro-escapism. 

There was even a woman among the players, so of course I had to make my own saving throw to avoid chanting, "Old enough to bleed; old enough to play a human barbarian!"  Thankfully, I just made it when the die ricocheted off my slimy green tentacle, and old school grognards were saved having to excuse my toxic masculine diversions. 

Some great and/or hilarious quotes from that session...

  • The New God's name is "wormy"... it's a work in progress.
  • "He who slithers through our minds.  Praise be!"
  • Purple priest playboy mag stuck behind the library books.
  • "Spicy sriracha worms."
  • "May the violet be with you... always."
  • "GnomeSphere... it never ends [TM]."
  • "Obey the slither!"

All in all, both groups were fantastic!  I had a blast GMing for everyone, and I hope the memories of Cha'alt will live long and prosper in our collective imaginations.

Ok, part 2 should come tomorrow.

VS

p.s. I managed to move all 4 sessions to a less noisy, less distracting area of the hotel so we could all enjoy the game without shouting, wondering if we actually heard the other person correctly, or daydreaming about the guy at the next table dressed as Gandalf being bludgeoned into unconsciousness with his own Dwarven Forge terrain.