sapphire-chess 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44df97bffb9cde364d3e734fc09129c0b0ffe860f8572ca47ed1cb304b121e2a
4
- data.tar.gz: 61aa950fb118981973b8d1d58fe2ad1e4cd3e85dc8ad2c20fafdab16ef1a257b
3
+ metadata.gz: 4d026a9d0869d78ce30cc4ae4e761fe2f299f37d40f17f379ec19093f52b7993
4
+ data.tar.gz: 92def346caccfeaa7b7fa20b12c4d68625bc07f6e985167395a56351217e6075
5
5
  SHA512:
6
- metadata.gz: 7dcdaf550d4b43871587ddacae31b28f1fe2b190732948a6db4a05423903769f16454eedf5aae3368b94e896497d96c014c96693c3b95fadfd660d1d3f377806
7
- data.tar.gz: d5c4d34fda192846826a3c39eef7f06808918fad7e545dcc00e739be28ef37f48595dd62f3fae5b209c05502b48e01eedd089542c63841e0ce53a6f5efc6b482
6
+ metadata.gz: 90c2103e67d8f4e541d9ad20a2016a4e00b3f316eea71d1454fd0a39d3cd1460263d996a39244d2e64722a4c7a6f405b52f35999af145e95dcad165d4161e300
7
+ data.tar.gz: d217a64d520044a5fc8a09a8fed7bad5638ed3796d1947171749fde84c862170344f7d3a75d4e24bbb003c194c2dd426c32e6ff89d6cdb1f542d7b1d6f17a767
data/CHANGELOG.md CHANGED
@@ -1,3 +1,29 @@
1
+ # 1.1.0 (January 25, 2023)
2
+
3
+ ## Bug fixes:
4
+
5
+ - Fix `#get_move` invocation (correct: `#select_move`) in `human_input_validation.rb`.
6
+ - Pawns were not promoted correctly due to an error in `Pawn#available_moves`.
7
+
8
+ ## Enhancement:
9
+
10
+ - Fine tune various pieces' location values for not hard difficulties
11
+ - The game now only allows legal movements. For example, when a player is in check,
12
+ the only valid movements are movements that move the player out of check.
13
+ - The white computer player now randomly chooses a classic opening, and the black computer player can answer with an appropriate defense.
14
+
15
+ ## Documentation:
16
+
17
+ - Fix various typos in documentation.
18
+
19
+ ## Style:
20
+
21
+ - I've abstracted logic in some methods to make them more readable, and changed a few variable names for clarity.
22
+
23
+ ## Current worries:
24
+
25
+ - The hardest difficulty is still too slow, I'm studying how to improve the performance of the method, which builds recursively a huge n-tree.
26
+
1
27
  # 1.0.1 (January 16, 2023)
2
28
 
3
29
  ## Enhancement:
@@ -13,6 +39,6 @@
13
39
 
14
40
  # 1.0.0 (January 15, 2023)
15
41
 
16
- Initial irst release.
42
+ Initial release.
17
43
 
18
44
  Please, visit https://medium.com/@lucas.sorribes/nostromo-my-ruby-chess-journey-part-i-7ef544b547a5 for a very detailed account of how I wrote this game.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sapphire-chess (1.0.1)
4
+ sapphire-chess (1.1.0)
5
5
  paint (~> 2.3)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
