<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>ChrisLo.ca</title>
	<atom:link href="http://chrislo.ca/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://chrislo.ca</link>
	<description>Blog Entries Produced by an Infinite Number of Typing Monkeys</description>
	<lastBuildDate>Sun, 21 Oct 2012 18:54:21 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2</generator>
		<item>
		<title>Rapidly Computing the Standard Deviation for a Fixed Period (Length of Data)</title>
		<link>http://chrislo.ca/?p=143</link>
		<comments>http://chrislo.ca/?p=143#comments</comments>
		<pubDate>Sun, 21 Oct 2012 18:47:13 +0000</pubDate>
		<dc:creator>Chris</dc:creator>
				<category><![CDATA[Algorithms]]></category>

		<guid isPermaLink="false">http://chrislo.ca/?p=143</guid>
		<description><![CDATA[Wikipedia&#8217;s page for Standard Deviation presents a formula for rapidly calculating the standard deviation of a growing set of numbers: [1] Qk = Qk-1 + (xk &#8211; Ak-1)(xk &#8211; Ak) where xk is the kth element in the set and Ak is the mean of the first kth numbers. The standard deviation squared of the [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods">Wikipedia&#8217;s page for Standard Deviation</a> presents a formula for rapidly calculating the standard deviation of a growing set of numbers:</p>
<p>[1] Q<sub>k</sub> = Q<sub>k-1</sub> + (x<sub>k</sub> &#8211; A<sub>k-1</sub>)(x<sub>k</sub> &#8211; A<sub>k</sub>) where x<sub>k</sub> is the kth element in the set and A<sub>k</sub> is the mean of the first kth numbers.</p>
<p>The standard deviation squared of the first n numbers of a set is Q<sub>n</sub> / n.</p>
<p>From this, it is easy to compute the standard deviation for a growing set of numbers without having to read through all numbers in the set a second time.  Recomputing the mean requires only an addition of (x<sub>k</sub> &#8211; A<sub>k-1</sub>) / k to the prior mean of A<sub>k-1</sub> — so this does not require looking back through all the elements of the set.</p>
<p>Therefore recomputing Q does not require looking back through all the elements in the set since it only depends on x<sub>k</sub>, Q<sub>k-1</sub>, A<sub>k</sub>, and A<sub>k-1</sub>.  We&#8217;ve deduced that A<sub>k</sub> can be derived from A<sub>k-1</sub> without having to look back through all elements of the set, so new values of Q can be computed likewise.</p>
<p>Now how would one rapidly calculate the standard deviation if the set was not growing, but rather was a moving window of fixed length?  This is useful in finance or science when we want to keep an up to date standard deviation for a set of constant size with numbers changing one at a time.  For example, in finance, when calculating <a href="http://en.wikipedia.org/wiki/Bollinger_bands">Bollinger Bands</a>, we want to always know the standard deviation in stock prices for the last 20 periods.  So the set of stock prices isn&#8217;t growing in our set, instead it is of constant size, removing the oldest stock price and replacing it with the latest one at every tick interval.  We can adapt formula [1] above for growing sets to meet our needs here by realizing that a moving window is a set that grows by one at one end, and shrinks by one at the other.</p>
<p>Without loss of generality, let&#8217;s assume the beginning of our window is element a of a given set (and we will be discarding the a-th element when the window moves by one), the second element is b, the last element of the window is c, and the element to be added is d.  We are moving from a window of data that encompasses elements a through c, to a window of data one over that encompasses elements b through d.  Our window length is constant, that is d &#8211; b = c &#8211; a.</p>
<p>So if our data set looks like &#8230; 1, 2, 3, 5, 8, 11, 13 &#8230; and our period is 4, the a-th element might have a value of 2, b-th is 3, c-th 8 and d-th 11.  We moved from a window looking at [2, 3, 5, 8] to [3, 5, 8, 11].</p>
<p>If we grow our set from b to c to b to d by adding the d-th element, the Q formula would look like the following:</p>
<p>[2] Q<sub>bd</sub> = Q<sub>bc</sub> + (x<sub>d</sub> &#8211; A<sub>bc</sub>)(x<sub>d</sub> &#8211; A<sub>bd</sub>)</p>
<p>Now this formula looks the same as growing a set from b to c to a to c by adding the a-th element:</p>
<p>[3] Q<sub>ac</sub> = Q<sub>bc</sub> + (x<sub>a</sub> &#8211; A<sub>bc</sub>)(x<sub>a</sub> &#8211; A<sub>ac</sub>)</p>
<p>Rearranging [3], we find out that shrinking a set, in this case, from a to c to b to c, is simply reducing Q by the product of terms on the RHS:</p>
<p>[4] Q<sub>bc</sub> = Q<sub>ac</sub> &#8211; (x<sub>a</sub> &#8211; A<sub>bc</sub>)(x<sub>a</sub> &#8211; A<sub>ac</sub>)</p>
<p>Substituting [4] into [2], we find out how to get Q (and consequently the standard deviation) for the new window b to d, from the Q (or standard deviation) of our old window a to c:</p>
<p>[5] Q<sub>bd</sub> = Q<sub>ac</sub> &#8211; (x<sub>a</sub> &#8211; A<sub>bc</sub>)(x<sub>a</sub> &#8211; A<sub>ac</sub>) + (x<sub>d</sub> &#8211; A<sub>bc</sub>)(x<sub>d</sub> &#8211; A<sub>bd</sub>)</p>
<p>We arrive at [5], a formula where we can compute new values of Q for a moving window (and we can easily calculate standard deviation from that) without having to go through a second pass of all numbers in the set.  The derivation is simple after realizing a moving window is simply a set that grows and shrinks by one element.</p>
]]></content:encoded>
			<wfw:commentRss>http://chrislo.ca/?feed=rss2&#038;p=143</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>List of MSN Gaming Zone Backgammon Cheaters</title>
		<link>http://chrislo.ca/?p=135</link>
		<comments>http://chrislo.ca/?p=135#comments</comments>
		<pubDate>Sun, 14 Oct 2012 22:42:16 +0000</pubDate>
		<dc:creator>Chris</dc:creator>
				<category><![CDATA[Backgammon]]></category>
		<category><![CDATA[Games]]></category>

		<guid isPermaLink="false">http://chrislo.ca/?p=135</guid>
		<description><![CDATA[Figured I&#8217;d compile this list after seeing several cheaters that exploit the &#8220;repeated-undo stall/timeout&#8221; bug. Hopefully this list will help legitimate players safely quit the game before entering a match with these sociopaths. Don&#8217;t know why these cheaters spend so much time on such fruitless (and mindless) idiocy when they could instead devote their time [...]]]></description>
			<content:encoded><![CDATA[<p>Figured I&#8217;d compile this list after seeing several cheaters that exploit the &#8220;repeated-undo stall/timeout&#8221; bug.  Hopefully this list will help legitimate players safely quit the game before entering a match with these sociopaths.  Don&#8217;t know why these cheaters spend so much time on such fruitless (and mindless) idiocy when they could instead devote their time to learning how to play properly.</p>
<p>Here&#8217;s my list so far and I&#8217;ll update it as it grows:</p>
<p>Stereo_Chart<br />
NathDan0615<br />
Cursive_Tiger<br />
BCCFMPTN<br />
hunter_gunter</p>
]]></content:encoded>
			<wfw:commentRss>http://chrislo.ca/?feed=rss2&#038;p=135</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP&#8217;s setcookie() with $expire set but not $domain will blow away the cookie in Internet Explorer (IE)</title>
		<link>http://chrislo.ca/?p=116</link>
		<comments>http://chrislo.ca/?p=116#comments</comments>
		<pubDate>Fri, 04 May 2012 07:56:41 +0000</pubDate>
		<dc:creator>Chris</dc:creator>
				<category><![CDATA[Dev]]></category>
		<category><![CDATA[cookie]]></category>
		<category><![CDATA[cookie domain]]></category>
		<category><![CDATA[cookie expiry]]></category>
		<category><![CDATA[expiration]]></category>
		<category><![CDATA[IE]]></category>
		<category><![CDATA[Internet Explorer]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[requests page twice]]></category>
		<category><![CDATA[setcookie]]></category>

		<guid isPermaLink="false">http://chrislo.ca/?p=116</guid>
		<description><![CDATA[Spent a whole day figuring out this one.  Yet another reason why Internet Explorer is the bane of web development. Found a plethora of suggested solutions on the net, and even an old bug logged for PHP in 2001, but the reason behind the bug is still a mystery. As the title states, if you [...]]]></description>
			<content:encoded><![CDATA[<p>Spent a whole day figuring out this one.  Yet another reason why Internet Explorer is the bane of web development.  Found a plethora of suggested solutions on the net, and even an old bug logged for PHP in 2001, but the reason behind the bug is still a mystery.</p>
<p>As the title states, if you use setcookie() with $expire set and $domain not set, then IE may simply let the cookie expire, ignoring the value you put in $expire.  Furthermore, IE may request the page a second time, as if it didn&#8217;t understand the cookie set directive and decided to start all over.  I couldn&#8217;t reproduce this in Firefox, Chrome, Safari, or any other browser, and oddly enough, I could not reproduce this using inPrivate browsing in IE.  In other words, all of the examples below work in other browsers as you would expect them to.  IE is the lone failure on the setcookie example #2 below.</p>
<p>PHP&#8217;s setcookie() syntax:</p>
<pre name="code" class="php">bool setcookie ( string $name [, string $value
  [, int $expire = 0 [, string $path [, string $domain
  [, bool $secure = false [, bool $httponly = false ]]]]]] )</pre>
<p>1. Cookie successfully stored in IE, but expires at the end of the browser session:</p>
<pre name="code" class="php">setcookie(session_name(), session_id(), 0, '/');</pre>
<p>2. The following blows away the cookie in IE:</p>
<pre name="code" class="php">setcookie(session_name(), session_id(), time() + 60 * 60 * 24, '/');</pre>
<p>3. To successfully store the cookie in IE and have an expiry date, we must supply the $domain to setcookie():</p>
<pre name="code" class="php">setcookie(session_name(), session_id(),
  time() + 60 * 60 * 24, '/', '.chrislo.ca');</pre>
<p>Some solutions on the net suggested that the time needed to be bigger in example #2 due to difference in GMT-ness of the time provided, but no matter how large the expiry time is set, the cookie still gets blown away in IE.  Appending the domain magically fixed it.</p>
]]></content:encoded>
			<wfw:commentRss>http://chrislo.ca/?feed=rss2&#038;p=116</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>An efficient but effective artificial intelligence for Big 2</title>
		<link>http://chrislo.ca/?p=106</link>
		<comments>http://chrislo.ca/?p=106#comments</comments>
		<pubDate>Tue, 24 Apr 2012 02:32:48 +0000</pubDate>
		<dc:creator>Chris</dc:creator>
				<category><![CDATA[Algorithms]]></category>
		<category><![CDATA[Big 2]]></category>
		<category><![CDATA[Games]]></category>
		<category><![CDATA[algorithm]]></category>
		<category><![CDATA[artificial intelligence]]></category>
		<category><![CDATA[big 2]]></category>
		<category><![CDATA[Big Deuce]]></category>
		<category><![CDATA[Big Two]]></category>
		<category><![CDATA[Cap Sa]]></category>
		<category><![CDATA[Card Game]]></category>
		<category><![CDATA[Chinese poker]]></category>
		<category><![CDATA[Ciniza]]></category>
		<category><![CDATA[duplicate]]></category>
		<category><![CDATA[free online multiplayer card game]]></category>
		<category><![CDATA[Giappuniza]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[President]]></category>
		<category><![CDATA[The Hannah Game]]></category>
		<category><![CDATA[Top Dog]]></category>
		<category><![CDATA[大老二]]></category>
		<category><![CDATA[鋤大D.]]></category>

		<guid isPermaLink="false">http://chrislo.ca/?p=106</guid>
		<description><![CDATA[(tl;dr: You can play against the computer opponents I designed for online Big 2 here.  The bots are capable of calulcating the expectation for its score for every possible line of play very quickly, resulting in a rather worthy opponent for even the most seasoned Big 2 players). The dynamics in Big 2 make programming [...]]]></description>
			<content:encoded><![CDATA[<p><strong>(tl;dr: You can play against the computer opponents I designed for <a href="http://www.hobub.com/big2">online Big 2</a> <a href="http://www.hobub.com">here</a>.  The bots are capable of calulcating the expectation for its score for every possible line of play very quickly, resulting in a rather worthy opponent for even the most seasoned Big 2 players)</strong>.</p>
<p>The dynamics in Big 2 make programming AI for it an interesting problem.  The usual &#8220;game tree&#8221; approach when solving games like Chess or Reversi can still somewhat be applied.  However, like programming optimal AI for backgammon, there is an element of the unknown to be included.  We know the cards we hold in our hand, but how might they be distributed amongst the other concealed hands?</p>
<p>There are many other factors that human players have to their advantage.  They can spot and exploit players&#8217; weaknesses and habits, bluff, and come to logical conclusions putting those two weapons together.</p>
<p>Emulating this behaviour and analyzing a fairly large &#8220;game tree&#8221; makes designing a fast, yet effective AI for Big 2 a challenge.  For AI that is controlled by the server (for obvious reasons it is server-side and not client-side), we have to be careful too to not take up many cycles per AI decision.  The assumption is that multiple computer players will be running at once, so for a multiplayer online gaming site, CPU cycles for AI should be minimal.</p>
<p>So instead of a game tree where we guess what cards will be played (and where they lie) and determine the optimal play by looking ahead, the approach taken is one giant heuristic: let&#8217;s calculate the expected value of a play simply from the cards we see and the cards we did not see up until the play.  Ultimately the goal of Big 2 is to get rid of all your cards, so we simplify the calculation by estimating how many cards will be shedded by playing a certain hand.</p>
<p>The process is simple: when it&#8217;s the computers turn, what are all the possible plays we have?  If a single was played, we can play another single, or pass.  If the computer is leading, then anything is possible, but passing.  Whichever the case, we have a small finite set of moves.  Now for each move, how will our hand look like after?  What will our possible plays be?  To answer this problem, we treat it as if the computer is leading a hand, and determine the set of all plays regardless if we are approached with a single, double, triple or poker hand.  Now, given the cards that are concealed in everyone else&#8217;s hand (and we know the exact set of cards, since the computer easily tracks what cards have already been played, we just don&#8217;t know how these cards are distributed &#8212; we could guess and assign probabilities, but for purposes of efficiency, we opt not to) let&#8217;s calculate how many cards we expect to shed by the end of the game.  If one of our plays in our &#8220;future&#8221; hand contains a poker hand, how likely is it going to beat all the other possible poker hands out there?  If it beats all the other possible poker hands (or a high percentage of them), how likely are we even going to be seeing a poker hand in play?  That factor is nil if the remaining players have 5 or less cards each.</p>
<p>Anyway, just by answering those questions above you have a relatively good gauge on expected value, and interestingly enough figuring out these probabilities is fast, by applying some good finite mathematics formulae.  One thing that human players do well is to have a game plan to run out their cards, waiting for a deuce to be played to promote one of their deuces, giving them an unbeatable exit plan.  To emulate this on the AI side, we simply do a small probability assignment ourselves: when we know a certain line of play has hands that can be unbeaten (or close to unbeaten), we can boost the probability to 1 (or close to 1) for a weaker hand that had no chance of being played unless the computer leads it itself.</p>
<p>So with the original probabilities calculated and the &#8220;fudging&#8221; of probabilities to emulate creating a &#8220;game plan&#8221; with a certain line of play, we have a rather decent AI for Big 2 that can give even the most seasoned players a challenge.  Although the computer still does funny stuff from time to time, it makes up for its shortcomings by doing pinpoint probability calculations quickly, and also has the inhuman ability to figure out exactly what cards are left, to accurately determine the strength of its hand.</p>
<p>So how does it play?  <a href="http://www.hobub.com/big2">You be the judge</a>!  I can barely scrape of my 25% share of 1st place finishes against the computer.  Not bad for a computer that reacts less than 250ms per move.</p>
]]></content:encoded>
			<wfw:commentRss>http://chrislo.ca/?feed=rss2&#038;p=106</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Using the background function of Processing.js with images</title>
		<link>http://chrislo.ca/?p=95</link>
		<comments>http://chrislo.ca/?p=95#comments</comments>
		<pubDate>Wed, 01 Feb 2012 08:02:22 +0000</pubDate>
		<dc:creator>Chris</dc:creator>
				<category><![CDATA[Dev]]></category>
		<category><![CDATA[alpha]]></category>
		<category><![CDATA[background]]></category>
		<category><![CDATA[background-size]]></category>
		<category><![CDATA[canvas]]></category>
		<category><![CDATA[contain]]></category>
		<category><![CDATA[cover]]></category>
		<category><![CDATA[CSS3]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[image]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[opacity]]></category>
		<category><![CDATA[processing]]></category>
		<category><![CDATA[processing.js]]></category>

		<guid isPermaLink="false">http://chrislo.ca/?p=95</guid>
		<description><![CDATA[&#8220;Background image must be the same dimensions as the canvas.&#8221; In case you are looking for a workaround for Processing.js background function that forces you to scale your background image, try this trick to make your browser do the scaling for you. Set the CSS background for the canvas element to your image, and use [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p><i>&#8220;Background image must be the same dimensions as the canvas.&#8221;</i></p></blockquote>
<p>In case you are looking for a workaround for Processing.js background function that forces you to scale your background image, try this trick to make your browser do the scaling for you.</p>
<p>Set the CSS background for the canvas element to your image, and use &#8220;cover&#8221; or &#8220;contain&#8221; to stretch your background to fit the canvas:</p>
<pre name="code" class="xml">&lt;canvas style="background: url(&lt;!-- your background image path --&gt;);
  background-size: cover"&gt;&lt;/canvas&gt;</pre>
<p>In your JS, you can set Processing&#8217;s background opacity to zero alpha (transparent), or simply use clearRect without the help from Processing:</p>
<pre name="code" class="js">background(0, 0);
// OR
document.getElementById('canvas').getContext('2d')
  .clearRect(/* x */, /* y */, /* w */, /* h */);
</pre>
<p>Of course you can dynamically set the CSS of the canvas in your script whenever you feel like changing your background image.</p>
]]></content:encoded>
			<wfw:commentRss>http://chrislo.ca/?feed=rss2&#038;p=95</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Making Facebook&#8217;s JavaScript SDK work with your AJAX pages</title>
		<link>http://chrislo.ca/?p=87</link>
		<comments>http://chrislo.ca/?p=87#comments</comments>
		<pubDate>Sun, 29 Jan 2012 00:37:11 +0000</pubDate>
		<dc:creator>Chris</dc:creator>
				<category><![CDATA[Dev]]></category>
		<category><![CDATA[AJAX]]></category>
		<category><![CDATA[Facebook]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[JS]]></category>
		<category><![CDATA[JScript]]></category>
		<category><![CDATA[Login button]]></category>
		<category><![CDATA[SDK]]></category>
		<category><![CDATA[text]]></category>

		<guid isPermaLink="false">http://chrislo.ca/?p=87</guid>
		<description><![CDATA[Facebook&#8217;s example code in its documentation would not go well with AJAX served pages. In particular, the login button does not get &#8220;converted&#8221; on subsequent attempts because the JS code required by Facebook is also loaded asynchronously. Their script is loaded into your document tree, and calls the FB.init function only that one time when [...]]]></description>
			<content:encoded><![CDATA[<p>Facebook&#8217;s example code in its documentation would not go well with AJAX served pages. In particular, the login button does not get &#8220;converted&#8221; on subsequent attempts because the JS code required by Facebook is also loaded asynchronously. Their script is loaded into your document tree, and calls the FB.init function only that one time when the script is loaded. All subsequent times, it sees that the script has already been loaded and does nothing (i.e. their d.getElementById(d) check). Therefore, if your login button appears in an AJAX served page, it may work the first time the button appears, but fail to work afterward.</p>
<p>The solution, which may be difficult to find on their documentation, or even elsewhere on the Internet, is to call FB.init at your leisure. Thus, if one of your AJAX served pages has a login button, it should make the call to FB.init itself. Combining a code snippet for determining if the remote script has been fully loaded with this approach provides a slightly more robust solution that won&#8217;t throw a JS error:</p>
<pre name="code" class="jscript">var stFBId = 'facebook-jssdk';
var fnFBInit =
  function()
  {
    FB.init({
      appId      : /* YOUR APP ID */,
      status     : true,
      cookie     : true,
      xfbml      : true,
      oauth      : true,
    });
    FB.Event.subscribe('auth.login', function(response) {
      window.location = /* URL after login */;
    });
    FB.Event.subscribe('auth.logout', function(response) {
      window.location = /* URL after logout */;
    });
  };
var fInitReady = false;

(function ()
{
  function loadScript(stUrl, fnCallback)
  {
    var script = document.createElement("script");
    script.type = "text/javascript";

    if (script.readyState)
    {
      script.onreadystatechange = function()
      {
        if (script.readyState == "loaded"
            || script.readyState == "complete")
        {
          script.onreadystatechange = null;
          fnCallback();
        }
      };
    }
    else
    {
      script.onload = function()
      {
        fnCallback();
      };
    }

    script.src = stUrl;
    script.id = stFBId;
    script.async = true;
    document.getElementsByTagName('head')[0].appendChild(script);
  }

  if (!(document.getElementById(stFBId)))
  {
    loadScript('//connect.facebook.net/en_US/all.js',
    function()
    {
      fInitReady = true;
      fnFBInit();
    });
  }
})();</pre>
<p>Finally, in any code that is served asynchronously that includes the FB login divs, you need to simply include the call to FB.init:</p>
<pre name="code" class="xml">
&lt;div id="fb-root"&gt;&lt;/div&gt;
&lt;div class="login-button"&gt;Log in with Facebook&lt;/div&gt;
&lt;script&gt;
if (fInitReady)
  fnFBInit();
&lt;/script&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://chrislo.ca/?feed=rss2&#038;p=87</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fonts in Processing.js</title>
		<link>http://chrislo.ca/?p=68</link>
		<comments>http://chrislo.ca/?p=68#comments</comments>
		<pubDate>Thu, 29 Sep 2011 19:43:21 +0000</pubDate>
		<dc:creator>Chris</dc:creator>
				<category><![CDATA[Dev]]></category>
		<category><![CDATA[canvas]]></category>
		<category><![CDATA[fonts]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[processing]]></category>
		<category><![CDATA[processing.js]]></category>

		<guid isPermaLink="false">http://chrislo.ca/?p=68</guid>
		<description><![CDATA[Since the documentation at http://processing.js is still tailored for processing code, I&#8217;ll post an example of how to port font usage to JS (assuming you have an &#8220;inArray&#8221; function like jQuery&#8217;s).  It shows how you can store all the fonts you need in an associative array for easy reference afterward: var fonts = {}; processing.setup [...]]]></description>
			<content:encoded><![CDATA[<p>Since the documentation at http://processing.js is still tailored for processing code, I&#8217;ll post an example of how to port font usage to JS (assuming you have an &#8220;inArray&#8221; function like jQuery&#8217;s).  It shows how you can store all the fonts you need in an associative array for easy reference afterward:</p>
<pre name="code" class="js">var fonts = {};

processing.setup = function()
{
  var fontList = processing.PFont.list();

  if (jQuery.inArray('sans-serif', fontList) &gt;= 0)
  {
    fonts['sans-serif'] =
      {'12': pr.createFont('sans-serif', 12, true)};
  }
  else
  {
    // Error
  }
}

// Somewhere else in your code ...
processing.textFont(fonts['sans-serif']['12']);
processing.text("Hello world", 10, 10);</pre>
]]></content:encoded>
			<wfw:commentRss>http://chrislo.ca/?feed=rss2&#038;p=68</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CSS: Two columns equal height, one fixed, the other fluid width</title>
		<link>http://chrislo.ca/?p=59</link>
		<comments>http://chrislo.ca/?p=59#comments</comments>
		<pubDate>Wed, 13 Jul 2011 01:21:20 +0000</pubDate>
		<dc:creator>Chris</dc:creator>
				<category><![CDATA[Dev]]></category>
		<category><![CDATA[annoying]]></category>

		<guid isPermaLink="false">http://chrislo.ca/?p=59</guid>
		<description><![CDATA[To save others the time and hassle, here&#8217;s my lesson learned: don&#8217;t try it.  No matter how close you can get with CSS, after almost a day of searching and experimentation, I eventually fell back to this: http://giveupandusetables.com/ Why?  At some point in time I had the basics working (in all browsers!), but then I [...]]]></description>
			<content:encoded><![CDATA[<p>To save others the time and hassle, here&#8217;s my lesson learned: don&#8217;t try it.  No matter how close you can get with CSS, after almost a day of searching and experimentation, I eventually fell back to this:</p>
<p><a href="http://giveupandusetables.com/">http://giveupandusetables.com/</a></p>
<p>Why?  At some point in time I had the basics working (in all browsers!), but then I tried to add another div centered in the fluid width column, and that became the new seemingly impossible task.  I can only conclude that no clean CSS can evolve from solving this.  Tables will always look right across all browsers (IE6 included), and will be much easier to manage to get the columns right.  It makes centering the div correctly in the fluid column trivial.  Sad how after several hours, I just ended up with this:</p>
<p>HTML:</p>
<pre name="code" class="xml">&lt;table id=container&gt;
  &lt;tr&gt;
    &lt;td id=sidebar&gt;
    &lt;/td&gt;
    &lt;td id=content&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
</pre>
<p>&nbsp;</p>
<p>CSS:</p>
<pre name="code" class="css">#container
{
  width: 70%;
  margin-top: 0;
  margin-left: auto;
  margin-right: auto;
  margin-bottom: 0;
  padding: 0;
  border-top: 0;
  border-left: 1px solid #000;
  border-right: 1px solid #000;
  border-bottom: 0;
}

#container td
{
  vertical-align: top;
  padding: 0;
  margin: 0;
}

#sidebar
{
  width: 180px;
  border-right: 1px solid #ddd;
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://chrislo.ca/?feed=rss2&#038;p=59</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mercurial: &#8220;hg push&#8221; over ssh, remote says &#8220;permission denied&#8221;</title>
		<link>http://chrislo.ca/?p=53</link>
		<comments>http://chrislo.ca/?p=53#comments</comments>
		<pubDate>Thu, 07 Jul 2011 04:15:34 +0000</pubDate>
		<dc:creator>Chris</dc:creator>
				<category><![CDATA[Dev]]></category>
		<category><![CDATA[error]]></category>
		<category><![CDATA[hg]]></category>
		<category><![CDATA[lock]]></category>
		<category><![CDATA[Mercurial]]></category>
		<category><![CDATA[permission denied]]></category>
		<category><![CDATA[push]]></category>
		<category><![CDATA[remote]]></category>
		<category><![CDATA[repository]]></category>
		<category><![CDATA[ssh]]></category>

		<guid isPermaLink="false">http://chrislo.ca/?p=53</guid>
		<description><![CDATA[I spent a decent amount of time figuring out this vague error message, so perhaps this post can save someone else some hassle.  I found that many people encountered this problem, posted it on some online forum, but no one gave a clear answer.  So here is one possible solution: Suppose you set up a [...]]]></description>
			<content:encoded><![CDATA[<p>I spent a decent amount of time figuring out this vague error message, so perhaps this post can save someone else some hassle.  I found that many people encountered this problem, posted it on some online forum, but no one gave a clear answer.  So here is one possible solution:</p>
<p>Suppose you set up a remote Mercurial repository, cloned it locally via SSH to your machine and then wished to push your changes to the remote repo.</p>
<p>The commands you ran may look something like this:</p>
<pre name="code" class="xml">hg clone ssh://remoteusername@remoteaddr//dir/to/repo/
[... make some changes ...]
hg pull
hg update
[... make some changes ...]
hg commit [...]
hg push [ssh://remoteusername@remoteaddr//dir/to/repo/]</pre>
<p>On the push, if you see:</p>
<pre name="code" class="xml">remote: abort: could not lock repository /dir/to/repo/: Permission denied</pre>
<p>you&#8217;ll want to check that &#8220;remoteusername&#8221; has write permissions to not just the &#8220;.hg&#8221; directory in /dir/to/repo/, but also every file and directory in it.  If not, you&#8217;ll want to add to the owner group or change the owner of the .hg directory and its contents recursively to &#8220;remoteusername&#8221; (e.g. using &#8220;chown -R&#8221;).  For obvious reasons, you don&#8217;t want to make the directory and its contents globally writable (e.g. don&#8217;t &#8220;chmod 777&#8243;).</p>
<p>I got into this bind by initializing the remote repository under sudo such that the owner was root, and not the username I was using for SSH.</p>
]]></content:encoded>
			<wfw:commentRss>http://chrislo.ca/?feed=rss2&#038;p=53</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Advanced Topics on Typing</title>
		<link>http://chrislo.ca/?p=49</link>
		<comments>http://chrislo.ca/?p=49#comments</comments>
		<pubDate>Thu, 30 Jun 2011 05:07:54 +0000</pubDate>
		<dc:creator>Chris</dc:creator>
				<category><![CDATA[Boggle]]></category>
		<category><![CDATA[Games]]></category>
		<category><![CDATA[Boggle Bash]]></category>
		<category><![CDATA[ChucklingRhinos]]></category>
		<category><![CDATA[Pogo]]></category>
		<category><![CDATA[Scramble]]></category>
		<category><![CDATA[Strategy]]></category>
		<category><![CDATA[Tips]]></category>
		<category><![CDATA[Word Racer]]></category>
		<category><![CDATA[Yahoo! Games]]></category>
		<category><![CDATA[Zynga]]></category>

		<guid isPermaLink="false">http://chrislo.ca/?p=49</guid>
		<description><![CDATA[Edit: I strongly recommend playing Jumblewords instead at hobub.com. Games are faster, more challenging, and include more detailed statistics to help you track and improve your skills. An Argument for Pressing Enter Rather Than Backspace In Boggle Bash, there is no penalty for entering an invalid word.  If in your spree of typing you made [...]]]></description>
			<content:encoded><![CDATA[<p>Edit: I strongly recommend playing Jumblewords instead at <a href="http://hobub.com">hobub.com</a>.  Games are faster, more challenging, and include more detailed statistics to help you track and improve your skills.</p>
<p><strong>An Argument for Pressing Enter Rather Than Backspace</strong></p>
<p>In Boggle Bash, there is no penalty for entering an invalid word.  If in your spree of typing you made a typo at the very beginning of a word, I would recommend NOT backspacing to correct the error if you have already typed most of the word.  Hit Enter to enter what you have anyway, and start over.  Who knows, the word you accidentally entered may actually be a legitimate one!</p>
<p>Note: Optimally you’d want to do this if the number of keystrokes to hit Enter and retype the letters up to your typo is less than the number of times you’d have to hit backspace (although one could argue the number can be slightly more since it is generally faster to hit Enter and other keys than hitting backspace the same number of times in succession; also, since the errant word may actually be a legitimate one, you don’t want to backspace a word that would have scored).</p>
<p><strong>An Argument to Keep Typing Rather Than Backspace</strong></p>
<p>Suppose the word you were thinking of doesn’t actually connect on the board.  In the middle of typing the word, Boggle Bash will give you an audio clue that the path does not exist upon entering the disjoint letter.  Again, I would suggest not to get rattled and start backspacing, but to simply follow through with the word you wanted to enter, and just hit Enter (again your errant word might actually score!).</p>
<p>If you want to keep it simple, I would suggest the following general guideline: never press backspace more than twice in a row.  Either hit Enter or keep typing anyway.</p>
<p><strong>Keeping a Mental Queue</strong></p>
<p>As illustrated in the previous post about typing, it is faster to simply think of words than typing those words.  Use the time it takes you to type to continue analyzing the board for words.  You will want to look ahead, building a mental queue of words you want to enter, and thus make typing a background task.  There is a limit on how big of a queue you can manage (depending on your short term memory), but any time you can focus less on typing and more on identifying words, clusters and high scoring point regions, the better.  This concept will become clearer when clusters and suffixes are discussed in future posts.</p>
<p><strong>Notes on Other Input Methods When You Can`t Use the Keyboard</strong></p>
<p>If you’re playing a version of Boggle where keyboard input is not allowed (e.g. the actual board game, or Zynga Scramble on the iPhone/iPad), I do have a few suggestions to offer based on my experience.</p>
<p>Writing down words in the actual board game is by far the slowest input method.  You will almost think many multiple words per minute more than you can write.   At some point in time early in the round you will want to give the entire board a quick analysis to identify the point-rich regions and clusters.  Identify some easy words at the beginning of the round, and while you are writing those words down, do the scan of the entire board and start keeping a mental queue.  Use some of the tips already described in this post: if a word doesn’t exist or is invalid, don’t erase letters, either stop writing the word or continue writing what you wanted, and move on.</p>
<p>Note that in Boggle Bash, scanning the entire board early in the round is less of a necessity.  If you manage to type 80+ words per minute, usually the time it takes to enter those first few words will not buy you enough time to scan the entire board.  It is usually sufficient to simply gravitate towards the obvious suffixes and general higher scoring regions, but not necessarily the highest scoring regions.  Usually there will be sufficient time to traverse the board and naturally find the highest scoring region.</p>
<p>On a further note, if you are playing a version where you cannot duplicate a word already entered by another player, it actually becomes more important to do the scan in the early part of the round.  Yahoo! Games Word Racer is one such variation of the game.  I would suggest here NOT starting in the upper-left corner: start typing small clusters in another region, and use that time to do a quick scan of the entire board.  Immediately attack the suffixes and higher scoring regions.  There are many other suggestions I have for variants such as Word Racer, but they are out of scope for this guide.</p>
<p>For touch devices such as IPad or IPhones, word entry should be done with a dragging motion between letters and not a touch-typing motion.  In the case of Zynga Scramble, a dragging motion automatically enters the word once your finger leaves the screen, whereas if you were selecting one letter at a time, you have to perform one more “stroke” to enter the word (it also takes longer to repeatedly having to lift and press your finger for each letter).  The finger-drag method can actually be just as fast as typing (I would estimate my speed around 60 words per minute), so essentially most of what has been discussed in this guide applies equally for touch devices.</p>
<p><strong>TL;DR</strong></p>
<p>Don’t use backspace to correct typos at the beginning of words.</p>
<p>Don’t worry if the word you think of does not even connect on the board.  Just type it in anyway.</p>
]]></content:encoded>
			<wfw:commentRss>http://chrislo.ca/?feed=rss2&#038;p=49</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
