sapphire-chess 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +45 -0
- data/LICENSE +21 -0
- data/README.md +24 -0
- data/Rakefile +1 -0
- data/bin/sapphire-chess +5 -0
- data/lib/sapphire-chess/ai.rb +101 -0
- data/lib/sapphire-chess/algebraic_conversion.rb +89 -0
- data/lib/sapphire-chess/board/board_analysis.rb +66 -0
- data/lib/sapphire-chess/board/board_evaluation.rb +25 -0
- data/lib/sapphire-chess/board/board_general.rb +155 -0
- data/lib/sapphire-chess/board/board_provisional_moves.rb +32 -0
- data/lib/sapphire-chess/board/board_renderer.rb +122 -0
- data/lib/sapphire-chess/board.rb +5 -0
- data/lib/sapphire-chess/display.rb +140 -0
- data/lib/sapphire-chess/engine.rb +153 -0
- data/lib/sapphire-chess/human_input_validation.rb +123 -0
- data/lib/sapphire-chess/movement_rules/castling_board_control.rb +37 -0
- data/lib/sapphire-chess/movement_rules/castling_piece_control.rb +9 -0
- data/lib/sapphire-chess/movement_rules/castling_rights.rb +79 -0
- data/lib/sapphire-chess/movement_rules/en_passant_board_control.rb +20 -0
- data/lib/sapphire-chess/movement_rules/en_passant_piece_control.rb +36 -0
- data/lib/sapphire-chess/movement_rules/move_slide_pattern.rb +23 -0
- data/lib/sapphire-chess/movement_rules/move_step_pattern.rb +17 -0
- data/lib/sapphire-chess/movement_rules/pawn_movement_and_promotion.rb +71 -0
- data/lib/sapphire-chess/movement_rules.rb +7 -0
- data/lib/sapphire-chess/pieces/bishop.rb +56 -0
- data/lib/sapphire-chess/pieces/empty_square.rb +13 -0
- data/lib/sapphire-chess/pieces/king.rb +77 -0
- data/lib/sapphire-chess/pieces/knight.rb +57 -0
- data/lib/sapphire-chess/pieces/pawn.rb +82 -0
- data/lib/sapphire-chess/pieces/piece.rb +77 -0
- data/lib/sapphire-chess/pieces/queen.rb +44 -0
- data/lib/sapphire-chess/pieces/rook.rb +62 -0
- data/lib/sapphire-chess/pieces.rb +8 -0
- data/lib/sapphire-chess/player.rb +43 -0
- data/lib/sapphire-chess/version.rb +3 -0
- data/lib/sapphire-chess.rb +9 -0
- data/sapphire-chess.gemspec +29 -0
- 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,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
|