HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyMinor

Chess engine in Ruby

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
chessengineruby

Problem

I've decided to write a Chess engine in Ruby. The first step (before any move generation, AI, search trees, etc.) is to get a solid board representation so the computer and I can effectively communicate the game state to one another. The board representation I am using here is of the 64-bitstring variety. (I implemented one using 0x88 board rep'n too, but that isn't here. From what I understand, having a several representations on hand in memory will be most useful to solve various number-crunching issues that will appear along the way.)

Please offer any advice on the beginnings of this Chess engine.

```
#!/usr/bin/env ruby
#################################################
=begin
A game of chess can be played in the terminal!
User(s) must know the rules because the program knows none.
Moves must be entered as "initial_square-terminal_square",
where the squares must be given in algebraic chess notation
and any whitespace is ok in that entered string.
The program checks if initial square contains a piece of your color
and if the termnal square does not.
There are no other restrictions on piece movement.
No saving, loading, undoing, etc.
Game runs in an infinite loop, so quit with Ctrl-C.
=end
#################################################
class Integer
# out: String = standard chessboard coordinates (e.g., "D4")
# in: Fixnum = coordinates on the 8x16 board in memory
def to_square
if (0..63).to_a.include?(self)
then
square = String.new
square 0
end
return cells_array
end
end
#################################################
class String
# out: String = standard chessboard coordinates (e.g., "A3", "D4")
# in: Fixnum = virtual cell location (e.g., 0,19,51,... )
def to_cell
# check if in standard form
return nil if self.length!= 2
rank = self[0].upcase
file = self[1]
cell = file.to_i*8 + (rank.ord - 65) - 8
return cell
end
end
#################################################
class Ga

Solution

I would separate logic from presentation and I would write unit tests from begin on.

I would split display in two methods:

  • build_board is creating a String or an Array



  • display writes the result of build_board



Advantage:
You can write unit tests:

  • Check the result of build_board



  • make a move



  • check again the result of build_board.



I made a modified version of play:

def play

  user_input = nil
  while true do
    # show board
    display
    # request move
    initial_cell, terminal_cell = nil
    until ! initial_cell.nil? & !terminal_cell.nil? do
      print " enter move : "
      # get move in D2-D4 format; break apart into array by "-" and remove any whitespace in each piece
      user_input = gets.strip.upcase.delete(' ')
      #Check user input
      case user_input 
        # if string entered is something like "A4-C5" or " a4  -C5  " etc
        when /[A-H][1-8]-[A-H][1-8]/
          user_move = user_input.split("-").map { |cell| cell.strip }
          # if initial square contains one of your pieces   & terminal square does not
          if ((2**user_move[0].to_cell & moversPieces) > 0) & ((2**user_move[1].to_cell & ~moversPieces) > 0)
              initial_cell, terminal_cell = user_move[0].to_cell, user_move[1].to_cell
          end
        when 'SAVE' #store
          puts "Store not implemented yet"
          next
        when 'END' #stop the game
          puts "Thanks for playing"
          exit
        else
            puts 'Invalid move. Please use ...' ##Add short description of usage ###
            next
      end #case user_input
    end
    case make_move(initial_cell,terminal_cell)
      when true
        @whitesMove = !@whitesMove
      when :empty_field
        puts "Start field was empty - please redo"
      else
        puts "Move not done - please redo"
    end
  end
end


This play offers the possibility to add more commands (I prepared END and SAVE).

Very important: Give a response if there is a problem

make_move should return a result. trueif it is ok, in other cases provide an error code.
So the player can get a response and you have the possibility to test the function in your unittest (make an invalid move and check if the error code is correct).

Code Snippets

def play

  user_input = nil
  while true do
    # show board
    display
    # request move
    initial_cell, terminal_cell = nil
    until ! initial_cell.nil? & !terminal_cell.nil? do
      print " enter move : "
      # get move in D2-D4 format; break apart into array by "-" and remove any whitespace in each piece
      user_input = gets.strip.upcase.delete(' ')
      #Check user input
      case user_input 
        # if string entered is something like "A4-C5" or " a4  -C5  " etc
        when /[A-H][1-8]-[A-H][1-8]/
          user_move = user_input.split("-").map { |cell| cell.strip }
          # if initial square contains one of your pieces   & terminal square does not
          if ((2**user_move[0].to_cell & moversPieces) > 0) & ((2**user_move[1].to_cell & ~moversPieces) > 0)
              initial_cell, terminal_cell = user_move[0].to_cell, user_move[1].to_cell
          end
        when 'SAVE' #store
          puts "Store not implemented yet"
          next
        when 'END' #stop the game
          puts "Thanks for playing"
          exit
        else
            puts 'Invalid move. Please use ...' ##Add short description of usage ###
            next
      end #case user_input
    end
    case make_move(initial_cell,terminal_cell)
      when true
        @whitesMove = !@whitesMove
      when :empty_field
        puts "Start field was empty - please redo"
      else
        puts "Move not done - please redo"
    end
  end
end

Context

StackExchange Code Review Q#10832, answer score: 4

Revisions (0)

No revisions yet.