# Chess960 (#106)

**URL:** <https://www.ruby-forum.com/t/chess960-106/80117>\
**Category:** Ruby\
**Created:** [December 21, 2006, 2:38pm UTC](https://www.ruby-forum.com/t/chess960-106/80117 "2006-12-21T14:38:25Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [December 21, 2006, 2:38pm UTC](https://www.ruby-forum.com/t/chess960-106/80117/1 "2006-12-21T14:38:25Z")

</div>

There are a surprising number of ways to think about this problem, each  
leading  
to a different solution. Let’s examine several.

First, you can think of this as a constraints problem. We have the  
rules of a  
board setup which are the constraints and any board meeting those rules  
is one  
of the 960 positions. Way back when we did the Constraint Processing  
Ruby Q.,  
I learned that the Amb library is a fun way to handle such problems.  
Here’s my  
solution, using Amb:

```
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

```

What I really love above this approach is that it requires almost no  
thought.  
All I am doing here is translating the rules to code. Ruby and Amb do  
the rest.

The above code works by building an Amb object that is considering piece  
placement in eight squares. After that, I define the rules for  
placement.

Since I gave it all eight pieces as possible choices for each square,  
the first  
two rules have to establish the allowed counts for each piece. This  
prevents  
Amb from building a position of eight rooks or similar wrong setups.

For the next two rules I locate the king and verify that we have a rook  
to the  
left of him as well as to the right. The following rule adds the  
locations of  
the two bishops and verifies that we got an odd number. This ensures  
the  
bishops are on different colors, since an even plus an odd will be odd  
but even  
plus even or odd plus odd both yield even numbers. This group of rules  
covers  
the constraints from the quiz description.

The final rule prevents duplicate positions being found. It’s needed  
because  
positions like the following example seem different to the computer, but  
not to  
a chess player:

```
N1 B1 B2 R1 K R2 Q N2
N2 B2 B1 R2 K R1 Q N1

```

With the rules in place, Amb will have found a viable solution. I print  
that  
out, then manually trigger a failure(), to cause it to find another.  
When it  
fails to find one an Exception will be thrown. I catch that and exit  
quietly  
since we will have seen all 960 positions at that point.

The downside of this approach is that Amb uses Ruby’s continuations to  
under the  
hood to backtrack and find new solutions. Those are a bit on the slow  
side and  
this simple script of mine takes about six and a half minutes to  
complete, on my  
box. One solution to this is just to generate the positions once and  
cache them  
for future runs but there were other solutions that could think a lot  
faster.

For a faster solution, let’s shift how we are thinking about the  
problem.  
Another approach is to think of this challenge as a permutations  
problem. We  
really just need to examine all possible permutations of the eight  
pieces and  
select those that match the quiz rules. Multiple solutions did this,  
including  
the following code from David T.:

```
def permutation(pieces)
 return [pieces] if pieces.length <= 1
 result = []
 pieces.uniq.each do |p|
   _pieces = pieces.dup
   _pieces.delete_at(pieces.index(p))
   permutation(_pieces).each do |perm|
     result << (perm << p)
   end
 end
 result
end

results = permutation("RNBKQBNR".split(//)).select do |position|
 r1 = position.index('R')
 r2 = position.rindex('R')
 b1 = position.index('B')
 b2 = position.rindex('B')
 k = position.index('K')
 r1 < k && k < r2 && ((b1+b2) % 2 != 0)
end

puts "Total positions = #{results.length}"
puts results[rand(results.length)].join(' ')

```

The permutation() method here is a recursive generator of all possible  
permutations. It works by adding each() uniq() piece to all possible  
smaller  
permutations. Those are found by removing one piece from the set each  
time and  
recursing.

The rest of the code calls that method and then select()s the positions  
matching  
the quiz rules. Those rules are almost identical to my implementation  
of them  
that we examined earlier.

This code does the same thing as mine but runs in under a second on my  
box.

Shifting the approach again, several methods have been developed to help  
players  
create positions using these rules as needed, some using dice.  
Bodlaender’s  
dice-rolling method is a pretty easy system to translate into code and  
Jamie  
Macey did just that:

```
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(_ _ _ _ _ _ _ _),
     %w(_ _ _ _ _ _ _ _),
     %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

```

The first two methods are trivial with initialize() kicking off the  
process and  
generate\_board() building a board representation. The interesting stuff  
happens  
in bodlaender\_line().

This algorithm works with a collection of free indices and an Array of  
the final  
piece arrangement. As pieces are placed in the Array, those indices are  
pulled  
from the free list so they won’t be reused.

The first step is to place both bishops. That’s done by choosing a  
random  
number between one and four and placing it on the selected light or dark  
square.  
After that, a random selection places the queen on one of the six  
remaining  
squares. The same technique is used to place the knights on the  
remaining five  
and then four squares. That leaves us three empty squares which must be  
R, K,  
and R, in that order, to satisfy the rules.

Making one more leap of thought with regard to this problem, there has  
been an  
effort to enumerate all 960 positions. This has a lot of value to chess  
players  
since we can record the game using our usual techniques and just add a  
note  
like, “Started from Chess960 position #351.” Even better, once you have  
a  
system for enumerating the positions, you can use that system to build  
the  
entire list or select a position. Morton G. gave us the code for  
that:

```
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
   ]

   # ...

```

The official numbering scheme, invented by Reinhard Scharnagl, works by  
using  
simple charts to position the pieces. Above you can see Morton’s  
translation of  
the two charts he will use, giving piece placements and their indices.  
The  
knight charts are narrower because they are placed after the bishops and  
queen,  
taking three squares out of consideration.

Here’s the main algorithm from Morton’s code (with a minor fix from me):

```
   # ...

   def initialize(number)
      q, @bishop_index = number.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

```
   # ...

```

Most of the clever work is done in initialize(). First, a little math  
is used  
to find the lookup index on the bishop’s chart, an index for the queen,  
and an  
index on the knight’s chart. The row selected from the bishop’s chart  
becomes  
the basis for the final arrangement of pieces and nth\_dash() is used to  
properly  
slot the queen in that template. The knight’s are then pulled from  
their chart  
by index and nth\_dash() is again used to place them. The three squares  
left  
then must be a rook, king, and rook in that order.

The rest of Morton’s code is just for displaying the results:

```
   # ...

   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

```

If you want to read more about the systems for assigning pieces, check  
out:

```
http://en.wikipedia.org/wiki/Chess960_starting_position

```

and:

```
http://en.wikipedia.org/wiki/Chess960_Enumbering_Scheme

```

There were a lot more creative elements in the solutions I didn’t cover.  
Jamie  
Macey even built a complete Camping application to display the  
positions.  
Definitely take the time to look over them. It’s worth it.

My thanks to all for another great quiz. I’m a big chess nut some  
problems like  
this always thrill me.

There will be no Ruby Q. tomorrow. I’ll be busy having a merry  
Christmas and  
I wish the same for others. See you in a week!

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [December 21, 2006, 4:26pm UTC](https://www.ruby-forum.com/t/chess960-106/80117/2 "2006-12-21T16:26:34Z")

</div>

On Dec 21, 2006, at 8:37 AM, Ruby Q. wrote:

> ```
> knights = N5N_TABLE[@knight_index]
> def nth_dash(n)
> dashes = []
> @white_pieces.each_with_index { |ch, i| dashes << i if ch ==  
> 
> ```
> 
> ‘-’ }  
> dashes[n]  
> end

If you’re going to make the change

- q, @bishop\_index = (number - 1).divmod 16

- q, @bishop\_index = number.divmod 16

then, to maintain consistency, you’ve got to make the following  
changes as well:

> if **FILE** == $0  
> begin

- 

```
         if ARGV.empty? then n = 1 + rand(960)

```

- 

```
         if ARGV.empty? then n = rand(960)

```

> ```
> else
> n = ARGV.first.to_i
> 
> ```

- 

```
        raise StandardError unless (1..960).include?(n)

```

- 

```
        raise StandardError unless (0..959).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 "where <integer> is in 0..959"

```

> ```
> puts "Omitting <integer> produces a random initial position"
> end
> 
> ```
> 
> end

Regards, Morton

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [December 21, 2006, 6:12pm UTC](https://www.ruby-forum.com/t/chess960-106/80117/3 "2006-12-21T18:12:57Z")

</div>

We’re currently wondering why none of the published solutions take the  
approach of:

First, place king between b to g, inclusive.

- Place rook on left of king.
- Place rook on right of king.
- Place bishop in empty black sqaure.
- Place bishop in empty white square.
- Fill remaining squares with knights and queen.

Note that the number of permutations generated there are VASTLY lowered  
as compared to the “generate everything, then throw away everything  
invalid” approach.

It also seems to be the common sense approach in my office…

(Red rag, have you met Mr Bull?)

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [December 21, 2006, 7:39pm UTC](https://www.ruby-forum.com/t/chess960-106/80117/4 "2006-12-21T19:39:08Z")

</div>

On Dec 21, 2006, at 11:10 AM, rik wrote:

> Note that the number of permutations generated there are VASTLY  
> lowered  
> as compared to the “generate everything, then throw away everything  
> invalid” approach.
> 
> It also seems to be the common sense approach in my office…

Well, Jamie M.'s dice rolling solution is fairly similar. The  
order is different but it works the same.

Jamie’s implementation requires even fewer decisions though, since  
three whole pieces are placed without any random maneuvering. I  
guess, in that respect, it seems pretty “common sensical” to me.

James Edward G. II

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [December 21, 2006, 7:37pm UTC](https://www.ruby-forum.com/t/chess960-106/80117/5 "2006-12-21T19:37:06Z")

</div>

rik wrote:

> Note that the number of permutations generated there are VASTLY lowered  
> as compared to the “generate everything, then throw away everything  
> invalid” approach.
> 
> It also seems to be the common sense approach in my office…
> 
> (Red rag, have you met Mr Bull?)

Did you look at my first solution?

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [December 21, 2006, 7:41pm UTC](https://www.ruby-forum.com/t/chess960-106/80117/6 "2006-12-21T19:41:09Z")

</div>

That’s what I did in my first solution (+take account on symmetry and  
only  
generate half  
of the solutions), but it does not preserve the so-thought-of “official”  
numbering scheme.  
Yes, that is the obvious solution.

_Heck_ I cannot see if my first solution is posted bc my IP’s internet  
filter filters just my  
posts. Did I write something obscene?

Pedro.

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [December 22, 2006, 1:05pm UTC](https://www.ruby-forum.com/t/chess960-106/80117/7 "2006-12-22T13:05:15Z")

</div>

> Did you look at my first solution?

Apparently not well enough.

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [December 21, 2006, 8:21pm UTC](https://www.ruby-forum.com/t/chess960-106/80117/8 "2006-12-21T20:21:21Z")

</div>

On Dec 21, 2006, at 9:24 AM, Morton G. wrote:

> > ```
> > @white_pieces = BISHOP_TABLE[@bishop_index].split('')
> > end
> > 
> > ```
> 
> - 
> 
> ```
> if ARGV.empty? then n = rand(960)
> 
> ```
> 
> > ```
> > print Chess960.new(n).to_s
> > rescue StandardError
> > puts "Usage: #{$PROGRAM_NAME} [<integer>]"
> > 
> > ```
> 
> - 
> 
> ```
> puts "where <integer> is in 1..960"
> 
> ```
> 
> - 
> 
> ```
> puts "where <integer> is in 0..959"
> 
> ```
> 
> > ```
> > puts "Omitting <integer> produces a random initial position"
> > end
> > 
> > ```
> > 
> > end

You’re absolutely right. Thanks for pointing that out.

James Edward G. II