- # Sapphire Chess [![Gem Version](https://badge.fury.io/rb/sapphire-chess.svg)](https://badge.fury.io/rb/sapphire-chess)
1
+ # Sapphire Chess
2
+ [![Gem Version](https://badge.fury.io/rb/sapphire-chess.svg?)](https://badge.fury.io/rb/sapphire-chess)
2
3
 
3
4
  Sapphire Chess is a command line-based chess game with an algebraic notation input system,
4
5
  complete chess rules, a beautiful interface and a functional AI. It provides three game modes:
@@ -1,8 +1,14 @@
1
+ require_relative 'openings_and_defenses'
2
+
1
3
  module AI
4
+ include OpeningsAndDefenses
5
+
2
6
  private
3
7
 
4
8
  # Chooses move by best possible outcome:
5
9
  def computer_chooses_move
10
+ return first_move(color) if first_turn?
11
+
6
12
  possible_moves = board.generate_moves(color)
7
13
  possible_moves << %i[castle king] if castle_rights?(:king)
8
14
  possible_moves << %i[castle queen] if castle_rights?(:queen)
@@ -10,13 +16,37 @@ module AI
10
16
  best_move(possible_moves)
11
17
  end
12
18
 
19
+ def first_move(color)
20
+ if color == :white then opening
21
+ else
22
+ defense
23
+ end
24
+ end
25
+
26
+ def opening
27
+ opening =
28
+ case rand(1..100)
29
+ when OPENINGS[:nf3][:probabilty] then :nf3
30
+ when OPENINGS[:e4][:probabilty] then :e4
31
+ when OPENINGS[:d4][:probabilty] then :d4
32
+ when OPENINGS[:c4][:probabilty] then :c4
33
+ end
34
+
35
+ OPENINGS[opening][:move]
36
+ end
37
+
38
+ def defense
39
+ DEFENSES[board.white_player.history.last] ||
40
+ best_move(board.generate_moves(:black))
41
+ end
42
+
13
43
  def best_move(possible_moves)
14
44
  evaluations = {}
15
45
  anti_loop_filter(possible_moves)
16
46
 
17
47
  possible_moves.each do |move|
18
- evaluations[move] =
19
- minimax(move, depth, -Float::INFINITY, Float::INFINITY, maximizing_player?)
48
+ evaluations[move] =
49
+ minimax(move, depth, -Float::INFINITY, Float::INFINITY, maximizing_player?)
20
50
  end
21
51
 
22
52
  move_randomizer(evaluations)
@@ -27,29 +57,29 @@ module AI
27
57
  return board.evaluate if depth.zero?
28
58
 
29
59
  board.provisional(move, color) do
30
- if maximizing_player
31
- best_minimizing_evaluation = Float::INFINITY
32
-
33
- board.generate_moves(:black).each do |possible_move|
34
- evaluation = minimax(possible_move, depth - 1, alpha, beta, false)
35
- best_minimizing_evaluation = [best_minimizing_evaluation, evaluation].min
36
- beta = [beta, evaluation].min
37
- break if beta <= alpha
38
- end
39
-
40
- best_minimizing_evaluation
41
- else
42
- best_maximizing_evaluation = -Float::INFINITY
43
-
44
- board.generate_moves(:white).each do |possible_move|
45
- evaluation = minimax(possible_move, depth - 1, alpha, beta, true)
46
- best_maximizing_evaluation = [best_maximizing_evaluation, evaluation].max
47
- alpha = [alpha, evaluation].max
48
- break if beta <= alpha
49
- end
50
-
51
- best_maximizing_evaluation
60
+ if maximizing_player
61
+ best_minimizing_evaluation = Float::INFINITY
62
+
63
+ board.generate_moves(:black).each do |possible_move|
64
+ evaluation = minimax(possible_move, depth - 1, alpha, beta, false)
65
+ best_minimizing_evaluation = [best_minimizing_evaluation, evaluation].min
66
+ beta = [beta, evaluation].min
67
+ break if beta <= alpha
52
68
  end
69
+
70
+ best_minimizing_evaluation
71
+ else
72
+ best_maximizing_evaluation = -Float::INFINITY
73
+
74
+ board.generate_moves(:white).each do |possible_move|
75
+ evaluation = minimax(possible_move, depth - 1, alpha, beta, true)
76
+ best_maximizing_evaluation = [best_maximizing_evaluation, evaluation].max
77
+ alpha = [alpha, evaluation].max
78
+ break if beta <= alpha
79
+ end
80
+
81
+ best_maximizing_evaluation
82
+ end
53
83
  end
54
84
  end
55
85
 
@@ -1,9 +1,8 @@
1
1
  require_relative '../pieces'
2
+ require_relative '../board'
2
3
  require_relative '../movement_rules/castling_board_control'
3
4
  require_relative '../movement_rules/en_passant_board_control'
4
- require_relative '../board/board_analysis'
5
- require_relative '../board/board_evaluation'
6
- require_relative '../board/board_provisional_moves'
5
+ require_relative '../algebraic_conversion'
7
6
 
8
7
  class Board
9
8
  SQUARE_ORDER = 8
@@ -11,6 +10,7 @@ class Board
11
10
  W_PAWN_ROW = 6
12
11
  FIRST_ROW = 0
13
12
  LAST_ROW = 7
13
+ MIN_FOR_HARD_DIFFICULTY = 2
14
14
  PIECES_SEQUENCE = [
15
15
  Rook, Knight, Bishop, Queen, King, Bishop, Knight, Rook
16
16
  ].freeze
@@ -20,8 +20,9 @@ class Board
20
20
  include ProvisionalMoves
21
21
  include CastlingBoardControl
22
22
  include EnPassantBoardControl
23
+ include AlgebraicConversion
23
24
 
24
- attr_reader :matrix, :white_player, :black_player, :hard_difficulty
25
+ attr_reader :matrix, :white_player, :black_player
25
26
 
26
27
  def self.initialize_board
27
28
  board = new(duplicated: false)
@@ -38,7 +39,7 @@ class Board
38
39
  def set_pawns(board)
39
40
  [B_PAWN_ROW, W_PAWN_ROW].each do |pawn_row|
40
41
  color = pawn_row == B_PAWN_ROW ? :black : :white
41
-
42
+
42
43
  SQUARE_ORDER.times do |column|
43
44
  board[[pawn_row, column]] = Pawn.new(board, [pawn_row, column], color)
44
45
  end
@@ -96,7 +97,7 @@ class Board
96
97
  self[target_square].location = target_square
97
98
 
98
99
  return unless permanent && was_en_passant?(piece, target_square)
99
-
100
+
100
101
  capture_passed_pawn!(target_square)
101
102
  end
102
103
 
@@ -110,16 +111,17 @@ class Board
110
111
  end
111
112
 
112
113
  def generate_moves(color)
113
- possible_moves =
114
114
  friendly_pieces(color).each_with_object([]) do |piece, possible_moves|
115
- location = piece.location
116
115
 
117
- piece.available_moves.each do |possible_move|
118
- possible_moves << [location, possible_move]
116
+ current_piece_location = piece.location
117
+
118
+ piece.available_moves.each do |possible_target_square|
119
+
120
+ move = [current_piece_location, possible_target_square]
121
+
122
+ possible_moves << move if legal_move?(piece, possible_target_square)
119
123
  end
120
124
  end
121
- # sort_moves!(possible_moves, color)
122
- possible_moves
123
125
  end
124
126
 
125
127
  # This method is avoids checking for availability of en passant
@@ -132,24 +134,19 @@ class Board
132
134
  self.hard_difficulty =
133
135
  [white_player, black_player].find do |player|
134
136
  player.is_a?(Computer)
135
- end.depth == 3
137
+ end.depth >= MIN_FOR_HARD_DIFFICULTY
136
138
  end
137
139
 
138
140
  def hard_difficulty?
139
- hard_difficulty
141
+ @hard_difficulty
140
142
  end
141
143
 
142
144
  private
143
145
 
144
146
  attr_writer :hard_difficulty
145
- # For testing purposes:
146
- def sort_moves!(possible_moves, color)
147
- possible_moves.sort! do |a, b|
148
- if color == :white
149
- evaluate_move(a, color) <=> evaluate_move(b, color)
150
- else
151
- evaluate_move(b, color) <=> evaluate_move(a, color)
152
- end
153
- end
147
+
148
+ def legal_move?(piece, target_square)
149
+ color = piece.color
150
+ !in_check?(color) || (in_check?(color) && piece.safe_moves.include?(target_square))
154
151
  end
155
152
  end
@@ -135,6 +135,6 @@ module Display
135
135
  nil,
136
136
  current_player.color
137
137
  ]
138
- puts 'Thanks for playing Ruby Chess'
138
+ puts "\nThanks for playing Sapphire Chess!\n"
139
139
  end
140
140
  end
@@ -68,7 +68,7 @@ module HumanInputValidation
68
68
  target_square = nil
69
69
  puts "Where do you want to move the #{board[piece].class}?"
70
70
  loop do
71
- target_square = current_player.get_move
71
+ target_square = current_player.select_move
72
72
  break if valid_target_square?(piece, target_square)
73
73
 
74
74
  puts "The #{board[piece].class} selected can't move to that square."
@@ -96,13 +96,21 @@ module HumanInputValidation
96
96
  player_move_input.first.is_a?(Integer)
97
97
  end
98
98
 
99
- def valid_piece_selection?(piece)
100
- board[piece].is_a?(Piece) &&
101
- board[piece].color == current_player.color
99
+ def valid_piece_selection?(piece_location)
100
+ piece = board[piece_location]
101
+
102
+ piece.is_a?(Piece) &&
103
+ piece.color == current_player.color &&
104
+ !piece.available_moves.empty? &&
105
+ (!board.in_check?(current_player.color) || board.in_check?(current_player.color) && !piece.safe_moves.empty?)
102
106
  end
103
107
 
104
- def valid_target_square?(piece, target_square)
105
- board[piece].available_moves.include?(target_square)
108
+ def valid_target_square?(piece_location, target_square)
109
+ piece = board[piece_location]
110
+ valid_target = piece.available_moves.include?(target_square)
111
+
112
+ (valid_target && !board.in_check?(current_player.color)) ||
113
+ (valid_target && board.in_check?(current_player.color) && piece.safe_moves.include?(target_square))
106
114
  end
107
115
 
108
116
  def valid_castling?(side)
@@ -1,17 +1,4 @@
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
-
1
+ module PawnMovementHelpersAndPromotion
15
2
  def promoted?
16
3
  @promoted
17
4
  end
@@ -0,0 +1,15 @@
1
+ module OpeningsAndDefenses
2
+ OPENINGS = {
3
+ nf3: { probabilty: (1..50), move: [[7, 6], [5, 5]] }, # Most flexible white opening favored by many
4
+ e4: { probabilty: (51..75), move: [[6, 4], [4, 4]] }, # The best and most famous white opening.
5
+ d4: { probabilty: (76..90), move: [[6, 3], [4, 3]] }, # This can lead to the Queen's Gambit
6
+ c4: { probabilty: (91..100), move: [[6, 2], [4, 2]] }
7
+ }.freeze
8
+
9
+ DEFENSES = {
10
+ [[7, 6], [5, 5]] => [[[1, 2], [3, 2]], [[0, 6], [2, 5]]].sample, # Answers to 1.nf3
11
+ [[6, 4], [4, 4]] => [[[1, 2], [3, 2]], [[1, 4], [3, 4]]].sample, # Answers to 1.e4
12
+ [[6, 3], [4, 3]] => [[[0, 6], [2, 5]], [[1, 2], [2, 2]]].sample, # Answers to 1.d4
13
+ [[6, 2], [4, 2]] => [[[1, 2], [3, 2]], [[1, 4], [3, 4]]].sample # Answers to 1.c4
14
+ }.freeze
15
+ end
@@ -34,9 +34,9 @@ class Bishop < Piece
34
34
  [-20, -10, -10, -10, -10, -10, -10, -20],
35
35
  [-10, 0, 0, 0, 0, 0, 0, -10],
36
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],
37
+ [-10, 5, 5, 50, 50, 5, 5, -10],
38
+ [-10, 0, 20, 50, 50, 20, 0, -10],
39
+ [-10, 10, 20, 50, 50, 20, 10, -10],
40
40
  [-10, 5, 0, 0, 0, 0, 5, -10],
