terminal_chess 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << __FILE__ # '.'
4
+
5
+ require_relative "terminal_chess/version"
6
+ require_relative "printer.rb"
7
+ require_relative "move.rb"
8
+ require_relative "board.rb"
9
+
10
+ class LocalChessClient
11
+ def initialize
12
+ @board = Board.new
13
+ @turn_by_turn_playback = []
14
+ @board.display_board
15
+
16
+ start
17
+ end
18
+
19
+ def start
20
+ # Gameplay
21
+ loop do
22
+ if @board.checkmate
23
+ puts "\nTurn by Turn Playback : #{@turn_by_turn_playback}\n"
24
+ exit
25
+ end
26
+
27
+ print "\nPiece to Move [#{@board.player_turn.capitalize}]: "
28
+ from = gets.chomp.upcase
29
+
30
+ begin
31
+ print "Valid destinations: #{@board.valid_destinations(from).join(", ")}"
32
+
33
+ print "\nLocation: "
34
+ to = gets.chomp.upcase
35
+ @board.move(from, to)
36
+
37
+ rescue Exception => e
38
+ puts "Invalid selection #{e if !ENV["DEV"].nil?}"
39
+ else
40
+ @turn_by_turn_playback << [from, to]
41
+ end
42
+ end
43
+ end
44
+ end
45
+
@@ -1,61 +1,63 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- module MOVE
3
+ module Move
4
4
 
5
5
  def constants(piece_mapping, color, piece)
6
6
  @@pieces = piece_mapping
7
- @@color = color
8
- @@enemy_color = (["black", "red"] - ["#{color.downcase}"]).first
9
- @@type = piece
7
+ @@color = color
8
+ @@type = piece
9
+ @@enemy_color = ([:black, :red] - [color]).first
10
10
  end
11
11
 
12
12
 
13
13
  # Calls methods below to return a list of positions which are valid moves
14
14
  # for piece at index p1, given the current board layout as defined in manifest
15
15
  def possible_moves(p1, manifest, castling = false)
16
+ return [] if manifest[p1][:type].nil?
17
+
16
18
  allowed = []
17
- type = manifest[p1]["type"]
18
- my_color = manifest[p1]["color"]
19
- constants(manifest, my_color, type)
19
+ type = manifest[p1][:type]
20
+ my_color = manifest[p1][:color]
20
21
 
21
- unless unoccupied?(p1)
22
+ constants(manifest, my_color, type)
22
23
 
23
- if type == "king"
24
- allowed += [move_lateral(p1, 1)].flatten
25
- allowed += [move_diagonal(p1, 1)].flatten
26
- allowed += [castle(p1)].flatten if castling
24
+ return [] if unoccupied?(p1)
27
25
 
28
- elsif type == "queen"
29
- allowed += [move_lateral(p1)].flatten
30
- allowed += [move_diagonal(p1)].flatten
26
+ if type == :king
27
+ allowed += [move_lateral(p1, 1)].flatten
28
+ allowed += [move_diagonal(p1, 1)].flatten
29
+ allowed += [castle(p1)].flatten if castling
31
30
 
32
- elsif type == "rook"
33
- allowed += [move_lateral(p1)].flatten
31
+ elsif type == :queen
32
+ allowed += [move_lateral(p1)].flatten
33
+ allowed += [move_diagonal(p1)].flatten
34
34
 
35
- elsif type == "bishop"
36
- allowed += [move_diagonal(p1)].flatten
35
+ elsif type == :rook
36
+ allowed += [move_lateral(p1)].flatten
37
37
 
38
- elsif type == "pawn"
39
- allowed += [pawn(p1)].flatten
38
+ elsif type == :bishop
39
+ allowed += [move_diagonal(p1)].flatten
40
40
 
41
- elsif type == "knight"
42
- allowed += [knight(p1)].flatten
43
- end
41
+ elsif type == :pawn
42
+ allowed += [move_pawn(p1)].flatten
44
43
 
44
+ elsif type == :knight
45
+ allowed += [move_knight(p1)].flatten
45
46
  end
46
- return allowed
47
+
48
+ allowed
47
49
  end
48
50
 
49
51
 
50
52
  # Returns all valid positions a pawn at index p1 can move to
51
- def pawn(p1)
53
+ def move_pawn(p1)
52
54
  row = get_row_from_index(p1)
53
55
  col = get_col_from_index(p1)
54
56
  valid = []
55
57
 
56
58
  # Piece color defines direction of travel. Enemy presence defines
