chessmate 0.7.1 → 0.8.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: b3eb25de34a44fabc8d5c94cff25979b5664c075c5cc897eb151c4513a749789
4
- data.tar.gz: 1ee304bf8f9ca8adb1cfceb143b44abeb6af447539ac1854622237d7b81e2791
3
+ metadata.gz: e8a0298012d7f8cad83eb00236534883f661bf131b826400de162a952d6a846e
4
+ data.tar.gz: b3047583dc762d0fa97c555102f59a7289e5dcd913ae76fa71a8b4b937a1b6ec
5
5
  SHA512:
6
- metadata.gz: 6e53760a34c345c7297cd59dbe6f77d06c3ac08f9507d7bdcf06528327199ccbbe808ea65f5103a688268b0b849039a31148ccd1c17b36ad091811d9be3bb89c
7
- data.tar.gz: 80089bb5808ab2fcb7f4a2dd8c65ef9500d5dc31759cfbd655c6552179e665698e8910867f2d24c94f4e7deac11f990c7714a34602f9ecfa9f04e7a14521fd8b
6
+ metadata.gz: c34f8fc54e8c829e454142f82f4c8b4f788018f32b646de78c39201462e57d849c50792b5cb5d591480fa99aba3fba56a328210ee1bfa5492779eb75bf0efd70
7
+ data.tar.gz: b865da7aba16232d0fadb70f7156bae86a8bbcf79688ae53644f6209f5bfbd427f246da18d8dc29c34778903551b60cd6dc6e8f1e16ecb2cec9125d2e028edc5
@@ -0,0 +1,76 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, sex characteristics, gender identity and expression,
9
+ level of experience, education, socio-economic status, nationality, personal
10
+ appearance, race, religion, or sexual identity and orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at tyler.b.porter@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72
+
73
+ [homepage]: https://www.contributor-covenant.org
74
+
75
+ For answers to common questions about this code of conduct, see
76
+ https://www.contributor-covenant.org/faq
data/Gemfile CHANGED
File without changes
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- chessmate (0.7.0)
4
+ chessmate (0.7.1)
5
5
  deep_dup (~> 0)
6
6
 
7
7
  GEM
@@ -37,7 +37,7 @@ DEPENDENCIES
37
37
  chessmate!
38
38
  pry (~> 0)
39
39
  rspec (~> 0)
40
- rubocop (>= 0.49.0)
40
+ rubocop (= 0.75.1)
41
41
 
42
42
  BUNDLED WITH
