chess_game 0.0.11
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 +7 -0
- data/.rspec +1 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +58 -0
- data/README.md +24 -0
- data/Rakefile +3 -0
- data/bin/chess +3 -0
- data/chess-0.0.11.gem +0 -0
- data/chess.gemspec +29 -0
- data/game.dat +0 -0
- data/lib/chess/bishop.rb +28 -0
- data/lib/chess/board.rb +170 -0
- data/lib/chess/game.rb +73 -0
- data/lib/chess/king.rb +101 -0
- data/lib/chess/knight.rb +28 -0
- data/lib/chess/move_helper.rb +185 -0
- data/lib/chess/pawn.rb +58 -0
- data/lib/chess/piece.rb +134 -0
- data/lib/chess/player.rb +36 -0
- data/lib/chess/queen.rb +30 -0
- data/lib/chess/rook.rb +30 -0
- data/lib/chess/version.rb +3 -0
- data/lib/chess.rb +71 -0
- data/save/test +0 -0
- data/spec/bishop_spec.rb +32 -0
- data/spec/board_spec.rb +206 -0
- data/spec/game_spec.rb +32 -0
- data/spec/king_spec.rb +167 -0
- data/spec/knight_spec.rb +57 -0
- data/spec/pawn_spec.rb +144 -0
- data/spec/piece_spec.rb +43 -0
- data/spec/player_spec.rb +47 -0
- data/spec/queen_spec.rb +35 -0
- data/spec/rook_spec.rb +31 -0
- data/spec/spec_helper.rb +3 -0
- metadata +147 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 843385b83bf123c7fc2b76d4612eebba1593b383
|
4
|
+
data.tar.gz: 8b448e09cd5eae1c95530a4cc350bbaa09f6c371
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: eebdb0014b9af53abc0969af0a4d6a7467f3b3c590f73bb104098f6385c7f240ed42d4602121d2342454c7dfc89bdc4690ebefb1ee0b17fd404c37e7daf3c265
|
7
|
+
data.tar.gz: 9af5375d92e601c8e96619bb072517fa0c5f8ab0bb23cb445662bacab54751b9e6e94e7a92fb570051f6b9aa1beb20b0de5bfa40bc32458d561029a2f0acdeb4
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (4.1.6)
|
5
|
+
i18n (~> 0.6, >= 0.6.9)
|
6
|
+
json (~> 1.7, >= 1.7.7)
|
7
|
+
minitest (~> 5.1)
|
8
|
+
thread_safe (~> 0.1)
|
9
|
+
tzinfo (~> 1.1)
|
10
|
+
coderay (1.1.0)
|
11
|
+
colorize (0.7.3)
|
12
|
+
columnize (0.8.9)
|
13
|
+
debugger (1.6.8)
|
14
|
+
columnize (>= 0.3.1)
|
15
|
+
debugger-linecache (~> 1.2.0)
|
16
|
+
debugger-ruby_core_source (~> 1.3.5)
|
17
|
+
debugger-linecache (1.2.0)
|
18
|
+
debugger-ruby_core_source (1.3.5)
|
19
|
+
diff-lcs (1.2.5)
|
20
|
+
i18n (0.6.11)
|
21
|
+
json (1.8.1)
|
22
|
+
method_source (0.8.2)
|
23
|
+
minitest (5.4.2)
|
24
|
+
pry (0.10.1)
|
25
|
+
coderay (~> 1.1.0)
|
26
|
+
method_source (~> 0.8.1)
|
27
|
+
slop (~> 3.4)
|
28
|
+
pry-debugger (0.2.3)
|
29
|
+
debugger (~> 1.3)
|
30
|
+
pry (>= 0.9.10, < 0.11.0)
|
31
|
+
rake (10.3.2)
|
32
|
+
rspec (3.1.0)
|
33
|
+
rspec-core (~> 3.1.0)
|
34
|
+
rspec-expectations (~> 3.1.0)
|
35
|
+
rspec-mocks (~> 3.1.0)
|
36
|
+
rspec-core (3.1.7)
|
37
|
+
rspec-support (~> 3.1.0)
|
38
|
+
rspec-expectations (3.1.2)
|
39
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
40
|
+
rspec-support (~> 3.1.0)
|
41
|
+
rspec-mocks (3.1.3)
|
42
|
+
rspec-support (~> 3.1.0)
|
43
|
+
rspec-support (3.1.2)
|
44
|
+
slop (3.6.0)
|
45
|
+
thread_safe (0.3.4)
|
46
|
+
tzinfo (1.2.2)
|
47
|
+
thread_safe (~> 0.1)
|
48
|
+
|
49
|
+
PLATFORMS
|
50
|
+
ruby
|
51
|
+
|
52
|
+
DEPENDENCIES
|
53
|
+
activesupport
|
54
|
+
colorize
|
55
|
+
pry
|
56
|
+
pry-debugger
|
57
|
+
rake
|
58
|
+
rspec
|
data/README.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Command Line Chess
|
2
|
+
==================
|
3
|
+
|
4
|
+
Play command line chess! Start the game by running "$ ruby lib/chess.rb" from the root directory.
|
5
|
+
|
6
|
+
You can save the game by typing "save" instead of a move.
|
7
|
+
|
8
|
+
You can load a game by typing "load" instead of a move.
|
9
|
+
|
10
|
+
Quit the game by typing "exit" or "quit".
|
11
|
+
|
12
|
+
Make moves by entering the two squares you want to move a piece from and to in the form '[file][rank] [file][rank]'. For example, to move a starting position pawn as the white palayer, type "D2 D3" or "d2 d3"
|
13
|
+
|
14
|
+
Castle by moving the King to its final position after castling.
|
15
|
+
|
16
|
+
Web App
|
17
|
+
-------
|
18
|
+
This libray is used in the Web App https://github.com/Hawkeye000/sinatra-chess.
|
19
|
+
|
20
|
+
See this in use at http://hawkeye-chess.herokuapp.com/
|
21
|
+
|
22
|
+
Dependencies
|
23
|
+
------------
|
24
|
+
See Gemfile.
|
data/Rakefile
ADDED
data/bin/chess
ADDED
data/chess-0.0.11.gem
ADDED
Binary file
|
data/chess.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'chess/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'chess_game'
|
7
|
+
spec.version = '0.0.11'
|
8
|
+
spec.date = '2014-10-26'
|
9
|
+
spec.authors = ["Benjamin Pearce"]
|
10
|
+
spec.email = ["hawk.git@bcpearce.com"]
|
11
|
+
spec.summary = "Play chess using the command line."
|
12
|
+
spec.description = "Contain libraries for setting up and playing games of chess,\
|
13
|
+
making and validating moves, and evaluating victory conditionspec."
|
14
|
+
spec.homepage = "https://github.com/bcpearce/command-line-chess"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files`.split($/)
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
23
|
+
spec.add_development_dependency 'rake', '10.3.2'
|
24
|
+
spec.add_development_dependency 'rspec', '~> 3.1'
|
25
|
+
|
26
|
+
spec.add_runtime_dependency 'colorize', '~> 0.7.3'
|
27
|
+
|
28
|
+
|
29
|
+
end
|
data/game.dat
ADDED
Binary file
|
data/lib/chess/bishop.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative 'piece.rb'
|
2
|
+
|
3
|
+
class Bishop < Piece
|
4
|
+
|
5
|
+
WHITE_BISHOP_ICON = "\u2657"
|
6
|
+
BLACK_BISHOP_ICON = "\u265D"
|
7
|
+
|
8
|
+
def initialize(color = "white", location = "A1")
|
9
|
+
case color.downcase
|
10
|
+
when "white"
|
11
|
+
@icon = WHITE_BISHOP_ICON
|
12
|
+
when "black"
|
13
|
+
@icon = BLACK_BISHOP_ICON
|
14
|
+
end
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def valid_move?(new_loc, board)
|
19
|
+
x, y = new_loc - @location
|
20
|
+
if x.abs == y.abs
|
21
|
+
else
|
22
|
+
board.move_status = "Bishops cannot make that move."
|
23
|
+
return false
|
24
|
+
end
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/lib/chess/board.rb
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
Dir[File.dirname(__FILE__) + '/*'].each { |file| require file }
|
2
|
+
require 'colorize'
|
3
|
+
|
4
|
+
class Board
|
5
|
+
|
6
|
+
attr_accessor :move_status
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@board = Hash.new
|
10
|
+
("A".."H").each do |letter|
|
11
|
+
("1".."8").each do |number|
|
12
|
+
loc = "#{letter}#{number}"
|
13
|
+
piece =
|
14
|
+
if number == "7"
|
15
|
+
Pawn.new("black", loc)
|
16
|
+
elsif loc == "A8" || loc == "H8"
|
17
|
+
Rook.new("black", loc)
|
18
|
+
elsif loc == "B8" || loc == "G8"
|
19
|
+
Knight.new("black", loc)
|
20
|
+
elsif loc == "C8" || loc == "F8"
|
21
|
+
Bishop.new("black", loc)
|
22
|
+
elsif loc == "D8"
|
23
|
+
Queen.new("black", loc)
|
24
|
+
elsif loc == "E8"
|
25
|
+
King.new("black", loc)
|
26
|
+
|
27
|
+
elsif number == "2"
|
28
|
+
Pawn.new("white", loc)
|
29
|
+
elsif loc == "A1" || loc == "H1"
|
30
|
+
Rook.new("white", loc)
|
31
|
+
elsif loc == "B1" || loc == "G1"
|
32
|
+
Knight.new("white", loc)
|
33
|
+
elsif loc == "C1" || loc == "F1"
|
34
|
+
Bishop.new("white", loc)
|
35
|
+
elsif loc == "D1"
|
36
|
+
Queen.new("white", loc)
|
37
|
+
elsif loc == "E1"
|
38
|
+
King.new("white", loc)
|
39
|
+
else
|
40
|
+
" "
|
41
|
+
end
|
42
|
+
@board[loc] = piece
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def set_piece(piece)
|
48
|
+
@board[piece.location] = piece
|
49
|
+
end
|
50
|
+
|
51
|
+
def rv_piece(location)
|
52
|
+
@board[location] = " " unless @board[location].nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
def board
|
56
|
+
@board
|
57
|
+
end
|
58
|
+
|
59
|
+
def [](string)
|
60
|
+
@board[string]
|
61
|
+
end
|
62
|
+
|
63
|
+
def each(&block)
|
64
|
+
@board.each(&block)
|
65
|
+
end
|
66
|
+
|
67
|
+
def clear
|
68
|
+
@board.each { |square, piece| @board[square] = " " }
|
69
|
+
end
|
70
|
+
|
71
|
+
def piece_to_promote?
|
72
|
+
@board.each_value do |piece|
|
73
|
+
if piece.is_a?(Piece)
|
74
|
+
if piece.can_promote?
|
75
|
+
return piece
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
return nil
|
80
|
+
end
|
81
|
+
|
82
|
+
def promote(loc, new_piece)
|
83
|
+
piece = board[loc]
|
84
|
+
new_piece = Kernel.const_get(new_piece.capitalize)
|
85
|
+
if piece.can_promote?
|
86
|
+
if new_piece == Queen || new_piece == Rook \
|
87
|
+
|| new_piece == Bishop || new_piece == Knight
|
88
|
+
promoted = new_piece.new(piece.color, loc)
|
89
|
+
promoted.moves = piece.moves
|
90
|
+
self.rv_piece(loc)
|
91
|
+
self.set_piece(promoted)
|
92
|
+
end
|
93
|
+
else
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def save_board
|
99
|
+
@saved_board = Marshal.load(Marshal.dump(@board))
|
100
|
+
end
|
101
|
+
|
102
|
+
def move(loc_1, loc_2)
|
103
|
+
return nil if loc_1.nil? || loc_2.nil?
|
104
|
+
piece = @board[loc_1]
|
105
|
+
self.save_board
|
106
|
+
|
107
|
+
if piece.move(loc_2, self)
|
108
|
+
@board[loc_1] = " "
|
109
|
+
old_piece = @board[loc_2]
|
110
|
+
self.set_piece(piece)
|
111
|
+
rv_piece(self.passed_piece)
|
112
|
+
castle_complement(piece, self)
|
113
|
+
self.clear_passable(piece.color)
|
114
|
+
return loc_2
|
115
|
+
else
|
116
|
+
return nil
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
def clear_passable(color)
|
122
|
+
@board.each_value { |piece| piece.passable = false if piece.color != color }
|
123
|
+
end
|
124
|
+
|
125
|
+
def passed_piece
|
126
|
+
@board.each_value { |piece| return piece.location if piece.passed? && piece.is_a?(Pawn) }
|
127
|
+
end
|
128
|
+
|
129
|
+
def restore_saved_board
|
130
|
+
self.clear
|
131
|
+
@board = Marshal.load(Marshal.dump(@saved_board))
|
132
|
+
end
|
133
|
+
|
134
|
+
def undo_last_move
|
135
|
+
self.restore_saved_board
|
136
|
+
end
|
137
|
+
|
138
|
+
def to_a
|
139
|
+
@board.to_a
|
140
|
+
end
|
141
|
+
|
142
|
+
def display
|
143
|
+
puts " " + ("A".."H").to_a.join(" ")
|
144
|
+
("1".."8").to_a.reverse.each do |row|
|
145
|
+
print row + " "
|
146
|
+
("A".."H").each do |col|
|
147
|
+
background =
|
148
|
+
if (col.ord + row.to_i).even?
|
149
|
+
:black
|
150
|
+
else
|
151
|
+
:red
|
152
|
+
end
|
153
|
+
print (@board["#{col}#{row}"].to_s + " ").colorize(:background => background)
|
154
|
+
end
|
155
|
+
print " #{row}\n"
|
156
|
+
end
|
157
|
+
puts " " + ("A".."H").to_a.join(" ")
|
158
|
+
end
|
159
|
+
|
160
|
+
def on_board?(loc)
|
161
|
+
!@board[loc].nil?
|
162
|
+
end
|
163
|
+
|
164
|
+
def piece_between?(loc_1, loc_2)
|
165
|
+
return false if loc_1 == loc_2
|
166
|
+
return false if self[loc_1].is_a?(Knight)
|
167
|
+
squares_between(loc_1, loc_2).any? { |square| @board[square] != " " }
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
data/lib/chess/game.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require_relative "board.rb"
|
2
|
+
require_relative "player.rb"
|
3
|
+
|
4
|
+
class Game
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@board = Board.new
|
8
|
+
@white_player = Player.new("white")
|
9
|
+
@black_player = Player.new("black")
|
10
|
+
@turn = @white_player
|
11
|
+
@opponent = @black_player
|
12
|
+
@movelist = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def movelist
|
16
|
+
@movelist
|
17
|
+
end
|
18
|
+
|
19
|
+
def save(file_name)
|
20
|
+
File.open(file_name, 'wb+') do |file|
|
21
|
+
Marshal.dump(self, file)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.load(file_name)
|
26
|
+
File.open(file_name) do |file|
|
27
|
+
Marshal.load(file)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def move(loc_1, loc_2)
|
32
|
+
if @board[loc_1].color == @turn.color
|
33
|
+
if @turn.move(loc_1, loc_2, @board)
|
34
|
+
@turn, @opponent = @opponent, @turn
|
35
|
+
@movelist << "#{@board[loc_2].icon} #{loc_1} #{loc_2}"
|
36
|
+
end
|
37
|
+
else
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def board
|
43
|
+
@board.board
|
44
|
+
end
|
45
|
+
|
46
|
+
def white_player
|
47
|
+
@white_player
|
48
|
+
end
|
49
|
+
|
50
|
+
def black_player
|
51
|
+
@black_player
|
52
|
+
end
|
53
|
+
|
54
|
+
def board
|
55
|
+
@board
|
56
|
+
end
|
57
|
+
|
58
|
+
def turn
|
59
|
+
@turn
|
60
|
+
end
|
61
|
+
|
62
|
+
def opponent
|
63
|
+
@opponent
|
64
|
+
end
|
65
|
+
|
66
|
+
def turn=(turn)
|
67
|
+
@turn = turn
|
68
|
+
end
|
69
|
+
|
70
|
+
def opponent=(opponent)
|
71
|
+
@opponent = opponent
|
72
|
+
end
|
73
|
+
end
|
data/lib/chess/king.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require_relative 'piece.rb'
|
2
|
+
|
3
|
+
class King < Piece
|
4
|
+
|
5
|
+
WHITE_KING_ICON = "\u2654"
|
6
|
+
BLACK_KING_ICON = "\u265A"
|
7
|
+
|
8
|
+
def initialize(color = "white", location = "A1")
|
9
|
+
case color.downcase
|
10
|
+
when "white"
|
11
|
+
@icon = WHITE_KING_ICON
|
12
|
+
when "black"
|
13
|
+
@icon = BLACK_KING_ICON
|
14
|
+
end
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def valid_move?(new_loc, board)
|
19
|
+
x, y = new_loc - @location
|
20
|
+
if x.abs <= 1 && y.abs <= 1
|
21
|
+
elsif check_castle(self, board, x, y) && !self.check?(board)
|
22
|
+
else
|
23
|
+
board.move_status = "Kings cannot make that move."
|
24
|
+
return false
|
25
|
+
end
|
26
|
+
super(new_loc, board)
|
27
|
+
end
|
28
|
+
|
29
|
+
def check?(board)
|
30
|
+
board.each do |loc, piece|
|
31
|
+
if piece.opponent?(self) && piece.valid_move?(@location, board)
|
32
|
+
return loc, @location
|
33
|
+
end
|
34
|
+
end
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
|
38
|
+
def checkmate?(board)
|
39
|
+
# the king must be in check to be in checkmate
|
40
|
+
unless self.check?(board)
|
41
|
+
return false
|
42
|
+
end
|
43
|
+
|
44
|
+
kings_men = []
|
45
|
+
squares = []
|
46
|
+
|
47
|
+
# check all possible moves and see if the king is still in check
|
48
|
+
board.board.keys.each do |loc_1|
|
49
|
+
board.board.keys.each do |loc_2|
|
50
|
+
if board[loc_1].friend?(self)
|
51
|
+
still_in_check = self.will_be_in_check?(loc_1, loc_2, board)
|
52
|
+
if still_in_check.nil?
|
53
|
+
next
|
54
|
+
elsif !still_in_check
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
return true
|
62
|
+
end
|
63
|
+
|
64
|
+
def stalemate?(board)
|
65
|
+
# the king must be in check to be in checkmate
|
66
|
+
if self.check?(board)
|
67
|
+
return false
|
68
|
+
end
|
69
|
+
|
70
|
+
kings_men = []
|
71
|
+
squares = []
|
72
|
+
|
73
|
+
# check all possible moves and see if the king is put in check
|
74
|
+
board.board.keys.each do |loc_1|
|
75
|
+
board.board.keys.each do |loc_2|
|
76
|
+
if board[loc_1].friend?(self)
|
77
|
+
still_in_check = self.will_be_in_check?(loc_1, loc_2, board)
|
78
|
+
if still_in_check.nil?
|
79
|
+
next
|
80
|
+
elsif still_in_check == false
|
81
|
+
return false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
return true
|
88
|
+
end
|
89
|
+
|
90
|
+
def will_be_in_check?(loc_1, loc_2, board)
|
91
|
+
test_board = Marshal.load(Marshal.dump(board))
|
92
|
+
test_king = test_board[@location]
|
93
|
+
if test_board.move(loc_1, loc_2)
|
94
|
+
check = test_king.check?(test_board)
|
95
|
+
return check
|
96
|
+
else
|
97
|
+
return nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
data/lib/chess/knight.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative 'piece.rb'
|
2
|
+
|
3
|
+
class Knight < Piece
|
4
|
+
|
5
|
+
WHITE_KNIGHT_ICON = "\u2658"
|
6
|
+
BLACK_KNIGHT_ICON = "\u265E"
|
7
|
+
|
8
|
+
def initialize(color = "white", location = "A1")
|
9
|
+
case color.downcase
|
10
|
+
when "white"
|
11
|
+
@icon = WHITE_KNIGHT_ICON
|
12
|
+
when "black"
|
13
|
+
@icon = BLACK_KNIGHT_ICON
|
14
|
+
end
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def valid_move?(new_loc, board)
|
19
|
+
x, y = new_loc - @location
|
20
|
+
if (x.abs == 2 && y.abs == 1) || (x.abs == 1 && y.abs == 2)
|
21
|
+
else
|
22
|
+
board.move_status = "Knights cannot make that move."
|
23
|
+
return false
|
24
|
+
end
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|