terminal_chess 0.1.2 → 0.2.0
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 +4 -4
- data/Gemfile.lock +38 -0
- data/README.md +76 -52
- data/lib/board.rb +357 -0
- data/lib/local_chess_client.rb +45 -0
- data/lib/move.rb +97 -107
- data/lib/network_chess_client.rb +104 -0
- data/lib/printer.rb +129 -54
- data/lib/server.rb +65 -0
- data/lib/terminal_chess.rb +7 -26
- data/lib/terminal_chess/messages.rb +27 -0
- data/lib/terminal_chess/version.rb +1 -1
- data/terminal_chess.gemspec +5 -1
- data/test/test_terminal_chess.rb +240 -41
- metadata +65 -4
- data/lib/Board.rb +0 -266
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: terminal_chess
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Willems
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-12-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -24,6 +24,48 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: em-websocket
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: eventmachine
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: faye-websocket
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
27
69
|
- !ruby/object:Gem::Dependency
|
28
70
|
name: bundler
|
29
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +94,20 @@ dependencies:
|
|
52
94
|
- - "~>"
|
53
95
|
- !ruby/object:Gem::Version
|
54
96
|
version: '10.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: minitest
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
55
111
|
description: Two player chess game through the terminal
|
56
112
|
email: hello@jasonwillems.com
|
57
113
|
executables:
|
@@ -60,14 +116,19 @@ extensions: []
|
|
60
116
|
extra_rdoc_files: []
|
61
117
|
files:
|
62
118
|
- Gemfile
|
119
|
+
- Gemfile.lock
|
63
120
|
- LICENSE
|
64
121
|
- README.md
|
65
122
|
- Rakefile
|
66
123
|
- bin/terminal_chess
|
67
|
-
- lib/
|
124
|
+
- lib/board.rb
|
125
|
+
- lib/local_chess_client.rb
|
68
126
|
- lib/move.rb
|
127
|
+
- lib/network_chess_client.rb
|
69
128
|
- lib/printer.rb
|
129
|
+
- lib/server.rb
|
70
130
|
- lib/terminal_chess.rb
|
131
|
+
- lib/terminal_chess/messages.rb
|
71
132
|
- lib/terminal_chess/version.rb
|
72
133
|
- terminal_chess.gemspec
|
73
134
|
- test/test_terminal_chess.rb
|
@@ -91,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
152
|
version: '0'
|
92
153
|
requirements: []
|
93
154
|
rubyforge_project:
|
94
|
-
rubygems_version: 2.
|
155
|
+
rubygems_version: 2.6.8
|
95
156
|
signing_key:
|
96
157
|
specification_version: 4
|
97
158
|
summary: Chess game playable via the terminal
|
data/lib/Board.rb
DELETED
@@ -1,266 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
load 'printer.rb'
|
4
|
-
load 'move.rb'
|
5
|
-
require 'colorize'
|
6
|
-
|
7
|
-
class Board
|
8
|
-
|
9
|
-
include PRINTER
|
10
|
-
include MOVE
|
11
|
-
|
12
|
-
def initialize
|
13
|
-
@@piece_locations = Hash.new
|
14
|
-
@@piece_locations_buffer = Hash.new
|
15
|
-
@@row_mappings = Hash[("A".."H").zip(1..8)]
|
16
|
-
@@taken_pieces = []
|
17
|
-
@@player_turn = "black"
|
18
|
-
@@checkmate = false
|
19
|
-
end
|
20
|
-
|
21
|
-
|
22
|
-
# Game logic
|
23
|
-
def move(p1, p2)
|
24
|
-
manifest = piece_manifest
|
25
|
-
p1 = get_index_from_rowcol(p1.to_s)
|
26
|
-
p2 = get_index_from_rowcol(p2.to_s)
|
27
|
-
valid_positions = possible_moves(p1, manifest, true)
|
28
|
-
|
29
|
-
##Subtract king current position from valid positions
|
30
|
-
valid_positions -= king_positions
|
31
|
-
|
32
|
-
# Allow piece movements, unless in checkmate
|
33
|
-
if !@@checkmate
|
34
|
-
# Ensure player is moving in turn
|
35
|
-
if @@player_turn == @@piece_locations[p1]["color"]
|
36
|
-
|
37
|
-
@@piece_locations_buffer = @@piece_locations.clone
|
38
|
-
|
39
|
-
# Chosen destination is within the list of valid destinations
|
40
|
-
if ([p2] - valid_positions).empty?
|
41
|
-
|
42
|
-
@@piece_locations_buffer[p2] = @@piece_locations_buffer[p1]
|
43
|
-
@@piece_locations_buffer[p2]["moved"] = true
|
44
|
-
@@piece_locations_buffer[p1] = {"type" => " ", "number" => nil, "color" => nil}
|
45
|
-
|
46
|
-
# If the current player is not in check at the end of the turn, allow them to proceed
|
47
|
-
if !check?(@@player_turn, @@piece_locations_buffer)
|
48
|
-
|
49
|
-
@@taken_pieces << @@piece_locations[p2] if !@@piece_locations[p2]["number"].nil?
|
50
|
-
|
51
|
-
# Check for Pawn Promotion (if pawn reaches end of the board, promote it)
|
52
|
-
if @@piece_locations_buffer[p2]["type"] == "pawn"
|
53
|
-
if p2 < 9 && @@piece_locations_buffer[p2]["color"] == "red"
|
54
|
-
promote(p2)
|
55
|
-
elsif p2 > 56 && @@piece_locations_buffer[p2]["color"] == "black"
|
56
|
-
promote(p2)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
# Check for Castling
|
61
|
-
if @@piece_locations_buffer[p2]["type"] == "king" && (p2 - p1).abs == 2
|
62
|
-
|
63
|
-
p2 < 9 ? y_offset = 0 : y_offset = 56
|
64
|
-
|
65
|
-
if p2 > p1
|
66
|
-
@@piece_locations_buffer[6+y_offset] = @@piece_locations_buffer[8+y_offset]
|
67
|
-
@@piece_locations_buffer[8+y_offset] = {"type" => " ", "number" => nil, "color" => nil}
|
68
|
-
else
|
69
|
-
@@piece_locations_buffer[4+y_offset] = @@piece_locations_buffer[1+y_offset]
|
70
|
-
@@piece_locations_buffer[1+y_offset] = {"type" => " ", "number" => nil, "color" => nil}
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
# Clean Up
|
75
|
-
@@piece_locations = @@piece_locations_buffer
|
76
|
-
@@player_turn = (["black", "red"] - [@@player_turn]).first
|
77
|
-
board_refresh
|
78
|
-
else
|
79
|
-
p "Please move #{@@player_turn} king out of check to continue"
|
80
|
-
end
|
81
|
-
else p "Please select a valid destination." end
|
82
|
-
else p "It is #{@@player_turn}'s turn. Please move a #{@@player_turn} piece." end
|
83
|
-
else p "Checkmate! Game Over." end
|
84
|
-
end
|
85
|
-
|
86
|
-
# Return the valid positions for piece at current_pos to move in readable format [A-H][1-8]
|
87
|
-
def valid_destinations(current_pos)
|
88
|
-
readable_positions = []
|
89
|
-
manifest = piece_manifest
|
90
|
-
p1 = get_index_from_rowcol(current_pos.to_s)
|
91
|
-
|
92
|
-
valid_positions = possible_moves(p1, manifest, true)
|
93
|
-
valid_positions.each do |pos|
|
94
|
-
grid_pos = get_rowcol_from_index(pos)
|
95
|
-
# Map first string character 1-8 to [A-H], for column, and then add second string character as [1-8]
|
96
|
-
readable_positions << (@@row_mappings.key(grid_pos[0].to_i) + grid_pos[1].to_s)
|
97
|
-
end
|
98
|
-
return readable_positions.sort
|
99
|
-
end
|
100
|
-
|
101
|
-
# Search piece manifest for kings. Remove them from the list of positions returned
|
102
|
-
# from the MOVE module (so that players cannot take the "king" type piece)
|
103
|
-
def king_positions
|
104
|
-
king_locations = []
|
105
|
-
@@piece_locations.each do |piece, details|
|
106
|
-
if details["type"] == "king"
|
107
|
-
king_locations << piece
|
108
|
-
end
|
109
|
-
end
|
110
|
-
return king_locations
|
111
|
-
end
|
112
|
-
|
113
|
-
|
114
|
-
# Once a pawn reaches the end, this method is called to swap the pawn
|
115
|
-
# for another piece (from the list below)
|
116
|
-
def promote(p1)
|
117
|
-
puts "Promote to: [Q]ueen, [K]night, [R]ook, [B]ishop"
|
118
|
-
while true
|
119
|
-
promo_piece = gets.chomp.downcase
|
120
|
-
if promo_piece == "q" || promo_piece == "queen"
|
121
|
-
@@piece_locations_buffer[p1]["type"] = "queen"
|
122
|
-
break
|
123
|
-
elsif promo_piece == "k" || promo_piece == "knight"
|
124
|
-
@@piece_locations_buffer[p1]["type"] = "knight"
|
125
|
-
break
|
126
|
-
elsif promo_piece == "r" || promo_piece == "rook"
|
127
|
-
@@piece_locations_buffer[p1]["type"] = "rook"
|
128
|
-
break
|
129
|
-
elsif promo_piece == "b" || promo_piece == "bishop"
|
130
|
-
@@piece_locations_buffer[p1]["type"] = "bishop"
|
131
|
-
break
|
132
|
-
else
|
133
|
-
puts "Please enter one of: [Q]ueen, [K]night, [R]ook, [B]ishop"
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
private :promote
|
139
|
-
|
140
|
-
# Return whether the player of a specified color has their king currently in check
|
141
|
-
# by checking the attack vectors of all the opponents players, versus the king location
|
142
|
-
# Also, check whether king currently in check, has all of their valid moves within
|
143
|
-
# their opponents attack vectors, and therefore are in checkmate (@@checkmate)
|
144
|
-
def check?(color, proposed_manifest = @@piece_locations)
|
145
|
-
path, king_loc = [], []
|
146
|
-
enemy_color = (["black", "red"] - ["#{color}"]).first
|
147
|
-
|
148
|
-
proposed_manifest.each do |piece, details|
|
149
|
-
if details["color"] == enemy_color
|
150
|
-
path << possible_moves(piece, proposed_manifest)
|
151
|
-
end
|
152
|
-
if details["color"] == color && details["type"] == "king"
|
153
|
-
king_loc = piece
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
danger_vector = path.flatten.uniq
|
158
|
-
king_positions = possible_moves(king_loc, proposed_manifest)
|
159
|
-
|
160
|
-
# If the King is in the attackable locations for the opposing player
|
161
|
-
if danger_vector.include? king_loc
|
162
|
-
# If all the positions the can move to is also attackable by the opposing player
|
163
|
-
if (king_positions - danger_vector).length != 0
|
164
|
-
# This is flawed. It verified whether the king could move out check
|
165
|
-
# There are two other cases: whether a piece can remove the enemy
|
166
|
-
# And whether the enemy's attack vector can be blocked
|
167
|
-
#@@checkmate = true
|
168
|
-
end
|
169
|
-
return true
|
170
|
-
# Piece not in check
|
171
|
-
else
|
172
|
-
return false
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
|
177
|
-
# Board spaces that are attackable by opposing pieces
|
178
|
-
# TODO: check? method should use this function
|
179
|
-
def attack_vectors(color = @@player_turn, proposed_manifest = @@piece_locations)
|
180
|
-
enemy_color = (["black", "red"] - ["#{color}"]).first
|
181
|
-
kill_zone = []
|
182
|
-
|
183
|
-
proposed_manifest.each do |piece, details|
|
184
|
-
if details["color"] == enemy_color
|
185
|
-
kill_zone << possible_moves(piece, proposed_manifest)
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
kill_zone.flatten.uniq
|
190
|
-
end
|
191
|
-
|
192
|
-
|
193
|
-
# Reprint the board. Called after every valid piece move
|
194
|
-
def board_refresh
|
195
|
-
print_board(@@piece_locations)
|
196
|
-
end
|
197
|
-
|
198
|
-
|
199
|
-
# Convert index [A-H][1-8] => (1 - 64)
|
200
|
-
def get_index_from_rowcol(row_col)
|
201
|
-
offset = @@row_mappings[row_col[0]].to_i
|
202
|
-
multiplier = row_col[1].to_i - 1
|
203
|
-
return multiplier * 8 + offset
|
204
|
-
end
|
205
|
-
|
206
|
-
|
207
|
-
# Convert index (1 - 64) => [A-H][1-8]
|
208
|
-
def get_rowcol_from_index(index)
|
209
|
-
letter = get_col_from_index(index)
|
210
|
-
number = get_row_from_index(index)
|
211
|
-
"#{letter}#{number}"
|
212
|
-
end
|
213
|
-
|
214
|
-
|
215
|
-
# Intial setup of board. Put pieces into the expected locations
|
216
|
-
def setup_board
|
217
|
-
(1..64).each do |location|
|
218
|
-
@@piece_locations[location] = {"type" => " ", "number" => nil, "color" => nil}
|
219
|
-
end
|
220
|
-
|
221
|
-
# Black Pieces
|
222
|
-
@@piece_locations[1] = {"type" => "rook", "number" => 1, "color" => "black", "moved" => false}
|
223
|
-
@@piece_locations[2] = {"type" => "knight", "number" => 1, "color" => "black", "moved" => false}
|
224
|
-
@@piece_locations[3] = {"type" => "bishop", "number" => 1, "color" => "black", "moved" => false}
|
225
|
-
@@piece_locations[4] = {"type" => "queen", "number" => 1, "color" => "black", "moved" => false}
|
226
|
-
@@piece_locations[5] = {"type" => "king", "number" => 1, "color" => "black", "moved" => false}
|
227
|
-
@@piece_locations[6] = {"type" => "bishop", "number" => 2, "color" => "black", "moved" => false}
|
228
|
-
@@piece_locations[7] = {"type" => "knight", "number" => 2, "color" => "black", "moved" => false}
|
229
|
-
@@piece_locations[8] = {"type" => "rook", "number" => 2, "color" => "black", "moved" => false}
|
230
|
-
(1..8).each do |col|
|
231
|
-
@@piece_locations[col + 8] = {"type" => "pawn", "number" => col, "color" => "black", "moved" => false}
|
232
|
-
end
|
233
|
-
|
234
|
-
# White Pieces
|
235
|
-
@@piece_locations[57] = {"type" => "rook", "number" => 1, "color" => "red", "moved" => false}
|
236
|
-
@@piece_locations[58] = {"type" => "knight", "number" => 1, "color" => "red", "moved" => false}
|
237
|
-
@@piece_locations[59] = {"type" => "bishop", "number" => 1, "color" => "red", "moved" => false}
|
238
|
-
@@piece_locations[60] = {"type" => "queen", "number" => 1, "color" => "red", "moved" => false}
|
239
|
-
@@piece_locations[61] = {"type" => "king", "number" => 1, "color" => "red", "moved" => false}
|
240
|
-
@@piece_locations[62] = {"type" => "bishop", "number" => 2, "color" => "red", "moved" => false}
|
241
|
-
@@piece_locations[63] = {"type" => "knight", "number" => 2, "color" => "red", "moved" => false}
|
242
|
-
@@piece_locations[64] = {"type" => "rook", "number" => 2, "color" => "red", "moved" => false}
|
243
|
-
(1..8).each do |col|
|
244
|
-
@@piece_locations[col + 48] = {"type" => "pawn", "number" => col, "color" => "red", "moved" => false}
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
|
249
|
-
def piece_manifest
|
250
|
-
return @@piece_locations
|
251
|
-
end
|
252
|
-
|
253
|
-
# Not currently used
|
254
|
-
def taken_pieces
|
255
|
-
return @@taken_pieces
|
256
|
-
end
|
257
|
-
|
258
|
-
def checkmate?
|
259
|
-
return @@checkmate
|
260
|
-
end
|
261
|
-
|
262
|
-
def player_turn
|
263
|
-
return @@player_turn
|
264
|
-
end
|
265
|
-
|
266
|
-
end
|