[QUIZ] Chess960 (#106)

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion. Please reply to the original quiz message,
if you can.

···

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Kieran Wild

Chess960, is a chess variant produced by Grandmaster Bobby Fischer by
formalizing the rules of Shuffle Chess. Its goal was to create a chess variant
in which chess creativity and talent would be more important than memorization
and analysis of opening moves. His approach was to create a randomized initial
chess position, which would thus make memorizing chess opening move sequences
far less helpful. The initial position is set up in a special way and there are
960 such positions, thus the name Chess960.

The starting position for Chess960 must meet certain rules. White pawns are
placed on the second rank as in chess. All remaining white pieces are placed
randomly on the first rank, but with the following restrictions:

  * The king is placed somewhere between the two rooks.
  * The bishops are placed on opposite-colored squares.

The black pieces are placed equal-and-opposite to the white pieces. For example,
if the white king is placed on b1, then the black king is placed on b8. Note
that the king never starts on file a or h, because there would be no room for a
rook

Can I suggest a nice little ruby program to generates all 960 possible starting
positions and outputs a random one on request.

Output could be as follows.

  Starting Position 432:
  
  White
  
  a1 b1 c1 d1 e1 f1 g1 h1
  N B B R K R Q N
  
  Black
  
  a8 b8 c8 d8 e8 f8 g8 h8
  N B B R K R Q N

Or some better output.

Ruby Quiz <james@grayproductions.net> writes:

Output could be as follows.

  Starting Position 432:
  
  White
  
  a1 b1 c1 d1 e1 f1 g1 h1
  N B B R K R Q N
  
  Black
  
  a8 b8 c8 d8 e8 f8 g8 h8
  N B B R K R Q N

Or some better output.

Let me suggest that another potential output format is an html page
that when viewed in a browser looks like the opening format. I happen
to think that the chess boards shown on
  PmWiki | Cookbook / ChessMarkup
look particularly nice, and shouldn't be too hard to duplicate.
(they're done as 8x8 tables, with the colors of the squares done by
CSS and the pieces being .png images with transparency) Presumably
some enterprising person could then churn out a Rails page that showed
a given starting position.

There's also this basic ascii art method: (black is the lowercase
letters)

nbbrkrqn
pppppppp
........
........
........
........
PPPPPPPP
NBBRKRQN

(It's traditional to show the place where white starts at the bottom,
and to number the rows upwards - that is, row "8" is at the top of the
diagram)

Then there's a FEN string inside PGN notation:

[Event "Starting Position 432"]
[SetUp "1"]
[FEN "nbbrkrqn/pppppppp/8/8/8/8/PPPPPPPP/NBBRKRQN w KQkq - 0 1" ]

The advantage of that format is that you can feed it right into
X-Board, WinBoard, or any other chess program that accepts PGN
notation, and it'll start play from that setup. (More on what that
FEN string means at
Forsyth–Edwards Notation - Wikipedia )

···

--
s=%q( Daniel Martin -- martin@snowplow.org
       puts "s=%q(#{s})",s.map{|i|i}[1] )
       puts "s=%q(#{s})",s.map{|i|i}[1]

Is that the real starting position 432? Or was that just a made up number?

ImageShack - Best place for all of your image hosting and image sharing needs Gives BBRNNQKR as number 432 as does my program.

Also, is the range of the numbers 0-956 or 1-960? I've seen things saying that both are acceptable with no definitive answer.

Thanks,
Dan

Ruby Quiz wrote:

···

  Starting Position 432:
  
  White
  
  a1 b1 c1 d1 e1 f1 g1 h1
  N B B R K R Q N
  
  Black
  
  a8 b8 c8 d8 e8 f8 g8 h8
  N B B R K R Q N

Attached is my solution.

-Chunyun

Sample output:

Generated 960 starting positions.

Starting position 93:

chess960.rb (2.19 KB)

···

+++++++++++++++++++++++++++++++|
b | n | r | b | k | q | r | n |
+++++++++++++++++++++++++++++++|
p | p | p | p | p | p | p | p |
+++++++++++++++++++++++++++++++|
  > > > > > > > >
+++++++++++++++++++++++++++++++|
  > > > > > > > >
+++++++++++++++++++++++++++++++|
  > > > > > > > >
+++++++++++++++++++++++++++++++|
  > > > > > > > >
+++++++++++++++++++++++++++++++|
P | P | P | P | P | P | P | P |
+++++++++++++++++++++++++++++++|
B | N | R | B | K | Q | R | N |
+++++++++++++++++++++++++++++++|

On 12/15/06, Ruby Quiz <james@grayproductions.net> wrote:

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz
until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps
everyone
on Ruby Talk follow the discussion. Please reply to the original quiz
message,
if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Kieran Wild

Chess960, is a chess variant produced by Grandmaster Bobby Fischer by
formalizing the rules of Shuffle Chess. Its goal was to create a chess
variant
in which chess creativity and talent would be more important than
memorization
and analysis of opening moves. His approach was to create a randomized
initial
chess position, which would thus make memorizing chess opening move
sequences
far less helpful. The initial position is set up in a special way and
there are
960 such positions, thus the name Chess960.

The starting position for Chess960 must meet certain rules. White pawns
are
placed on the second rank as in chess. All remaining white pieces are
placed
randomly on the first rank, but with the following restrictions:

        * The king is placed somewhere between the two rooks.
        * The bishops are placed on opposite-colored squares.

The black pieces are placed equal-and-opposite to the white pieces. For
example,
if the white king is placed on b1, then the black king is placed on b8.
Note
that the king never starts on file a or h, because there would be no room
for a
rook

Can I suggest a nice little ruby program to generates all 960 possible
starting
positions and outputs a random one on request.

