Newbie program

I discovered Ruby this week. I was amazed at how easy it was to program in it without a book or any real manual.
I've written the following program. It looks too much like the python original to be good idiomatic ruby. I'd appreciate suggestions how to improve it.
Thanks.
Logesh
#returns the ways one can place n queens on a
#chessboard of size n*n so that no queen is attacking another
#Ruby 1.8.3; 15 June 2006
def nqueens
  $n = 8
  $board = [nil] * ($n+1)
  def choose(k)
    if k > $n
      print $board, "\n"
    else
      for i in 1..$n
        $board[k] = i
        choose(k + 1) if k == 1 or stillgood(k)
      end
    end
  end
  def stillgood(k)
    for i in 1...k
      return false if ($board[k] == $board[i]) or
        (k-i == ($board[k] - $board[i]).abs)
    end
    return true
  end
end
nqueens.choose(1)

Well, I've tried to Rubify the code a bit. I did these things;

* Stopped defining methods in methods.
* Used a class.
* Switched the global variables to instance variables.
* Used upto() iterators.
* Got rid of print().
* Made stillgood() method name more Rubish.
* Added the application code test at the end.

The thing I think you should still do:

* Pick more meaningful variable names than @n and k.

Here's the code.

class NQueens
   def initialize
     @n = 8
     @board = [nil] * (@n + 1)
   end

   def choose(k)
     if k > @n
       puts @board.join
     else
       1.upto(@n) do |i|
         @board[k] = i
         choose(k + 1) if k == 1 or still_good?(k)
       end
     end
   end

   def still_good?(k)
     1.upto(k - 1) do |i|
       return false if (@board[k] == @board[i]) or
                       (k-i == (@board[k] - @board[i]).abs)
     end
     true
   end
end

if __FILE__ == $PROGRAM_NAME
   NQueens.new.choose(1)
end

Hope that helps.

James Edward Gray II

···

On Jun 15, 2006, at 1:34 PM, Logesh Pillay wrote:

I'd appreciate suggestions how to improve it.

Just for another data point, here's a version that I wrote ages ago that attempts to be as Ruby-ish as possible. The board is stored in such a way that a single line of Ruby can test if a new queen can be placed in a given square, and solutions are yielded to a block as they're found. (Put a 'break' into the block if you only want the first solution.)

Pete Yandell
http://9cays.com

module Queens
   Queen = Struct.new(:x, :y)

   class Board < Array
     def to_s
       map {|q| "."*q.x + "X" + "."*(size-q.x-1)}.join("\n") + "\n"
     end

     def add(*q)
       Board.new(self + q)
     end
   end

   def self.solve(size = 8, queens = Board.new, &block)
     if queens.size == size
       yield queens
     else
       y = queens.size
       for x in 0...size
         unless queens.any? {|q| x == q.x or (x-q.x).abs == (y-q.y).abs }
           solve(size, queens.add(Queen.new(x,y)), &block)
         end
       end
     end
   end

end

Queens::solve {|q| print q.to_s, "\n" }

> I'd appreciate suggestions how to improve it.

Well, I've tried to Rubify the code a bit.

If I could just make a couple of small extra suggestions,

Here's the code.

class NQueens

     class << self
       def choose(k)
         new.choose(k)
       end
     end

   def initialize
     @n = 8
     @board = [nil] * (@n + 1)
   end

   def choose(k)
     if k > @n
       puts @board.join
     else
       1.upto(@n) do |i|
         @board[k] = i
         choose(k + 1) if k == 1 or still_good?(k)
       end
     end
   end

     # explicit returns are a pet peeve of mine :slight_smile:
     def still_good(k)
       !(1...k).any? do |i|
         @board[k] == @board[i] or k-i == (@board[k] - @board[i]).abs
      end
    end

end

if __FILE__ == $PROGRAM_NAME

    NQueens.choose(1)

···

On Fri, 2006-06-16 at 06:05 +0900, James Edward Gray II wrote:

On Jun 15, 2006, at 1:34 PM, Logesh Pillay wrote:
end

Hope that helps.

James Edward Gray II

--
Ross Bamford - rosco@roscopeco.REMOVE.co.uk