57
59
  # the validity of diagonal movements
58
- if @@color == "red"
60
+ if @@color == :red
59
61
  if unoccupied?(p1 - 8)
60
62
  valid << (p1 - 8)
61
63
  end
@@ -66,10 +68,12 @@ module MOVE
66
68
  valid << (p1 - 9)
67
69
  end
68
70
  # Only if the pieces is unmoved, can it move forward two rows
69
- if !@@pieces[p1]["moved"] && unoccupied?(p1 - 16) && unoccupied?(p1 - 8)
71
+ if !@@pieces[p1][:moved] && unoccupied?(p1 - 8) && unoccupied?(p1 - 16)
70
72
  valid << (p1 - 16)
71
73
  end
72
- elsif @@color == "black"
74
+
75
+ elsif @@color == :black
76
+
73
77
  if unoccupied?(p1 + 8)
74
78
  valid << (p1 + 8)
75
79
  end
@@ -79,21 +83,22 @@ module MOVE
79
83
  if piece_color(p1 + 9) == @@enemy_color && col < 8
80
84
  valid << (p1 + 9)
81
85
  end
82
- if !@@pieces[p1]["moved"] && unoccupied?(p1 + 16) && unoccupied?(p1 + 8)
86
+ if !@@pieces[p1][:moved] && unoccupied?(p1 + 8) && unoccupied?(p1 + 16)
83
87
  valid << (p1 + 16)
84
88
  end
85
89
  end
86
90
 
87
- return valid
91
+ valid
88
92
  end
89
93
 
90
94
 
91
95
  # Returns valid positions a knight at index p1 can move to
92
- def knight(p1)
96
+ def move_knight(p1)
93
97
  row = get_row_from_index(p1)
94
98
  col = get_col_from_index(p1)
99
+
95
100
  valid = []
96
- valid_no_ff = []
101
+ valid_moves_no_friendly_fire = []
97
102
 
98
103
  # Valid knight moves based on its board position
99
104
  if row < 7 && col < 8
@@ -125,11 +130,11 @@ module MOVE
125
130
  # This iterator filters for friendly fire, and removes indexes pointing to same color pices
126
131
  valid.each do |pos|
127
132
  unless piece_color(pos) == @@color
128
- valid_no_ff << pos
133
+ valid_moves_no_friendly_fire << pos
129
134
  end
130
135
  end
131
136
 
132
- return valid_no_ff
137
+ valid_moves_no_friendly_fire
133
138
  end
134
139
 
135
140
 
@@ -140,66 +145,70 @@ module MOVE
140
145
  def move_lateral(index, limit = 8)
141
146
  row = get_row_from_index(index)
142
147
  col = get_col_from_index(index)
148
+
143
149
  left, right = [col-1, limit].min, [8-col, limit].min
144
150
  up, down = [row-1, limit].min, [8-row, limit].min
151
+
145
152
  valid = []
146
153
 
147
154
  # Move down N places until board limit, piece in the way, or specified limit
148
155
  down.times do |i|
149
- next_pos = index + (i+1)*8
150
- # Valid move if position is unoccupied
151
- if unoccupied?(next_pos)
156
+ next_pos = index + (i+1)*8
157
+ # Valid move if position is unoccupied
158
+ if unoccupied?(next_pos)
159
+ valid << next_pos
160
+ else
161
+ # Valid move is piece is an enemy, but then no subsequent tiles are attackable
162
+ # if the piece is not an enemy, it's not added as a valid move, and no subsequent tiles are attackable
163
+ # This function doesn't filter out the king from a valid enemy, but the Board class will drop King indexes
164
+ if piece_color(next_pos) == @@enemy_color
152
165
  valid << next_pos
153
- else
154
- # Valid move is piece is an enemy, but then no subsequent tiles are attackable
155
- # if the piece is not an enemy, it's not added as a valid move, and no subsequent tiles are attackable
156
- # This function doesn't filter out the king from a valid enemy, but the Board class will drop King indexes
157
- if piece_color(next_pos) == @@enemy_color
158
- valid << next_pos
159
- end
160
- break
161
166
  end
167
+
168
+ break
169
+ end
162
170
  end
163
171
 
164
172
  # Move up N places until board limit, piece in the way, or specified limit
165
173
  up.times do |i|
