Module: Move

Included in:
Board
Defined in:
lib/move.rb

Instance Method Summary collapse

Instance Method Details

#castle(index) ⇒ Object

Castle: king cannot move into check, or through check



287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/move.rb', line 287

def castle(index)
  valid = []
  dangerous_tiles = attack_vectors

  # King may never have moved
  return valid unless [5, 61].include?(index) && @@pieces[index][:moved] == false

  # Ensure empty space between a King and a Rook
  if unoccupied?(index - 1) && unoccupied?(index - 2) && unoccupied?(index - 3) && @@pieces[index - 4][:moved] == false
    # Ensure king does not move through check or into check, and then add its castle position
    valid << index - 2 if !dangerous_tiles.include?(index - 1) && !dangerous_tiles.include?(index - 2)
  end

  if unoccupied?(index + 1) && unoccupied?(index + 2) && @@pieces[index + 3][:moved] == false
    valid << index + 2 if !dangerous_tiles.include?(index + 1) && !dangerous_tiles.include?(index + 2)
  end

  valid
end

#constants(piece_mapping, color, piece) ⇒ Object



5
6
7
8
9
10
# File 'lib/move.rb', line 5

def constants(piece_mapping, color, piece)
  @@pieces = piece_mapping
  @@color  = color
  @@type   = piece
  @@enemy_color = ([:black, :red] - [color]).first
end

#get_col_from_index(index) ⇒ Object

Obtain chess board column number (1 - 8) from an index (1 - 64)



334
335
336
# File 'lib/move.rb', line 334

def get_col_from_index(index)
  index % 8 == 0 ? 8 : index % 8
end

#get_row_from_index(index) ⇒ Object

Obtain chess board row number (1 + 8) from an index (1 - 64)



329
330
331
# File 'lib/move.rb', line 329

def get_row_from_index(index)
  (index - 1)/8 + 1
end

#move_diagonal(index, limit = 8) ⇒ Object

Diagonal movements. Will return all valid diagonal movies for a piece at index By default, it will extend the piece movement diagonally until it hits a board edge or until it hits a piece. This can be changed by passing the limit argument For example, the king can only move diagonally 1 position, so it would pass limit=1



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/move.rb', line 219

def move_diagonal(index, limit = 8)

  row = get_row_from_index(index)
  col = get_col_from_index(index)
  left, right = [col-1, limit].min, [8-col, limit].min
  up, down = [row-1, limit].min, [8-row, limit].min
  valid = []

  # up and to the right
  ([up, right, limit].min).times do |i|
    next_pos = index - (i+1)*7
    # Valid move if position is unoccupied
    if unoccupied?(next_pos)
      valid << next_pos
    else
      # Valid move is piece is an enemy, but then no subsequent tiles are attackable
      # if the piece is not an enemy, it's not added as a valid move, and no subsequent tiles are attackable
      # This function doesn't filter out the king from a valid enemy, but the Board class will drop King indexes
      if piece_color(next_pos) == @@enemy_color
        valid << next_pos
      end
      break
    end
  end

  # up and to the left
  ([up, left, limit].min).times do |i|
    next_pos = index - (i+1)*9
    if unoccupied?(next_pos)
      valid << next_pos
    else
      if piece_color(next_pos) == @@enemy_color
        valid << next_pos
      end
      break
    end
  end

  # down and to the right
  ([down, right, limit].min).times do |i|
    next_pos = index + (i+1)*9
    if unoccupied?(next_pos)
      valid << next_pos
    else
      if piece_color(next_pos) == @@enemy_color
        valid << next_pos
      end
      break
    end
  end

  # down and to the left
  ([down, left, limit].min).times do |i|
    next_pos = index + (i+1)*7
    if unoccupied?(next_pos)
      valid << next_pos
    else
      if piece_color(next_pos) == @@enemy_color
        valid << next_pos
      end
      break
    end
  end

  valid
end

#move_knight(p1) ⇒ Object

Returns valid positions a knight at index p1 can move to



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/move.rb', line 96

def move_knight(p1)
  row = get_row_from_index(p1)
  col = get_col_from_index(p1)

  valid = []
  valid_moves_no_friendly_fire = []

  # Valid knight moves based on its board position
  if row < 7 && col < 8
    valid << (p1 + 17)
  end
  if row < 7 && col > 1
    valid << (p1 + 15)
  end
  if row < 8 && col < 7
    valid << (p1 + 10)
  end
  if row < 8 && col > 2
    valid << (p1 + 6)
  end
  if row > 1 && col < 7
    valid << (p1 - 6)
  end
  if row > 1 && col > 2
    valid << (p1 - 10)
  end
  if row > 2 && col < 8
    valid << (p1 - 15)
  end
  if row > 2 && col > 1
    valid << (p1 - 17)
  end

  # All possible moves for the knight based on board boundaries will added
  # This iterator filters for friendly fire, and removes indexes pointing to same color pices
  valid.each do |pos|
    unless piece_color(pos) == @@color
      valid_moves_no_friendly_fire << pos
    end
  end

  valid_moves_no_friendly_fire
end

#move_lateral(index, limit = 8) ⇒ Object

Lateral movements (Left, Right, Up, Down). Will return all valid lateral movies for a piece at index By default, it will extend the piece movement laterally until it hits a board edge or until it hits a piece. This can be changed by passing the limit argument For example, the king can only move laterally 1 position, so it would pass limit=1



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/move.rb', line 145