41
41
  [-20, -10, -10, -10, -10, -10, -10, -20]
42
42
  ].freeze
@@ -44,9 +44,9 @@ class Bishop < Piece
44
44
  BLACK_LOCATION_VALUE_EASY = [
45
45
  [-20, -10, -10, -10, -10, -10, -10, -20],
46
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],
47
+ [-10, 10, 20, 50, 50, 20, 10, -10],
48
+ [-10, 0, 20, 50, 50, 20, 0, -10],
49
+ [-10, 5, 5, 50, 50, 5, 5, -10],
50
50
  [-10, 0, 5, 15, 15, 5, 0, -10],
51
51
  [-10, 0, 0, 0, 0, 0, 0, -10],
52
52
  [-20, -10, -10, -10, -10, -10, -10, -20]
@@ -10,4 +10,8 @@ class EmptySquare
10
10
  def white
11
11
  '██'
12
12
  end
13
+
14
+ def hash_value
15
+ '0'
16
+ end
13
17
  end
@@ -35,8 +35,8 @@ class Knight < Piece
35
35
  [-50, -40, -30, -30, -30, -30, -40, -50],
36
36
  [-40, -20, 0, 0, 0, 0, -20, -40],
37
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],
38
+ [-30, 5, 25, 50, 50, 25, 5, -30],
39
+ [-30, 0, 25, 50, 50, 25, 0, -30],
40
40
  [-30, 5, 20, 25, 25, 20, 5, -30],