166
- next_pos = index - (i+1)*8
167
- if unoccupied?(next_pos)
174
+ next_pos = index - (i+1)*8
175
+ if unoccupied?(next_pos)
176
+ valid << next_pos
177
+ else
178
+ if piece_color(next_pos) == @@enemy_color
168
179
  valid << next_pos
169
- else
170
- if piece_color(next_pos) == @@enemy_color
171
- valid << next_pos
172
- end
173
- break
174
180
  end
181
+ break
182
+ end
175
183
  end
176
184
 
177
185
  # Move right N places until board limit, piece in the way, or specified limit
178
186
  right.times do |i|
179
- next_pos = index + (i+1)
180
- if unoccupied?(next_pos)
187
+ next_pos = index + (i+1)
188
+ if unoccupied?(next_pos)
189
+ valid << next_pos
190
+ else
191
+ if piece_color(next_pos) == @@enemy_color
181
192
  valid << next_pos
182
- else
183
- if piece_color(next_pos) == @@enemy_color
184
- valid << next_pos
185
- end
186
- break
187
193
  end
194
+ break
195
+ end
188
196
  end
189
197
 
190
198
  # Move left N places until board limit, piece in the way, or specified limit
191
199
  left.times do |i|
192
- next_pos = index - (i+1)
193
- if unoccupied?(next_pos)
200
+ next_pos = index - (i+1)
201
+ if unoccupied?(next_pos)
202
+ valid << next_pos
203
+ else
204
+ if piece_color(next_pos) == @@enemy_color
194
205
  valid << next_pos
195
- else
196
- if piece_color(next_pos) == @@enemy_color
197
- valid << next_pos
198
- end
199
- break
200
206
  end
207
+ break
208
+ end
201
209
  end
202
- return valid
210
+
211
+ valid
203
212
  end
204
213
 
205
214
 
@@ -271,7 +280,7 @@ module MOVE
271
280
  end
272
281
  end
273
282
 
274
- return valid
283
+ valid
275
284
  end
276
285
 
277
286
  # Castle: king cannot move into check, or through check
@@ -279,70 +288,51 @@ module MOVE
279
288
  valid = []
280
289
  dangerous_tiles = attack_vectors
281
290
 
282
- # Valid positions for a King to be in order to castle
283
- if index == 5 || index == 61
284
- # Empty space between a King and a Rook
285
- if unoccupied?(index - 1) && unoccupied?(index - 2) && unoccupied?(index - 3) && @@pieces[index - 4]["moved"] == false
286
- # Ensure king does not move through check or into check
287
- if !dangerous_tiles.include?(index - 1) && !dangerous_tiles.include?(index - 2)
288
- # King's castle position
289
- valid << index - 2
290
- end
291
- elsif unoccupied?(index + 1) && unoccupied?(index + 2) && @@pieces[index + 3]["moved"] == false
292
- if !dangerous_tiles.include?(index + 1) && !dangerous_tiles.include?(index + 2)
293
- valid << index + 2
294
- end
295
- end
291
+ # King may never have moved
292
+ return valid unless [5, 61].include?(index) && @@pieces[index][:moved] == false
293
+
294
+ # Ensure empty space between a King and a Rook
295
+ if unoccupied?(index - 1) && unoccupied?(index - 2) && unoccupied?(index - 3) && @@pieces[index - 4][:moved] == false
296
+ # Ensure king does not move through check or into check, and then add its castle position
297
+ valid << index - 2 if !dangerous_tiles.include?(index - 1) && !dangerous_tiles.include?(index - 2)
298
+ end
299
+
300
+ if unoccupied?(index + 1) && unoccupied?(index + 2) && @@pieces[index + 3][:moved] == false
301
+ valid << index + 2 if !dangerous_tiles.include?(index + 1) && !dangerous_tiles.include?(index + 2)
296
302
  end
297
- return valid
303
+
304
+ valid
298
305
  end
299
306
 
300
307
 
301
308
  # Check if board tile currently has a piece
302
309
  def unoccupied?(index)
303
- if @@pieces[index]["color"].nil?
304
- return true
305
- else
306
- return false
307
- end
310
+ @@pieces[index][:color] == nil ? true : false
308
311
  end
309
312
 
310
-
311
313
  # Return true if the piece has moved before
312
314
  def moved?(index)
313
- if @@pieces[index]["moved"]
314
- return true
315
- else
316
- return false
317
- end
315
+ @@pieces[index][:moved] ? true : false
318
316
  end
319
317
 
320
-
321
318
  # Return piece color ("red" or "black") from index (1 - 64)
322
319
  def piece_color(index)
