chess_rb 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fa3d318cecdbf7ca433d3dbd4a05f865d6acd390
4
- data.tar.gz: 0674fc6547bed63a77ad029af20815221f1731c7
3
+ metadata.gz: 1584e23695704e645f8088827c43f71e60c9ce07
4
+ data.tar.gz: 9ebf999832dee1ff44b892126e558bb1675369d6
5
5
  SHA512:
6
- metadata.gz: 0fbc9bf5696a3b468989ee3b00b151f9e36ca6a84ab4a6e3249c3313e6e1ff2be8c25fdf9a5b63d9c4ed05f36600ba897fba4c7aea9daa25afc7d837068ae1eb
7
- data.tar.gz: 7dc55e6ddb44420f5ff44c0abe1c5aced4243015ecfa195a5888bb3e8a232a4a1b29cb3eb636bf7ca7294493433b5b302857fd172f64cb3c06d10bbd3d927753
6
+ metadata.gz: cd5db4e7c13262c0076171467f2de6b16862fe844e1ab5f69b76a0776bc7b5562b71a8f2d059c2780f06f35aa7e0487f10962aa5400ae3201eeaff1937d7e49c
7
+ data.tar.gz: 9366f64fe308afc5e1d655cbf24a580b9702c8ff34e63cc2e19239486912f0916a42714079ef83b48ff6ad191ae52672c389993b834771573f93d967f23ed610
data/lib/chess_rb/move.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  class ChessRB::Move
2
- FILE_TO_NUM = [nil, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
2
+ FILE_CONV = [nil, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
3
3
 
4
4
  attr_reader :to, :from, :promotion, :square, :san, :board
5
5
 
@@ -10,7 +10,7 @@ class ChessRB::Move
10
10
  @san = move
11
11
  end
12
12
 
13
- if pos.is_a? String
13
+ if pos.is_a?(String)
14
14
  @board = ChessRB::Position.new pos
15
15
  else
16
16
  @board = pos
@@ -38,20 +38,19 @@ class ChessRB::Move
38
38
 
39
39
  # Returns true if move represents a queen-side castle, false otherwise.
40
40
  def queen_castle?
41
-
42
- return valid? && ChessRB::Piece.new(@board.piece_on(@to)).type == 'k' &&
43
- (from_file - to_file).abs == 3
41
+ return valid? && @board.piece_on(@from).type == 'K' &&
42
+ from_file - to_file == 2
44
43
  end
45
44
 
46
45
  # Returns true if move represents a king-side castle, false otherwise.
47
46
  def king_castle?
48
- return valid? && ChessRB::Piece.new(@board.piece_on(@to)).type == 'k' &&
49
- (from_file - to_file).abs == 2
47
+ return valid? && @board.piece_on(@from).type == 'K' &&
48
+ to_file - from_file == 2
50
49
  end
51
50
 
52
51
  # Returns true if move results in a capture, false otherwise.
53
52
  def capture?
54
- return valid? && @board.piece_on(@to) != 0
53
+ return valid? && @board.piece_on(@to).desc != 'E'
55
54
  end
56
55
 
57
56
  # Returns the rank of a given square
@@ -61,26 +60,26 @@ class ChessRB::Move
61
60
 
62
61
  # Returns rank of this move's @to square
63
62
  def to_rank
64
- return self.rank(@to)
63
+ return self.class.rank(@to)
65
64
  end
66
65
 
67
66
  # Returns rank of this move's @from square
68
67
  def from_rank
69
- return self.rank(@from)
68
+ return self.class.rank(@from)
70
69
  end
71
70
 
72
71
  # Returns the file of a given square as a number (e.g., a = 1, b = 2, ...)
73
72
  def self.file(square)
74
- return FILE_TO_NUM.index(square[0])
73
+ return FILE_CONV.index(square[0])
75
74
  end
76
75
 
77
76
  # Returns file of this move's @to square
78
77
  def to_file
79
- return self.file(@to)
78
+ return self.class.file(@to)
80
79
  end
81
80
 
82
81
  # Returns file of this move's @from square
83
82
  def from_file
84
- return self.file(@from)
83
+ return self.class.file(@from)
85
84
  end
86
85
  end
@@ -1,7 +1,7 @@
1
1
  class ChessRB::Notation
2
2
  def self.square_to_algebraic(move)
3
3
  board = move.board
4
- piece = ChessRB::Piece.new(board.piece_on(move.from))
4
+ piece = board.piece_on(move.from)
5
5
  san = ""
6
6
 
7
7
  raise ArgumentError.new "Invalid move and/or position" if !move.valid? ||
@@ -9,16 +9,17 @@ class ChessRB::Notation
9
9
 
10
10
 
11
11
  if move.queen_castle?
12
- san = "O-O-O"
12
+ return "O-O-O"
13
13
  elsif move.king_castle?
14
- san = "O-O"
14
+ return "O-O"
15
15
  else
16
- if piece.type == 'p'
16
+ if piece.type == 'P'
17
17
  if move.capture?
18
- san = file_to_char(move.from_file)
18
+ san = move.from[0]
19
19
  end
20
20
  else
21
21
  san += piece.type.upcase
22
+
22
23
  # test ambiguities
23
24
  # file
24
25
  # rank
@@ -34,13 +35,12 @@ class ChessRB::Notation
34
35
 
35
36
  san += "=#{move.promotion}" if move.promotion
36
37
 
37
- =begin
38
+ undo_info = board.piece_on(move.to)
38
39
  board.make_move(move)
39
40
  if (board.check?)
40
41
  san += board.mate? ? "#" : "+"
41
42
  end
42
- board.undo_move(move)
43
- =end
43
+ board.undo_move(move, undo_info)
44
44
 
45
45
  return san
46
46
  end
@@ -1,23 +1,25 @@
1
1
  class ChessRB::Piece
2
+ E = 0
2
3
  WP = 1; WN = 2; WLB = 3; WDB = 4; WR = 5; WQ = 6; WK = 7
3
4
  BP = 11; BN = 12; BLB = 13; BDB = 14; BR = 15; BQ = 16; BK = 17
4
5
 
5
- attr_accessor :code
6
+ attr_reader :code, :desc
6
7
 
7
8
  def initialize(code)
8
9
  @code = code
10
+ @desc = ChessRB::Piece.constants.select{
11
+ |v| ChessRB::Piece.const_get(v) == code }[0].to_s
9
12
  end
10
13
 
11
14
  def type
12
- self.to_desc[-1, 1]
15
+ self.desc[-1, 1]
13
16
  end
14
17
 
15
- def to_desc
16
- return ChessRB::Piece.constants.select{
17
- |v| ChessRB::Piece.const_get(v) == @code }[0].to_s
18
+ def color
19
+ self.desc[0]
18
20
  end
19
21
 
20
- def to_s(p, d)
22
+ def to_s(d)
21
23
  case @code
22
24
  when WP
23
25
  d ? "♟" : "♙"
@@ -1,3 +1,5 @@
1
+ require 'matrix'
2
+
1
3
  class ChessRB::Position
2
4
  attr_accessor :fen
3
5
 
@@ -5,40 +7,172 @@ class ChessRB::Position
5
7
  @fen = fen
6
8
  @fen_components = fen.split(' ')
7
9
  @board = fen_to_board(fen)
10
+ @valid = valid?
11
+ end
12
+
13
+ def self.valid_square?(s)
14
+ i = s[0]; j = s[1]
15
+ return i.is_a?(Integer) && j.is_a?(Integer) &&
16
+ i >= 0 && i < 8 && j >= 0 && j < 8
8
17
  end
9
18
 
10
19
  # TODO
11
20
  def valid?
21
+ return @valid if !@valid.nil?
12
22
  return true
13
23
  end
14
24
 
25
+ # Returns 'w' or 'b' if it is white or black's move, respectively
26
+ def turn
27
+ return @fen_components[1]
28
+ end
29
+
15
30
  # Returns the piece code on the given square
16
31
  def piece_on(square)
17
- file = ChessRB::Move.file(square)
18
- rank = ChessRB::Move.rank(square)
19
- return @board[8 - rank][file - 1]
32
+ if square.is_a?(String)
33
+ file = ChessRB::Move.file(square)
34
+ rank = ChessRB::Move.rank(square)
35
+ return ChessRB::Piece.new(@board[8 - rank][file - 1])
36
+ else
37
+ return ChessRB::Piece.new(@board[square[0]][square[1]])
38
+ end
20
39
  end
21
40
 
22
- # TODO
41
+ def squares_with(piece)
42
+ squares = []
43
+ code = ChessRB::Piece.const_get(piece)
44
+
45
+ @board.each_with_index do |r , i|
46
+ r.each_with_index do |p, j|
47
+ if p == code
48
+ squares << [i, j]
49
+ end
50
+ end
51
+ end
52
+
53
+ return squares
54
+ end
55
+
56
+ # Returns if the current position is check, false otherwise
23
57
  def check?
58
+ raise RuntimeError "Invalid Position" if !valid?
59
+
60
+ t = turn().upcase
61
+ not_t = t == 'W' ? 'B' : 'W'
62
+ king_vector = Vector[*(squares_with(t + 'K')[0])]
63
+
64
+ # check pawn squares
65
+ pawn_vectors = t == 'W' ? [[-1, -1], [1, -1]] : [[-1, 1], [1, 1]]
66
+ pawn_vectors.each do |s|
67
+ s = (king_vector + Vector[*s]).to_a
68
+ next if !self.class.valid_square?(s)
69
+ return true if piece_on(s).desc == (not_t + 'P')
70
+ end
71
+
72
+ # check knight squares
73
+ knight_vectors = [[1,2], [-1,2], [1,-2], [-1,-2], [2,1], [-2,1], [2,-1],
74
+ [-2,-1]]
75
+ knight_vectors.each do |s|
76
+ s = (king_vector + Vector[*s]).to_a
77
+ next if !self.class.valid_square?(s)
78
+ return true if piece_on(s).desc == (not_t + 'N')
79
+ end
80
+
81
+ # check bishop/queen squares
82
+ diagonal_vectors = [[1, 1], [-1, 1], [1, -1], [-1, -1]]
83
+ diagonal_vectors.each do |v|
84
+ dist = 1
85
+ v = Vector[*v]
86
+ while dist < 8
87
+ current_vector = v * dist
88
+ current_square = (king_vector + current_vector).to_a
89
+
90
+ break if !self.class.valid_square?(current_square)
91
+
92
+ p = piece_on(current_square).desc
93
+ return true if p == (not_t + 'B') || p == (not_t + 'Q')
94
+ break if p != 'E'
95
+
96
+ dist += 1
97
+ end
98
+ end
24
99
 
100
+ # check rook/queen squares
101
+ straight_vectors = [[1, 0], [0, 1], [-1, 0], [0, -1]]
102
+ straight_vectors.each do |v|
103
+ dist = 1
104
+ v = Vector[*v]
105
+ while dist < 8
106
+ current_vector = v * dist
107
+ current_square = (king_vector + current_vector).to_a
108
+
109
+ break if !self.class.valid_square?(current_square)
110
+
111
+ p = piece_on(current_square).desc
112
+ return true if p == (not_t + 'B') || p == (not_t + 'Q')
113
+ break if p != 'E'
114
+
115
+ dist += 1
116
+ end
117
+ end
118
+
119
+ return false
25
120
  end
26
121
 
27
- # TODO
122
+ # Returns if the current position is checkmate, false otherwise
28
123
  def mate?
124
+ return false if !check?
125
+
126
+ t = turn().upcase
127
+ king_vector = Vector[*(squares_with(t + 'K')[0])]
128
+
129
+ around_vectors = [[1, 0], [0, 1], [-1, 0], [0, -1], [1, 1], [-1, 1],
130
+ [1, -1], [-1, -1]]
131
+ around_vectors.each do |v|
132
+ v = Vector[*v]
133
+ current_square = (king_vector + v).to_a
134
+ if !self.class.valid_square?(current_square) ||
135
+ piece_on(current_square).color == t
136
+
137
+ next
138
+ else
139
+ undo_code = piece_on(current_square).code
140
+ move(king_vector.to_a, current_square)
141
+ if !check?
142
+ undo(king_vector.to_a, current_square, undo_code)
143
+ return false
144
+ end
145
+ undo(king_vector.to_a, current_square, undo_code)
146
+ end
147
+ end
148
+
149
+ return true
150
+ end
151
+
152
+ def make_move(move)
153
+ move([8 - move.from_rank, move.from_file - 1],
154
+ [8 - move.to_rank, move.to_file - 1])
155
+
156
+ @fen_components[1] = @fen_components[1] == 'w' ? 'b' : 'w'
157
+ end
158
+
159
+ def undo_move(move, piece)
160
+ undo([8 - move.from_rank, move.from_file - 1],
161
+ [8 - move.to_rank, move.to_file - 1], piece.code)
29
162
 
163
+ @fen_components[1] = @fen_components[1] == 'w' ? 'b' : 'w'
30
164
  end
31
165
 
32
166
  def to_s(dark_background = true)
33
167
  str = ""
34
- board.each_with_index do |r, i|
168
+ @board.each_with_index do |r, i|
35
169
  str += (8 - i).to_s + "║"
36
170
  r.each do |s|
37
171
  str += " "
38
172
  if s == 0
39
173
  str += "…"
40
174
  else
41
- str += ChessRB::Piece.code_to_s(s, dark_background)
175
+ str += ChessRB::Piece.new(s).to_s(dark_background)
42
176
  end
43
177
  end
44
178
  str += "\n"
@@ -69,4 +203,14 @@ class ChessRB::Position
69
203
  end
70
204
  return board
71
205
  end
206
+
207
+ def move(from, to)
208
+ @board[to[0]][to[1]] = @board[from[0]][from[1]]
209
+ @board[from[0]][from[1]] = 0
210
+ end
211
+
212
+ def undo(from, to, code)
213
+ @board[from[0]][from[1]] = @board[to[0]][to[1]]
214
+ @board[to[0]][to[1]] = code
215
+ end
72
216
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chess_rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sabar Dasgupta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-16 00:00:00.000000000 Z
11
+ date: 2014-06-20 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Chess functions written in Ruby
14
14
  email: sabar.dasgupta@gmail.com