43
43
  2.0.2
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Tyler Porter
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,129 @@
1
+ <p align="center">
2
+ <img width="800" height="150" src="https://imgur.com/WFCwf9p.png">
3
+ </p>
4
+ <h2 align="center">A dead-simple chess validation gem for Rails</h2>
5
+ <p align="center">
6
+ <a href="https://travis-ci.com/pawptart/ChessMate">
7
+ <img src="https://travis-ci.com/pawptart/ChessMate.svg?branch=master">
8
+ </a>
9
+ <a href="https://github.com/pawptart/ChessMate/issues">
10
+ <img src="https://img.shields.io/github/issues/pawptart/chessmate">
11
+ </a>
12
+ <a href="https://rubygems.org/gems/chessmate">
13
+ <img src="https://img.shields.io/gem/v/chessmate">
14
+ </a>
15
+ </p>
16
+
17
+ ## About
18
+ ChessMate was built in around 2 months to be as easy to use as possible to quickly and easily bootstrap a chess game in Rails. The original idea came from a bootcamp exercise to build a working chess game, and so the code that was written there has been ported to ChessMate in order to benefit future programmers.
19
+
20
+ ChessMate is designed to be a backend tool to validate possible chess moves. Therefore, it is intentionally as agnostic to your frontend as possible to integrate into many different frameworks. Because it's designed to be flexible, it even supports custom game boards and can allow out-of-turn movement!
21
+
22
+ Click [here](http://chessmate-demo.herokuapp.com/) to play with a Rails app running ChessMate! (Or check out the source code [here](https://github.com/pawptart/ChessTest).)
23
+
24
+ <p align="center">
25
+ <img src="https://i.imgur.com/vyQjL4Y.png">
26
+ </p>
27
+
28
+ ## Usage
29
+ Simply add ChessMate to your Gemfile:
30
+
31
+ ```
32
+ gem 'chessmate'
33
+ ```
34
+
35
+ and then you can require it in a model, controller, etc.
36
+
37
+ ```
38
+ require 'chessmate'
39
+ ```
40
+
41
+ From there, you are free to utilize all the functions and features of ChessMate!
42
+
43
+ ## Building a new game and playing
44
+
45
+ ```
46
+ game = ChessMate.new
47
+ ```
48
+
49
+ Moving pieces works based on chess notation: for example, a common first move is `e2` to `e4`. ChessMate accepts these squares as arguments to the `move` method, so you don't need to specify a piece type like in normal chess notation. To make this move in ChessMate:
50
+
51
+ ```
52
+ game.move('e2', 'e4')
53
+ ```
54
+
55
+ If a move is invalid, `ChessMate.move` will return `false`, and the board and game state will not update. You also do not need to specify for any "special" moves, like castling or _en passant_, as ChessMate can handle this for you.
56
+
57
+ Other functions you might call:
58
+
59
+ ```
60
+ game.promote?(square) # Check if a square is capable of pawn promotion
61
+ game.promote!(square, piece) # Promote a promotable pawn, accepts 'rook'/'knight'/'bishop'/'queen'
62
+ game.in_check? # Determine if either color is currently in check
63
+ game.checkmate?(color) # Determine if the game is over, accepts 'W'/'B'
64
+ game.draw?(color) # Same as checkmate, for draw
65
+ ```
66
+
67
+ Using these functions, it's quite simple to build a working chess game!
68
+
69
+ ## Custom games
70
+ With ChessMate, it's very easy to build your own custom games. Below is a list of all the defaults ChessMate uses to build a game board, but they can be modified and passed into the `ChessMate.new` method as named arguments in any order, and ChessMate will build a game around your custom params.
71
+
72
+ For instance, for a custom board, on turn 10, with the white king in check:
73
+
74
+ ```
75
+ custom_board = [
76
+ [nil, nil, nil, nil, 'BK', nil, nil, nil],
77
+ [nil, nil, nil, nil, 'BQ', nil, nil, nil],
78
+ [nil, nil, nil, nil, nil, nil, nil, nil],
79
+ [nil, nil, nil, nil, nil, nil, nil, nil],
80
+ [nil, nil, nil, nil, nil, nil, nil, nil],
81
+ [nil, nil, nil, nil, nil, nil, nil, nil],
82
+ ['WP', nil, nil, nil, nil, nil, nil, nil],
83
+ [nil, nil, nil, nil, 'WK', nil, nil, nil]
84
+ ]
85
+ turn = 10
86
+ in_check = { white: true, black: false }
87
+
88
+ game = ChessMate.new(board: custom_board, turn: turn, in_check: in_check)
89
+ ```
90
+
91
+ ChessMate can build a game with those parameters!
92
+
93
+ Here is a list of all the defaults:
94
+ ```
95
+ board: [
96
+ %w[BR BN BB BQ BK BB BN BR],
97
+ %w[BP BP BP BP BP BP BP BP],
98
+ [nil, nil, nil, nil, nil, nil, nil, nil],
99
+ [nil, nil, nil, nil, nil, nil, nil, nil],
100
+ [nil, nil, nil, nil, nil, nil, nil, nil],
101
+ [nil, nil, nil, nil, nil, nil, nil, nil],
102
+ %w[WP WP WP WP WP WP WP WP],
103
+ %w[WR WN WB WQ WK WB WN WR]
104
+ ]
105
+
106
+ turn: 1
107
+
108
+ promotable: nil
109
+
110
+ en_passant: { white: nil, black: nil }
111
+
112
+ in_check: { white: false, black: false }
113
+
114
+ castling: {
115
+ white: {
116
+ kingside: true,
117
+ queenside: true
118
+ },
119
+ black: {
120
+ kingside: true,
121
+ queenside: true
122
+ }
123
+ }
124
+ ```
125
+
126
+ ## Contributing
127
+ ChessMate is open to contributions! If you have a feature request, a bug, or a suggestion, please open an issue! Please note that a review is required and passing TravisCI build before your PR can be merged with master.
128
+
129
+ Currently looking to expand ChessMate features by allowing chess notation logging as well as better documentation!
data/lib/chessmate.rb CHANGED
@@ -11,8 +11,16 @@ class ChessMate
11
11
  require 'pieces/queen'
12
12
  require 'pieces/king'
13
13
  require 'helpers/default'
14
+ require 'helpers/logger'
14
15
 
15
- attr_reader :board, :turn, :in_check, :promotable, :en_passant, :castling, :allow_out_of_turn
16
+ attr_reader :board,
17
+ :turn,
18
+ :in_check,
19
+ :promotable,
20
+ :en_passant,
21
+ :castling,
22
+ :allow_out_of_turn,
23
+ :move_history
16
24
 
17
25
  def initialize(board: nil,
18
26
  turn: nil,
@@ -20,10 +28,12 @@ class ChessMate
20
28
  en_passant: nil,
21
29
  castling: nil,
22
30
  in_check: nil,
23
- allow_out_of_turn: nil)
31
+ allow_out_of_turn: nil,
32
+ move_history: nil,
33
+ ignore_logging: false)
24
34
  @board = board || DEFAULT[:board].map(&:dup)
25
35
  @turn = turn || DEFAULT[:turn]