Output could be as follows.

        Starting Position 432:

        White

        a1 b1 c1 d1 e1 f1 g1 h1
        N B B R K R Q N

        Black

        a8 b8 c8 d8 e8 f8 g8 h8
        N B B R K R Q N

Or some better output.

Ruby Quiz wrote:

by Kieran Wild

Chess960, is a chess variant produced by Grandmaster Bobby Fischer by
formalizing the rules of Shuffle Chess. Its goal was to create a chess variant
in which chess creativity and talent would be more important than memorization
and analysis of opening moves. His approach was to create a randomized initial
chess position, which would thus make memorizing chess opening move sequences
far less helpful. The initial position is set up in a special way and there are
960 such positions, thus the name Chess960.

The starting position for Chess960 must meet certain rules. White pawns are
placed on the second rank as in chess. All remaining white pieces are placed
randomly on the first rank, but with the following restrictions:

  * The king is placed somewhere between the two rooks.
  * The bishops are placed on opposite-colored squares.

The black pieces are placed equal-and-opposite to the white pieces. For example,
if the white king is placed on b1, then the black king is placed on b8. Note
that the king never starts on file a or h, because there would be no room for a
rook

Can I suggest a nice little ruby program to generates all 960 possible starting
positions and outputs a random one on request.

which = ( ARGV.first || rand(960) + 1 ).to_i
count = 0

(1..6).each{|k|
  (0...k).each{|r1|
    (k+1..7).each{|r2|
      ((0..7).to_a - [k,r1,r2]).each{|q|
        used = [k,r1,r2,q]
        ((0..7).select{|i| i % 2 == 0} - used).each{|b1|
          ((0..7).select{|i| i % 2 == 1} - used).each{|b2|
            count += 1
            if which == count
              puts "Position #{ count }"
              s = 'N' * 8
              [k,q,r1,r2,b1,b2].zip(%w(K Q R R B B)).each{|i,p|
                s[i] = p }
              puts s.downcase,'p'*8,('.'*8+"\n")*4,'P'*8,s
            end } } } } } }