41
41
  [-40, -20, 0, 5, 5, 0, -20, -40],
42
42
  [-50, -40, -30, -30, -30, -30, -40, -50]
@@ -46,8 +46,8 @@ class Knight < Piece
46
46
  [-50, -40, -30, -30, -30, -30, -40, -50],
47
47
  [-40, -20, 0, 5, 5, 0, -20, -40],
48
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],
49
+ [-30, 5, 25, 50, 50, 25, 5, -30],
50
+ [-30, 0, 25, 50, 50, 25, 0, -30],
51
51
  [-30, 5, 20, 25, 25, 20, 5, -30],
52
52
  [-40, -20, 0, 0, 0, 0, -20, -40],
53
53
  [-50, -40, -30, -30, -30, -30, -40, -50]
@@ -1,5 +1,5 @@
1
1
  require_relative '../movement_rules/en_passant_piece_control'
2
- require_relative '../movement_rules/pawn_movement_and_promotion'
2
+ require_relative '../movement_rules/pawn_movement_helpers_and_promotion'
3
3
 
4
4
  class Pawn < Piece
5
5
  BLACK = ['♟', '♛'].freeze
@@ -42,8 +42,8 @@ class Pawn < Piece
42
42
  [60, 60, 60, 60, 60, 60, 60, 60],
