sapphire-chess 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +45 -0
  4. data/LICENSE +21 -0
  5. data/README.md +24 -0
  6. data/Rakefile +1 -0
  7. data/bin/sapphire-chess +5 -0
  8. data/lib/sapphire-chess/ai.rb +101 -0
  9. data/lib/sapphire-chess/algebraic_conversion.rb +89 -0
  10. data/lib/sapphire-chess/board/board_analysis.rb +66 -0
  11. data/lib/sapphire-chess/board/board_evaluation.rb +25 -0
  12. data/lib/sapphire-chess/board/board_general.rb +155 -0
  13. data/lib/sapphire-chess/board/board_provisional_moves.rb +32 -0
  14. data/lib/sapphire-chess/board/board_renderer.rb +122 -0
  15. data/lib/sapphire-chess/board.rb +5 -0
  16. data/lib/sapphire-chess/display.rb +140 -0
  17. data/lib/sapphire-chess/engine.rb +153 -0
  18. data/lib/sapphire-chess/human_input_validation.rb +123 -0
  19. data/lib/sapphire-chess/movement_rules/castling_board_control.rb +37 -0
  20. data/lib/sapphire-chess/movement_rules/castling_piece_control.rb +9 -0
  21. data/lib/sapphire-chess/movement_rules/castling_rights.rb +79 -0
  22. data/lib/sapphire-chess/movement_rules/en_passant_board_control.rb +20 -0
  23. data/lib/sapphire-chess/movement_rules/en_passant_piece_control.rb +36 -0
  24. data/lib/sapphire-chess/movement_rules/move_slide_pattern.rb +23 -0
  25. data/lib/sapphire-chess/movement_rules/move_step_pattern.rb +17 -0
  26. data/lib/sapphire-chess/movement_rules/pawn_movement_and_promotion.rb +71 -0
  27. data/lib/sapphire-chess/movement_rules.rb +7 -0
  28. data/lib/sapphire-chess/pieces/bishop.rb +56 -0
  29. data/lib/sapphire-chess/pieces/empty_square.rb +13 -0
  30. data/lib/sapphire-chess/pieces/king.rb +77 -0
  31. data/lib/sapphire-chess/pieces/knight.rb +57 -0
  32. data/lib/sapphire-chess/pieces/pawn.rb +82 -0
  33. data/lib/sapphire-chess/pieces/piece.rb +77 -0
  34. data/lib/sapphire-chess/pieces/queen.rb +44 -0
  35. data/lib/sapphire-chess/pieces/rook.rb +62 -0
  36. data/lib/sapphire-chess/pieces.rb +8 -0
  37. data/lib/sapphire-chess/player.rb +43 -0
  38. data/lib/sapphire-chess/version.rb +3 -0
  39. data/lib/sapphire-chess.rb +9 -0
  40. data/sapphire-chess.gemspec +29 -0
  41. metadata +142 -0
