LSRC Name Picker (#129)

Everyone who had my name in their example prize list deserves extra
credit. I
won millions in cash and prizes as I examined the solutions this week.
I can’t
wait until the checks start coming in the mail.

I was occasionally beat out by some Yukihiro M. guy though.
Weird.

As usual, there were a lot of great solutions. We had everything from
clever
ASCII art to full GUI applications. There were also a lot of clever
ways to
taunt the attendees before a name is picked.

I’m going to look at Carl P.'s solution because the code is compact
and
pretty approachable. I also laughed out-loud when I ran his code. Do
play
around with the other solutions though or you will be missing a lot of
fun
applications and code.

Carl’s solution is a Camping application. If you aren’t too familiar
with
Camping, as I wasn’t, Carl’s solution makes a nice gentle introduction.
Here’s
how it begins:

#!/usr/bin/env ruby -wKU

require “rubygems”
require “camping”
require “RMagick”

module Enumerable
def rand
entries[Kernel.rand(entries.size)]
end
end

Camping.goes :NamePicker

First we see that Carl loads Camping and RMagick as his weapons of
choice. Then
we have a simple helper method for choosing a random member of some
Enumerable
object.

The final line is how you always begin a Camping application. It tells
the
framework to create a namespace for your application in a module called
NamePicker. The code then goes on to fill in that namespace:

module NamePicker::Controllers
class Index < R ‘/’
def get
render :index
end
end

class Stylesheet < R '/style.css'
  def get
    @headers['Content-Type'] = 'text/css'
    File.read(__FILE__).gsub(/.*__END__/m, '')
  end
end

class StaticImage < R '/images/(.*)'
  def get(static_name)
    @headers['Content-Type'] = "image/jpg"
    @headers['X-Sendfile'] = "#{current_dir}/images/#{static_name}"
  end
end

class PickedNameImage < R '/picked_names/(.*?)\.gif'
  def get(name)
    make_image(name)
    @headers['Content-Type'] = "image/gif"
    @headers['X-Sendfile'] = 

“#{current_dir}/picked_names/#{name}.gif”
end
end

class Page < R '/(\w+)'
  def get(page_name)
    render page_name
  end
end

end

These are the controllers for this application. They manage all of the
traffic
through the pages.

The main point of interest in these is Camping’s R() routing method. As
you can
see, it builds classes these controllers can inherit from. The regular
expression you pass to it are the URLs that controller will service.

The first controller covers index requests. The next two serve up
static file
and image content. PickedNameImage also serves images, but these are
dynamically generated from a selected name using the make_image() helper
method.
Both types of images are handled by Camping just by the code setting
some
headers about the file to send. The final controller handles all other
page
requests.

You can see that the Stylesheet controller makes use of some content
embedded
later in the file. It looks like this:

END

body {
background-color:black;
text-align:center;
font-size:30px;
font-family:impact;
color:red;
letter-spacing:3px;
}
a { color:red }
p { margin:80px }

Now, unlike Rails, Camping views are just simple Ruby methods. Here’s a
look at
those:

module NamePicker::Views
def layout
html do
head do
title “LOLPIXNAMES”
link :href=> R(Stylesheet), :rel=>‘stylesheet’,
:type=>‘text/css’
end
body { self << yield }
end
end

def index
  p { img :src => R(StaticImage, "icanpixname.jpg") }
  p { a "PIX A NAME", :href => '/pick_name' }
end

def pick_name
  all_names = open('names').readlines.map do |e|
    e.gsub(/[^a-zA-Z 0-9]/,'')
  end.reject { |e| e.empty? }

  picked_names = Dir["picked_names/*.gif"].map do |e|
    e.sub(/picked_names\/(.*?)\.gif$/,'\\1')
  end

  unpicked_names = all_names - picked_names
  name = unpicked_names.rand

  p do
    img :src => R(StaticImage, "ipixedname.jpg")
    br
    img :src => "picked_names/#{name}.gif"
  end
  p { a "I CAN PIX YR NAME AGAIN", :href => '/pick_name' }
  p { a "KTHXBYE", :href => '/credits' }
end

def credits
  h1 "CREDITZ"
  ul do
    li "http://flickr.com/photos/mag3737/296800129/"
    li "http://flickr.com/photos/brian-fitzgerald/608882248/"
    li "http://www.ocf.berkeley.edu/~gordeon/fonts.html"
  end
  p "Carl P.: [email protected]"
end

end

The point of interest in these methods is the appearance of Markaby,
which is
used to build HTML pages. Markaby is very similar to Builder, if you
are
familiar with that library, save that it doesn’t need the explicit
receiver.

The only action that does some work here is pick_name(). It pulls in
names from
a name file, cross-checks those against what it has previously
generated, and
selects an unpicked name. Then it renders some HTML to show the
selection.

All we have left at this point are the helper methods:

module NamePicker::Helpers
def make_image(text)
gif = Magick::ImageList.new

  decode_name(text.upcase).each do |frame_text|
    frame = Magick::Image.new(30*frame_text.size, 52) do
      self.background_color = 'black'
    end

    Magick::Draw.new.annotate(frame, 0,0,0,0, frame_text) do
      self.font         = 'Impact.ttf'
      self.pointsize    = 50
      self.fill         = 'white'
      self.stroke       = 'black'
      self.stroke_width = 2
      self.gravity      = Magick::CenterGravity
    end

    gif << frame
  end

  gif.delay = 15
  gif.iterations = 1

  gif.write("picked_names/#{text}.gif")
end

def encode_name(name, indexes=[])
  return [] if name.size == indexes.size + 1

  new_index = ((0...name.size).to_a - indexes).rand
  random_words(name, indexes) + encode_name(name, indexes << 

new_index)
end

def random_words(word, indexes_to_replace,
                 number_of_words=indexes_to_replace.size)
  (0..number_of_words).to_a.map do
    new_word = word.dup
    indexes_to_replace.each { |i| new_word[i] = ("A".."Z").rand }
    new_word
  end
end

def decode_name(name)
  encode_name(name).reverse
end

def current_dir
  File.expand_path(File.dirname(__FILE__))
end

end

All of these methods work together to generate the selected name image
shown
when the application runs. The process builds an animated Gif image
where the
frames show the name in various degrees of being scrambled. As the
animation
plays out, the name unscrambles to reveal a winner.

The make_image() method does the heavy lifting by iterating over
variations of
the name and using RMagick to build frames for them. The other methods
scramble
the name by replacing certain indices with random letters.

That’s about all there is to Carl’s code, save some humor. Fire it up
in your
browser to make sure you get a taste of that. “KTHXBYE!”

My thanks to all who solved the quiz, made me laugh, and, most
importantly, to
those who awarded me prizes.

Tomorrow we have a problem using a c__ss_c g_me…

Ruby Q. wrote:

I’m going to look at Carl P.'s solution because the code is compact and
pretty approachable. I also laughed out-loud when I ran his code. Do play
around with the other solutions though or you will be missing a lot of fun
applications and code.

Carl’s solution is a Camping application. If you aren’t too familiar with
Camping, as I wasn’t, Carl’s solution makes a nice gentle introduction. Here’s
how it begins:

Apart from being such a fun solution and being picked by you James I
would give Carl extra credit because this is one of the best
intros/examples to Camping you can find on the Net.
_why you should pick and edit this summary and post it as an example in
the Camping wiki.
Cheers,
V.-

Sorry, I just didn’t have enough time to get this done until now. See
below
for an explanation of my solution. Quick summary:

On Thu, Jul 05, 2007 at 09:51:11PM +0900, Ruby Q. wrote:

Everyone who had my name in their example prize list deserves extra
credit. I won millions in cash and prizes as I examined the solutions
this week. I can’t wait until the checks start coming in the mail.

I was occasionally beat out by some Yukihiro M. guy though.
Weird.

Heh. I was using both names for my testing as well, and they appear in
my
included sample list.txt file.

As usual, there were a lot of great solutions. We had everything from clever
ASCII art to full GUI applications. There were also a lot of clever ways to
taunt the attendees before a name is picked.
[…]

I went for gratuitous in my solution, largely because I used it as an
excuse to brush up on my long-languishing OpenGL skills. It depends on
the
ruby-opengl gem for 3D rendering and the RMagick gem for dynamically
generated textures. Anyone planning on working with the ruby-opengl
library, especially anyone planning on creating or loading textures from
disk, may find it helpful to look through the framework I set up and the
texture code.

The actual name picking is dead simple, and all it does to maintain
state
is to save the list of names, minus those which were chosen, to the same
file it got as input when quitting. The main.rb file should be run and
passed a filename as its only argument. The file should contain the list
from which names will be chosen, one name per line. Note that the list
file
must use UTF8 encoding; RMagick can handle UTF8-encoded non-ASCII
characters, though you may need to change the font family in
magick_texture.rb (see below) to get some of the more interesting
characters.

The zip file (see above for URL) includes the following source files:

main.rb
run the program as: ruby main.rb list.txt
render_layer.rb
the base framework for dealing with OpenGL
name_file.rb
the part that actually picks names out of the hat
interpolate.rb
handle simple one-shot and repeating animation
rotate_anim.rb
wrap rendering in an animated rotation
scale_anim.rb
wrap rendering in an animated scaling
magick_texture.rb
dynamically generate an OpenGL texture with RMagick
string_texture.rb
use MagicTexture to generate a string as a texture
letter_ball.rb
render a letter (via StringTexture) texture-mapped onto a sphere;
the code for the sphere and its texture coordinates was modified
and ported from code found at
http://ozviz.wasp.uwa.edu.au/~pbourke/texture_colour/spheremap/
reveal_ball_string.rb
uses animations and LetterBall to reveal the chosen name
render_text.rb
render letters with lines (rather than textures or polygons)
lone_star.rb
displays the spinning letters of Lone Star Ruby Conf and uses
RenderText to do so

Also included is a list.txt file, with one accented character to show
that
it works. I’ve only tested this on MacOS X, but I have every reason to
believe that it will work well anywhere as long as ruby-opengl and
RMagick
are properly installed.

–Greg