<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' version='2.0'><channel><atom:id>tag:blogger.com,1999:blog-4653046199291468715</atom:id><lastBuildDate>Mon, 14 Dec 2009 01:13:58 +0000</lastBuildDate><title>Jason Rush</title><description></description><link>http://jasonrush.com/blog/</link><managingEditor>noreply@blogger.com (Jason)</managingEditor><generator>Blogger</generator><openSearch:totalResults>18</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-7841616094462024116</guid><pubDate>Mon, 14 Dec 2009 01:08:00 +0000</pubDate><atom:updated>2009-12-13T17:13:58.296-08:00</atom:updated><title>Rave: Back at it</title><description>Last year, I started working on a JRuby port of the Google Wave robot API called Rave.  I took a bit of a hiatus from working on Rave, since the Google Wave API was a bit unstable, and I had been spinning my wheels trying to solve some things that turned out to be unsolvable...  Anyway, the Wave API has stabilized a bit, so I'm back at it. And I have some help - a couple of people have been contributing code to the project.  Which is, by the way, awesome.&lt;br /&gt;&lt;br /&gt;Anyway, I'll post more on Rave pretty soon. In the mean time, check it out on &lt;a href="http://github.com/diminish7/rave"&gt;Github&lt;/a&gt;, for more info. Particularly the README.&lt;span style="text-decoration: underline;"&gt;&lt;/span&gt;&lt;a href="http://github.com/diminish7/rave"&gt;&lt;br /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-7841616094462024116?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2009/12/rave-back-at-it.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-1104134379639078738</guid><pubDate>Mon, 23 Nov 2009 05:15:00 +0000</pubDate><atom:updated>2009-11-22T22:14:32.148-08:00</atom:updated><title>Vendor Jars</title><description>I'm hanging out at JRubyConf today, and I got all inspired to contribute something to the community.  So I decided to put together a little plugin for auto-loading jars into a JRuby on Rails project.  One of the key advantages of using JRuby instead of MRI is the availability of all of the mature Java libraries.  In JRuby, you have the ability to easily wrap those libraries and access them directly from your Ruby code.  The process is pretty easy, you just require each jar that you want to use, just like you'd require a gem, or another .rb file.  But there is no consistent pattern for this -- no convention for where to place those files.  What I wanted was to have a jars folder under &lt;span style="font-style: italic;"&gt;vendor&lt;/span&gt; where you could just drop jars and have them automatically required in your app.  The end result is a ridiculously simple plugin I've imaginatively called &lt;a href="http://github.com/diminish7/vendor_jars"&gt;Vendor Jars&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;To install it, just cd to the top-level folder of your rails app and run&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;script/plugin install git://github.com/diminish7/vendor_jars.git&lt;/pre&gt;&lt;/blockquote&gt;This will add the plugin to your &lt;span style="font-style: italic;"&gt;vendor/plugins&lt;/span&gt; folder, and it will create a &lt;span style="font-style: italic;"&gt;vendor/jars&lt;/span&gt; folder.  Then, for any Java jar that you want to be accessible from your JRuby code, just drop the jar into that &lt;span style="font-style: italic;"&gt;vendor/jars&lt;/span&gt; folder, and it will automatically get required at startup.&lt;br /&gt;&lt;br /&gt;That's it.  I know it's not much (it's like 4 lines of code or something) but I feel like I write that same code every time I do a new JRuby app, so, simple as it is, I think it'll be useful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-1104134379639078738?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2009/11/vendor-jars.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-4180116617097024336</guid><pubDate>Sun, 13 Sep 2009 20:07:00 +0000</pubDate><atom:updated>2009-09-13T13:22:40.023-07:00</atom:updated><title>Taking a Break from Rave</title><description>I've had a frustrating couple of weeks working on &lt;a href="http://github.com/diminish7/rave/tree"&gt;Rave&lt;/a&gt;. Rave is a Ruby port of the &lt;a href="http://wave-robot-python-client.googlecode.com/svn/trunk/pydocs/index.html"&gt;Python API&lt;/a&gt; for building robots in &lt;a href="http://wave.google.com/"&gt;Google Wave&lt;/a&gt;. Wave is pretty unstable, and so I keep running into problems where I'll spend hours debugging my code only to determine that it's a problem with Wave. So I'm going to set it aside for a little while. In about a month, Google is going to release new versions of everything, and start letting the general public sign up for Wave accounts. I assume that they are planning on having a more stable version of Wave at that point. So, I'll revisit it then. As it stands, Rave is in a pretty good spot for building a lot of types of robots. The two big things I've been fighting with recently are that the capabilities versioning doesn't work correctly (so Wave caches your robot's capabilities and you can't add or remove capabilities in new versions), and cron events don't work. I'll revisit both of those in October once a more stable version of Wave is out. Until then, I need to stop spinning my wheels and start focusing on some side projects that I can make some actual progress on.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-4180116617097024336?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2009/09/taking-break-from-rave.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-151131889078347837</guid><pubDate>Sun, 21 Jun 2009 17:03:00 +0000</pubDate><atom:updated>2009-07-05T19:57:10.209-07:00</atom:updated><title>Rave: Google Wave Robots in Ruby</title><description>&lt;span style="font-weight: bold;font-size:130%;" &gt;Google Wave&lt;/span&gt;&lt;br /&gt;There's been a lot of talk about Google Wave lately, so what's Wave all about?  Wave is a &lt;a href="http://www.waveprotocol.org/"&gt;protocol&lt;/a&gt;, that extends XMPP.  In practice, it contains elements of email, instant messaging and threaded discussion. Oh, and document management. Yeah, it seems to do just about everything.  The problem with that is that it makes it really hard to describe.  It always sounds like it is trying to be everything - to solve every problem.  But really, it's all about online collaborative communication. Here, take a look at the &lt;a href="http://www.youtube.com/watch?v=v_UyVmITiYQ"&gt;demo from the Google I/O keynote&lt;/a&gt; this year.  It makes more sense once you've seen it in action.&lt;br /&gt;&lt;br /&gt;Wave is in an alpha release right now.  As far as I know, only the attendees of Google I/O have accounts on the sandbox.  Hopefully that will change before too long.  Google's only saying that it will be released "before the end of the year" right now.  In the mean time, there's a ton of information on the main &lt;a href="http://wave.google.com/"&gt;Wave &lt;/a&gt;site, the &lt;a href="http://www.waveprotocol.org/draft-protocol-spec"&gt;wave protocol &lt;/a&gt;site, and the &lt;a href="http://googleblog.blogspot.com/2009/05/went-walkabout-brought-back-google-wave.html"&gt;Google blog&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Some Terminology&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;There are three primary components to Google Wave: &lt;span style="font-style: italic;"&gt;waves&lt;/span&gt;, &lt;span style="font-style: italic;"&gt;wavelets &lt;/span&gt;and &lt;span style="font-style: italic;"&gt;blips&lt;/span&gt;. &lt;span style="font-style: italic;"&gt;Waves &lt;/span&gt;are the central component. Think of a &lt;span style="font-style: italic;"&gt;wave &lt;/span&gt;as a conversation.  A &lt;span style="font-style: italic;"&gt;wave &lt;/span&gt;has one or more &lt;span style="font-style: italic;"&gt;wavelets&lt;/span&gt;, which are sort of like sub-conversations, or threads of discussion under a conversation.  Finally, &lt;span style="font-style: italic;"&gt;blips &lt;/span&gt;are the actual messages in the discussion, so a &lt;span style="font-style: italic;"&gt;wavelet &lt;/span&gt;can contain one or more &lt;span style="font-style: italic;"&gt;blips&lt;/span&gt;.  There's a better description of these terms in &lt;a href="http://www.waveprotocol.org/draft-protocol-spec#wavelets"&gt;the protocol spec&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Wasn't This Post Supposed to be About Robots?&lt;/span&gt;&lt;br /&gt;Here's what's really cool about Google Wave: there's an API for building extensions for it.  There are three things that a developer can do with Wave: embed a wave on a website, build gadgets to be embedded in a wave, and build robots.  There's a discussion on each of these on the &lt;a href="http://code.google.com/apis/wave/"&gt;Wave API&lt;/a&gt; site.  My focus here is going to be on robots, because let's face it, robots are awesome.&lt;br /&gt;&lt;br /&gt;Robots are automated participants in a wave.  They can do basically anything that a human participant can do: they can read the conversation, add to the conversation, and they can edit a conversation.  So you can, for example, build a spell checker that reads everthing you post to a wave and edits it to change typos as you type.  There are some cool examples of things you can do with robots in the &lt;a href="http://www.youtube.com/watch?v=v_UyVmITiYQ"&gt;Google I/O keynote demo&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Google Wave Robot Tools&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Google released two toolkits for building robots, one in &lt;a href="http://code.google.com/apis/wave/extensions/robots/java-tutorial.html"&gt;Java&lt;/a&gt;, and one in &lt;a href="http://code.google.com/apis/wave/extensions/robots/python-tutorial.html"&gt;Python&lt;/a&gt;.  Furthermore, there is currently a restriction in place that requires robots to live on &lt;a href="http://code.google.com/appengine/"&gt;Google App Engine&lt;/a&gt;. So that means that a robot has to either be in Python or Java...  or some other language implemented on the JVM.  Happily, for those of us who prefer to code in Ruby, we've got JRuby.  Now all we need is an implementation of the robot client API in Ruby.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Rave: A Google Wave robot client framework for Ruby&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;There are several parallel efforts to create a Ruby implementation of the robot API. The rest of this post is going to focus on &lt;a href="http://github.com/diminish7/rave"&gt;Rave&lt;/a&gt;, but I just wanted to link to the other implementations that I'm aware of:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Sam Ruby's &lt;a href="http://github.com/rubys/wave-robot-ruby-client"&gt;Wave Robot Ruby Client&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Jack Danger's &lt;a aiotarget="false" aiotitle="Wave" href="http://github.com/JackDanger/wave"&gt;Wave&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Mike Sofaer's &lt;a href="http://github.com/MikeSofaer/Wave-Robot-Sinatra-Template"&gt;Wave Robot Sinatra Template&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Warning!&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Both Rave and Google Wave are super-alpha. There are a lot of things in the protocol that aren't implemented in Rave yet. And the protocol is changing, so the things that are implemented are likely to break at some point. I will try to keep up, but if you notice anything broken, or if you have a need for something that isn't implemented yet, shoot me an email: diminish7 at gmail dot com.&lt;br /&gt;&lt;br /&gt;Okay.  You've been warned.&lt;br /&gt;   &lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;The Basics&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Since Google currently requires that all robots run on App Engine, this tutorial assumes you are using JRuby. You should have JRuby already installed, and you should have the App Engine SDK installed.  You can find more information on JRuby at &lt;a href="http://jruby.org/"&gt;http://jruby.org/&lt;/a&gt;, and you can find more information about the App Engine SDK at &lt;a href="http://code.google.com/appengine/downloads.html"&gt;http://code.google.com/appengine/downloads.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;What We're Going to Build&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;I hate it when people yell in writing.  I want a robot that will moniter discussions and tone down any yelling.  So, for example, this robot will turn a post like "I'M NOT YELLING!!!!" into "I'm not yelling."  The full source for this robot is in &lt;a href="http://github.com/diminish7/rave"&gt;git&lt;/a&gt; under examples/appropriate-casey/.  Here's how you build it yourself:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Install Rave&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   sudo jruby -S gem install rave&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Start a new Rave Project&lt;/span&gt;&lt;br /&gt;Rave comes with a "rave" executable that, among other things, sets up the project structure for you.  Setting up a new project looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   jruby -S rave create [robot_name] [options]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The options include profile_url and image_url, which will set the URL for the robot's profile and avatar, respectively. Here's how the example project "appropriate casey" was set up:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   jruby -S rave create appropriate-casey&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I haven't bothered to create an avatar for casey, but if I had, I would instead have done:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  jruby -S rave create appropriate-casey image_url=http://myimageurl/avatar.png&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This will stub out a project called "appropriate-casey".  It automatically creates a robot class AppropriateCasey, creates the config files for both &lt;a href="http://rack.rubyforge.org/"&gt;Rack&lt;/a&gt; and &lt;a href="http://caldersphere.rubyforge.org/warbler/"&gt;Warbler&lt;/a&gt;, and creates the appengine-web.xml file that App Engine will need.  For the App Engine file, it assumes that the App Engine project name is the same as the robot's name.  So in this case, the application name is appropriate-casey, which means I have to have the URL http://appropriate-casey.appspot.com.  If you name your robot something different than the App Engine application ID, just change the &lt;application&gt; line in appengine-web.xml.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Build your robot!&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;The robot.rb file that Rave created contains a class that extends Rave::Models::Robot. All of the logic needed for your robot to talk to App Engine is included in Rave::Models::Robot, so all you really need to do now is define your robot's event listeners.  Here is a list of the events that Google Wave can send your robot:&lt;br /&gt;&lt;ul&gt;&lt;li&gt; WAVELET_BLIP_CREATED&lt;/li&gt;&lt;li&gt;WAVELET_BLIP_REMOVED&lt;/li&gt;&lt;li&gt;WAVELET_PARTICIPANTS_CHANGED&lt;/li&gt;&lt;li&gt;WAVELET_TIMESTAMP_CHANGED&lt;/li&gt;&lt;li&gt;WAVELET_TITLE_CHANGED&lt;/li&gt;&lt;li&gt;WAVELET_VERSION_CHANGED&lt;/li&gt;&lt;li&gt;BLIP_CONTRIBUTORS_CHANGED&lt;/li&gt;&lt;li&gt;BLIP_DELETED&lt;/li&gt;&lt;li&gt;BLIP_SUBMITTED&lt;/li&gt;&lt;li&gt;BLIP_TIMESTAMP_CHANGED&lt;/li&gt;&lt;li&gt;BLIP_VERSION_CHANGED&lt;/li&gt;&lt;li&gt;DOCUMENT_CHANGED&lt;/li&gt;&lt;li&gt;FORM_BUTTON_CLICKED&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;To add a listener to your robot that will respond to a given event, just define a method with the lower-case version of the event name, that accepts an event and a context.  So, I want appropriate-casey to do something whenever a document is changed (the DOCUMENT_CHANGED event), so I add the following method to my robot:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   def document_changed(event, context)&lt;br /&gt;      # Do some stuff&lt;br /&gt;   end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Event object contains the following properties:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;type (the type of event, "DOCUMENT_CHANGED" in this case)&lt;/li&gt;&lt;li&gt;timestamp (the timestamp that the event occurred)&lt;/li&gt;&lt;li&gt;modified_by (the user ID of the user who modified the document)&lt;/li&gt;&lt;li&gt;properties (varies by event type, but this will be a range of affected text in this case)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;The Context object contains the following properties:&lt;br /&gt;&lt;ul&gt;&lt;li&gt; waves (a hash - the keys are the wave IDs, and the values are the waves)&lt;/li&gt;&lt;li&gt; wavelets (a hash - the keys are the wavelet IDs, and the values are the wavelets)&lt;/li&gt;&lt;li&gt; blips (a hash - the keys are the blip IDs, and the values are the blips)&lt;/li&gt;&lt;li&gt; operations (an array of operations)&lt;/li&gt;&lt;/ul&gt;See the &lt;a href="http://www.waveprotocol.org/draft-protocol-spec"&gt;Google Wave protocol documentation&lt;/a&gt; for definitions of each of these things...&lt;br /&gt;&lt;br /&gt;So, with Appropriate Casey, I want to create a robot that looks for people "shouting" in Waves. To do this, I just create a document_changed method in my robot that looks something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   def document_changed(event, context)&lt;br /&gt;     unless event.modified_by == "appropriate-casey@appspot.com" || &lt;br /&gt;            event.modified_by == "spelly@gwave.com"&lt;br /&gt;        context.blips.values.each do |blip|&lt;br /&gt;          if blip.content&lt;br /&gt;            new_sentence = true&lt;br /&gt;            blip.content.length.times do |index|&lt;br /&gt;              range = index..index+1&lt;br /&gt;              char = blip.content[index, 1]&lt;br /&gt;              if char =~ /[A-Z]/ &amp;&amp; !new_sentence&lt;br /&gt;                blip.set_text_in_range(range, char.downcase)&lt;br /&gt;              elsif char =~ /[a-z]/ &amp;&amp; new_sentence&lt;br /&gt;                blip.set_text_in_range(range, char.upcase)&lt;br /&gt;              elsif char == "!"&lt;br /&gt;                if new_sentence&lt;br /&gt;                  blip.delete_range(range)&lt;br /&gt;                else&lt;br /&gt;                  blip.set_text_in_range(range, ".")&lt;br /&gt;                end&lt;br /&gt;              end&lt;br /&gt;              new_sentence = (char =~ /\.!?/ || (char =~ /\s/ &amp;&amp; new_sentence))&lt;br /&gt;            end&lt;br /&gt;          end&lt;br /&gt;        end&lt;br /&gt;      end&lt;br /&gt;   end&lt;br /&gt;&lt;/pre&gt;  &lt;br /&gt;For simplicity's sake, I'm brute-forcing this a bit. It would be better to look at the event properties and just change the range of text that the event is telling me about.&lt;br /&gt;&lt;br /&gt;So that's it, my robot is now ready to go: just 17 lines of code.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Packaging for deploy&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;I'm assuming that you have an application set up on App Engine already.  Again, for the time being, all robots have to be on App Engine to work with Google Wave.&lt;br /&gt;&lt;br /&gt;First of all, we need to turn our project into the correct format for App Engine.  There is a utility&lt;br /&gt;in the "rave" executable for this, so from your robot's top-level folder, run:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   jruby -S rave war&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is basically just a wrapper around the &lt;a href="http://caldersphere.rubyforge.org/warbler/"&gt;warbler&lt;/a&gt; gem, but with some additional cleanup to get things in the right format for App Engine.  For example, the complete JRuby jar is too large for App Engine, so Rave breaks it up into two jars.  Once you've run "rave war", you'll see a tmp/ folder, and a .war file.  You can ignore the .war file, as App Engine needs the unpacked version. The tmp/war folder is what will get deployed.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Testing&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;The worst part about App Engine is testing...&lt;br /&gt;&lt;br /&gt;Rave includes a "server" command that starts up Rack for your application, but that is of limited use, since App Engine has many App Engine-specific issues. Better to use the App Engine SDK (which is still not a perfect match to the deployed environment, but it's better).  The App Engine SDK comes with a dev_server command.  From your project directory run:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   path/to/appengine_sdk/bin/dev_server tmp/war&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This will start the development server on port 8080.  You can now hit the following three URLs:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;  &lt;a href="http://localhost:8080/_wave/capabilities.xml"&gt;http://localhost:8080/_wave/capabilities.xml&lt;/a&gt;&lt;/li&gt;&lt;li&gt;  &lt;a href="http://localhost:8080/_wave/robot/profile"&gt;http://localhost:8080/_wave/robot/profile&lt;/a&gt;&lt;/li&gt;&lt;li&gt;  &lt;a href="http://localhost:8080/_wave/robot/jsonrpc"&gt;http://localhost:8080/_wave/robot/jsonrpc&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;The first two are GETs, but the last one requires a POST. See the &lt;a href="http://www.waveprotocol.org/draft-protocol-spec"&gt;Google Wave protocol&lt;/a&gt; for the expected body of the POST if you want to test locally.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Deploying&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Use the App Engine SDK to deploy.  From your project directory run:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  path/to/appengine_sdk/bin/appcfg update tmp/war&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Using the Robot&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;From a Google Wave client, start a new wave, and invite your robot as a participant.  Your robot's user name will be [robot_name]@appspot.com.  So Casey is at appropriate-casey@appspot.com.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The End&lt;/span&gt;&lt;br /&gt;That's it.  You now have a working robot!  Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-151131889078347837?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2009/06/rave-google-wave-robots-in-ruby.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>4</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-5715362740605969648</guid><pubDate>Wed, 17 Jun 2009 01:03:00 +0000</pubDate><atom:updated>2009-06-16T18:22:53.825-07:00</atom:updated><title>Moonstone</title><description>We recently open-sourced Moonstone, our JRuby wrapper around Lucene, the Java search engine toolkit.  Moonstone is simple, and easy to use - it lets you put together a powerful search engine quickly.  It gives you the full capabilities of Lucene, but replaces the Java-isms with a more Ruby-ish syntax.  I wanted to take a quick minute to run through the basics:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Installation&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;    sudo jruby -S gem install moonstone&lt;br /&gt;&lt;/pre&gt;Note that if you are on Windows, drop the "sudo".  Also, for the record, no one's ever tested Moonstone on Windows.  But since it's all JRuby and Java, there shouldn't be any issues...&lt;br /&gt;&lt;br /&gt;If you want the source, you can get it from &lt;a href="http://github.com/automatthew/moonstone/tree/master"&gt;automatthew's github repo&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Engines&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Creating an engine requires two things: a doc_from method and an analyzer.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  class SimplestEngine &lt; Moonstone::Engine&lt;br /&gt;    def doc_from(record)&lt;br /&gt;      doc = Lucene::Document::Doc.new&lt;br /&gt;      doc.add_field('name', record[:name])&lt;br /&gt;      doc.add_field('desc', record[:description])&lt;br /&gt;      doc&lt;br /&gt;    end&lt;br /&gt;   def analyzer&lt;br /&gt;     @analyzer ||= Lucene::Analysis::WhitespaceAnalyzer.new&lt;br /&gt;   end&lt;br /&gt; end    &lt;br /&gt; # We can now instantiate the engine&lt;br /&gt; engine = SimplestEngine.new&lt;br /&gt; # and create an index of some data&lt;br /&gt; records = [&lt;br /&gt;     {:name =&gt; "Moonstone", :description =&gt; "A simple JRuby wrapper around Lucene"},&lt;br /&gt;     {:name =&gt; "Foo", :description =&gt; "A search engine based on Moonstone"}&lt;br /&gt;   ]&lt;br /&gt; # By convention the index method accepts an Enumerable that yields hashes&lt;br /&gt; engine.index(records)&lt;br /&gt;&lt;br /&gt; #Search for data&lt;br /&gt; q = Lucene::Search::TermQuery.new("name", "Moonstone")&lt;br /&gt; documents = engine.search(q)&lt;br /&gt; documents.each {|d| puts d['name']}&lt;br /&gt;&lt;/pre&gt;That's it in a nutshell.  There are some more examples in the README on github, and more documentation is coming soon.&lt;br /&gt;&lt;br /&gt;Happy searching!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-5715362740605969648?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2009/06/moonstone.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-1320617763945966086</guid><pubDate>Wed, 13 May 2009 04:28:00 +0000</pubDate><atom:updated>2009-05-12T22:03:26.006-07:00</atom:updated><title>Beholder: A Ruby Template Engine</title><description>I started up a new project to create a template engine for Ruby web apps.  The beginnings of it are on github &lt;a href="http://github.com/diminish7/beholder/tree/master"&gt;here&lt;/a&gt;.  It's very, very rough, so don't judge yet. :-)&lt;br /&gt;&lt;br /&gt;I know there are a ton of template engines out there.  But here's my complaint with them: often the person developing the front end of an app is primarily an HTML/CSS/JavaScript developer.  They may not know Ruby at all.  So template engines like Markaby and Hoshi are out.  ERB is the most HTML-like template engine, but a) it's still got Ruby mixed into it, so it isn't ideal for strictly front-end developers, and b) it's a mixture of Ruby and HTML, which is ugly and spaghetti-ish.&lt;br /&gt;&lt;br /&gt;Enter Beholder.&lt;br /&gt;&lt;br /&gt;Beholder is a template engine comprised of HTML tags.  Dynamic behavior is accomplished through the use attributes.  So, for example, if you have a page and you want to show a chunk of it conditionally, you could do something like this:&lt;br /&gt;&lt;blockquote&gt;&amp;#060;html&gt;&lt;br /&gt;    &amp;#060;head&gt;&lt;br /&gt;        &amp;#060;title&gt;Hello World&amp;#060;/title&gt;&lt;br /&gt;    &amp;#060;/head&gt;&lt;br /&gt;    &amp;#060;body&gt;&lt;br /&gt;        &amp;#060;span component="if" condition="true"&gt;&lt;br /&gt;            Body of 'if' component&lt;br /&gt;        &amp;#060;/span&gt;&lt;br /&gt;        &amp;#060;span component="elsif" condition="true"&gt;&lt;br /&gt;            Body of 'elsif' component&lt;br /&gt;        &amp;#060;/span&gt;&lt;br /&gt;        &amp;#060;span component="else"&gt;&lt;br /&gt;            Body of 'else' component&lt;br /&gt;        &amp;#060;/span&gt;&lt;br /&gt;    &amp;#060;/body&gt;&lt;br /&gt;&amp;#060;/html&gt;&lt;br /&gt;&lt;/blockquote&gt;Which would be rendered as:&lt;br /&gt;&lt;blockquote&gt;&amp;#060;html&gt;&lt;br /&gt;    &amp;#060;head&gt;&lt;br /&gt;        &amp;#060;title&gt;Hello World&amp;#060;/title&gt;&lt;br /&gt;    &amp;#060;/head&gt;&lt;br /&gt;    &amp;#060;body&gt;&lt;br /&gt;            Body of 'if' component&lt;br /&gt;    &amp;#060;/body&gt;&lt;br /&gt;&amp;#060;/html&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;/blockquote&gt;The 'component' attribute can either refer to a built in control flow structure, such as 'if', or 'foreach', or it can refer to a partial, so you can embed mini-beholder templates in your main template.  There is also a 'yield' component, so each component can act like a layout, generating it's HTML, and then yielding the content within the component node in the template.&lt;br /&gt;&lt;br /&gt;In addition, any attribute can be made dynamically by including the 'prop:' prefix.  So if you have a template like this:&lt;br /&gt;&lt;blockquote&gt;&amp;#060;html&gt;&lt;br /&gt;    &amp;#060;head&gt;&lt;br /&gt;        &amp;#060;title&gt;Hello World&amp;#060;/title&gt;&lt;br /&gt;    &amp;#060;/head&gt;&lt;br /&gt;    &amp;#060;body&gt;&lt;br /&gt;        &amp;#060;span component="simple_component" message_class="beholder" /&gt;&lt;br /&gt;    &amp;#060;/body&gt;&lt;br /&gt;&amp;#060;/html&gt;&lt;span component="simple_component" message_class="beholder"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;And a partial called simple_component.beh like this:&lt;br /&gt;&lt;blockquote&gt;&amp;#060;p class="prop:message_class"&gt;What's up, world?&amp;#060;/p&gt;&lt;br /&gt;&lt;/blockquote&gt;It would be rendered like this:&lt;br /&gt;&lt;blockquote&gt;&lt;blockquote&gt;&amp;#060;html&gt;&lt;br /&gt;    &amp;#060;head&gt;&lt;br /&gt;        &amp;#060;title&gt;Hello World&amp;#060;/title&gt;&lt;br /&gt;    &amp;#060;/head&gt;&lt;br /&gt;    &amp;#060;body&gt;&lt;br /&gt;        &amp;#060;p class="beholder"&gt;What's up, world?&amp;#060;/p&gt;&lt;br /&gt;    &amp;#060;/body&gt;&lt;br /&gt;&amp;#060;/html&gt;&lt;span component="simple_component" message_class="beholder"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;&lt;/blockquote&gt;The 'prop:' prefixed attributes get evaluated as (in this order) a) the value of the parent component's attribute with that name, b) the value of a local variable of that name, or c) a method in the helper class.  (Note that I've only implemented a) right now... so you'll just have to trust me that that's where I'm going.)&lt;br /&gt;&lt;br /&gt;This is modeled after &lt;a href="http://tapestry.apache.org/"&gt;Tapestry &lt;/a&gt;templates (Java) and &lt;a href="http://www.kid-templating.org/"&gt;Kid &lt;/a&gt;templates (Python).  Anyway, there's a lot of work to be done on it (particularly around the evaluation of 'prop:' attributes), but I like the idea of it a lot.&lt;br /&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-1320617763945966086?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2009/05/beholder-ruby-template-engine.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-7007738027708279559</guid><pubDate>Mon, 04 May 2009 02:35:00 +0000</pubDate><atom:updated>2009-05-03T19:49:51.598-07:00</atom:updated><title>Ruby HTML Template Engines</title><description>I've been thinking a bit about template engines in Ruby.  There's a ton of them out there.  &lt;a href="http://www.hokstad.com/mini-reviews-of-19-ruby-template-engines.html"&gt;Here's&lt;/a&gt; a good review of a bunch of them.  We generally use either &lt;a href="http://debu.gs/hoshi"&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;Hoshi&lt;/span&gt; &lt;/a&gt;or &lt;a href="http://markaby.rubyforge.org/"&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;Markaby&lt;/span&gt;&lt;/a&gt; at my job right now.  For our purposes, these have been great.  We all work on all aspects of a project, from front to back, and we'd all rather write Ruby than HTML.  However, in several jobs I've had, we've had a front-end developer doing all the HTML and &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;CSS&lt;/span&gt;.  The problem is that people who really specialize in front-end development often don't know Ruby.  So &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;Hoshi&lt;/span&gt; and &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;Markaby&lt;/span&gt; are out.  In those situations, we've used &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5"&gt;ERB&lt;/span&gt; templates.  But then you've got Ruby and HTML mixed together, and that's never pretty.  What I'd really like is a template engine that just looks like HTML, with no Ruby mixed into it.  Something along the lines of Python's &lt;a href="http://www.kid-templating.org/"&gt;Kid&lt;/a&gt;, or Java's &lt;a href="http://tapestry.apache.org/"&gt;Tapestry&lt;/a&gt; templates.  I've been looking around a bit and haven't found anything like that yet in Ruby.  Sounds like time to spin up a new project!  More to come...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-7007738027708279559?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2009/05/ruby-html-template-engines.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-4402814699176555749</guid><pubDate>Wed, 29 Apr 2009 04:39:00 +0000</pubDate><atom:updated>2009-04-28T21:47:09.123-07:00</atom:updated><title>More Logic Games</title><description>The two position-based logic games (&lt;a href="http://github.com/diminish7/logic-games/blob/c221beb6ff2990ae4fde2423c723982a5cf2f159/games/broadcast.rb"&gt;broadcast &lt;/a&gt;and &lt;a href="http://github.com/diminish7/logic-games/blob/c221beb6ff2990ae4fde2423c723982a5cf2f159/games/piano_instructor.rb"&gt;piano_instructor&lt;/a&gt;) are working correctly now, with all rules and facts coming from the DSL, including the rules inferred by the combination of other rules.&lt;br /&gt;&lt;br /&gt;It ain't pretty though...  The inference of rules is very non-generic, and very spaghetti code-ish.  I really just wanted to get the logic straight in my head first.  I'll look at pretty-ing it up next.  But it works!  Better to have ugly, but working code today, right?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-4402814699176555749?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2009/04/more-logic-games.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-4859524726323652473</guid><pubDate>Sat, 25 Apr 2009 20:21:00 +0000</pubDate><atom:updated>2009-04-25T13:34:45.775-07:00</atom:updated><title>Logic Games: Almost There!</title><description>Made a bit more progress on the Logic Games project today.  I have all of the rules for both the broadcast game and the piano instructor game set up generically, so that all of the base rules are specified in the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;DSL&lt;/span&gt;.  Here's an example, taken from the &lt;a href="http://github.com/diminish7/logic-games/blob/d10512af4ddf9dc1d77b8ac25769001ad93a6747/games/broadcast.rb"&gt;broadcast game&lt;/a&gt;:&lt;br /&gt;&lt;blockquote&gt;new_game&lt;br /&gt;called "Broadcast"&lt;br /&gt;described_as("Seven consecutive time slots... etc")&lt;br /&gt;&lt;br /&gt;with_property "Position"&lt;br /&gt;with_range 1, 7&lt;br /&gt;for_entities "G", "H", "L", "O", "P", "S", "News"&lt;br /&gt;&lt;br /&gt;new_question&lt;br /&gt;described_as "If G is played 2&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;nd&lt;/span&gt;... etc"&lt;br /&gt;with_fact "G", "Position", :is, 2&lt;br /&gt;determines ["News", "H", "L", "O", "S"], "Position", :is, 3&lt;br /&gt;&lt;br /&gt;#L must be played immediately before O&lt;br /&gt;new_rule "L", :before, "O", 1&lt;br /&gt;#The news tape must be played at some time after L&lt;br /&gt;new_rule "News", :after, "L"&lt;br /&gt;#There must be exactly two time slots between G and P, regardless of&lt;br /&gt;#   whether G comes before P or whether G comes after P&lt;br /&gt;new_rule "G", :separated_by, "P", 2&lt;br /&gt;&lt;br /&gt;evaluate&lt;br /&gt;&lt;/blockquote&gt;That generates almost all the facts and rules for the game.  There are a few rules that are implied by the combination of these rules - since "O" is immediately after "L", and "News" is sometime after "L", then "News" must be at least 2 after "L".  So that's next: figure out how to imply rules from combinations of explicit rules.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-4859524726323652473?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2009/04/logic-games-almost-there.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-6529852417802745053</guid><pubDate>Fri, 17 Apr 2009 00:50:00 +0000</pubDate><atom:updated>2009-04-16T18:11:25.580-07:00</atom:updated><title>The Start of a DSL</title><description>I've started refactoring my logic games into a DSL that more closely approximates the English language logic games questions.  Here's what I have so far:&lt;br /&gt;&lt;br /&gt;&lt;blockquote  style="font-family:arial;"&gt;&lt;span style="font-size:85%;"&gt;#Set up game description&lt;br /&gt;new_game&lt;br /&gt;called "Broadcast"&lt;br /&gt;described_as(&lt;br /&gt;"Seven consecutive time slots for a broadcast, numbered in chronological order 1 through 7, will be filled by six song tapes - G, H, L, O, P, S - and exactly one news tape. Each tape is to be assigned to a different time slot, and no tape is longer than any other tape. The broadcast is subject to the following restrictions:&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-size:85%;"&gt;   L must be played immediately before O&lt;/span&gt;&lt;/li&gt;&lt;li&gt;The news tape must be played at some time after L&lt;/li&gt;&lt;li&gt;There must be exactly two time slots between G and P, regardless of whether G comes before P or whether G comes after P"&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-size:85%;"&gt; )&lt;br /&gt;with_property "Position"&lt;br /&gt;with_range 1, 7&lt;br /&gt;for_entities "G", "H", "L", "O", "P", "S", "News"&lt;br /&gt;&lt;br /&gt;#Create the questions&lt;br /&gt;new_question&lt;br /&gt;described_as "If G is played second, which one of the following tapes must be played third?"&lt;br /&gt;with_fact "G", "Position", :is, 2&lt;br /&gt;determines ["News", "H", "L", "O", "S"], "Position", :is, 3&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;This replaces the first 70 lines of the game.  It sets up the game, and adds all the standard rules, like one-place-at-a-time rules, mutual-exclusion rules, and last-available-position rules.&lt;br /&gt;&lt;br /&gt;My goal was to make this read more or less like the English version of the game.  One interesting requirement for that has been that it needs to remember context.  So if I say "new_question" on one line, then "described_as ..." on the next line, the game needs to remember that "described_as" applies to the new question we just created.  I'm doing this by maintaining state.  So each action keeps a record of the thing it acted upon, so that the next action knows what the context is.  The alternative was to string together actions, like "new_game.described_as(description).with_fact(...)" etc.  I didn't like that approach because it looks less like English, which was, again, my goal.&lt;br /&gt;&lt;br /&gt;The next step in the DSL will be a simple way to specify the rules.  I want it to be close to the English version of the rule, so&lt;br /&gt;&lt;blockquote&gt;"Henry's lesson is later in the schedule than Janet's"&lt;/blockquote&gt; might turn into something like&lt;br /&gt;&lt;blockquote&gt;new_rule "Henry", "Position", :after, "Janet"&lt;br /&gt;&lt;/blockquote&gt;Which will then get broken up into the 20 or so rules that that implies behind the scenes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-6529852417802745053?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2009/04/start-of-dsl.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-1262747978573879710</guid><pubDate>Wed, 08 Apr 2009 04:18:00 +0000</pubDate><atom:updated>2009-04-07T21:41:12.120-07:00</atom:updated><title>Victory!</title><description>The &lt;a href="http://github.com/diminish7/logic-games/tree/master"&gt;Logic Games project &lt;/a&gt;now officially solves its first game!  It's admittedly a pretty manual process right now.  I still have to break the logic game up into all the initial rules and facts.  But then it successfully runs through the rule engine and generates all the facts that can be inferred, and answers the question.&lt;br /&gt;&lt;br /&gt;Here's the game it solves right now:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Seven consecutive time slots for a broadcast, numbered in chronological order 1 through 7, will be filled by six song tapes - G, H, L, O, P, S - and exactly one news tape. Each tape is to be assigned to a different time slot, and no tape is longer than any other tape. The broadcast is subject to the following restrictions:&lt;/span&gt;&lt;br /&gt;&lt;ul style="font-style: italic;"&gt;&lt;li&gt;L must be played immediately before O&lt;/li&gt;&lt;li&gt;The news tape must be played at some time after L&lt;/li&gt;&lt;li&gt;There must be exactly two time slots between G and P, regardless of whether G comes before P or whether G comes after P&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-style: italic;"&gt;    If G is played second, which one of the following tapes must be played third?&lt;/span&gt;&lt;br /&gt;&lt;ul style="font-style: italic;"&gt;&lt;li&gt;The news&lt;/li&gt;&lt;li&gt;H&lt;/li&gt;&lt;li&gt;L&lt;/li&gt;&lt;li&gt;O&lt;/li&gt;&lt;li&gt;S&lt;/li&gt;&lt;/ul&gt;If you grab the code from github, and run&lt;br /&gt;    &lt;br /&gt;       ruby logic_games.rb games&lt;br /&gt;&lt;br /&gt;it will correctly tell you L is in the 3rd position.&lt;br /&gt;Yay!&lt;br /&gt;&lt;br /&gt;It was interesting to me that there were a lot of rules that I was using intuitively when I solved the problem by hand, but that I neglected to add to rule base because I never explicitly acknowledged the rule in my head.  An example of this is the rules and facts generated by the combination of the fact that L must be played immediately before O, and the News must be played some time after L.  What that implicitly means is that the News must be &lt;span style="font-style: italic;"&gt;at least&lt;/span&gt; 2 slots after L, because the slot immediately after L is taken up by O.  These types of rules will be the hardest to generalize, because they need to take external knowledge (previous statements) into account.&lt;br /&gt;&lt;br /&gt;So the next step is to start generifying the logic for creating rules and facts.  I would like to come up with some sort of DSL for specifying knowledge in a way that approximates the written game statements.  So, for example, to specify that L must be played immediately before O, you might have something like&lt;br /&gt;&lt;br /&gt;"L".before("O", 1)&lt;br /&gt;&lt;br /&gt;which would translate into "The entity with the name of 'L', must be positionally before the entity named 'O', by 1 position."  This would then generate the 20 or so rules that this implies, like "If L is in position 1, then O is in position 2", etc.&lt;br /&gt;&lt;br /&gt;So, to accomplish this, I'm going to work on implementing several other games so I can start getting a feel for the types of generalizations I'll need to implement.  Then I'll go from there.&lt;br /&gt;&lt;br /&gt;Good times.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-1262747978573879710?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2009/04/victory.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-2670895596062204200</guid><pubDate>Tue, 17 Feb 2009 01:55:00 +0000</pubDate><atom:updated>2009-02-16T18:28:58.365-08:00</atom:updated><title>Rule Engine</title><description>So I am basically at the point now where I have a functioning rule engine for the logic games project.  It's all out on &lt;a href="http://github.com/diminish7/logic-games/tree/master"&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;github&lt;/span&gt;&lt;/a&gt;.  Here's the gist of it:&lt;br /&gt;&lt;br /&gt;The class structure is described &lt;a href="http://jasonrush.com/images/logic_games_UML.jpg"&gt;here&lt;/a&gt;, from a few posts ago.  The rule evaluation is the new part.  The &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;RuleBase&lt;/span&gt; stores a list of Facts, which are in a hash keyed by the Entity and the Property to which they apply.&lt;br /&gt;&lt;br /&gt;A Clause can be evaluated by determining if there is a Fact with the same information.  So the Clause's truth is evaluated by looking at the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;RuleBase's&lt;/span&gt; facts hash with the Clause's Entity and Property.  If a Fact is found for that Entity and Property, and the Fact's property_value and &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_3"&gt;comparator&lt;/span&gt; match those of the Clause, then the Clause evaluates as TRUE.  If the Fact's property_value matches that of the Clause, but the &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_4"&gt;comparator&lt;/span&gt; is NOT equal to the Clause's, then the Clause evaluates as FALSE.  In all other situations, the Clause evaluates as UNKNOWN.&lt;br /&gt;&lt;br /&gt;A &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5"&gt;ClauseCluster&lt;/span&gt; is evaluated by looking at the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_6"&gt;rhs&lt;/span&gt; and &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_7"&gt;lhs&lt;/span&gt; (right hand side and left hand side of the clause) and using the operator to perform Boolean logic.  So if the operator is AND, then the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_8"&gt;lhs&lt;/span&gt; and &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_9"&gt;rhs&lt;/span&gt; must both be TRUE for the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_10"&gt;ClauseCluster&lt;/span&gt; to evaluate to TRUE, and if the operator is OR, then the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_11"&gt;lhs&lt;/span&gt; and the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_12"&gt;rhs&lt;/span&gt; must both be FALSE for the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_13"&gt;ClauseCluster&lt;/span&gt; to evaluate to FALSE.  If the truth of the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_14"&gt;ClauseCluster&lt;/span&gt; can't be determined (for example, if the operator is AND and the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_15"&gt;lhs&lt;/span&gt; is TRUE but the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_16"&gt;rhs&lt;/span&gt; is UNKNOWN, then the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_17"&gt;ClauseCluster&lt;/span&gt; evaluates to UNKNOWN.&lt;br /&gt;&lt;br /&gt;Rule's are evaluated by evaluating the Clause or &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_18"&gt;ClauseCluster&lt;/span&gt; that is the antecedent.  All the work is delegated to the antecedent's evaluation method.  The interesting part of the Rule's evaluation is what happens after the truth of the antecedent is determined.  If the antecedent is TRUE, then a new Fact gets added to the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_19"&gt;RuleBase's&lt;/span&gt; facts hash based on the consequent.  If the antecedent is TRUE or FALSE (i.e. not UNKNOWN), then a flag gets set marking the Rule as having been fired.&lt;br /&gt;&lt;br /&gt;The &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_20"&gt;RuleEngine&lt;/span&gt; gets evaluated by evaluating all of its Rules.  It iterates over all of its Rules, evaluating any Rule that has not already been fired (i.e. who's truth is still UNKNOWN) until either a) all Rules have been fired, or b) the remaining unfired Rules cannot be evaluated because we don't have enough information.  In the case of the latter, this is determined by completing a full iteration without inferring any new knowledge.  So if all Rules get evaluated, and no new Facts get generated, then the no new Facts will be generated in the next iteration either, so we can stop trying.&lt;br /&gt;&lt;br /&gt;My next step is to start putting together a suite of tests that represent real logic games data.  For right now, I am going to manually convert a set of logic games into sets of Rules and Facts just to test out the rule engine.  After that, the really hard work begins: parsing a logic game, turning natural language into a set of Rules.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-2670895596062204200?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2009/02/rule-engine.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-8737773975147020216</guid><pubDate>Sun, 01 Feb 2009 00:47:00 +0000</pubDate><atom:updated>2009-01-31T16:49:53.891-08:00</atom:updated><title>Finally, Some Progress</title><description>Whew, I have finally had enough time to get going on the logic games project.  It's out on github at &lt;a href="http://github.com/diminish7/logic-games/"&gt;http://github.com/diminish7/logic-games/&lt;/a&gt;.  Right now it just the model, some basic validations and specs.  More to come, I swear!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-8737773975147020216?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2009/01/finally-some-progress.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-3668952566274529483</guid><pubDate>Mon, 12 Jan 2009 01:01:00 +0000</pubDate><atom:updated>2009-01-11T17:06:05.503-08:00</atom:updated><title>Crazy Busy...</title><description>So, I have been so crazy busy lately that I didn't even notice that my blog was down for like two weeks...  Sorry, all of my loyal readers (that's funny because right now that's just my wife...).&lt;br /&gt;&lt;br /&gt;I hope to make some more progress on the logic games project next weekend.  I've been doing some stuff with natural language processing at work.  So I am rethinking how I want to persist the data: rather than trying to persist it relationally, I am thinking more and more about trying to parse the text of the logic games questions into these objects on the fly, so all that needs to be persisted is the text.&lt;br /&gt;&lt;br /&gt;So I think I will simplify phase one of the project a little by cutting persistence out of the picture (this means you DataMapper).&lt;br /&gt;&lt;br /&gt;More on this next weekend (I hope).  And hopefully more progress next weekend.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-3668952566274529483?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2009/01/crazy-busy.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-3394624941403676227</guid><pubDate>Sun, 07 Dec 2008 19:48:00 +0000</pubDate><atom:updated>2008-12-07T12:14:39.362-08:00</atom:updated><title>Rule Grammar Persistence</title><description>&lt;a href="http://jasonrush.com/images/logic_games_UML.jpg"&gt;Here&lt;/a&gt; is a very high-level diagram of the grammar for storing rules in the &lt;a href="http://jasonrush.com/blog/2008/11/logic-games-project.html"&gt;logic games project&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;RuleBase&lt;/span&gt; is the top-level class that stores the collection of Rules and the collection of known Facts.  A Rule consists of an Antecedent (the "if" portion of the rule) and a Consequent (the "then" portion of the rule).  The consequent is a simple Clause, which consists of an Entity / Property pair a comparator and a value for the property.  So to represent a car being painted blue, we would have an Entity of "car", a property of "color", a comparator of "equals" and a value of "blue".  The antecedent is a &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;ClauseCluster&lt;/span&gt;, which represents one or more Clauses joined by the Boolean operators AND, OR and NOT.  To represent this, a &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;ClauseCluster&lt;/span&gt; has a left-hand side (&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;lhs&lt;/span&gt;) and a right-hand side (&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;rhs&lt;/span&gt;), each of which can either be a Clause, or another &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5"&gt;ClauseCluster&lt;/span&gt;.  So you get a tree of &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_6"&gt;ClauseClusters&lt;/span&gt;, where the leaves of the tree are Clauses, and the depth of the tree represents different levels of &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_7"&gt;parenthesization&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;I want to be able to persist &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_8"&gt;RuleBases&lt;/span&gt;.  It's not really necessary for this project, but it will help with testing, and since this is all just a way for me to learn more about knowledge representation, I wanted to make sure I could persist that knowledge as well.&lt;br /&gt;&lt;br /&gt;I started working with &lt;a href="http://www.datamapper.org/doku.php"&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_9"&gt;DataMapper&lt;/span&gt;&lt;/a&gt; as the persistence layer.  I've done a bunch of work with &lt;a href="http://ar.rubyonrails.com/"&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_10"&gt;ActiveRecord&lt;/span&gt;&lt;/a&gt;, and I wanted to try something new.  &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_11"&gt;DataMapper&lt;/span&gt; has several features that make it really interesting to me.  I like the query syntax, and I love that it only keeps one instance of each object in memory at a time.  However, one thing it is lacking that I need in this project is polymorphic associations.  Since the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_12"&gt;lhs&lt;/span&gt; and &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_13"&gt;rhs&lt;/span&gt; of a &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_14"&gt;ClauseCluster&lt;/span&gt; can either be a Clause or another &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_15"&gt;ClauseCluster&lt;/span&gt;, I need a way to define a relationship based on both id and type.  I only need to ever walk the tree from top to bottom, so only need one side of the polymorphic relationship (i.e. a &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_16"&gt;ClauseCluster&lt;/span&gt; has one &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_17"&gt;lhs&lt;/span&gt; and has one &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_18"&gt;rhs&lt;/span&gt;, but I don't care if the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_19"&gt;lhs&lt;/span&gt; and &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_20"&gt;rhs&lt;/span&gt; know who their parent &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_21"&gt;ClauseCluster&lt;/span&gt; is).  So I'm putting together a very simple implementation of polymorphic associations for &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_22"&gt;DataMapper&lt;/span&gt;.  It's too simple to be of much use to anyone outside of this project, but I'll post the code when it's ready.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-3394624941403676227?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2008/12/rule-grammar-persistence.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-351294898906999393</guid><pubDate>Thu, 27 Nov 2008 21:39:00 +0000</pubDate><atom:updated>2008-11-27T14:28:56.715-08:00</atom:updated><title>Brute Force</title><description>Thinking some more about the &lt;a href="http://jasonrush.com/blog/2008/11/logic-games-project.html"&gt;Logic Games project&lt;/a&gt;, my first attempt is going to be brute force.  Each of the statements in the logic game text can be converted into a series of Boolean rules (rules whose conditions either evaluate to true or false), and then I can create a rule engine to iteratively generate facts until the question has been answered.&lt;br /&gt;&lt;br /&gt;Here's an example, taken from another LSAT question:&lt;br /&gt;&lt;blockquote&gt;A piano instructor will schedule exactly one lesson for each of six students - Grace, Henry, Janet, Steve, Tom and Una - one lesson per day for six consecutive days.  The schedule must conform to the following conditions:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Henry's lesson is later in the schedule than Janet's lesson&lt;/li&gt;&lt;li&gt;Una's lesson is later in the schedule than Steve's lesson&lt;/li&gt;&lt;li&gt;Steve's lesson is exactly three days after Grace's lesson&lt;/li&gt;&lt;li&gt;Janet's lesson is on the first day or else the third day&lt;/li&gt;&lt;/ol&gt;If Janet's lesson is scheduled for the first day, the the lesson for which of the following students must be scheduled on the sixth day - Grace, Henry, Steve, Tom or Una?&lt;/blockquote&gt;From this, we can generate a large number of Boolean rules to describe the problem.  These rules will take the form of &lt;span style="font-style: italic;"&gt;if ... then ...&lt;/span&gt; statements.  Taking a look at the first condition - Henry's lesson is later in the schedule than Janet's lesson - we can derive the following rules:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;If Janet is in the fifth position, then Henry is in the sixth position.&lt;/li&gt;&lt;li&gt;If Janet is in the fourth position, then Henry is NOT in the third position&lt;/li&gt;&lt;li&gt;If Janet is in the fourth position, then Henry is NOT in the second position&lt;/li&gt;&lt;li&gt;If Janet is in the fourth position, then Henry is NOT in the first position&lt;/li&gt;&lt;li&gt;If Janet is in the third position, then Henry is NOT in the second position&lt;/li&gt;&lt;li&gt;If Janet is in the third position, then Henry is NOT in the first position&lt;/li&gt;&lt;li&gt;If Janet is in the second position, then Henry is NOT in the first position&lt;/li&gt;&lt;li&gt;If Henry is in the second position, then Janet is in the first position&lt;/li&gt;&lt;li&gt;If Henry is in the third position, then Janet is NOT in the fourth position&lt;/li&gt;&lt;li&gt;If Henry is in the third position, then Janet is NOT in the fifth position&lt;/li&gt;&lt;li&gt;If Henry is in the third position, then Janet is NOT in the sixth position&lt;/li&gt;&lt;li&gt;If Henry is in the fourth position, then Janet is NOT in the fifth position&lt;/li&gt;&lt;li&gt;If Henry is in the fourth position, then Janet is NOT in the sixth position&lt;/li&gt;&lt;li&gt;If Henry is in the fifth position, then Janet is NOT in the sixth position&lt;/li&gt;&lt;/ul&gt;In addition, we can derive two facts from the first condition:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Janet is NOT in the sixth position&lt;/li&gt;&lt;li&gt;Henry is NOT in the first position&lt;/li&gt;&lt;/ul&gt;Then there are three types of additional rules that are implied for all combinations of person/position:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;If person A is in position X, then persons B, C, D, E, and F are NOT in position X&lt;/li&gt;&lt;li&gt;If person A is in position X, the person A is not in any of the other positions&lt;br /&gt;&lt;/li&gt;&lt;li&gt;If person A is NOT in positions 1, 2, 3, 4, or 5, then person A is in position 6&lt;/li&gt;&lt;/ul&gt;So, for example, we would have a set of rules for Henry like this:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;If Henry is in position 1, then Grace is NOT in position 1&lt;br /&gt;&lt;/li&gt;&lt;li&gt;If Henry is in position 1, then Janet is NOT in position 1&lt;/li&gt;&lt;li&gt;If Henry is in position 1, then Steve is NOT in position 1&lt;/li&gt;&lt;li&gt;etc...&lt;/li&gt;&lt;/ul&gt;And&lt;br /&gt;&lt;ul&gt;&lt;li&gt;If Henry is in position 1, then Henry is NOT in position 2&lt;/li&gt;&lt;li&gt;If Henry is in position 1, then Henry is NOT in position 3&lt;/li&gt;&lt;li&gt;If Henry is in position 1, then Henry is NOT in position 4&lt;/li&gt;&lt;li&gt;etc...&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;And&lt;br /&gt;&lt;ul&gt;&lt;li&gt;If Henry is NOT in position 1, and Henry is NOT in position 2, and Henry is NOT in position 3, and Henry is NOT in position 4, and Henry is NOT in position 5, then Henry is in position 6&lt;/li&gt;&lt;li&gt;etc...&lt;/li&gt;&lt;/ul&gt;Once you have generated all of the rules, the way that the algorithm will work is to iterate over the rules deriving new facts.  We start off with 1 fact: Janet is in position 1.  So as we iterate over the rules, we can fire at least 10 additional rules that will derive new facts: Grace is NOT in position 1, Henry is NOT in position 1, Steve is NOT in position 1, Tom is NOT in position 1, Una is NOT in position 1, Janet is NOT in position 2, Janet is NOT in position 3, Janet is NOT in position 4, Janet is NOT in position 5, Janet is NOT in position 6.&lt;br /&gt;&lt;br /&gt;After you make a pass across every rule, you start over at the top, testing each rule again given the facts that we derived from the previous pass, then continue until you have gathered enough facts to answer the question (in this case, Una is the correct answer).&lt;br /&gt;&lt;br /&gt;Because this is a brute force approach, we get a lot of rules to test, so the algorithm will need to keep track of which rules have already been fired (i.e. which rules have a known answer), and on each iteration, only test the unfired rules.&lt;br /&gt;&lt;br /&gt;Now, there is still a lot of hard manual work to do to solve this problem - for each condition, we have to manually break it up into a series of Boolean rules. So, in the long run, I will want to at least make some sort of &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;DSL&lt;/span&gt; to allow me to put in the conditions in a more-or-less English syntax, and let the system generate all the rules for me.  But let's call that phase 2 :-)  For now, I want to focus on building the rule engine.  I am looking through the book &lt;span style="font-style: italic;"&gt;Constructing Intelligent Agents Using Java&lt;/span&gt; by Joseph and Jennifer &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;Bigus&lt;/span&gt;.  The fourth chapter, &lt;span style="font-style: italic;"&gt;Reasoning Systems&lt;/span&gt;, outlines a rule inference engine that I am planning on using as a model for mine.  &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_3"&gt;Their engine&lt;/span&gt; does a lot of stuff that I don't need for mine, like accounting for fuzzy logic, so mine will be a lot simpler.  Also, I'll be working in Ruby rather than Java, and I think that the dynamic nature of Ruby will also simplify the code quite a bit.  So it isn't exactly a port of the Java code to Ruby, I'm just using it as a good starting point.&lt;br /&gt;&lt;br /&gt;I hope to get started over the long weekend, and I'll stick my code up on &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;Github&lt;/span&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-351294898906999393?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2008/11/brute-force.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-5165690707284713828</guid><pubDate>Fri, 14 Nov 2008 00:44:00 +0000</pubDate><atom:updated>2008-11-13T17:36:34.077-08:00</atom:updated><title>Logic Games Project</title><description>Lately I've become interested in knowledge representation as it applies to computer science.  I'm no expert on the subject.  The opposite, really.  But interest is a good place to start, I guess.  So, I wanted to start a project that would give me a good excuse to really explore the topic.&lt;br /&gt;&lt;br /&gt;I've been following &lt;a href="http://olabini.com/blog/"&gt;Ola &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;Bini's&lt;/span&gt; blog&lt;/a&gt;, and he's had a really interesting series of posts where he has been porting the code from Peter &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;Norvig's&lt;/span&gt; book, &lt;span style="font-style: italic;"&gt;Paradigms of Artificial Intelligence Programming&lt;/span&gt;, from Common Lisp to Ruby.  I've been sitting on that book for a couple of years now, and every time I pick it up, my lousy Lisp skills get in the way, and I eventually drop it.  But having &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;Bini's&lt;/span&gt; Ruby code to compare it with has re-inspired me to pick my way through it again.&lt;br /&gt;&lt;br /&gt;One of the things that really stands out with early AI research is how AI is defined.  The &lt;a href="http://en.wikipedia.org/wiki/Turing_test"&gt;Turing Test&lt;/a&gt; is one of the best known intelligence indicators.  It goes like this: A human participates in two conversations, one is with another human, and the other is with a computer program.  If the person can't tell the difference between the two, then the computer program has demonstrated intelligence. &lt;br /&gt;&lt;br /&gt;There's a &lt;a href="http://www.loebner.net/Prizef/loebner-prize.html"&gt;competition&lt;/a&gt; every year where programmers try to pass the Turing Test.  No one has won yet, but people are coming close.  What I really find interesting about it, though, is &lt;span style="font-style: italic;"&gt;how&lt;/span&gt; people are coming close.  If you look at some of the code that is producing human-like conversations, you find that a lot of it doesn't seem "intelligent" at all.  At least not in the way that we intuitively think of "intelligence" - it has no &lt;span style="font-style: italic;"&gt;understanding&lt;/span&gt; of the conversation.  Take a look at &lt;a href="http://en.wikipedia.org/wiki/ELIZA"&gt;Eliza&lt;/a&gt;, for example.  Eliza is a very early attempt (1966) to pass the Turing Test.  Eliza basically looks for patterns in the human statements and maps those patterns to responses.  As a simple example, you might say to Eliza, "I want a new car" and Eliza's response might be "What would it mean to you if you got a new car?".  So "I want X" gets mapped to "What would it mean to you if you got X?".&lt;br /&gt;&lt;br /&gt;This is obviously a very simple example, Eliza is capable of a lot more complexity, but it is still pattern matching, not understanding.  In addition, Eliza is 40 years old - a lot has happened in AI in the last 40 years.  However, thinking about this approach to passing the Turing Test got me thinking about other measures of intelligence, and whether or not it would be possible to solve various intelligence tests &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_3"&gt;programmatic&lt;/span&gt;ally without actually achieving what the average person would intuitively know as "intelligence".  So here's what I landed on:  I want to try to solve certain types of standardized test questions.  Specifically the type of logic games you find on the LSAT and &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;GRE&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Here's an example (pulled from an old LSAT):&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;Seven consecutive time slots for a broadcast, numbered in chronological order 1 through 7, will be filled by six song tapes - G, H, L, O, P, S - and exactly one news tape.  Each tape is to be assigned to a different time slot, and no tape is longer than any other tape.  The broadcast is subject to the following restrictions:&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;L must be played immediately before O&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;The news tape must be played at some time after L&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;There must be exactly two time slots between G and P, regardless of whether G comes before P or whether G comes after P&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-size:100%;"&gt;If G is played second, which one of the following tapes must be played third?&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;The news&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;H&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;L&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;O&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;S&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/blockquote&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;The way I see it, there are two major parts in solving something like this &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_3"&gt;programmatic&lt;/span&gt;ally.  First, you have to parse the English language into some sort of knowledge system that your software can understand, then you need to use that knowledge to infer an answer.  For the moment, I am going to concentrate on the latter.  For two reasons: I need to know what type of knowledge representation I am going to end up with before I can start trying to parse the English, so this seems like the better place to start, and it's the part of the problem I find more interesting (and more attainable...)&lt;br /&gt;&lt;br /&gt;So there you have it.   That's my project.&lt;br /&gt;&lt;br /&gt;For a while I am going to solve a bunch of logic games manually to start picking out patterns.  I'll post the more interesting ones, and what I learned from them.  Once I start getting some code written, I'll post that as well.&lt;br /&gt;&lt;br /&gt;I have a bad habit of getting really interested in a project for a short while, and then dropping the ball.  I'm hoping that committing to it here will keep me honest.  As long as there's a chance (however unlikely) that someone is reading this, I'll be shamed into finishing the project ;-)&lt;br /&gt;&lt;br /&gt;More to come soon!&lt;br /&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-5165690707284713828?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2008/11/logic-games-project.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-4653046199291468715.post-4263361451268048844</guid><pubDate>Mon, 10 Nov 2008 01:09:00 +0000</pubDate><atom:updated>2008-11-09T17:16:21.864-08:00</atom:updated><title>Welcome to Jason's Blog</title><description>Hello!  Welcome to Jason's blog!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4653046199291468715-4263361451268048844?l=jasonrush.com%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://jasonrush.com/blog/2008/11/welcome-to-jasons-blog.html</link><author>noreply@blogger.com (Jason)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item></channel></rss>