Archive for the 'Ruby' Category
Retris lives. Gosu rocks. Object thinking prevails.
I pushed the first version of Retris to github about ten minutes ago. It feels pretty good.
- Retris screen cast (large version)
- Github repo: http://github.com/angryrabbit/retris/tree/master
- gosu library
There are quite a few missing features, but it’s playable. There are probably a small handful of bugs. I’m only aware of one or two. Both are documented in the readme.
I want to thank Julian Raschke and the other gosu contributors. I love Ruby and it’s awesome to be able to concentrate on my game rather than dick around with low-level details.
As happy as I am that I’ve “finally made a game”, I have to confess, I don’t care for Tetris. I suck at it, too. I wrote it because I read in an old game programming book that Tetris is a good first game.
So what’s next? Well, I have a few things I want to finish up. As Diana Gruber says, “the last 10% of the game takes as long as the first 90%”. Or something like that.
At the very least I need to instate loss conditions. Currently, overflowing the grid simply causes the game to go on a freak rampage, creating shape after shape in the grid’s origin. It doesn’t crash, but you can’t do anything but exit when you reach that point.
I’d also like to put in a menu, pause feature, difficulty levels and a menu.
When those things are done I’ll start to think about another game.
I’d like to point out that I ditched the idea of using a matrix (nested arrays, actually) to model either the grid or shape objects. I made this move for several reasons:
- Nested arrays require some math
- I can’t ask a nested array meaningful questions
- Previous attempts at using nested arrays fell short for me
- It doesn’t feel very object oriented
There’s no doubt in my mind nested arrays are a viable implementation to Tetris, but personally, I prefer objects.
True, I could have wrapped the nested arrays into an object and queried that, but it’s just not decomposed enough for my tastes. My landscape (objectscape?) includes Grid, GridLocation, Cursor, Shape, Location and Block objects to orchestrate the interactions required of Tetris. (There are a few more objects, but the ones I listed are my solution to NOT using nested arrays.)
Similar to my object-oriented solution to the Tower of Hanoi, (portions of) my code read like a story. Stories are easier to grasp than mathematics. At least for now they are. I’ve decided to go back to school primarily for math.
I imagine though, that even as I become more proficient with math, the “stage” my code lives in, and the messages passed back and forth, will remain largely true to my current style.
In fact, if object-oriented programming is really all it’s cracked up to be, my style will only get tighter and more nuanced; only the implementations will change.
Five years from now I just might use an (actual!) matrix object to handle the implementation for Tetris interactions. But, my guess (indeed, my hope) is that the act of sending messages to create a story will remain in force.
Here’s to the human mind.
Since when does initialize not take arguments?
Since WHEN?!
def initialze(attributes) @attributes = attributes load_xml_request end
Since you SPELLED IT WRONG! :)
Infinitely iterate through an array in Ruby.
class Array def roll loop { each { |e| yield(e) } } end end
>> a = [ 'a', 'b', 'c' ]
=> ["a", "b", "c"]
>> a.roll { |e| puts e; sleep 1 }
a
b
c
a
b
c
a
...
Comparing custom objects with uniq.
You author a Client model. You have an array of clients and you want to call Ruby’s uniq method on them. You define “unique” clients as having the same name. This doesn’t work:
>> c1 = Client.find(1) => #<Client id: 1, artist_id: 1, name: "Daniel Waite", alias: "Rabbit", birthdate: nil, created_at: "2008-01-13 13:20:14", updated_at: "2008-01-23 01:03:26"> >> c2 = Client.find(4) => #<Client id: 4, artist_id: 1, name: "Daniel Waite", alias: nil, birthdate: nil, created_at: "2008-01-14 23:44:58", updated_at: "2008-01-14 23:44:58"> [ c1, c2 ].uniq.size # 2
Being the clever Ruby programmer you are, you quickly modify client…
class Client < ActiveRecord::Base def ==(other) name == other.name end end
Satisfied, you run the above code again.
[ c1, c2 ].uniq.size # 2
What the fuck? Frustrated, you go medieval…
class Client < ActiveRecord::Base def ==(other) name == other.name end def ===(other) name == other.name end def eql?(other) name == other.name end def equal?(other) name == other.name end end
Huffing and puffing, you carefully run your code again…
[ c1, c2 ].uniq.size # 2
ANGER SHARKS ARE SWIMMING!
But fear not my friend, for you have sought, and so the answer shall be revealed to you…
class Client < ActiveRecord::Base def hash name.hash end def eql?(other) name == other.name end end
Wearily, you try again…
[ c1, c2 ].uniq.size # 1
Success!
Yes, both methods must be overridden.
You may have never seen it, but the hash method is available to all objects. I’m not quite sure how or why this works, but it does. Rejoice.
**UPDATE**
Russ pointed out that for new or unsaved objects, yes, all you have to override is the eql? method. However, to compare for equality for existing objects (i.e. objects retrieved from the database) you must override both methods.
This makes things more interesting, too. If you wanted to define equality between clients as having both the same name and the same birthdate, you can write…
class Client < ActiveRecord::Base def hash %{#{ name }#{ birthdate }}.hash end def eql?(other) hash == other.hash end end
And now you have a more complex definition of what equality means. Gotta love Ruby.
The Ruby forum is hot, hot today!
An avid Rubyist proposes a very interesting #as method. It definitely looks rubyesque, but others disagree, namely James Gray and a bright fellow named Ara.
The original posting that generated a lot of good debate on the merits of such an approach.
I don’t mean to be sensational about it, but the code presented by Ara was simply gorgeous. It reeked of solid programming and, more importantly, a fundamental understanding that if you’re resorting to “neat tricks,” you your code needs to be smothered into submission.
That said, I still like the idea of #as. It’s different, and there are some drawbacks (mentioned on the forum), but I think it’s worth feeling out. It’s easy as hell to implement:
class Object def as yield(self) end end
So it’s worth trying.
Here are the two examples in question.
Ara’s suggestion (”traditional” method, in my mind):
guesses = stems.map{|stem| "#{ stem }.#{ guess_extension stem }"} basenames = transform2 source_files + transform1(guesses) expanded = basenames.map{|basename| File.join dir basename}
FC’s suggestion with #as:
expanded = stems.map { |stem| "#{ stem }.#{ guess_extension stem }" }.as { |guesses| transform2(source_files + transform1(guesses)) }.as { |basenames| basenames.map { |basename| File.join dir, basename } }
Now, I will not use curly braces for multi-line blocks. Forget about it. Here’s what it looks like with do…end.
expanded = stems.map do |stem| "#{ stem }.#{ guess_extension stem }" end.as do |guesses| transform2(source_files + transform1(guesses)) end.as do |basenames| basenames.map do |basename| File.join dir, basename end end
Not so pretty, is it? It’s definitely less sentence-like than with curly braces.
In the end I suppose it comes down to style and being flexible. If I were so dead-set against { and } for multi-line blocks I could definitely see using it. On the other hand I question my conviction in that area… is it worth missing out on a potentially useful feature just to follow dogma?
Programming. It’s definitely an art.
In case you ever forget the order of Ruby’s alias method.
Think of it like this…
alias :new_method, :original_method
Create a new method called ‘new_method’, based on the contents of the method called ‘original_method’.
Example…
class Rabbit def eat puts "You eat a carrot." end end irb(main):008:0> Rabbit.new.eat You eat a carrot.
class Rabbit def eat puts "You eat a carrot." end alias :eat_original :eat end irb(main):010:0> Rabbit.new.eat You eat a carrot. => nil irb(main):011:0> Rabbit.new.eat_original You eat a carrot. => nil
You can now call both methods and they do the same thing, because they are copies.
class Rabbit def eat puts "You eat a carrot." end alias :eat_original :eat def eat puts "You eat celery." eat_original end end irb(main):015:0> Rabbit.new.eat You eat celery. You eat a carrot.
Now you have two methods, each one is different, but one calls the other, so we have just added functionality to the original method name without losing the original functionality.
The above technique is used a lot in Rails. (Although they have a helper called alias_chain_method, which I won’t go into here, but if you’re interested, Err has a good writeup.)
One more time, for posterity:
alias :new_method, :original_method
Create a new method called ‘new_method’, based on the contents of the method called ‘original_method’.
Now if anyone can tell me how to use alias with methods that end in a question mark I’d be happy!
Pinging Technorati (or anything) with Ruby.
Searching Google has probably led you to _why’s code. While it works, I’m not satisfied with it. Why? Frankly, I don’t understand it. Not that the code itself is horribly difficult, but I’ve read Technorati’s page on how you generate your own ping, and I see very little of what they describe in _why’s code.
Technorati describes a ping as an HTTP header and an XML document. Well… okay… How do I make that? How do I tell Ruby to send what is essentially an arbitrary HTTP request out over the wire?
Enter Net::HTTP.
There’s some documentation for Net::HTTP, and even some good examples, but by and large, it’s a confusing set of libraries. It’s been a few days since I wrote this code, and I doubt I could retrace my mental steps, so I’ll just lay it on you. As with most code I’m sure it can be made better, but here she is, a fairly manual, custom ping to Technorati:
require 'rubygems' require 'hpricot' require 'uri' require 'net/http' xmlrpc_request = <<EOF <?xml version="1.0"?> <methodCall> <methodName>weblogUpdates.ping</methodName> <params> <param> <value>Rabbit Creative</value> </param> <param> <value>http://rabbitcreative.com/</value> </param> </params> </methodCall> EOF url = URI.parse('http://rpc.technorati.com/rpc/ping') request = Net::HTTP::Post.new(url.path) request.body = xmlrpc_request request['User-Agent'] = 'Ruby' request['Host'] = 'rpc.technorati.com' request['Content-Type'] = 'text/xml' request['Content-Length'] = 250 resource = Net::HTTP.new(url.host).start do |http| http.request(request) end document = Hpricot.XML(resource.body) puts document.search(:string).inner_html
The Hpricot bit isn’t necessary, but I figured I’d throw it in there in case you wanted to make your own interface and wanted to grab the result of the ping.
Oh yeah, the W3C actually has some decent documentation on request headers, and I will mention that the User Agent attribute could be (and probably should be) a little more accurate. From the W3C:
This line if present gives the software program used by the original client. This is for statistical purposes and the tracing of protocol violations. It should be included. The first white space delimited word must be the software product name, with an optional slash and version designator. Other products which form part of the user agent may be put as separate words.
Their example: User-Agent: LII-Cello/1.0 libwww/2.5
So perhaps my User Agent could be… User Agent: Ruby/1.8 Net::HTTP or something like that.
All in all everything I’ve shown you is pretty damn procedural, but it’s meant to shed light on the questions left after reading Technorati’s ping docs and _why’s code. Think of it as a shallow peek into the internals of how Ruby’s XMLRPC library could possibly work.
As always, feel free to desecrate anything here and rebuild; that’s how you learn!
Escaping URI strings the Ruby way.
You could say:
URI.encode('hello world') # hello%20world
But that’s ugly, so why not:
'hello world'.encode # hello%20world
Much nicer and more rubyesque!
Here’s one way to do it:
require 'uri' class String def encode URI.encode(self) end def decode URI.decode(self) end end
Why do you use decode and encode instead of escape and unescape?
Because encode and decode make more sense to me. :)
More fun with extend.
To complement a previous post about using extend, here’s a fun tidbit:
module Consume def eat "You eat a carrot." end end String.extend(Consume) String.eat # You eat a carrot. s = String.new s.extend(Consume) s.eat # You eat a carrot.
Moral of the story? The extend method can be used at the class- or instance-level. Neato!