43
43
  [50, 50, 60, 60, 60, 60, 50, 50],
44
44
  [50, 50, 60, 60, 60, 60, 50, 50],
45
- [50, 50, 60, 0, 0, 60, 50, 50],
46
- [50, 40, 10, 0, 0, 10, 40, 50],
45
+ [50, 50, 60, 60, 60, 60, 50, 50],
46
+ [50, 40, 10, 70, 70, 10, 40, 50],
47
47
  [5, 10, 10, -20, -20, 10, 10, 5],
48
48
  [0, 0, 0, 0, 0, 0, 0, 0]
49
49
  ].freeze
@@ -51,8 +51,8 @@ class Pawn < Piece
51
51
  BLACK_LOCATION_VALUE_EASY = [
52
52
  [0, 0, 0, 0, 0, 0, 0, 0],
53
53
  [5, 10, 10, -20, -20, 10, 10, 5],
54
- [50, 40, 10, 0, 0, 10, 40, 50],
55
- [50, 50, 60, 0, 0, 60, 50, 50],
54
+ [50, 40, 10, 70, 70, 10, 40, 50],
55
+ [50, 50, 60, 60, 60, 60, 50, 50],
56
56
  [50, 50, 60, 60, 60, 60, 50, 50],
57
57
  [50, 50, 60, 60, 60, 60, 50, 50],
58
58
  [60, 60, 60, 60, 60, 60, 60, 60],
@@ -61,7 +61,7 @@ class Pawn < Piece
61
61
 
62
62
  include SlidePattern
63
63
  include EnPassantPieceControl
64
- include PawnMovementAndPromotion
64
+ include PawnMovementHelpersAndPromotion
65
65
 
66
66
  def initialize(board, location, color)
67
67
  super(board, location, color)
@@ -79,4 +79,17 @@ class Pawn < Piece
79
79
  Paint[self.class::BLACK.first, :blue]
80
80
  end
81
81
  end
82
+
83
+ def available_moves
84
+ if promoted? then super
85
+ else
86
+ moves = []
87
+ add_one_square_movement!(moves)
88
+ add_two_square_movement!(moves)
89
+ add_diagonal_movement!(moves)
90
+ add_en_passant_movement!(moves)
91
+
92
+ moves.select { |move| board.within_limits?(move) }
93
+ end
94
+ end
82
95
  end
@@ -33,10 +33,10 @@ class Rook < Piece
33
33
  WHITE_LOCATION_VALUE_EASY = [
34
34
  [0, 0, 0, 0, 0, 0, 0, 0],
35
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],
36
+ [-5, 5, 10, 10, 10, 5, 0, -5],
37
+ [-5, 5, 10, 10, 10, 5, 0, -5],
38
+ [-5, 5, 10, 10, 10, 5, 0, -5],
39
+ [-5, 5, 10, 10, 10, 5, 0, -5],
40
40
  [-5, 0, 0, 0, 0, 0, 0, -5],
41
41
  [0, 0, 0, 5, 5, 0, 0, 0]
42
42
  ].freeze