26
- @promotable = promotable || DeepDup.deep_dup(DEFAULT[:promotable])
36
+ @promotable = promotable
27
37
  @en_passant = en_passant || DeepDup.deep_dup(DEFAULT[:en_passant])
28
38
  @castling = castling || DeepDup.deep_dup(DEFAULT[:castling])
29
39
  @in_check = in_check || DeepDup.deep_dup(DEFAULT[:in_check])
@@ -35,13 +45,13 @@ class ChessMate
35
45
  else
36
46
  false
37
47
  end
48
+ @move_history = move_history || []
49
+ @ignore_logging = ignore_logging
38
50
  end
39
51
 
40
52
  def update(orig, dest = nil)
41
- orig_y = orig[0]
42
- orig_x = orig[1]
43
- dest_y = dest[0]
44
- dest_x = dest[1]
53
+ orig_y, orig_x = orig
54
+ dest_y, dest_x = dest
45
55
  piece_type = @board[orig_y][orig_x]
46
56
  piece_color = piece_type[0] == 'W' ? :white : :black
47
57
  opposite_color = piece_type[0] == 'W' ? :black : :white
@@ -79,10 +89,16 @@ class ChessMate
79
89
  @board[orig_y][new_rook_x_position] = piece_type[0] + 'R'
80
90
  end
81
91
 
92
+ @promotable = dest if piece_type[1] == 'P' && promote?(orig)
93
+
94
+ unless @ignore_logging
95
+ logger = Logger.new(orig, dest, @board)
96
+ @move_history << logger.log_move
97
+ end
98
+
82
99
  @board[orig_y][orig_x] = nil
83
100
  @board[dest_y][dest_x] = piece_type
84
101
 
85
- @promotable = dest if piece_type[1] == 'P' && promote?(dest)
86
102
  @in_check = in_check?
87
103
  @turn += 1
88
104
  end
@@ -117,6 +133,8 @@ class ChessMate
117
133
  end
118
134
 
119
135
  def move(orig, dest, test = false, test_board = nil)
136
+ return false if @promotable
137
+
120
138
  orig_pos = NotationParser.parse_notation(orig)
121
139
  dest_pos = NotationParser.parse_notation(dest)
122
140
 
@@ -149,7 +167,7 @@ class ChessMate
149
167
  false
150
168
  end
151
169
  unless test
152
- in_check_after_move = in_check_after_move?(orig_pos, dest_pos)
170
+ in_check_after_move = in_check_after_move?(orig_pos, dest_pos)
153
171
  update(orig_pos, dest_pos) if valid_move && !test && !in_check_after_move
154
172
  end
155
173
 
@@ -159,10 +177,8 @@ class ChessMate
159
177
  def in_check_after_move?(orig, dest)
160
178
  test_board = @board.map(&:dup)
161
179
 
162
- orig_y = orig[0]
163
- orig_x = orig[1]
164
- dest_y = dest[0]
165
- dest_x = dest[1]
180
+ orig_y, orig_x = orig
181
+ dest_y, dest_x = dest
166
182
  piece = test_board[orig_y][orig_x]
167
183
  piece_type = test_board[orig_y][orig_x]
168
184
  opposite_color = piece_type[0] == 'W' ? :black : :white
@@ -236,8 +252,8 @@ class ChessMate
236
252
  def promote?(square)
237
253
  square_y = square[0]
238
254
  square_x = square[1]
239
- piece = @board[square_y][square_x][0]
240
- promote_column = piece.downcase == 'w' ? 0 : 7
255
+ piece_color = @board[square_y][square_x][0]
256
+ promote_column = piece_color.downcase == 'w' ? 1 : 6
241
257
  promote_column == square_y
242
258
  end
243
259
 
@@ -246,7 +262,7 @@ class ChessMate
246
262
  square_x = square[1]
247
263
 
248
264
  old_piece = @board[square_y][square_x]
249
- return nil if old_piece.nil? || !promote?(square)
265
+ return nil if old_piece.nil? || @promotable != [square_y, square_x]
250
266
 
251
267
  case piece.downcase
252
268
  when 'rook'
@@ -262,5 +278,11 @@ class ChessMate
262
278
  end
263
279
 
264
280
  @board[square_y][square_x] = old_piece[0] + piece_type
281
+ @promotable = nil
282
+
283
+ return if @ignore_logging
284
+
285
+ logger = Logger.new(nil, nil, nil, promotion_type: piece_type, history: @move_history)
286
+ @move_history[-1] += logger.log_promotion
265
287
  end
266
288
  end
@@ -12,7 +12,6 @@ DEFAULT = {
12
12
  %w[WR WN WB WQ WK WB WN WR]
13
13
  ],
14
14
  turn: 1,
15
- promotable: nil,
16
15
  en_passant: { white: nil, black: nil },
17
16
  in_check: { white: false, black: false },