Here is my solution to #106 I thaught I'll make a more readable amb based
soluytion but I did not succeed so far :(, maybe I'll come up with it before
not too long.

···

-----------------------------------------
#!/usr/bin/ruby
class Chess960
    class ::Range
        class << self
            def free= args
                @@free = args
            end
        end
        def each_free
            each do
                >ele>
                next unless @@free.include? ele
                @@free.delete ele
                yield ele
                @@free.unshift ele
            end
        end #def each_free
    end

    N = 8
    def initialize
        @solutions = []
        Range.free = [*1..N]
        init
        generate
    end
    def [] sol_nb
        @solutions[ sol_nb ]
    end

    private
    def init
        @sol="Q " * N
    end
    def generate
        (1..N-1).each_free do
            >@b1|
            (@b1.succ..N).each_free do
                >@b2|
                next if @b1 & 1 == @b2 & 1
                (1..N-2).each_free do
                    >@r1|
                    (@r1.succ..N-1).each_free do
                        >@k|
                        (@k.succ..N).each_free do
                            >@r2|
                            (1..N-1).each_free do
                                >@n1|
                                (@n1.succ..N).each_free do
                                    >@n2|
                                    save_solution
                                end
                            end
                        end #(@k.succ..N).each_free do
                    end
                end #(1..N-2).each_free do
            end
        end
    end
    def save_solution
        @sol[2*(@b1-1)]= ?B
        @sol[2*(@b2-1)]= ?B
        @sol[2*(@r1-1)]= ?R
        @sol[2*(@r2-1)]= ?R
        @sol[2*(@n1-1)]= ?N
        @sol[2*(@n2-1)]= ?N
        @sol[2*(@k-1)] = ?K

        @solutions << @sol
        init
    end

end

c = Chess960.new
puts %<enter a number to show a specific solution (0 based) or
enter r for a random solution or
enter q to go back to work>
until (n = gets.strip) =~ /^q/i
    i = n.to_i
    i = rand(960) if n =~ /^r/i
    puts "Solution #{i}"
    puts c[i]
end
-----------------------------------------

Cheers
Robert
--
"The real romance is out ahead and yet to come. The computer revolution
hasn't started yet. Don't be misled by the enormous flow of money into bad
defacto standards for unsophisticated buyers using poor adaptations of
incomplete ideas."

- Alan Kay

Here goes my second solution, looks a little bit better, using Jim Weirich's
Amb class

···

------------------------------------------------------------------
#!/usr/bin/ruby
# This solution uses a cut down version of Jim Weirich's Amb class
# submitted to Ruby Quiz # 70. Hope that's ok?
#
# The purpose of this solution is to show how #generate becomes more
readable
# and there is a fix of the "place 2 Knights instead of 1 Queen" error.
class Amb
  class ExhaustedError < RuntimeError; end

  def initialize
    @fail = proc { fail ExhaustedError, "amb tree exhausted" }
  end

  def choose(*choices)
    prev_fail = @fail
    callcc { |sk|
      choices.each { |choice|
        callcc { |fk|
            @fail = proc {
                @fail = prev_fail
                    fk.call(:fail)
                }
                sk.call(choice)
            }
        }
        @fail.call
    }
  end

  def assert(cond)
    choose unless cond
  end
end

class Chess960
    N = 8
    Queen = ?Q
    King = ?K
    Rook = ?R
    Bishop = ?B
    def initialize
        @amb = Amb.new
        @solutions = []
        init
        generate
        raise RuntimeError, "Illegal Number of solutions #{@solutions.length}"
unless
            @solutions.length == 960
    end
    def [] sol_nb
        @solutions[ sol_nb ]
    end

    private
    def init
        @sol="N " * N
    end
    def generate
        @b1 = @amb.choose( *1..N-1 )
        @b2 = @amb.choose( *@b1.succ..N )
        @amb.assert @b1 & 1 != @b2 & 1
        @r1 = @amb.choose( *1..N-2 )
        @k = @amb.choose( *@r1.succ..N-1 )
        @r2 = @amb.choose( *@k.succ..N )
        @q = @amb.choose( *1..N )
        # This late check makes the whole thing more readable
        # we can easily afford the additional computations
        @amb.assert [@b1,@b2,@r1,@k,@r2,@q].uniq.length == 6
        save_solution
            rescue Amb::ExhaustedError
    end
    def save_solution
        @sol[2*(@b1-1)]= Bishop
        @sol[2*(@b2-1)]= Bishop
        @sol[2*(@r1-1)]= Rook
        @sol[2*(@r2-1)]= Rook
        @sol[2*(@q-1)]= Queen
        @sol[2*(@k-1)] = King
        @solutions << @sol
        init
        @amb.choose
    end

end

c = Chess960.new
puts %<enter a number to show a specific solution (0 based) or
enter r for a random solution or
enter q to go back to work>
until (n = gets.strip) =~ /^q/i
    i = n.to_i
    i = rand(960) if n =~ /^r/i
    puts "Solution #{i}"
    puts c[i]
end

------------------------------------------------------------------
Cheers
Robert
--
"The real romance is out ahead and yet to come. The computer revolution
hasn't started yet. Don't be misled by the enormous flow of money into bad
defacto standards for unsophisticated buyers using poor adaptations of
incomplete ideas."

- Alan Kay

I just did up a random generator using the die-rolling method
mentioned on Wikipedia. As such, it's not deterministic, so my boards
can't be referenced by number.

After my Chess360 class is a Camping app to host it - my current code
is live at http://tracefunc.com:3301/ - the images were shamelessly stolen from
PmWiki | Cookbook / ChessMarkup, and the whole thing
(code plus images) can be downloaded from
Index - set_trace_func - I'd've attached it but ruby-talk
rejected it as too large a message.

- Jamie

class Chess960
  attr_reader :board_id, :board

  def initialize
    @board = generate_board(bodlaender_line)
  end

  def generate_board(white)
    # Black's starting line is mirror of white's
    black = white.map{|piece| piece.downcase}

    # middle of board is always the same
    middle = [
      %w(p p p p p p p p),
      %w(_ _ _ _ _ _ _ _),
      %w(P P P P P P P P)
    ]

    # add back rows
    [black] + middle + [white]
  end

  def bodlaender_line
    free = (0...8).to_a
    white =

    dark_bishop = rand(4) * 2
    light_bishop = rand(4) * 2 + 1
    white[dark_bishop] = 'B'
    white[light_bishop] = 'B'
    free.delete(dark_bishop)
    free.delete(light_bishop)

    queen = rand(6)
    white[free[queen]] = 'Q'
    free.delete_at(queen)

    knight1 = rand(5)
    white[free[knight1]] = 'N'
    free.delete_at(knight1)
    knight2 = rand(4)
    white[free[knight2]] = 'N'
    free.delete_at(knight2)

    white[free[0]] = 'R'
    white[free[1]] = 'K'
    white[free[2]] = 'R'
    white
  end
end

···

On 12/15/06, Ruby Quiz <james@grayproductions.net> wrote:

by Kieran Wild

Chess960, is a chess variant produced by Grandmaster Bobby Fischer by
formalizing the rules of Shuffle Chess. Its goal was to create a chess variant
in which chess creativity and talent would be more important than memorization
and analysis of opening moves. His approach was to create a randomized initial
chess position, which would thus make memorizing chess opening move sequences
far less helpful. The initial position is set up in a special way and there are
960 such positions, thus the name Chess960.

The starting position for Chess960 must meet certain rules. White pawns are
placed on the second rank as in chess. All remaining white pieces are placed
randomly on the first rank, but with the following restrictions:

        * The king is placed somewhere between the two rooks.
        * The bishops are placed on opposite-colored squares.

The black pieces are placed equal-and-opposite to the white pieces. For example,
if the white king is placed on b1, then the black king is placed on b8. Note
that the king never starts on file a or h, because there would be no room for a
rook

Can I suggest a nice little ruby program to generates all 960 possible starting
positions and outputs a random one on request.

###########

require 'rubygems'
require 'camping'
require 'chess960'

Camping.goes :Chess

module Chess::Controllers
  # main page
  class Index < R '/'
    def get
      @chess = Chess960.new
      render :index
    end
  end

  # image passthrough
  class Images < R '/images/(.+)'
    MIME_TYPES = {'.png' => 'image/png'}
    PATH = __FILE__[/(.*)\//, 1]

    def get(path)
      @headers['Content-Type'] = MIME_TYPES[path[/\.\w+$/, 0]] || "text/plain"
      unless path =~ /\.\./ # sample test to prevent directory traversal attacks
        @headers['X-Sendfile'] = "#{PATH}/images/#{path}"
      else
        "404 - Invalid path"
      end
    end
  end
end

module Chess::Views
  def layout
    html do
      body do
        style :type => 'text/css' do
          "#chess { border-collapse: collapse;
                    float: left; margin-right: 2em; } " +
          ".dark { background-color: #888; } " +
          ".light { background-color: #ddd; } " +
          ".thin { width: 50em; } "
        end
        self << yield
      end
    end
  end

  def index
    c = 0
    table.chess! do
      @chess.board.each do |row|
        c = 1 - c
        tr do
          row.each do |tile|
            c = 1 - c
            td :class => c==0 ? 'light' : 'dark' do
              img :src => "images/#{tile}.png"
            end
          end
        end
      end
    end
    h1 "Chess 960"
    div.thin do
      text "<p>Randomly created board, using the #{a 'Bodlaendar', :href =>
         "Fischer random chess - Wikipedia}
         method for generating piece order.</p>"
      p "Result was #{@chess.board.last.join(", ")}."
    end
  end
end

Here's my solution. I hadn't realized that there was an official
numbering scheme to the Chess960 starting positions. So my program
uses an ad hoc numbering scheme.

The program uses a simple recursive descent technique. Seen positions
(full and parital) are memorizeed so as not to revisit them again. All
possible board positions are generated and placed in an array, and the
number chosen is used as an index into this array.

Eric

···

----
Considering Ruby Training? Visit http://learnruby.com .

----------------------------------------

# Returns true if a layout or partial layout is legal, false if it
# isn't. Makes sure the bishops are on different colors and the king
# is between the rooks.
def good?(layout)
  bishop1 = layout.index(:b)
  bishop2 = layout.rindex(:b)
  return false if bishop1 != bishop2 && bishop1 % 2 == bishop2 % 2

  rook1 = layout.index(:r)
  rook2 = layout.rindex(:r)
  king = layout.index(:k)
  !(rook1 != rook2 && (king.nil? || king < rook1 || king > rook2))
end

# Generates all possible layouts. pieces contains all the remaining
# pieces to be placed. layout is the layout so far. layout_set are
# the completed layouts that have so far been generated. layouts_seen
# are the full and partial layouts that have already been seen, to
# avoid duplicate efforts.
def generate(pieces, layout, layout_set, seen_layouts)
  if pieces.empty? : layout_set << layout.dup # complete layout
  elsif seen_layouts[layout] : return # layout already seen
  else # partial layout; do
next square
    seen_layouts[layout.dup] = true
    pieces.each_index do |i|
      layout.push(pieces.delete_at(i))
      generate(pieces, layout, layout_set, seen_layouts) if
good?(layout)
      pieces.insert(i, layout.pop)
    end
  end
end

# Generates a string that describes a given layout.
def display(layout)
  [["White", 1], ["Black", 8]].map do |color, rank|
    color << "\n" <<
      ('a'..'h').map { |file| file + rank.to_s }.join(" ") << "\n" <<
      layout.map{|sym| sym.to_s.upcase}.join(" ") << "\n"
  end.join("\n")
end

layouts = []

generate([:r, :n, :b, :q, :k, :b, :n, :r], [], layouts, {})

if ARGV.size > 1
  $stderr.puts "Usage: #{$0} [layout-index]"
  exit 1
elsif ARGV.size == 1
  layout_index = ARGV[0].to_i
  if layout_index < 1 || layout_index > layouts.size
    $stderr.puts "Error: layout-index must be from 1 to
#{layouts.size}."
    exit 2
  end
else
  layout_index = rand(layouts.size) + 1
end

puts "Layout ##{layout_index}:\n\n"
puts display(layouts[layout_index - 1])

D:\Docs\ruby>ruby chess960.rb
[Event "Starting Position 787"]
[SetUp "1"]
[FEN "bbrnkrqn/pppppppp/8/8/8/8/PPPPPPPP/BBRNKRQN w KQkq - 0 1" ]

    a b c d e f g h

···

+-----------------+
8 | b b r n k r q n | 8
7 | p p p p p p p p | 7
6 | . . . . . . . . | 6
5 | . . . . . . . . | 5
4 | . . . . . . . . | 4
3 | . . . . . . . . | 3
2 | P P P P P P P P | 2
1 | B B R N K R Q N | 1
  +-----------------+
    a b c d e f g h

D:\Docs\ruby>cat chess960.rb
class Array
  def permute
    if empty?
      []
    elsif size == 1
      [self]
    else
      heads = uniq
      ret = []
      heads.each do |head|
        tails = dup
        tails.delete_at index(head)
        ret.concat tails.permute.map {|tail| [head, *tail] }
      end
      ret
    end
  end
end

module Chess960

  def all
    _all.dup
  end

  def random
    _all[rand(960)].dup
  end

  def ascii_board_showing(n)
    top_row = _all[n].join(' ')
    bottom_row = top_row.upcase
    <<-END
    a b c d e f g h
  +-----------------+
8 | #{ top_row } | 8
7 | p p p p p p p p | 7
6 | | 6
5 | | 5
4 | | 4
3 | | 3
2 | P P P P P P P P | 2
1 | #{ bottom_row } | 1
  +-----------------+
    a b c d e f g h
      END
  end

  def fen_notation_for(n)
    "#{all[n]}/pppppppp/8/8/8/8/PPPPPPPP/#{all[n].join.upcase} w KQkq - 0 1"
  end

  def pgn_notation_for(n)
    <<-END
[Event "Starting Position #{n}"]
[SetUp "1"]
[FEN "#{fen_notation_for(n)}" ]
    END
  end

  private

    def _all
      @all ||= %w[r n b q k b n r].permute.select {|x| valid_position?(x) }
    end

    def valid_position?(array)
      # array.sort == %w [b b k n n q r r] &&
      (array.index("b") + array.rindex("b")) % 2 == 1 &&
      array.grep(/[rk]/) == %w[r k r]
    end

  extend self
end

if $0 == __FILE__

  n = rand(960)
  puts Chess960.pgn_notation_for(n)
  puts
  puts Chess960.ascii_board_showing(n)
end

Here is my solution. Run with no argument, it generates a random initial position. Run with an argument in the range 1..960, it provides the initial position for the specified game. It uses Scharnagl's method which is quite easy to implement in Ruby.

Here is what it produces when given 519 as its argument:

Initial position 519
rnbqkbnr
pppppppp
........
PPPPPPPP
RNBQKBNR

Regards, Morton

<code>
#! /usr/bin/env ruby -w

···

#
# Ruby Quiz 106 -- Chess960 Starting Positions
# Implementation uses Scharnagl's tables. See
# http://en.wikipedia.org/wiki/Chess960_starting_position

class Chess960
    BISHOP_TABLE = [
       "BB------", #0
       "B--B----", #1
       "B----B--", #2
       "B------B", #3
       "-BB-----", #4
       "--BB----", #5
       "--B--B--", #6
       "--B----B", #7
       "-B--B---", #8
       "---BB---", #9
       "----BB--", #10
       "----B--B", #11
       "-B----B-", #12
       "---B--B-", #13
       "-----BB-", #14
       "------BB" #15
    ]

    N5N_TABLE = [
       "NN---", #0
       "N-N--", #1
       "N--N-", #2
       "N---N", #3
       "-NN--", #4
       "-N-N-", #5
       "-N--N", #6
       "--NN-", #7
       "--N-N", #8
       "---NN" #9
    ]

    def initialize(number)
       q, @bishop_index = (number - 1).divmod 16
       @knight_index, @queen_index = q.divmod 6
       @white_pieces = BISHOP_TABLE[@bishop_index].split('')
       @white_pieces[nth_dash(@queen_index)] = 'Q'
       knights = N5N_TABLE[@knight_index]
       m = knights.index('N')
       n = knights.index('N', m + 1)
       m, n = nth_dash(m), nth_dash(n)
       @white_pieces[m] = 'N'
       @white_pieces[n] = 'N'
       @white_pieces[@white_pieces.index('-')] = 'R'
       @white_pieces[@white_pieces.index('-')] = 'K'
       @white_pieces[@white_pieces.index('-')] = 'R'
    end

    def nth_dash(n)
       dashes = []
       @white_pieces.each_with_index { |ch, i| dashes << i if ch == '-' }
       dashes[n]
    end

    def inspect
       @white_pieces.join
    end

    def to_s
       white_pieces = @white_pieces.join + "\n"
       white_pawns = 'P' * 8 + "\n"
       black_pieces = white_pieces.downcase
       black_pawns = 'p' * 8 + "\n"
       empty = ('.' * 8 + "\n") * 4
       black_pieces + black_pawns + empty + white_pawns + white_pieces
    end
end

if __FILE__ == $0
    begin
       if ARGV.empty? then n = 1 + rand(960)
       else
          n = ARGV.first.to_i
          raise StandardError unless (1..960).include?(n)
       end
       puts "Initial position #{n}"
       print Chess960.new(n).to_s
    rescue StandardError
       puts "Usage: #{$PROGRAM_NAME} [<integer>]"
       puts "where <integer> is in 1..960"
       puts "Omitting <integer> produces a random initial position"
    end
end
</code>

I found the Wikipedia article that described the Chess960_Enumbering_Scheme and used its direct derivation of the placement from the number. The relevant portion of the article is mixed in as comments in the code below.

-Rob

Rob Biedenharn http://agileconsultingllc.com
Rob@AgileConsultingLLC.com

#!/usr/bin/env ruby -w

···

#
# Solution by: Rob Biedenharn
#
# From: james@grayproductions.net
# Subject: [QUIZ] Chess960 (#106)
# Date: December 15, 2006 8:50:58 AM EST
# To: ruby-talk@ruby-lang.org
#
# by Kieran Wild
#
# Chess960, is a chess variant produced by Grandmaster Bobby Fischer by
# formalizing the rules of Shuffle Chess. Its goal was to create a chess
# variant in which chess creativity and talent would be more important than
# memorization and analysis of opening moves. His approach was to create a
# randomized initial chess position, which would thus make memorizing chess
# opening move sequences far less helpful. The initial position is set up in a
# special way and there are 960 such positions, thus the name Chess960.
#
# The starting position for Chess960 must meet certain rules. White pawns are
# placed on the second rank as in chess. All remaining white pieces are placed
# randomly on the first rank, but with the following restrictions:
#
# * The king is placed somewhere between the two rooks.
# * The bishops are placed on opposite-colored squares.
#
# The black pieces are placed equal-and-opposite to the white pieces. For
# example, if the white king is placed on b1, then the black king is placed on
# b8. Note that the king never starts on file a or h, because there would be
# no room for a rook
#
# Can I suggest a nice little ruby program to generates all 960 possible
# starting positions and outputs a random one on request.
# ----------------------------------------------------------------------------
# From wikipedia:
# http://en.wikipedia.org/wiki/Chess960_starting_position
#
# http://en.wikipedia.org/wiki/Chess960_Enumbering_Scheme
#
# Direct Derivation
#
# The accurate sequence of White's Chess960 starting array could be derived
# from its number as follows:
#
debug=ENV['DEBUG']
puts "ARGV: #{ARGV.join(', ')}" if debug

starting_position = ARGV.empty? ? rand(960) : ARGV[0].to_i
string = '-' * 8
# a) Divide the number by 960, determine the remainder (0 ... 959) and use
# that number thereafter.
temp = starting_position % 960

puts "starting_position #{starting_position}" if debug
puts "a) #{string}" if debug

# b) Divide the number by 4, determine the remainder (0 ... 3) and
# correspondingly place a Bishop upon the matching bright square (b, d, f, h).
temp,lb = temp.divmod 4
string[2*lb+1]='B'

puts "b) #{lb} #{string} #{temp}" if debug

# c) Divide the number by 4, determine the remainder (0 ... 3) and
# correspondingly place a Bishop upon the matching dark square (a, c, e, g).
temp,db = temp.divmod 4
string[2*db]='B'

puts "c) #{db} #{string} #{temp}" if debug

# d) Divide the number by 6, determine the remainder (0 ... 5) und
# correspondingly place the Queen upon the matching of the six free squares.
n,q = temp.divmod 6
print "d) #{q} " if debug
string.gsub!(/-/) { |p| p='Q' if q.zero?; q -= 1; p }
puts "#{string} #{n}" if debug

# e) Now only one digit (0 ... 9) is left on hand; place the both Knights upon
# the remaining five free squares according to following scheme:
#
# Digit Knights' Positioning
# 0 N N - - -
# 1 N - N - -
# 2 N - - N -
# 3 N - - - N
# 4 - N N - -
# 5 - N - N -
# 6 - N - - N
# 7 - - N N -
# 8 - - N - N
# 9 - - - N N

require 'enumerator'
class Integer
   def comb(r=1)
     if self < r or r < 1
     elsif r == 1
       0.upto(self-1) { |x| yield [x] }
     else
       (0...self).each_cons(1) do |i|
         (self-1).comb(r-1) do |j|
           next if j.last + i.last >= self-1
           bump=i.last+1
           yield(i + j.map! { |e| e+bump })
         end
       end
     end
   end
end

print "e) #{n} " if debug
5.comb(2) do |c|
# puts "comb: #{c.join(' ')}" if debug
   if n.zero?
     c.reverse.each do |q|
       string.gsub!(/-/) { |p| p='N' if q.zero?; q -= 1; p }
     end
     break
   end
   n -= 1
end

puts "#{string}" if debug

# f) The now still remaining three free squares will be filled in the
# following sequence: Rook, King, Rook.
puts "f)" if debug
%w[ R K R ].each do |p|
   string[string.index('-')] = p
   puts " #{string}" if debug
end

fen = "#{string.downcase}/#{'p'*8}/8/8/8/8/#{'P'*8}/#{string} w KQkq - 0 1"

puts %{[Event "Starting Position #{starting_position}"]}
puts %{[SetUp "1"]}
puts %{[FEN "#{fen}"]}

__END__

I decided to make a second submission that uses the official board
position numbering scheme as described on Wikipedia.

Eric

···

----
Interested in Ruby Training? See http://learnruby.com .

----------------------------------------

# Generates a string that describes a given layout.
def display(layout)
  [["White", 1], ["Black", 8]].map do |color, rank|
    color << "\n" <<
      ('a'..'h').map { |file| file + rank.to_s }.join(" ") << "\n" <<
      layout.map{|sym| sym.to_s.upcase}.join(" ") << "\n"
  end.join("\n")
end

# Places the given piece in an empty cell of positions indexed by
# index.
def place_in_empty(positions, index, piece)
  positions[(0..7).to_a.select { |i| positions[i].nil? }[index]] =
piece
end

index = (ARGV[0] || rand(960)).to_i
index %= 960

positions = Array.new(8)

positions[(index % 4) * 2 + 1] = :b
index /= 4

positions[(index % 4) * 2] = :b
index /= 4

place_in_empty(positions, index % 6, :q)
index /= 6

[[0, 1], [0, 2], [0, 3], [0, 4], [1, 2],
[1, 3], [1, 4], [2, 3], [2, 4], [3, 4]][index].reverse.each do |i|
  place_in_empty(positions, i, :n)
end

place_in_empty(positions, 0, :r)
place_in_empty(positions, 0, :k)
place_in_empty(positions, 0, :r)

puts display(positions)

This is my solution. It makes use of my ArrayValue class which was discussed on the list previously. It calculate the placement of pieces for a certain number without first calculating the placement of the pieces for all numbers lower than it by using the following algorithm (found at http://frcec.tripod.com/fischerrandomchessstartingpositions/\):
1. id % 4 * 2 = light square bishop (counting left to right
2. (id / 4) % 4 * 2 + 1 = dark square bishop ^ starting at 0)
3. (id / 4 / 6) % 6 = queen, number of vacant squares from the left
4. (id / 4 / 6) = KeRN code of the other pieces (see the website for more. The KeRN codes do have a pattern but I hard coded it).

First, an explaination of the ArrayValue class. The code is below:
daniel@daniel-desktop:~$ cat arrayvalue.rb
class ArrayValue
         instance_methods.each do |m|
                 undef_method(m) unless m =~ /^_*(method_missing|send|id)_*$/
         end

         def initialize(origArray, origIndex)
                 @origArray, @origIndex = origArray, origIndex
         end

         def set(newObj)
                 @origArray[@origIndex] = newObj
         end

         def get
                 @origArray[@origIndex]
         end

         def method_missing(method, *args)
                         get.send(method, *args)
                 rescue
                         super
         end

         define_method(:'= ') {|other| set(other)}
end

class Array
         def to_av()
                 ret =
                 each_index {|x| ret << ArrayValue.new(self, x) }
                 ret
         end
end

So, an ArrayValue stores the array and index it originally came from and it can edit/get those values. Otherwise, an ArrayValue inherits all of the methods of the value it represents because of the method_missing definition. One thing missing from the ArrayValue class is what to do if CRD (CRUD minus the U) operations are performed on the original array. Because of this, I recommend never actually storing an ArrayValue but creating them again every time you need one.

Also, the array class is edited to provide a to_av method, which returns an array of ArrayValues. This is the only recommended way of creating ArrayValues.

Note that Logan Capaldo wrote something with the same concept but that uses some tricks with arrays and overriding = so that calling ArrayValue#Set is not necessary, you just go ArrayOfArrayValues[0]=Something and it changes the original array.

Some uses of ArrayValue:
daniel@daniel-desktop:~$ irb -r arrayvalue.rb
irb(main):001:0> ary = [1, 2, 3, "a", :b]
=> [1, 2, 3, "a", :b]
irb(main):002:0> ary.to_av[4].set("c")
=> "c"
irb(main):003:0> ary
=> [1, 2, 3, "a", "c"]
irb(main):005:0> ary.to_av.select{|x| x.kind_of?(Numeric)}.each{|x| x.set(42)}
=> [42, 42, 42]
irb(main):006:0> ary
=> [42, 42, 42, "a", "c"]
irb(main):013:0> ary = ["Skip me!", nil, nil, nil, 43]
=> ["Skip me!", nil, nil, nil, 43]
irb(main):014:0> ary.to_av.select{|x| x.nil?}[1].set("The 2nd nil")
=> "The 2nd nil"
irb(main):015:0> ary
=> ["Skip me!", nil, "The 2nd nil", nil, 43]

Now to the real code:
daniel@daniel-desktop:~$ cat chess960short.rb
#! /usr/bin/ruby
require 'arrayvalue.rb'

KeRN = <<-END.split("\n").collect{|x| x.split(" ")}
         N N R K R
         N R N K R
         N R K N R
         N R K R N
         R N N K R
         R N K N R
         R N K R N
         R K N N R
         R K N R N
         R K R N N
         END

id = ARGV[0].to_i % 960
out = Array.new(8)
1.downto(0) {|x| out[id % 4 *2 + x] = "B"; id /= 4 }
out.to_av.select{|x| x.nil?}[id % 6].set("Q"); id /= 6
KeRN[id].each{ |currentPiece| out.to_av.select{|x| x.nil?}.first.set(currentPiece) }

Another, commented and not written for brevity version will be posted shortly.

Ruby Quiz wrote:

arrayvalue.rb (1.58 KB)

chess960short.rb (459 Bytes)

···

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion. Please reply to the original quiz message,
if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Kieran Wild

Chess960, is a chess variant produced by Grandmaster Bobby Fischer by
formalizing the rules of Shuffle Chess. Its goal was to create a chess variant
in which chess creativity and talent would be more important than memorization
and analysis of opening moves. His approach was to create a randomized initial
chess position, which would thus make memorizing chess opening move sequences
far less helpful. The initial position is set up in a special way and there are
960 such positions, thus the name Chess960.

The starting position for Chess960 must meet certain rules. White pawns are
placed on the second rank as in chess. All remaining white pieces are placed
randomly on the first rank, but with the following restrictions:

  * The king is placed somewhere between the two rooks.
  * The bishops are placed on opposite-colored squares.

The black pieces are placed equal-and-opposite to the white pieces. For example,
if the white king is placed on b1, then the black king is placed on b8. Note
that the king never starts on file a or h, because there would be no room for a
rook

Can I suggest a nice little ruby program to generates all 960 possible starting
positions and outputs a random one on request.

Output could be as follows.

  Starting Position 432:
  
  White
  
  a1 b1 c1 d1 e1 f1 g1 h1
  N B B R K R Q N
  
  Black
  
  a8 b8 c8 d8 e8 f8 g8 h8
  N B B R K R Q N

Or some better output.

This is the full version of the quiz that I wrote first. It uses the ArrayValues class which is described in my previous email. I think it is adequately documented as it is, especially because the algorithm and main logic parts are described in the previous email. Run with no options to get the usage. One thing to say, though is that the facets bracket method is basically this:
class String
  def bracket(wrapper)
   wrapper + self + wrapper
  end
end

It is actually more complex, there is a second argument you can pass in.

daniel@daniel-desktop:~$ cat chess960.rb
#! /usr/bin/ruby -w
require 'arrayvalue.rb'
require 'rubygems'
require 'facet/string/bracket'

# Represents a row ("rank") on a chess board
class ChessRow < Array
         def initialize
                 replace(Array.new(8))
         end

         # Sets the specified vacant square to the specified piece, with nthVacant starting at 0.
         def setVacantSquare(nthVacantSquare, piece)
                 to_av.select{|x| x.nil?}[nthVacantSquare].set(piece)
         end

         def to_s
                 collect{|x| x.bracket(" ")}.join("|").bracket("|")
         end
end

class Chess960Row < ChessRow
         KeRN = <<-END.split("\n").collect{|x| x.split(" ")}
         N N R K R
         N R N K R
         N R K N R
         N R K R N
         R N N K R
         R N K N R
         R N K R N
         R K N N R
         R K N R N
         R K R N N
         END

         def setFromNum(id)
                 # Set the bishops, light first then dark.
                 1.downto(0) do |x|
                         self[(id % 4)*2 + x] = "B"
                         id /= 4
                 end

                 # Set the queen
                 setVacantSquare(id % 6, "Q")
                 id /= 6

                 # Set everything else using KeRN codes.
                 KeRN[id].each do |currentPiece|
                         setVacantSquare(0, currentPiece)
                 end
                 self
         end
end

Pawns = ChessRow.new.fill("p").to_s
EmptyRows = [ChessRow.new.fill {|i| i % 2 == 0? " " : "#" }.to_s,
                         ChessRow.new.fill {|i| i % 2 == 1? " " : "#" }.to_s]
Spacer = "+---" * (Pawns.to_s.length / 4) + "+"

def parseInput(input)
         case input
                 when nil
                         puts "Usage:",
                                 "\tchess960 all - Print all the possible Chess960 lineups",
                                 "\tchess960 rnd - Print a random Chess960 lineup",
                                 "\tchess960 ID - Print ID Chess960 lineup"
                 when /all/
                         0.upto(959) {|x| parseInput(x) }
                 when /(ra?nd)/
                         parseInput(rand(960).to_i)
                 else # is a number
                         input = input.to_i % 960 # Change 960 into 0.
                         mainRow = Chess960Row.new.setFromNum(input).to_s
                         [input.to_s + ": ",
                                 mainRow.downcase,
                                 Pawns.downcase,
                                 EmptyRows * 2,
                                 Pawns.upcase,
                                 mainRow.upcase].
                         flatten.each{|x| puts x, Spacer}
         end
end
parseInput(ARGV[0])

Ruby Quiz wrote:

chess960.rb (1.81 KB)

arrayvalue.rb (1.58 KB)

···

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion. Please reply to the original quiz message,
if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Kieran Wild

Chess960, is a chess variant produced by Grandmaster Bobby Fischer by
formalizing the rules of Shuffle Chess. Its goal was to create a chess variant
in which chess creativity and talent would be more important than memorization
and analysis of opening moves. His approach was to create a randomized initial
chess position, which would thus make memorizing chess opening move sequences
far less helpful. The initial position is set up in a special way and there are
960 such positions, thus the name Chess960.

The starting position for Chess960 must meet certain rules. White pawns are
placed on the second rank as in chess. All remaining white pieces are placed
randomly on the first rank, but with the following restrictions:

  * The king is placed somewhere between the two rooks.
  * The bishops are placed on opposite-colored squares.

The black pieces are placed equal-and-opposite to the white pieces. For example,
if the white king is placed on b1, then the black king is placed on b8. Note
that the king never starts on file a or h, because there would be no room for a
rook

Can I suggest a nice little ruby program to generates all 960 possible starting
positions and outputs a random one on request.

Output could be as follows.

  Starting Position 432:
  
  White
  
  a1 b1 c1 d1 e1 f1 g1 h1
  N B B R K R Q N
  
  Black
  
  a8 b8 c8 d8 e8 f8 g8 h8
  N B B R K R Q N

Or some better output.

Or with a touch more window dressing:

···

On Dec 15, 2006, at 1:25 PM, Daniel Martin wrote:

There's also this basic ascii art method: (black is the lowercase
letters)

nbbrkrqn
pppppppp
........
PPPPPPPP
NBBRKRQN

+---+---+---+---+---+---+---+---+

n | b | b | r | k | r | q | n |

+---+---+---+---+---+---+---+---+

p | p | p | p | p | p | p | p |

+---+---+---+---+---+---+---+---+

  > . | | . | | . | | . |

+---+---+---+---+---+---+---+---+

. | | . | | . | | . | |

+---+---+---+---+---+---+---+---+

  > . | | . | | . | | . |

+---+---+---+---+---+---+---+---+

. | | . | | . | | . | |

+---+---+---+---+---+---+---+---+

P | P | P | P | P | P | P | P |

+---+---+---+---+---+---+---+---+

N | B | B | R | K | R | Q | N |

+---+---+---+---+---+---+---+---+

James Edward Gray II

Ask Ruby.

James Edward Gray II

···

On Dec 15, 2006, at 9:25 PM, Daniel Finnie wrote:

Also, is the range of the numbers 0-956 or 1-960? I've seen things saying that both are acceptable with no definitive answer.

I haven't bothered generating the full table, I'm just randomly
generating the layouts. I thought the app was a bit too lightweight
for Rails, but I stole the images from pmwiki.org and set up a camping
app here: http://www.tracefunc.com:3301/ - just refresh for a
different layout.

If I can find more time this weekend, I'll see about doing the table
lookup and adding that info to the page.

- Jamie

···

On 12/15/06, Daniel Martin <martin@snowplow.org> wrote:

Ruby Quiz <james@grayproductions.net> writes:

> Output could be as follows.
>
> Starting Position 432:
>
> White
>
> a1 b1 c1 d1 e1 f1 g1 h1
> N B B R K R Q N
>
> Black
>
> a8 b8 c8 d8 e8 f8 g8 h8
> N B B R K R Q N
>
> Or some better output.

Let me suggest that another potential output format is an html page
that when viewed in a browser looks like the opening format. I happen
to think that the chess boards shown on
  PmWiki | Cookbook / ChessMarkup
look particularly nice, and shouldn't be too hard to duplicate.
(they're done as 8x8 tables, with the colors of the squares done by
CSS and the pieces being .png images with transparency) Presumably
some enterprising person could then churn out a Rails page that showed
a given starting position.

This is not a very elegant or concise or even efficient solution. I tried
finding the weirdest solution I could think of. The idea is to reduce (or in
this case, enlarge :slight_smile: the problem to the exact-cover problem. So I
formalized 6 constraints that need to be satisfied by a solution:

1) all the pieces need to be placed
2) all the columns need to be occupied
3) the left rook must appear to the left of the king and the right rook to
the right
4) the left bishop must appear to the left of the right one
5) the left knight must appear to the left of the right knight
6) each color must be occupied by exactly one bishop