@@ -44,10 +44,10 @@ class Rook < Piece
44
44
  BLACK_LOCATION_VALUE_EASY = [
45
45
  [0, 0, 0, 5, 5, 0, 0, 0],
46
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],
47
+ [-5, 0, 5, 10, 10, 5, 0, -5],
48
+ [-5, 0, 5, 10, 10, 5, 0, -5],
49
+ [-5, 0, 5, 10, 10, 5, 0, -5],
50
+ [-5, 0, 5, 10, 10, 5, 0, -5],
51
51
  [5, 10, 10, 10, 10, 10, 10, 5],
52
52
  [0, 0, 0, 0, 0, 0, 0, 0]
53
53
  ].freeze
@@ -16,6 +16,10 @@ class Player
16
16
 
17
17
  private
18
18
 
19
+ def first_turn?
20
+ history.empty?
21
+ end
22
+
19
23
  def maximizing_player?
20
24
  color == :white
21
25
  end
@@ -1,3 +1,3 @@
1
1
  module SapphireChess
2
- VERSION ||= '1.0.1'
2
+ VERSION ||= '1.1.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sapphire-chess
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lucas Sorribes
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-16 00:00:00.000000000 Z
11
+ date: 2023-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: paint
@@ -104,7 +104,8 @@ files:
104
104
  - lib/sapphire-chess/movement_rules/en_passant_piece_control.rb
105
105
  - lib/sapphire-chess/movement_rules/move_slide_pattern.rb
106
106
  - lib/sapphire-chess/movement_rules/move_step_pattern.rb
107
- - lib/sapphire-chess/movement_rules/pawn_movement_and_promotion.rb
107
+ - lib/sapphire-chess/movement_rules/pawn_movement_helpers_and_promotion.rb
108
+ - lib/sapphire-chess/openings_and_defenses.rb
108
109
  - lib/sapphire-chess/pieces.rb
109
110
  - lib/sapphire-chess/pieces/bishop.rb
110
111
  - lib/sapphire-chess/pieces/empty_square.rb
@@ -136,7 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
136
137
  - !ruby/object:Gem::Version
137
138
  version: '0'
138
139
  requirements: []
139
- rubygems_version: 3.1.6
140
+ rubygems_version: 3.4.4
140
141
  signing_key:
141
142
  specification_version: 4
142
143
  summary: Command line-based chess game