323
- return @@pieces[index]["color"]
320
+ @@pieces[index][:color]
324
321
  end
325
322
 
326
-
327
323
  # Method used when moving, to verify the piece at index (1 - 64) is not of type "king"
328
324
  def not_king(index)
329
- return @@piece_locations[index]["type"] == "king"
325
+ @@piece_locations[index][:type] == :king
330
326
  end
331
327
 
332
-
333
328
  # Obtain chess board row number (1 + 8) from an index (1 - 64)
334
329
  def get_row_from_index(index)
335
- return (index - 1)/8 + 1
330
+ (index - 1)/8 + 1
336
331
  end
337
332
 
338
-
339
333
  # Obtain chess board column number (1 - 8) from an index (1 - 64)
340
334
  def get_col_from_index(index)
341
- if index % 8 == 0
342
- return 8
343
- else
344
- return index % 8
345
- end
335
+ index % 8 == 0 ? 8 : index % 8
346
336
  end
347
337
 
348
338
  end
@@ -0,0 +1,104 @@
1
+
2
+ $LOAD_PATH << __FILE__ # '.'
3
+
4
+ require 'eventmachine'
5
+ require 'faye/websocket'
6
+
7
+ require_relative "terminal_chess/version"
8
+ require_relative "terminal_chess/messages"
9
+ require_relative "printer.rb"
10
+ require_relative "move.rb"
11
+ require_relative "board.rb"
12
+
13
+
14
+ class NetworkChessClient
15
+ def initialize(ngrok)
16
+ @board = Board.new
17
+ @turn_by_turn_playback = []
18
+ @game_started = false
19
+ @player_num = nil
20
+ @player_turn = false
21
+ @messages = []
22
+ @socket_url = "ws://#{ngrok}.ngrok.io"
23
+ start_client
24
+ end
25
+
26
+ private
27
+
28
+ def start_client
29
+ Thread.new {
30
+ EM.run do
31
+ ws = Faye::WebSocket::Client.new(@socket_url)
32
+
33
+ ws.on :open do
34
+ p [:open]
35
+ end
36
+
37
+ ws.on :message do |msg|
38
+ p [:message, msg.data]
39
+
40
+ if msg.data.match "SETUP: You are player [1|2]"
41
+ @player_num = msg.data.split(' ').last.strip.to_i
42
+ @player_turn = true if @player_num == 1
43
+ @game_started = true
44
+
45
+ p [:local, "Awaiting opponent move"] if @player_num == 2
46
+ end
47
+
48
+ if msg.data.match "MOVE: [a-zA-Z][0-9], [a-zA-Z][0-9]"
49
+ from, to = msg.data.match("MOVE: \([a-zA-Z][0-9]\), \([a-zA-Z][0-9]\)").captures
50
+
51
+ @board.move(from, to)
52
+ @player_turn = true
53
+ end
54
+ end
55
+
56
+ ws.on :close do |e|
57
+ p [:closed, e.code, e.reason]
58
+ ws = nil
59
+ EventMachine::stop_event_loop
60
+ end
61
+
62
+ EventMachine::PeriodicTimer.new(1) do
63
+ next unless @player_turn && @game_started
64
+
65
+ piece_moved = local_move
66
+ if piece_moved
67
+ @player_turn = false
68
+ ws.send "MOVE: #{@turn_by_turn_playback.last[0]}, #{@turn_by_turn_playback.last[1]}"
69
+ puts "Awaiting remote player move"
70
+ end
71
+ end
72
+ end
73
+ }.join
74
+ end
75
+
76
+
77
+ def local_move
78
+ if @board.checkmate
79
+ puts "\nTurn by Turn Playback : #{@turn_by_turn_playback}\n"
80
+ exit
81
+ end
82
+
83
+ print "\nPiece to Move [#{@board.player_turn.capitalize}]: "
84
+ from = gets.chomp.upcase
85
+
86
+ begin
87
+ print "Valid destinations: #{@board.valid_destinations(from).join(", ")}"
88
+
89
+ print "\nLocation: "
90
+ to = gets.chomp.upcase
91
+ moved = @board.move(from, to)
92
+
93
+ return local_move() unless moved == Messages.piece_moved
94
+
95
+ rescue Exception => e
96
+ puts "Invalid selection #{e if !ENV["DEV"].nil?}"
97
+ else
98
+ @turn_by_turn_playback << [from, to]
99
+ true
100
+ end
101
+ end
102
+
103
+ end
104
+