def move_lateral(index, limit = 8)
  row = get_row_from_index(index)
  col = get_col_from_index(index)

  left, right = [col-1, limit].min, [8-col, limit].min
  up, down = [row-1, limit].min, [8-row, limit].min

  valid = []

  # Move down N places until board limit, piece in the way, or specified limit
  down.times do |i|
    next_pos = index + (i+1)*8
    # Valid move if position is unoccupied
    if unoccupied?(next_pos)
      valid << next_pos
    else
      # Valid move is piece is an enemy, but then no subsequent tiles are attackable
      # if the piece is not an enemy, it's not added as a valid move, and no subsequent tiles are attackable
      # This function doesn't filter out the king from a valid enemy, but the Board class will drop King indexes
      if piece_color(next_pos) == @@enemy_color
        valid << next_pos
      end

      break
    end
  end

  # Move up N places until board limit, piece in the way, or specified limit
  up.times do |i|
    next_pos = index - (i+1)*8
    if unoccupied?(next_pos)
      valid << next_pos
    else
      if piece_color(next_pos) == @@enemy_color
        valid << next_pos
      end
      break
    end
  end

  # Move right N places until board limit, piece in the way, or specified limit
  right.times do |i|
    next_pos = index + (i+1)
    if unoccupied?(next_pos)
      valid << next_pos
    else
      if piece_color(next_pos) == @@enemy_color
        valid << next_pos
      end
      break
    end
  end

  # Move left N places until board limit, piece in the way, or specified limit
  left.times do |i|
    next_pos = index - (i+1)
    if unoccupied?(next_pos)
      valid << next_pos
    else
      if piece_color(next_pos) == @@enemy_color
        valid << next_pos
      end
      break
    end
  end

  valid
end

#move_pawn(p1) ⇒ Object

Returns all valid positions a pawn at index p1 can move to



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/move.rb', line 53

def move_pawn(p1)
  row = get_row_from_index(p1)
  col = get_col_from_index(p1)
  valid = []

  # Piece color defines direction of travel. Enemy presence defines
  # the validity of diagonal movements
  if @@color == :red
    if unoccupied?(p1 - 8)
      valid << (p1 - 8)
    end
    if piece_color(p1 - 7) == @@enemy_color && col < 8
      valid << (p1 - 7)
    end
    if piece_color(p1 - 9) == @@enemy_color && col > 1
      valid << (p1 - 9)
    end
    # Only if the pieces is unmoved, can it move forward two rows
    if !@@pieces[p1][:moved] && unoccupied?(p1 - 8) && unoccupied?(p1 - 16)
      valid << (p1 - 16)
    end

  elsif @@color == :black

    if unoccupied?(p1 + 8)
      valid << (p1 + 8)
    end
    if piece_color(p1 + 7) == @@enemy_color && col > 1
      valid << (p1 + 7)
    end
    if piece_color(p1 + 9) == @@enemy_color && col < 8
      valid << (p1 + 9)
    end
    if !@@pieces[p1][:moved] && unoccupied?(p1 + 8) && unoccupied?(p1 + 16)
      valid << (p1 + 16)
    end
  end

  valid
end

#moved?(index) ⇒ Boolean

Return true if the piece has moved before

Returns:

  • (Boolean)


314
315
316
# File 'lib/move.rb', line 314

def moved?(index)
  @@pieces[index][:moved] ? true : false
end

#not_king(index) ⇒ Object

Method used when moving, to verify the piece at index (1 - 64) is not of type “king”



324
325
326
# File 'lib/move.rb', line 324

def not_king(index)
  @@piece_locations[index][:type] == :king
end

#piece_color(index) ⇒ Object

Return piece color (“red” or “black”) from index (1 - 64)



319
320
321
# File 'lib/move.rb', line 319

def piece_color(index)
  @@pieces[index][:color]
end

#possible_moves(p1, manifest, castling = false) ⇒ Object

Calls methods below to return a list of positions which are valid moves for piece at index p1, given the current board layout as defined in manifest



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/move.rb', line 15

def possible_moves(p1, manifest, castling = false)
  return [] if manifest[p1][:type].nil?

  allowed   = []
  type      = manifest[p1][:type]
  my_color  = manifest[p1][:color]

  constants(manifest, my_color, type)

  return [] if unoccupied?(p1)

  if type == :king
    allowed += [move_lateral(p1, 1)].flatten
    allowed += [move_diagonal(p1, 1)].flatten
    allowed += [castle(p1)].flatten if castling

  elsif type == :queen
    allowed += [move_lateral(p1)].flatten
    allowed += [move_diagonal(p1)].flatten

  elsif type == :rook
    allowed += [move_lateral(p1)].flatten

  elsif type == :bishop
    allowed += [move_diagonal(p1)].flatten

  elsif type == :pawn
    allowed += [move_pawn(p1)].flatten

  elsif type == :knight
    allowed += [move_knight(p1)].flatten
  end

  allowed
end

#unoccupied?(index) ⇒ Boolean

Check if board tile currently has a piece

Returns:

  • (Boolean)


309
310
311
# File 'lib/move.rb', line 309

def unoccupied?(index)
  @@pieces[index][:color] == nil ? true : false
end