18
17
  castling: {
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'helpers/notation_parser'
4
+ require 'chessmate'
5
+
6
+ class Logger
7
+ def initialize(orig, dest, board, en_passant: false, promotion_type: nil, history: nil)
8
+ @promotion_type = promotion_type
9
+ @history = history
10
+
11
+ return unless orig && dest && board
12
+
13
+ @orig = orig
14
+ @dest = dest
15
+ @board = board
16
+ @en_passant = en_passant
17
+ @orig_y, @orig_x = @orig
18
+ @dest_y, @dest_x = @dest
19
+ @piece = @board[@orig_y][@orig_x]
20
+ @piece_color, @piece_type = @piece.chars
21
+ end
22
+
23
+ def log_move
24
+ if @piece_type == 'K' && (@orig_x - @dest_x).abs > 1
25
+ return (@dest_x - @orig_x).positive? ? '0-0' : '0-0-0'
26
+ end
27
+
28
+ origin = encode_origin
29
+ capture = @board[@dest_y][@dest_x] || @en_passant ? 'x' : ''
30
+ destination = NotationParser.encode_notation(@dest)
31
+
32
+ origin + capture + destination + check_or_mate
33
+ end
34
+
35
+ def log_promotion
36
+ @promotion_type ? "=(#{@promotion_type})" : ''
37
+ end
38
+
39
+ private
40
+
41
+ def encode_origin
42
+ notation_required = [false, false]
43
+ game = ChessMate.new(board: @board)
44
+ encoded_dest = NotationParser.encode_notation(@dest)
45
+
46
+ @board.each_with_index do |row, y|
47
+ row.each_with_index do |col, x|
48
+ next unless @piece == col && [@orig_y, @orig_x] != [y, x]
49
+
50
+ encoded_orig = NotationParser.encode_notation([y, x])
51
+
52
+ if game.move(encoded_orig, encoded_dest, true)
53
+ notation_required[0] = true if @orig_y == y || (@orig_y != y && @orig_x != x)
54
+ notation_required[1] = true if @orig_x == x
55
+ end
56
+ end
57
+ end
58
+
59
+ if notation_required.nil? || notation_required.none?(true)
60
+ if @piece_type == 'P'
61
+ return NotationParser.encode_notation(@orig)[0] if @board[@dest_y][@dest_x] || @en_passant
62
+
63
+ return ''
64
+ end
65
+ return @piece_type
66
+ end
67
+ ambiguous_encoded = @piece_type
68
+ encoded_origin_chars = NotationParser.encode_notation(@orig).chars
69
+ notation_required.each_with_index do |value, i|
70
+ ambiguous_encoded += encoded_origin_chars[i] if value
71
+ end
72
+ ambiguous_encoded
73
+ end
74
+
75
+ def check_or_mate
76
+ game = ChessMate.new(board: @board, ignore_logging: true)
77
+ encoded_orig = NotationParser.encode_notation(@orig)
78
+ encoded_dest = NotationParser.encode_notation(@dest)
79
+ opposite_color_letter = @piece_color == 'W' ? 'B' : 'W'
80
+ opposite_color_string = opposite_color_letter == 'W' ? 'white' : 'black'
81
+
82
+ game.move(encoded_orig, encoded_dest)
83
+ checkmate = game.checkmate?(opposite_color_letter)
84
+ check = game.in_check?[opposite_color_string.to_sym]
85
+
86
+ return '#' if checkmate
87
+ return '+' if check
88
+
89
+ ''
90
+ end
91
+ end
File without changes
data/lib/pieces/bishop.rb CHANGED
File without changes
data/lib/pieces/king.rb CHANGED
@@ -16,10 +16,8 @@ class King < Piece
16
16
  end
17
17
 
18
18
  def self.valid_castling_move?(orig, dest, board, castling)
19
- orig_y = orig[0]
20
- orig_x = orig[1]
21
- dest_y = dest[0]
22
- dest_x = dest[1]
19
+ orig_y, orig_x = orig
20
+ dest_y, dest_x = dest
23
21
 
24
22
  return false if orig_y != dest_y || (orig_x - dest_x).abs != 2
25
23
 
data/lib/pieces/knight.rb CHANGED
File without changes
data/lib/pieces/pawn.rb CHANGED
@@ -6,10 +6,8 @@ class Pawn < Piece
6
6
  def self.move_is_valid?(orig, dest, board, en_passant)
7
7
  return true if en_passant(orig, dest, board, en_passant)
8
8
 
9
- orig_y = orig[0]
10
- orig_x = orig[1]
11
- dest_y = dest[0]
12
- dest_x = dest[1]
9
+ orig_y, orig_x = orig
10
+ dest_y, dest_x = dest
13
11
  piece_type = board[orig_y][orig_x]
14
12
  piece_color = piece_type[0].downcase
15
13
 
@@ -36,10 +34,8 @@ class Pawn < Piece
36
34
  end
37
35
 
38
36
  def self.en_passant(orig, dest, board, en_passant)
39
- orig_y = orig[0]
40
- orig_x = orig[1]
41
- dest_y = dest[0]
42
- dest_x = dest[1]
37
+ orig_y, orig_x = orig
38
+ dest_y, dest_x = dest
43
39
  piece_type = board[orig_y][orig_x]
44
40
  opposite_color = piece_type[0].downcase == 'w' ? :black : :white
45
41
  direction = opposite_color == :white ? -1 : 1
data/lib/pieces/piece.rb CHANGED
@@ -2,10 +2,8 @@
2
2
 
3
3
  class Piece
4
4
  def self.obstructed?(orig, dest, board)
5
- orig_y = orig[0]
6
- orig_x = orig[1]
7
- dest_y = dest[0]
8
- dest_x = dest[1]
5
+ orig_y, orig_x = orig
6
+ dest_y, dest_x = dest
9
7
 
10
8
  if orig_y == dest_y
11
9
 
@@ -14,7 +12,8 @@ class Piece
14
12
  test_pos = orig_x - (x * direction) - direction
15
13
  return true unless board[orig_y][test_pos].nil?
16
14
  end
17
- return false
15
+
16
+ false
18
17
 
19
18
  elsif orig_x == dest_x
20
19
 
@@ -24,7 +23,7 @@ class Piece
24
23
  return true unless board[test_pos][orig_x].nil?
25
24
  end
26
25
 
27
- return false
26
+ false
28
27
 
29
28
  elsif (orig_y - dest_y).abs == (orig_x - dest_x).abs
30
29
 
@@ -37,18 +36,14 @@ class Piece
37
36
  return true unless board[test_y_pos][test_x_pos].nil?
38
37
  end
39
38
 
40
- return false
39
+ false
41
40
 
42
- else
43
- return nil
44
41
  end
45
42
  end
46
43
 
47
44
  def self.capturable?(orig, dest, board)
48
- orig_y = orig[0]
49
- orig_x = orig[1]
50
- dest_y = dest[0]
51
- dest_x = dest[1]
45
+ orig_y, orig_x = orig
46
+ dest_y, dest_x = dest
52
47
  orig_piece = board[orig_y][orig_x]
53
48
  dest_piece = board[dest_y][dest_x]
54
49
 
data/lib/pieces/queen.rb CHANGED
File without changes
data/lib/pieces/rook.rb CHANGED
File without changes
data/pre-commit.sh ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'english'
4
+ require 'rubocop'
5
+
6
+ ADDED_OR_MODIFIED = /A|AM|^M/.freeze
7
+
8
+ changed_files = `git status --porcelain`.split(/\n/).
9
+ select { |file_name_with_status|
10
+ file_name_with_status =~ ADDED_OR_MODIFIED
11
+ }.
12
+ map { |file_name_with_status|
13
+ file_name_with_status.split(' ')[1]
14
+ }.
15
+ select { |file_name|
16
+ File.extname(file_name) == '.rb'
17
+ }.join(' ')
18
+
19
+ success = system(%(rubocop #{changed_files}))
20
+
21
+ STDIN.reopen('/dev/tty')
22
+
23
+ if success == false
24
+ puts "Would you like to continue press 'any key' or 'n/N' to halt?"
25
+ exit(1) if %w(N n).include?(gets.chomp)
26
+ end
@@ -59,7 +59,7 @@ describe ChessMate do
59
59
  chess = ChessMate.new
60
60
  expect(chess.board).to eql(DEFAULT[:board])
61
61
  expect(chess.turn).to eql(DEFAULT[:turn])
62
- expect(chess.promotable).to eql(DEFAULT[:promotable])
62
+ expect(chess.promotable).to eql(nil)
63
63
  expect(chess.en_passant).to eql(DEFAULT[:en_passant])
64
64
  expect(chess.castling).to eql(DEFAULT[:castling])
65
65
  expect(chess.in_check).to eql(DEFAULT[:in_check])
@@ -356,6 +356,21 @@ describe ChessMate do
356
356
  )
357
357
  end
358
358
 
359
+ it 'should return false if there is a promotable pawn on the board' do
360
+ board = [
361
+ ['WP', nil, nil, nil, nil, nil, nil, nil],
362
+ [nil, nil, nil, nil, nil, nil, nil, nil],
363
+ [nil, nil, nil, nil, nil, nil, nil, nil],
364
+ [nil, nil, nil, nil, nil, nil, nil, nil],
365
+ [nil, nil, nil, nil, nil, nil, nil, nil],
366
+ [nil, nil, nil, nil, nil, nil, nil, nil],
367
+ ['WP', nil, nil, nil, nil, nil, nil, nil],
368
+ [nil, nil, nil, nil, nil, nil, nil, nil]
369
+ ]
370
+ chess = ChessMate.new(board: board, promotable: [0, 0])
371
+ expect(chess.move('a2', 'a3')).to eql(false)
372
+ end
373
+
359
374
  context 'for pawns' do
360
375
  it 'should update the board if pawn move is valid' do
361
376
  @chess.move('c2', 'c3')
@@ -1357,27 +1372,27 @@ describe ChessMate do
1357
1372
  end
1358
1373
 
1359
1374
  describe 'promote? method' do
1360
- it 'should return true if white piece is on last rank' do
1375
+ it 'should return true if white piece is moving to last rank' do
1361
1376
  board = Array.new(8) { Array.new(8, nil) }
1362
- board[0][0] = 'WP'
1377
+ board[1][0] = 'WP'
1363
1378
  chess = ChessMate.new(board: board)
1364
- expect(chess.promote?([0, 0])).to eql(true)
1379
+ expect(chess.promote?([1, 0])).to eql(true)
1365
1380
  end
1366
1381
 
1367
- it 'should return true if black piece is on last rank' do
1382
+ it 'should return true if black piece is moving to last rank' do
1368
1383
  board = Array.new(8) { Array.new(8, nil) }
1369
- board[7][0] = 'BP'
1384
+ board[6][0] = 'BP'
1370
1385
  chess = ChessMate.new(board: board)
1371
- expect(chess.promote?([7, 0])).to eql(true)
1386
+ expect(chess.promote?([6, 0])).to eql(true)
1372
1387
  end
1373
1388
 
1374
1389
  it 'should return false otherwise' do
1375
1390
  board = Array.new(8) { Array.new(8, nil) }
1376
- board[1][0] = 'WP'
1377
- board[6][0] = 'BP'
1391
+ board[2][0] = 'WP'
1392
+ board[5][0] = 'BP'
1378
1393
  chess = ChessMate.new(board: board)
1379
- expect(chess.promote?([1, 0])).to eql(false)
1380
- expect(chess.promote?([6, 0])).to eql(false)
1394
+ expect(chess.promote?([2, 0])).to eql(false)
1395
+ expect(chess.promote?([5, 0])).to eql(false)
1381
1396
  end
1382
1397
  end
1383
1398
 
@@ -1399,8 +1414,9 @@ describe ChessMate do
1399
1414
  context 'should promote to' do
1400
1415
  before :each do
1401
1416
  board = Array.new(8) { Array.new(8, nil) }
1402
- board[0][0] = 'WP'
1417
+ board[1][0] = 'WP'
1403
1418
  @chess = ChessMate.new(board: board)
1419
+ @chess.move('a7', 'a8')
1404
1420
  end
1405
1421
 
1406
1422
  it 'queen' do
@@ -1423,6 +1439,25 @@ describe ChessMate do
1423
1439
  expect(@chess.board[0][0]).to eql('WR')
1424
1440
  end
1425
1441
  end
1442
+
1443
+ context 'logging' do
1444
+ it 'should correctly handle logging promotion' do
1445
+ board = [
1446
+ [nil, nil, nil, nil, nil, nil, nil, nil],
1447
+ ['WP', nil, nil, nil, nil, nil, nil, nil],
1448
+ [nil, nil, nil, nil, nil, nil, nil, nil],
1449
+ [nil, nil, nil, nil, nil, nil, nil, nil],
1450
+ [nil, nil, nil, nil, nil, nil, nil, nil],
1451
+ [nil, nil, nil, nil, nil, nil, nil, nil],
1452
+ [nil, nil, nil, nil, nil, nil, nil, nil],
1453
+ [nil, nil, nil, nil, nil, nil, nil, nil]
1454
+ ]
1455
+ chess = ChessMate.new(board: board)
1456
+ chess.move('a7', 'a8')
1457
+ chess.promote!([0, 0], 'queen')
1458
+ expect(chess.move_history[-1]).to eql('a8=(Q)')
1459
+ end
1460
+ end
1426
1461
  end
1427
1462
 
1428
1463
  describe 'en_passant method' do
@@ -0,0 +1,182 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require_relative '../lib/helpers/logger'
5
+ require 'chessmate'
6
+
7
+ describe 'Logger' do
8
+ before :each do
9
+ @normal_moves_board = [
10
+ [nil, nil, nil, nil, nil, nil, nil, nil],
11
+ [nil, nil, nil, nil, nil, nil, nil, nil],
12
+ [nil, nil, nil, nil, nil, nil, nil, nil],
13
+ [nil, nil, nil, nil, nil, nil, nil, nil],
14
+ [nil, nil, nil, nil, nil, nil, nil, nil],
15
+ [nil, nil, nil, nil, nil, nil, nil, nil],
16
+ ['WP', 'WN', 'WB', 'WQ', 'WK', nil, nil, nil],
17
+ [nil, nil, nil, nil, nil, nil, nil, nil]
18
+ ]
19
+
20
+ @capture_moves_board = [
21
+ [nil, nil, nil, nil, nil, nil, nil, nil],
22
+ [nil, nil, nil, nil, nil, nil, nil, nil],
23
+ [nil, nil, nil, nil, nil, nil, nil, nil],
24
+ [nil, nil, nil, nil, nil, nil, 'WP', 'BP'],
25
+ ['BP', nil, nil, nil, nil, nil, nil, nil],
26
+ [nil, 'BP', nil, 'BP', nil, nil, nil, nil],
27
+ ['WP', 'WN', 'WB', 'WQ', 'WK', nil, nil, nil],
28
+ [nil, nil, nil, nil, nil, nil, nil, nil]
29
+ ]
30
+
31
+ @ambiguous_moves_board = [
32
+ [nil, nil, nil, 'BR', nil, nil, nil, 'BR'],
33
+ ['BB', nil, nil, nil, nil, nil, nil, nil],
34
+ [nil, nil, nil, 'BB', nil, nil, nil, nil],
35
+ ['WR', nil, nil, nil, nil, nil, nil, nil],
36
+ [nil, nil, nil, nil, 'WQ', nil, nil, 'WQ'],
37
+ [nil, nil, nil, nil, nil, nil, nil, nil],
38
+ [nil, nil, nil, nil, nil, nil, nil, nil],
39
+ ['WR', nil, nil, nil, nil, nil, nil, 'WQ']
40
+ ]
41
+
42
+ @specialty_moves_board = [
43
+ [nil, nil, nil, nil, nil, nil, nil, nil],
44
+ ['WP', nil, nil, nil, nil, nil, nil, nil],
45
+ [nil, nil, nil, nil, nil, nil, nil, nil],
46
+ [nil, nil, nil, nil, nil, nil, nil, nil],
47
+ [nil, nil, nil, nil, nil, nil, nil, nil],
48
+ [nil, nil, nil, nil, nil, nil, nil, nil],
49
+ [nil, nil, nil, nil, nil, nil, nil, nil],
50
+ ['WR', nil, nil, nil, 'WK', nil, nil, 'WR']
51
+ ]
52
+
53
+ @check_board = [
54
+ [nil, nil, nil, 'BQ', nil, nil, nil, nil],
55
+ [nil, nil, nil, nil, nil, nil, nil, nil],
56
+ [nil, nil, nil, nil, nil, nil, nil, nil],
57
+ [nil, nil, nil, nil, nil, nil, nil, nil],
58
+ [nil, nil, nil, nil, nil, nil, nil, nil],
59
+ ['BN', nil, nil, nil, nil, nil, nil, nil],
60
+ [nil, nil, nil, 'WP', nil, 'WP', nil, nil],
61
+ [nil, nil, nil, 'WR', 'WK', 'WR', nil, nil]
62
+ ]
63
+ end
64
+
65
+ context 'pawn moves' do
66
+ it 'should log normal pawn moves' do
67
+ logger = Logger.new([6, 0], [5, 0], @normal_moves_board)
68
+ expect(logger.log_move).to eql('a3')
69
+ end
70
+
71
+ it 'should log capturing moves' do
72
+ logger = Logger.new([6, 0], [5, 1], @capture_moves_board)
73
+ expect(logger.log_move).to eql('axb3')
74
+ end
75
+
76
+ it 'should log en passant moves' do
77
+ logger = Logger.new([3, 6], [2, 7], @capture_moves_board, en_passant: true)
78
+ expect(logger.log_move).to eql('gxh6')
79
+ end
80
+ end
81
+
82
+ context 'knight moves' do
83
+ it 'should log normal knight moves' do
84
+ logger = Logger.new([6, 1], [4, 0], @normal_moves_board)
85
+ expect(logger.log_move).to eql('Na4')
86
+ end
87
+
88
+ it 'should log capturing moves' do
89
+ logger = Logger.new([6, 1], [4, 0], @capture_moves_board)
90
+ expect(logger.log_move).to eql('Nxa4')
91
+ end
92
+ end
93
+
94
+ context 'bishop moves' do
95
+ it 'should log normal bishop moves' do
96
+ logger = Logger.new([6, 2], [5, 1], @normal_moves_board)
97
+ expect(logger.log_move).to eql('Bb3')
98
+ end
99
+
100
+ it 'should log capturing moves' do
101
+ logger = Logger.new([6, 2], [5, 1], @capture_moves_board)
102
+ expect(logger.log_move).to eql('Bxb3')
103
+ end
104
+ end
105
+
106
+ context 'queen moves' do
107
+ it 'should log normal queen moves' do
108
+ logger = Logger.new([6, 3], [5, 3], @normal_moves_board)
109
+ expect(logger.log_move).to eql('Qd3')
110
+ end
111
+
112
+ it 'should log capturing moves' do
113
+ logger = Logger.new([6, 3], [5, 3], @capture_moves_board)
114
+ expect(logger.log_move).to eql('Qxd3')
115
+ end
116
+ end
117
+
118
+ context 'king moves' do
119
+ it 'should log normal king moves' do
120
+ logger = Logger.new([6, 4], [5, 4], @normal_moves_board)
121
+ expect(logger.log_move).to eql('Ke3')
122
+ end
123
+
124
+ it 'should log capturing moves' do
125
+ logger = Logger.new([6, 4], [5, 3], @capture_moves_board)
126
+ expect(logger.log_move).to eql('Kxd3')
127
+ end
128
+ end
129
+
130
+ context 'ambiguous moves' do
131
+ it 'should add file info to log moves with pieces in same file' do
132
+ logger = Logger.new([7, 0], [5, 0], @ambiguous_moves_board)
133
+ expect(logger.log_move).to eql('R1a3')
134
+ end
135
+
136
+ it 'should add rank info to log moves with pieces in same rank' do
137
+ logger = Logger.new([0, 3], [0, 5], @ambiguous_moves_board)
138
+ expect(logger.log_move).to eql('Rdf8')
139
+ end
140
+
141
+ it 'should add file and rank info to log moves with multiple pieces in same file and rank' do
142
+ logger = Logger.new([4, 7], [7, 4], @ambiguous_moves_board)
143
+ expect(logger.log_move).to eql('Qh4e1')
144
+ end
145
+
146
+ it 'should handle knights/bishops/queens with ability to move to dest square' do
147
+ logger = Logger.new([2, 3], [0, 1], @ambiguous_moves_board)
148
+ expect(logger.log_move).to eql('Bdb8')
149
+ end
150
+ end
151
+
152
+ context 'specialty moves' do
153
+ it 'should log kingside castles' do
154
+ logger = Logger.new([7, 4], [7, 6], @specialty_moves_board)
155
+ expect(logger.log_move).to eql('0-0')
156
+ end
157
+
158
+ it 'should log queenside castles' do
159
+ logger = Logger.new([7, 4], [7, 2], @specialty_moves_board)
160
+ expect(logger.log_move).to eql('0-0-0')
161
+ end
162
+
163
+ it 'should log pawn promotion' do
164
+ %w[R N B Q].each do |piece|
165
+ logger = Logger.new(nil, nil, nil, promotion_type: piece)
166
+ expect(logger.log_promotion).to eql("=(#{piece})")
167
+ end
168
+ end
169
+ end
170
+
171
+ context 'game status indications' do
172
+ it 'should log check' do
173
+ logger = Logger.new([5, 0], [6, 2], @check_board)
174
+ expect(logger.log_move).to eql('Nc2+')
175
+ end
176
+
177
+ it 'should log checkmate' do
178
+ logger = Logger.new([0, 3], [0, 4], @check_board)
179
+ expect(logger.log_move).to eql('Qe8#')
180
+ end
181
+ end
182
+ end
File without changes
data/spec/piece_spec.rb CHANGED
File without changes
data/spec/spec_helper.rb CHANGED
File without changes
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chessmate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler Porter
@@ -42,16 +42,16 @@ dependencies:
42
42
  name: rubocop
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - '='
46
46
  - !ruby/object:Gem::Version
47
- version: 0.49.0
47
+ version: 0.75.1
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - '='
53
53
  - !ruby/object:Gem::Version
54
- version: 0.49.0
54
+ version: 0.75.1
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: deep_dup
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -72,10 +72,14 @@ executables: []
72
72
  extensions: []
73
73
  extra_rdoc_files: []
74
74
  files:
75
+ - CODE_OF_CONDUCT.md
75
76
  - Gemfile
76
77
  - Gemfile.lock
78
+ - LICENSE
79
+ - README.md
77
80
  - lib/chessmate.rb
78
81
  - lib/helpers/default.rb
82
+ - lib/helpers/logger.rb
79
83
  - lib/helpers/notation_parser.rb
80
84
  - lib/pieces/bishop.rb
81
85
  - lib/pieces/king.rb
@@ -84,7 +88,9 @@ files:
84
88
  - lib/pieces/piece.rb
85
89
  - lib/pieces/queen.rb
86
90
  - lib/pieces/rook.rb
91
+ - pre-commit.sh
87
92
  - spec/chessmate_spec.rb
93
+ - spec/logger_spec.rb
88
94
  - spec/notation_parser_spec.rb
89
95
  - spec/piece_spec.rb
90
96
  - spec/spec_helper.rb
@@ -108,8 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
114
  - !ruby/object:Gem::Version
109
115
  version: '0'
110
116
  requirements: []
111
- rubyforge_project:
112
- rubygems_version: 2.7.6.2
117
+ rubygems_version: 3.0.3
113
118
  signing_key:
114
119
  specification_version: 4
115
120
  summary: Chess for Rails