The program constructs rows of a DLX matrix (if you don't know this
algorithm, it's described here: http://en.wikipedia.org/wiki/Dancing_Links).
It then uses a DLX solver that I wrote to find legal combinations of piece
placements. The enumeration order is not the same as the one on the internet
but is deterministic.

The file 'dlx.rb' contains the DLX solver.

Mushfeq.

chess960.rb (3.73 KB)

dlx.rb (3.46 KB)

I decided to try it with Amb too. It's slow but works:

#!/usr/bin/env ruby -w

require "amb"

setup = Amb.new
count = 0
seen = Hash.new
begin
   squares = Array.new(8) { setup.choose(*%w[r n b q k b n r]) }

   %w[r n b].each do |piece|
     setup.assert(squares.select { |s| s == piece }.size == 2)
   end
   %w[k q].each do |piece|
     setup.assert(squares.select { |s| s == piece }.size == 1)
   end
   king = squares.index("k")
   setup.assert(squares.index("r") < king)
   setup.assert(squares.rindex("r") > king)
   setup.assert((squares.index("b") + squares.rindex("b")) % 2 == 1)
   board = squares.join(' ')
   setup.assert(seen[board].nil?)

   puts "#{count += 1}: #{board}"

   seen[board] = true
   setup.failure
rescue
   # do nothing, we're done
end

__END__

James Edward Gray II

···

On Dec 17, 2006, at 1:28 PM, Robert Dober wrote:

Here goes my second solution, looks a little bit better, using Jim Weirich's Amb class