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.