chess_game 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- 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
|