@@ -0,0 +1,36 @@
1
+ module EnPassantPieceControl
2
+ def add_en_passant_movement!(moves)
3
+ adyacent_enemy_pawn = pawn_to_pass.first
4
+ return if adyacent_enemy_pawn.nil?
5
+
6
+ moves << en_passant_target_square(adyacent_enemy_pawn)
7
+ end
8
+
9
+ def pawn_to_pass(current_square = location)
10
+ # See Piece#safe_moves, Board#is_a_duplicate?
11
+ return [] if board.a_duplicate?
12
+
13
+ left_square = [current_square.first, current_square.last - 1]
14
+ right_square = [current_square.first, current_square.last + 1]
15
+
16
+ [left_square, right_square].select do |square|
17
+ board[square].is_a?(Pawn) && pawn_just_moved_two?(square)
18
+ end
19
+ end
20
+
21
+ def pawn_just_moved_two?(square)
22
+ if color == :white
23
+ board.black_player.history.last ==
24
+ [[square.first - 2, square.last], square]
25
+ else
26
+ board.white_player.history.last ==
27
+ [[square.first + 2, square.last], square]
28
+ end
29
+ end
30
+
31
+ def en_passant_target_square(adyacent_enemy_pawn)
32
+ direction = color == :white ? -1 : 1
33
+
34
+ [adyacent_enemy_pawn.first + direction, adyacent_enemy_pawn.last]
35
+ end
36
+ end
@@ -0,0 +1,23 @@
1
+ module SlidePattern
2
+ def available_moves
3
+ move_directions.each_with_object([]) do |(row_direction, column_direction), moves|
4
+ current_row, current_column = location
5
+
6
+ loop do
7
+ current_row += row_direction
8
+ current_column += column_direction
9
+ possible_location = [current_row, current_column]
10
+
11
+ break unless board.within_limits?(possible_location)
12
+ break if friend_in?(possible_location)
13
+
14
+ moves << possible_location if board.empty_square?(possible_location)
15
+
16
+ if enemy_in?(possible_location)
17
+ moves << possible_location
18
+ break
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ module StepPattern
2
+ def available_moves
3
+ move_directions.each_with_object([]) do |(row_direction, column_direction), moves|
4
+ current_row, current_column = location
5
+
6
+ current_row += row_direction
7
+ current_column += column_direction
8
+ possible_location = [current_row, current_column]
9
+
10
+ next unless board.within_limits?(possible_location)
11
+
12
+ if board.empty_square?(possible_location) || enemy_in?(possible_location)
13
+ moves << possible_location
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,71 @@
1
+ module PawnMovementAndPromotion
2
+ def available_moves
3
+ if promoted? then SlidePattern::available_moves
4
+ else
5
+ moves = []
6
+ add_one_square_movement!(moves)
7
+ add_two_square_movement!(moves)
8
+ add_diagonal_movement!(moves)
9
+ add_en_passant_movement!(moves)
10
+
11
+ moves.select { |move| board.within_limits?(move) }
12
+ end
13
+ end
14
+
15
+ def promoted?
16
+ @promoted
17
+ end
18
+
19
+ private
20
+
21
+ attr_writer :promoted
22
+
23
+ def promote
24
+ self.promoted = true if location.first == opposite_row
25
+ end
26
+
27
+ def opposite_row
28
+ case color
29
+ when :white then self.class::W_OPPOSITE_ROW
30
+ else self.class::B_OPPOSITE_ROW
31
+ end
32
+ end
33
+
34
+ def at_start?
35
+ start_row = (color == :white ? Board::W_PAWN_ROW : Board::B_PAWN_ROW)
36
+
37
+ current_row == start_row
38
+ end
39
+
40
+ def forward_direction
41
+ color == :white ? -1 : 1
42
+ end
43
+
44
+ def add_one_square_movement!(moves)
45
+ current_row, current_column = location
46
+
47
+ one_forward = [current_row + forward_direction, current_column]
48
+ moves << one_forward if board.empty_square?(one_forward)
49
+ end
50
+
51
+ def add_two_square_movement!(moves)
52
+ current_row, current_column = location
53
+
54
+ one_forward = [current_row + forward_direction, current_column]
55
+ two_forward = [current_row + (forward_direction * 2), current_column]
56
+ if board.empty_square?(two_forward) &&
57
+ board.empty_square?(one_forward) &&
58
+ at_start?
59
+ moves << two_forward
60
+ end
61
+ end
62
+
63
+ def add_diagonal_movement!(moves)
64
+ current_row, current_column = location
65
+
66
+ diagonal_left = [current_row + forward_direction, current_column + 1]
67
+ diagonal_right = [current_row + forward_direction, current_column - 1]
68
+ moves << diagonal_left if enemy_in?(diagonal_left)
69
+ moves << diagonal_right if enemy_in?(diagonal_right)
70
+ end
71
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'movement_rules/castling_board_control'
2
+ require_relative 'movement_rules/castling_piece_control'
3
+ require_relative 'movement_rules/castling_rights'
4
+ require_relative 'movement_rules/en_passant_board_control'
5
+ require_relative 'movement_rules/en_passant_piece_control'
6
+ require_relative 'movement_rules/move_slide_pattern'
7
+ require_relative 'movement_rules/move_step_pattern'
@@ -0,0 +1,56 @@
1
+ class Bishop < Piece
2
+ MOVE_DIRECTIONS = [
3
+ [1, 1], [1, -1], [-1, 1], [-1, -1]
4
+ ].freeze
5
+
6
+ BLACK = '♝'
7
+ WHITE = '♗'
8
+
9
+ VALUE = 330
10
+
11
+ WHITE_LOCATION_VALUE = [
12
+ [-20, -10, -10, -10, -10, -10, -10, -20],
13
+ [-10, 0, 0, 0, 0, 0, 0, -10],
14
+ [-10, 0, 5, 10, 10, 5, 0, -10],
15
+ [-10, 5, 5, 10, 10, 5, 5, -10],
16
+ [-10, 0, 10, 10, 10, 10, 0, -10],
17
+ [-10, 10, 10, 10, 10, 10, 10, -10],
18
+ [-10, 5, 0, 0, 0, 0, 5, -10],
19
+ [-20, -10, -10, -10, -10, -10, -10, -20]
20
+ ].freeze
21
+
22
+ BLACK_LOCATION_VALUE = [
23
+ [-20, -10, -10, -10, -10, -10, -10, -20],
24
+ [-10, 5, 0, 0, 0, 0, 5, -10],
25
+ [-10, 10, 10, 10, 10, 10, 10, -10],
26
+ [-10, 0, 10, 10, 10, 10, 0, -10],
27
+ [-10, 5, 5, 10, 10, 5, 5, -10],
28
+ [-10, 0, 5, 10, 10, 5, 0, -10],
29
+ [-10, 0, 0, 0, 0, 0, 0, -10],
30
+ [-20, -10, -10, -10, -10, -10, -10, -20]
31
+ ].freeze
32
+
33
+ WHITE_LOCATION_VALUE_EASY = [
34
+ [-20, -10, -10, -10, -10, -10, -10, -20],
35
+ [-10, 0, 0, 0, 0, 0, 0, -10],
36
+ [-10, 0, 5, 15, 15, 5, 0, -10],
37
+ [-10, 5, 5, 20, 20, 5, 5, -10],
38
+ [-10, 0, 15, 20, 20, 15, 0, -10],
39
+ [-10, 10, 15, 20, 20, 15, 10, -10],
40
+ [-10, 5, 0, 0, 0, 0, 5, -10],
41
+ [-20, -10, -10, -10, -10, -10, -10, -20]
42
+ ].freeze
43
+
44
+ BLACK_LOCATION_VALUE_EASY = [
45
+ [-20, -10, -10, -10, -10, -10, -10, -20],
46
+ [-10, 5, 0, 0, 0, 0, 5, -10],
47
+ [-10, 10, 15, 20, 20, 15, 10, -10],
48
+ [-10, 0, 15, 20, 20, 15, 0, -10],
49
+ [-10, 5, 5, 20, 20, 5, 5, -10],
50
+ [-10, 0, 5, 15, 15, 5, 0, -10],
51
+ [-10, 0, 0, 0, 0, 0, 0, -10],
52
+ [-20, -10, -10, -10, -10, -10, -10, -20]
53
+ ].freeze
54
+
55
+ include SlidePattern
56
+ end
@@ -0,0 +1,13 @@
1
+ require 'singleton'
2
+
3
+ class EmptySquare
4
+ include Singleton
5
+
6
+ def to_s
7
+ ' '
8
+ end
9
+
10
+ def white
11
+ '██'
12
+ end
13
+ end
@@ -0,0 +1,77 @@
1
+ class King < Piece
2
+ MOVE_DIRECTIONS = [
3
+ [0, 1], [1, 1], [1, 0], [0, -1],
4
+ [1, -1], [-1, 1], [-1, -1], [-1, 0]
5
+ ].freeze
6
+
7
+ BLACK = '♚'
8
+ WHITE = '♔'
9
+
10
+ VALUE = 20_000
11
+
12
+ WHITE_LOCATION_VALUE = [
13
+ [-30, -40, -40, -50, -50, -40, -40, -30],
14
+ [-30, -40, -40, -50, -50, -40, -40, -30],
15
+ [-30, -40, -40, -50, -50, -40, -40, -30],
16
+ [-30, -40, -40, -50, -50, -40, -40, -30],
17
+ [-20, -30, -30, -40, -40, -30, -30, -20],
18
+ [-10, -20, -20, -20, -20, -20, -20, -10],
19
+ [20, 20, 0, 0, 0, 0, 20, 20],
20
+ [20, 30, 10, 0, 0, 10, 30, 20]
21
+ ].freeze
22
+
23
+ BLACK_LOCATION_VALUE = [
24
+ [20, 30, 10, 0, 0, 10, 30, 20],
25
+ [20, 20, 0, 0, 0, 0, 20, 20],
26
+ [-10, -20, -20, -20, -20, -20, -20, -10],
27
+ [-20, -30, -30, -40, -40, -30, -30, -20],
28
+ [-30, -40, -40, -50, -50, -40, -40, -30],
29
+ [-30, -40, -40, -50, -50, -40, -40, -30],
30
+ [-30, -40, -40, -50, -50, -40, -40, -30],
31
+ [-30, -40, -40, -50, -50, -40, -40, -30]
32
+ ].freeze
33
+
34
+ WHITE_LOCATION_VALUE_END = [
35
+ [-50, -40, -30, -20, -20, -30, -40, -50],
36
+ [-30, -20, -10, 0, 0, -10, -20, -30],
37
+ [-30, -10, 20, 30, 30, 20, -10, -30],
38
+ [-30, -10, 30, 40, 40, 30, -10, -30],
39
+ [-30, -10, 30, 40, 40, 30, -10, -30],
40
+ [-30, -10, 20, 30, 30, 20, -10, -30],
41
+ [-30, -30, 0, 0, 0, 0, -30, -30],
42
+ [-50, -30, -30, -30, -30, -30, -30, -50]
43
+ ].freeze
44
+
45
+ BLACK_LOCATION_VALUE_END = [
46
+ [-50, -30, -30, -30, -30, -30, -30, -50],
47
+ [-30, -30, 0, 0, 0, 0, -30, -30],
48
+ [-30, -10, 20, 30, 30, 20, -10, -30],
49
+ [-30, -10, 30, 40, 40, 30, -10, -30],
50
+ [-30, -10, 30, 40, 40, 30, -10, -30],
51
+ [-30, -10, 20, 30, 30, 20, -10, -30],
52
+ [-30, -20, -10, 0, 0, -10, -20, -30],
53
+ [-50, -40, -30, -20, -20, -30, -40, -50]
54
+ ].freeze
55
+
56
+ include StepPattern
57
+ include CastlingPieceControl
58
+
59
+ def initialize(board, location, color)
60
+ super
61
+ @moved = false
62
+ end
63
+
64
+ def location_value
65
+ row, column = location
66
+
67
+ location_value_table =
68
+ if board.end_game? && color == :white then self.class::WHITE_LOCATION_VALUE_END
69
+ elsif board.end_game? && color == :black then self.class::BLACK_LOCATION_VALUE_END
70
+ elsif !board.end_game? && color == :white then self.class::WHITE_LOCATION_VALUE
71
+ else
72
+ self.class::BLACK_LOCATION_VALUE
73
+ end
74
+
75
+ location_value_table[row][column]
76
+ end
77
+ end
@@ -0,0 +1,57 @@
1
+ class Knight < Piece
2
+ MOVE_DIRECTIONS = [
3
+ [1, 2], [2, 1], [-1, 2], [-2, 1],
4
+ [1, -2], [2, -1], [-1, -2], [-2, -1]
5
+ ].freeze
6
+
7
+ BLACK = '♞'
8
+ WHITE = '♘'
9
+
10
+ VALUE = 320
11
+
12
+ WHITE_LOCATION_VALUE = [
13
+ [-50, -40, -30, -30, -30, -30, -40, -50],
14
+ [-40, -20, 0, 0, 0, 0, -20, -40],
15
+ [-30, 0, 10, 15, 15, 10, 0, -30],
16
+ [-30, 5, 15, 20, 20, 15, 5, -30],
17
+ [-30, 0, 15, 20, 20, 15, 0, -30],
18
+ [-30, 5, 10, 15, 15, 10, 5, -30],
19
+ [-40, -20, 0, 5, 5, 0, -20, -40],
20
+ [-50, -40, -30, -30, -30, -30, -40, -50]
21
+ ].freeze
22
+
23
+ BLACK_LOCATION_VALUE = [
24
+ [-50, -40, -30, -30, -30, -30, -40, -50],
25
+ [-40, -20, 0, 5, 5, 0, -20, -40],
26
+ [-30, 5, 10, 15, 15, 10, 5, -30],
27
+ [-30, 0, 15, 20, 20, 15, 0, -30],
28
+ [-30, 5, 15, 20, 20, 15, 5, -30],
29
+ [-30, 0, 10, 15, 15, 10, 0, -30],
30
+ [-40, -20, 0, 0, 0, 0, -20, -40],
31
+ [-50, -40, -30, -30, -30, -30, -40, -50]
32
+ ].freeze
33
+
34
+ WHITE_LOCATION_VALUE_EASY = [
35
+ [-50, -40, -30, -30, -30, -30, -40, -50],
36
+ [-40, -20, 0, 0, 0, 0, -20, -40],
37
+ [-30, 0, 20, 25, 25, 20, 0, -30],
38
+ [-30, 5, 25, 40, 40, 25, 5, -30],
39
+ [-30, 0, 25, 40, 40, 25, 0, -30],
40
+ [-30, 5, 20, 25, 25, 20, 5, -30],
41
+ [-40, -20, 0, 5, 5, 0, -20, -40],
42
+ [-50, -40, -30, -30, -30, -30, -40, -50]
43
+ ].freeze
44
+
45
+ BLACK_LOCATION_VALUE_EASY = [
46
+ [-50, -40, -30, -30, -30, -30, -40, -50],
47
+ [-40, -20, 0, 5, 5, 0, -20, -40],
48
+ [-30, 0, 20, 25, 25, 20, 0, -30],
49
+ [-30, 5, 25, 40, 40, 25, 5, -30],
50
+ [-30, 0, 25, 40, 40, 25, 0, -30],
51
+ [-30, 5, 20, 25, 25, 20, 5, -30],
52
+ [-40, -20, 0, 0, 0, 0, -20, -40],
53
+ [-50, -40, -30, -30, -30, -30, -40, -50]
54
+ ].freeze
55
+
56
+ include StepPattern
57
+ end
@@ -0,0 +1,82 @@
1
+ require_relative '../movement_rules/en_passant_piece_control'
2
+ require_relative '../movement_rules/pawn_movement_and_promotion'
3
+
4
+ class Pawn < Piece
5
+ BLACK = ['♟', '♛'].freeze
6
+ WHITE = ['♙', '♕'].freeze
7
+
8
+ B_OPPOSITE_ROW = 7
9
+ W_OPPOSITE_ROW = 0
10
+
11
+ MOVE_DIRECTIONS = [
12
+ [0, 1], [0, -1], [1, 0], [-1, 0],
13
+ [1, 1], [1, -1], [-1, 1], [-1, -1]
14
+ ].freeze
15
+
16
+ VALUE = 100
17
+
18
+ WHITE_LOCATION_VALUE = [
19
+ [0, 0, 0, 0, 0, 0, 0, 0],
20
+ [50, 50, 50, 50, 50, 50, 50, 50],
21
+ [10, 10, 20, 30, 30, 20, 10, 10],
22
+ [5, 5, 10, 25, 25, 10, 5, 5],
23
+ [0, 0, 0, 20, 20, 0, 0, 0],
24
+ [5, -5, -10, 0, 0, -10, -5, 5],
25
+ [5, 10, 10, -20, -20, 10, 10, 5],
26
+ [0, 0, 0, 0, 0, 0, 0, 0]
27
+ ].freeze
28
+
29
+ BLACK_LOCATION_VALUE = [
30
+ [0, 0, 0, 0, 0, 0, 0, 0],
31
+ [5, 10, 10, -20, -20, 10, 10, 5],
32
+ [5, -5, -10, 0, 0, -10, -5, 5],
33
+ [0, 0, 0, 20, 20, 0, 0, 0],
34
+ [5, 5, 10, 25, 25, 10, 5, 5],
35
+ [10, 10, 20, 30, 30, 20, 10, 10],
36
+ [50, 50, 50, 50, 50, 50, 50, 50],
37
+ [0, 0, 0, 0, 0, 0, 0, 0]
38
+ ].freeze
39
+
40
+ WHITE_LOCATION_VALUE_EASY = [
41
+ [100, 100, 100, 100, 100, 100, 100, 100],
42
+ [60, 60, 60, 60, 60, 60, 60, 60],
43
+ [50, 50, 60, 60, 60, 60, 50, 50],
44
+ [50, 50, 60, 60, 60, 60, 50, 50],
45
+ [50, 50, 60, 0, 0, 60, 50, 50],
46
+ [50, 50, 10, 0, 0, 10, 50, 50],
47
+ [5, 10, 10, -20, -20, 10, 10, 5],
48
+ [0, 0, 0, 0, 0, 0, 0, 0]
49
+ ].freeze
50
+
51
+ BLACK_LOCATION_VALUE_EASY = [
52
+ [0, 0, 0, 0, 0, 0, 0, 0],
53
+ [5, 10, 10, -20, -20, 10, 10, 5],
54
+ [50, 50, 10, 0, 0, 10, 50, 50],
55
+ [50, 50, 60, 0, 0, 60, 50, 50],
56
+ [50, 50, 60, 60, 60, 60, 50, 50],
57
+ [50, 50, 60, 60, 60, 60, 50, 50],
58
+ [60, 60, 60, 60, 60, 60, 60, 60],
59
+ [100, 100, 100, 100, 100, 100, 100, 100]
60
+ ].freeze
61
+
62
+ include SlidePattern
63
+ include EnPassantPieceControl
64
+ include PawnMovementAndPromotion
65
+
66
+ def initialize(board, location, color)
67
+ super(board, location, color)
68
+ @promoted = false
69
+ end
70
+
71
+ def to_s
72
+ promote unless promoted?
73
+
74
+ white = color == :white
75
+ if promoted? && white then Paint[self.class::WHITE.last, :white, :bright]
76
+ elsif promoted? && !white then Paint[self.class::BLACK.last, :blue, :bright]
77
+ elsif !promoted? && white then Paint[self.class::WHITE.first, :white]
78
+ else
79
+ Paint[self.class::BLACK.first, :blue]
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,77 @@
1
+ require_relative '../board'
2
+ require_relative '../movement_rules/castling_piece_control'
3
+ require_relative '../movement_rules/move_slide_pattern'
4
+ require_relative '../movement_rules/move_step_pattern'
5
+
6
+ require 'bundler/setup'
7
+ require 'paint'
8
+
9
+ class Piece
10
+ attr_reader :color, :board
11
+ attr_accessor :location
12
+
13
+ def initialize(board, location, color)
14
+ @board = board
15
+ @color = color
16
+ @location = location
17
+ end
18
+
19
+ def to_s
20
+ case color
21
+ when :white then Paint[self.class::WHITE, :white]
22
+ else Paint[self.class::BLACK, :blue]
23
+ end
24
+ end
25
+
26
+ # Available moves that don't move us into check
27
+ def safe_moves
28
+ available_moves.each_with_object([]) do |move, moves|
29
+ new_board = board.duplicate
30
+
31
+ new_board.move_piece!(location, move)
32
+ moves << move unless new_board.in_check?(color)
33
+ end
34
+ end
35
+
36
+ def value
37
+ self.class::VALUE
38
+ end
39
+
40
+ def location_value
41
+ row, column = location
42
+ white = color == :white
43
+ if board.hard_difficulty? && white
44
+ self.class::WHITE_LOCATION_VALUE[row][column]
45
+ elsif board.hard_difficulty? && !white
46
+ self.class::BLACK_LOCATION_VALUE[row][column]
47
+ elsif !board.hard_difficulty? && white
48
+ self.class::WHITE_LOCATION_VALUE_EASY[row][column]
49
+ else
50
+ self.class::BLACK_LOCATION_VALUE_EASY[row][column]
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def current_row
57
+ location.first
58
+ end
59
+
60
+ def current_column
61
+ location.last
62
+ end
63
+
64
+ def move_directions
65
+ self.class::MOVE_DIRECTIONS
66
+ end
67
+
68
+ def enemy_in?(location)
69
+ board.within_limits?(location) &&
70
+ board[location].is_a?(Piece) &&
71
+ board[location].color != color
72
+ end
73
+
74
+ def friend_in?(location)
75
+ board[location].is_a?(Piece) && board[location].color == color
76
+ end
77
+ end
@@ -0,0 +1,44 @@
1
+ class Queen < Piece
2
+ MOVE_DIRECTIONS = [
3
+ [0, 1], [0, -1], [1, 0], [-1, 0],
4
+ [1, 1], [1, -1], [-1, 1], [-1, -1]
5
+ ].freeze
6
+
7
+ BLACK = '♛'
8
+ WHITE = '♕'
9
+
10
+ VALUE = 900
11
+
12
+ WHITE_LOCATION_VALUE = [
13
+ [-20, -10, -10, -5, -5, -10, -10, -20],
14
+ [-10, 0, 0, 0, 0, 0, 0, -10],
15
+ [-10, 0, 5, 5, 5, 5, 0, -10],
16
+ [-5, 0, 5, 5, 5, 5, 0, -5],
17
+ [0, 0, 5, 5, 5, 5, 0, -5],
18
+ [-10, 5, 5, 5, 5, 5, 0, -10],
19
+ [-10, 0, 5, 0, 0, 0, 0, -10],
20
+ [-20, -10, -10, -5, -5, -10, -10, -20]
21
+ ].freeze
22
+
23
+ BLACK_LOCATION_VALUE = [
24
+ [-20, -10, -10, -5, -5, -10, -10, -20],
25
+ [-10, 0, 0, 0, 0, 5, 0, -10],
26
+ [-10, 0, 5, 5, 5, 5, 5, -10],
27
+ [-5, 0, 5, 5, 5, 5, 0, 0],
28
+ [-5, 0, 5, 5, 5, 5, 0, -5],
29
+ [-10, 0, 5, 5, 5, 5, 0, -10],
30
+ [-10, 0, 0, 0, 0, 0, 0, -10],
31
+ [-20, -10, -10, -5, -5, -10, -10, -20]
32
+ ].freeze
33
+
34
+ include SlidePattern
35
+
36
+ def location_value
37
+ row, column = location
38
+
39
+ case color
40
+ when :white then self.class::WHITE_LOCATION_VALUE[row][column]
41
+ else self.class::BLACK_LOCATION_VALUE[row][column]
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,62 @@
1
+ class Rook < Piece
2
+ MOVE_DIRECTIONS = [
3
+ [0, 1], [0, -1], [1, 0], [-1, 0]
4
+ ].freeze
5
+
6
+ BLACK = '♜'
7
+ WHITE = '♖'
8
+
9
+ VALUE = 500
10
+
11
+ WHITE_LOCATION_VALUE = [
12
+ [0, 0, 0, 0, 0, 0, 0, 0],
13
+ [5, 10, 10, 10, 10, 10, 10, 5],
14
+ [-5, 0, 0, 0, 0, 0, 0, -5],
15
+ [-5, 0, 0, 0, 0, 0, 0, -5],
16
+ [-5, 0, 0, 0, 0, 0, 0, -5],
17
+ [-5, 0, 0, 0, 0, 0, 0, -5],
18
+ [-5, 0, 0, 0, 0, 0, 0, -5],
19
+ [0, 0, 0, 5, 5, 0, 0, 0]
20
+ ].freeze
21
+
22
+ BLACK_LOCATION_VALUE = [
23
+ [0, 0, 0, 5, 5, 0, 0, 0],
24
+ [-5, 0, 0, 0, 0, 0, 0, -5],
25
+ [-5, 0, 0, 0, 0, 0, 0, -5],
26
+ [-5, 0, 0, 0, 0, 0, 0, -5],
27
+ [-5, 0, 0, 0, 0, 0, 0, -5],
28
+ [-5, 0, 0, 0, 0, 0, 0, -5],
29
+ [5, 10, 10, 10, 10, 10, 10, 5],
30
+ [0, 0, 0, 0, 0, 0, 0, 0]
31
+ ].freeze
32
+
33
+ WHITE_LOCATION_VALUE_EASY = [
34
+ [0, 0, 0, 0, 0, 0, 0, 0],
35
+ [5, 10, 10, 10, 10, 10, 10, 5],
36
+ [-5, 0, 10, 10, 10, 10, 0, -5],
37
+ [-5, 0, 10, 10, 10, 10, 0, -5],
38
+ [-5, 0, 10, 10, 10, 10, 0, -5],
39
+ [-5, 0, 10, 10, 10, 10, 0, -5],
40
+ [-5, 0, 0, 0, 0, 0, 0, -5],
41
+ [0, 0, 0, 5, 5, 0, 0, 0]
42
+ ].freeze
43
+
44
+ BLACK_LOCATION_VALUE_EASY = [
45
+ [0, 0, 0, 5, 5, 0, 0, 0],
46
+ [-5, 0, 0, 0, 0, 0, 0, -5],
47
+ [-5, 0, 10, 10, 10, 10, 0, -5],
48
+ [-5, 0, 10, 10, 10, 10, 0, -5],
49
+ [-5, 0, 10, 10, 10, 10, 0, -5],
50
+ [-5, 0, 10, 10, 10, 10, 0, -5],
51
+ [5, 10, 10, 10, 10, 10, 10, 5],
52
+ [0, 0, 0, 0, 0, 0, 0, 0]
53
+ ].freeze
54
+
55
+ include SlidePattern
56
+ include CastlingPieceControl
57
+
58
+ def initialize(board, location, color)
59
+ super
60
+ @moved = false
61
+ end
62
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'pieces/empty_square.rb'
2
+ require_relative 'pieces/piece.rb'
3
+ require_relative 'pieces/pawn.rb'
4
+ require_relative 'pieces/knight.rb'
5
+ require_relative 'pieces/bishop.rb'
6
+ require_relative 'pieces/rook.rb'
7
+ require_relative 'pieces/queen.rb'
8
+ require_relative 'pieces/king.rb'
@@ -0,0 +1,43 @@
1
+ require_relative 'ai'
2
+ require_relative 'algebraic_conversion'
3
+ require_relative 'movement_rules/castling_rights'
4
+
5
+ class Player
6
+ include CastlingRights
7
+
8
+ attr_accessor :last_move
9
+ attr_reader :color, :board, :history
10
+
11
+ def initialize(color, board)
12
+ @color = color
13
+ @board = board
14
+ @history = []
15
+ end
16
+
17
+ private
18
+
19
+ def maximizing_player?
20
+ color == :white
21
+ end
22
+ end
23
+
24
+ class Computer < Player
25
+ include AI
26
+
27
+ # depth: Levels of AI#minimax recursion.
28
+ # Deeper means harder (computer can think `depth` turns ahead)
29
+ # See Engine#set_difficulty
30
+ attr_accessor :depth
31
+
32
+ def select_move
33
+ computer_chooses_move
34
+ end
35
+ end
36
+
37
+ class Human < Player
38
+ include AlgebraicConversion
39
+
40
+ def select_move
41
+ algebraic_input
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ module SapphireChess
2
+ VERSION ||= '1.0.0'
3
+ end