On Dec 10, 2004, at 8:29 AM, Ruby Quiz wrote:

> This week's Ruby Quiz is to implement an AI for playing Tic-Tac-Toe, 
> with a
> catch:  You're not allowed to embed any knowledge of the game into 
> your creation beyond how to make legal moves and recognizing that it 
> has won or lost.

Okay, I think I have a working solution.  My problem is in another 
area.  My thinking about Tic-Tac-Toe itself seems flawed.

First, I decided to generate all the positions and came up with:

#!/usr/bin/env ruby

$seen = [ ]

def generate( board, piece, &pos_handler )
	(0..8).each do |i|
		next unless board[i, 1] == " "
		pos = board.dup
		pos[i] = piece

		next if $seen.include?(pos)
		$seen << pos
		pos_handler.call(pos)
		
		next if pos.split("").values_at(0..2)		== [piece] * 3 or
				pos.split("").values_at(3..5)		== [piece] * 3 or
				pos.split("").values_at(6..8)		== [piece] * 3 or
				pos.split("").values_at(0, 3, 6)	== [piece] * 3 or
				pos.split("").values_at(1, 4, 7)	== [piece] * 3 or
				pos.split("").values_at(2, 5, 8)	== [piece] * 3 or
				pos.split("").values_at(0, 4, 8)	== [piece] * 3 or
				pos.split("").values_at(2, 4, 6)	== [piece] * 3
		
		generate(pos, (["X", "O"] - [piece])[0], &pos_handler)
	end
end

puts "1\n   \n   \n   \n\n"
count = 1
generate(" " * 9, "X") do |pos|
	count += 1
	puts "#{count}\n#{pos[0..2]}\n#{pos[3..5]}\n#{pos[6..8]}\n\n"
end

__END__

Does the 5478 positions I'm getting from that sound right?

Then I tried to brainstorm transforms which yield equivalent positions. 
  Obviously rotating 90, 180, or 270 are some.  I also use "mirror" 
(swap first and third columns), and mirror with 90, 180, and 270 
rotates.

That gets me down to:

#!/usr/bin/env ruby

class Position
	def self.rotate( pos )
		pos.split("").values_at(6, 3, 0, 7, 4, 1, 8, 5, 2).join
	end
	
	def self.mirror( pos )
		pos.split("").values_at(2, 1, 0, 5, 4, 3, 8, 7, 6).join
	end

	def initialize( pos )
		@pos = pos
	end
	
	def ==( pos )
		return true if @pos == pos
		
		transform = pos
		3.times do
			transform = Position.rotate(transform)
			return true if @pos == transform
		end

		transform = Position.mirror(pos)
		return true if @pos == transform
		3.times do
			transform = Position.rotate(transform)
			return true if @pos == transform
		end
		
		false
	end
	
	def to_s; "#{@pos[0..2]}\n#{@pos[3..5]}\n#{@pos[6..8]}\n" end
	def inspect; @pos end
end

$seen = [ ]

def generate( board, piece, &pos_handler )
	(0..8).each do |i|
		next unless board[i, 1] == " "
		pos = board.dup
		pos[i] = piece

		next if $seen.include?(pos)
		$seen << Position.new(pos)
		pos_handler.call($seen[-1])
		
		next if pos.split("").values_at(0..2)		== [piece] * 3 or
				pos.split("").values_at(3..5)		== [piece] * 3 or
				pos.split("").values_at(6..8)		== [piece] * 3 or
				pos.split("").values_at(0, 3, 6)	== [piece] * 3 or
				pos.split("").values_at(1, 4, 7)	== [piece] * 3 or
				pos.split("").values_at(2, 5, 8)	== [piece] * 3 or
				pos.split("").values_at(0, 4, 8)	== [piece] * 3 or
				pos.split("").values_at(2, 4, 6)	== [piece] * 3
		
		generate(pos, (["X", "O"] - [piece])[0], &pos_handler)
	end
end

puts "1\n   \n   \n   \n\n"
count = 1
generate(" " * 9, "X") do |pos|
	count += 1
	puts "#{count}\n#{pos}\n"
end

__END__

That gives me 765 positions, which I know is wrong, but I can't find 
the error in my logic...

James Edward Gray II