ruby-chess 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1452333c430641f1e9a88d3fc161f93088a77711
4
+ data.tar.gz: fc97265dbdb0b88f318f62f3154b29f9d26cd770
5
+ SHA512:
6
+ metadata.gz: 298dd4574960b152a2492067d73b066663494f14efe1654f44a2871a1379e6e5edf8fe83c28924dfd53809419810ebaf24f12e4ecf9619e8c0a9812717a708db
7
+ data.tar.gz: d17700fb66c60e0e9e95bfe97cfd98a4465075395bfaf85b7fa4d9bf94d4a99da58184a414a1259c2e4ddf8de2fb490f440da9d827d625ef3be4452d4cc1bfaa
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 John Hager
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ chess
2
+ =====
3
+
4
+ Chess in ruby
data/bin/ruby-chess.rb ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/chess'
4
+
5
+ game = Game.new
6
+
7
+ def game.prompt_for_turn
8
+ puts "Resign or enter piece coordinates then move coordinate."
9
+ print "Format: x,y:a,b for move (e.g. 3,1:3,3): "
10
+ ans = gets.chomp.downcase.gsub(/\s/, '')
11
+
12
+ if ans =~ /res/
13
+ self.resign
14
+ sleep(3)
15
+ nil
16
+ else
17
+ ans.split(':').map do |point|
18
+ point.split(',').map {|i| i.to_i}
19
+ end
20
+ end
21
+ end
22
+
23
+ system "clear"
24
+ game.show
25
+
26
+ until game.over?
27
+ begin
28
+ puts "#{game.turn.to_s.capitalize}'s turn"
29
+ ans = game.prompt_for_turn
30
+ game.play(*ans) if ans
31
+
32
+ system "clear"
33
+ game.show
34
+ rescue Game::IllegalMove
35
+ next
36
+ end
37
+ end
38
+
39
+ system "clear"
data/lib/bishop.rb ADDED
@@ -0,0 +1,43 @@
1
+ module Bishop
2
+
3
+ extend self
4
+ include Moves
5
+
6
+ def illegal(board,x,y)
7
+ possible_moves = []
8
+
9
+ 7.times do |coord|
10
+ possible_moves += Board.board_safe(
11
+ diagonal(coord)
12
+ )
13
+ end
14
+
15
+ if not(possible_moves.any? {|move| move == [x,y]})
16
+ raise Game::IllegalMove, "#{x},#{y} is not a possible move"
17
+ elsif board.at(x,y).friend_of?(self)
18
+ raise Game::IllegalMove, "#{x},#{y} is occupied by a friend"
19
+ elsif jumped?(board,x,y)
20
+ raise Game::IllegalMove, "Bishops cannot jump"
21
+ else
22
+ :legal_move
23
+ end
24
+ end
25
+ end
26
+
27
+ class WhiteBishop < WhitePiece
28
+
29
+ include Bishop
30
+
31
+ def to_s
32
+ "WB"
33
+ end
34
+ end
35
+
36
+ class BlackBishop < BlackPiece
37
+
38
+ include Bishop
39
+
40
+ def to_s
41
+ "BB"
42
+ end
43
+ end
data/lib/board.rb ADDED
@@ -0,0 +1,78 @@
1
+ class Board
2
+
3
+ BackRow = { white: 7, black: 0 }
4
+
5
+ attr_accessor :board, :en_passent
6
+ def initialize(flag = false)
7
+ @board = []
8
+ 8.times do |y|
9
+ row = []
10
+ 8.times {|x| row << EmptySpace.new(x,y)}
11
+ @board << row
12
+ end
13
+
14
+ unless flag
15
+ pieces = []
16
+ 8.times do |x|
17
+ pieces << BlackPawn.new(x,6) << WhitePawn.new(x,1)
18
+ end
19
+
20
+ [0,7].each do |x|
21
+ pieces << BlackRook.new(x,7) << WhitePawn.new(x,0)
22
+ end
23
+
24
+ [1,6].each do |x|
25
+ pieces << BlackKnight.new(x,7) << WhiteKnight.new(x,0)
26
+ end
27
+
28
+ [2,5].each do |x|
29
+ pieces << BlackBishop.new(x,7) << WhiteBishop.new(x,0)
30
+ end
31
+
32
+ pieces << BlackQueen.new(4,7) << WhiteQueen.new(3,0)
33
+ pieces << BlackKing.new(3,7) << WhiteKing.new(4,0)
34
+
35
+ pieces.each {|piece| place(piece)}
36
+ end
37
+
38
+ @en_passent = NullSpace.new
39
+ end
40
+
41
+ def place(piece)
42
+ piece.place_on(self)
43
+ end
44
+
45
+ def remove(piece)
46
+ piece.remove_from(self)
47
+ end
48
+
49
+ def at(x,y)
50
+ board[y][x]
51
+ end
52
+
53
+ def enemies(piece)
54
+ board.flatten.select {|other| other.enemy_of?(piece)}
55
+ end
56
+
57
+ def move(piece, x, y)
58
+ piece.move(self, x, y)
59
+ end
60
+
61
+ def self.board_safe(points)
62
+ points.select do |point|
63
+ not(point.any? {|cord| cord < 0 or cord > 7})
64
+ end
65
+ end
66
+
67
+ def to_s
68
+ out = "\s\s"
69
+ out << (0..7).to_a.join(' ') << "\n"
70
+
71
+ @board.each_with_index do |row, i|
72
+ out << "#{i} " << (row.collect{|pc| pc.to_s}).join('') << "\n"
73
+ end
74
+
75
+ out
76
+ end
77
+ end
78
+
data/lib/chess.rb ADDED
@@ -0,0 +1,11 @@
1
+ require_relative 'board'
2
+ require_relative 'space'
3
+ require_relative 'moves'
4
+ require_relative 'piece'
5
+ require_relative 'pawn'
6
+ require_relative 'rook'
7
+ require_relative 'knight'
8
+ require_relative 'bishop'
9
+ require_relative 'queen'
10
+ require_relative 'king'
11
+ require_relative 'game'
data/lib/game.rb ADDED
@@ -0,0 +1,81 @@
1
+ class Game
2
+
3
+ attr_reader :turn
4
+ def initialize
5
+ @board = Board.new
6
+ @kings = {white: @board.at(4,0), black: @board.at(3,7)}
7
+ @moves = []
8
+ @turn = :white
9
+ @over = false
10
+ end
11
+
12
+ def show
13
+ puts @board
14
+ end
15
+
16
+ def over?
17
+ @over
18
+ end
19
+
20
+ def at(x,y)
21
+ @board.at(x,y)
22
+ end
23
+
24
+ def undo
25
+ move = @moves.pop
26
+ piece = at(*move[:piece][:point_a])
27
+ new_piece = piece.class.new(*move[:piece][:point_b])
28
+ capture = move[:capture]
29
+
30
+ @board.remove(piece)
31
+ @board.place(new_piece)
32
+ @board.place(capture)
33
+
34
+ switch_turn
35
+ end
36
+
37
+ def play(point_a, point_b)
38
+ raise Game::IllegalMove, "Game over" if @over
39
+
40
+ piece, at_destination = at(*point_a), at(*point_b)
41
+
42
+ unless piece.color == @turn
43
+ raise Game::IllegalMove,
44
+ "It is #{@turn.to_s.capitalize}'s turn"
45
+ end
46
+
47
+ @board.move(piece, *point_b)
48
+ @moves << {
49
+ piece: {
50
+ point_a: point_a,
51
+ point_b: point_b,
52
+ },
53
+ capture: at_destination,
54
+ }
55
+
56
+ if @kings[@turn].checked?(@board)
57
+ undo
58
+ raise Game::IllegalMove, "#{@turn}'s king is in check!"
59
+ end
60
+
61
+ switch_turn
62
+ end
63
+
64
+ def resign
65
+ @over = true
66
+ print "#{@turn.to_s.capitalize} resigns, "
67
+ switch_turn
68
+ puts "#{@turn.to_s.capitalize} wins!"
69
+ end
70
+
71
+ private
72
+ def switch_turn
73
+ @turn = @turn == :white ? :black : :white
74
+ end
75
+
76
+ public
77
+ class IllegalMove < Exception
78
+ end
79
+ end
80
+
81
+
data/lib/king.rb ADDED
@@ -0,0 +1,107 @@
1
+ module King
2
+
3
+ extend self
4
+ include Moves
5
+
6
+ def illegal(board,x,y)
7
+ x_diff = x-@x
8
+ possible_moves = []
9
+
10
+ possible_moves += Board.board_safe(
11
+ diagonal(1) + horizontal(1) + vertical(1)
12
+ )
13
+
14
+ if @start #castling
15
+ possible_moves += [
16
+ [@x-(2 * @direction), @y], [@x+(2 * @direction), @y]
17
+ ]
18
+ end
19
+
20
+ if not(possible_moves.any? {|move| move == [x,y]})
21
+ raise Game::IllegalMove, "#{x},#{y} is not a possible move"
22
+ elsif board.at(x,y).friend_of?(self)
23
+ raise Game::IllegalMove, "#{x},#{y} is occupied by a friend"
24
+ elsif jumped?(board,x,y)
25
+ raise Game::IllegalMove, "Kings can't jump"
26
+ elsif checked?(board,x,y,x_diff)
27
+ raise Game::IllegalMove, "King can't move into check"
28
+ else
29
+ castle(board,x,y,x_diff) if (x_diff).abs > 1
30
+ :legal_move
31
+ end
32
+ end
33
+
34
+ def castle(board,x,y,x_diff)
35
+ direction = x_diff/x_diff.abs
36
+
37
+ # get rook, raise illegal move unless start, else move the rook
38
+ if direction > 0 # right
39
+ rook = board.at(7,y)
40
+ rook_x = x-1
41
+ else # left
42
+ rook = board.at(0,y)
43
+ rook_x = x+1
44
+ end
45
+
46
+ if rook.is_a?(Rook) && rook.friend_of?(self) && rook.start
47
+ board.move(rook,rook_x,y)
48
+ else
49
+ raise Game::IllegalMove, "King cannot castle this way, now"
50
+ end
51
+ end
52
+
53
+ def checked?(board,x = @x, y = @y, x_diff = 0)
54
+ enemies = board.enemies(self)
55
+ spaces = [[x,y]]
56
+
57
+ if x_diff.abs > 1 #castling
58
+ spaces << [x - 1*(x_diff/x_diff.abs), y]
59
+ end
60
+
61
+ enemies.each do |enemy|
62
+ spaces.each do |space|
63
+ begin
64
+ enemy.illegal(board,*space)
65
+ return true
66
+ rescue Game::IllegalMove
67
+ :not_checked
68
+ end
69
+ end
70
+ end
71
+
72
+ return false
73
+ end
74
+ end
75
+
76
+ class WhiteKing < WhitePiece
77
+
78
+ include King
79
+
80
+ attr_reader :start
81
+ def initialize(x,y)
82
+ super
83
+ @start = true
84
+ @direction = 1
85
+ end
86
+
87
+ def to_s
88
+ "W!"
89
+ end
90
+ end
91
+
92
+ class BlackKing < BlackPiece
93
+
94
+ include King
95
+
96
+ attr_reader :start
97
+ def initialize(x,y)
98
+ super
99
+ @start = true
100
+ @direction = -1
101
+ end
102
+
103
+ def to_s
104
+ "B!"
105
+ end
106
+ end
107
+
data/lib/knight.rb ADDED
@@ -0,0 +1,45 @@
1
+ module Knight
2
+
3
+ extend self
4
+ include Moves
5
+
6
+ def illegal(board,x,y)
7
+ possible_moves = [
8
+ [@x+2, @y-1],
9
+ [@x+2, @y+1],
10
+ [@x-2, @y+1],
11
+ [@x-2, @y-1],
12
+ [@x+1, @y-2],
13
+ [@x-1, @y-2],
14
+ [@x+1, @y+2],
15
+ [@x-1, @y+2],
16
+ ]
17
+
18
+ if not(possible_moves.any? {|move| move == [x,y]})
19
+ raise Game::IllegalMove, "#{x},#{y} is not a possible move"
20
+ elsif board.at(x,y).friend_of?(self)
21
+ raise Game::IllegalMove, "#{x},#{y} is occupied by a friend"
22
+ else
23
+ :legal_move
24
+ end
25
+ end
26
+ end
27
+
28
+ class WhiteKnight < WhitePiece
29
+
30
+ include Knight
31
+
32
+ def to_s
33
+ "WK"
34
+ end
35
+ end
36
+
37
+ class BlackKnight < BlackPiece
38
+
39
+ include Knight
40
+
41
+ def to_s
42
+ "BK"
43
+ end
44
+ end
45
+
data/lib/moves.rb ADDED
@@ -0,0 +1,37 @@
1
+ module Moves
2
+
3
+ def move(board,x,y)
4
+ illegal(board,x,y)
5
+
6
+ remove_from(board)
7
+ @x,@y = x,y
8
+ place_on(board)
9
+
10
+ @start &&= false
11
+ end
12
+
13
+ def diagonal(coord)
14
+ [
15
+ [@x-coord, @y-coord],
16
+ [@x+coord, @y-coord],
17
+ [@x-coord, @y+coord],
18
+ [@x+coord, @y+coord],
19
+ ]
20
+ end
21
+
22
+ def horizontal(coord)
23
+ [
24
+ [@x-coord, @y],
25
+ [@x+coord, @y],
26
+ ]
27
+ end
28
+
29
+ def vertical(coord)
30
+ [
31
+ [@x, @y-coord],
32
+ [@x, @y+coord],
33
+ ]
34
+ end
35
+ end
36
+
37
+
data/lib/pawn.rb ADDED
@@ -0,0 +1,111 @@
1
+ module Pawn
2
+
3
+ extend self
4
+
5
+ attr_reader :direction
6
+
7
+ # attempt to move the piece to x,y on the board
8
+ def move(board,x,y)
9
+ # check if it is an illegal move (raises Game::IllegalMove if it is)
10
+ illegal(board, x, y)
11
+
12
+ # make start false if it not already false
13
+ !@start || @start = false
14
+
15
+ # remove the piece from it's original position
16
+ board.remove(self)
17
+
18
+ # save the piece if it can be taken by en passent
19
+ board.en_passent = (@y-y).abs == 2 ? self : NullSpace.new
20
+
21
+ # update the coordinates
22
+ @x, @y = x, y
23
+
24
+ # replace with queen if on back row
25
+ if @y == Board::BackRow[color]
26
+ board.place(@queen.new(@x,@y))
27
+ else
28
+ # or place on board
29
+ place_on(board)
30
+ end
31
+ end
32
+
33
+ def illegal(board,x,y)
34
+ possible_moves = [
35
+ [@x, @y+(1 * @direction)],
36
+ ]
37
+ possible_moves << [@x, @y+(2 * @direction)] if @start
38
+ possible_moves = Board.board_safe(possible_moves)
39
+
40
+ possible_attack_moves = Board.board_safe([
41
+ [@x-1, @y+(1 * @direction)],
42
+ [@x+1, @y+(1 * @direction)]
43
+ ])
44
+
45
+ en_passent = board.en_passent
46
+ en_passent_coord = en_passent.to_coord
47
+ en_passent_coord[1] &&= en_passent_coord[1] - en_passent.direction
48
+
49
+ if board.at(x,y).friend_of?(self)
50
+ error = "Your piece is blocking your path"
51
+ elsif board.at(x,y).enemy_of?(self)
52
+ unless possible_attack_moves.find {|move| move == [x,y]}
53
+ error = "Cannot move this way!"
54
+ end
55
+ elsif en_passent_coord == [x,y]
56
+ unless possible_attack_moves.find {|move| move == [x,y]}
57
+ error = "Cannot move this way!"
58
+ else
59
+ en_passent.remove_from(board)
60
+ end
61
+ else
62
+ unless possible_moves.find {|move| move == [x,y]}
63
+ error = "Cannot move here!"
64
+ else
65
+ if jumped?(board,x,y)
66
+ raise Game::IllegalMove, "Pawns cannot jump!"
67
+ end
68
+ end
69
+ end
70
+
71
+ raise Game::IllegalMove, error if error
72
+ end
73
+
74
+ def jumped?(board,x,y)
75
+ return false unless (@y-y).abs == 2
76
+ !board.at(x,y-@direction).empty?
77
+ end
78
+ end
79
+
80
+ class WhitePawn < WhitePiece
81
+
82
+ include Pawn
83
+
84
+ def initialize(x,y)
85
+ super
86
+ @start = true
87
+ @direction = 1
88
+ @queen = WhiteQueen
89
+ end
90
+
91
+ def to_s
92
+ "WP"
93
+ end
94
+ end
95
+
96
+ class BlackPawn < BlackPiece
97
+
98
+ include Pawn
99
+
100
+ def initialize(x,y)
101
+ super
102
+ @start = true
103
+ @direction = -1
104
+ @queen = BlackQueen
105
+ end
106
+
107
+ def to_s
108
+ "BP"
109
+ end
110
+ end
111
+
data/lib/piece.rb ADDED
@@ -0,0 +1,74 @@
1
+ class Piece < EmptySpace
2
+
3
+ private
4
+ def jumped?(board,x,y)
5
+ start, new = self.to_coord, [x,y]
6
+
7
+ # find all the spaces ([x,y]) between start and new
8
+ x_diff = (start.first - new.first)
9
+ y_diff = (start.last - new.last)
10
+
11
+ moves_between = []
12
+
13
+ if x_diff == 0 # vertical movement
14
+ multiplyer = y_diff / y_diff.abs
15
+
16
+ ((y_diff.abs)-1).times do |aug|
17
+ aug = (aug + 1) * multiplyer
18
+ moves_between << [x, y + aug]
19
+ end
20
+
21
+ elsif y_diff == 0 # horizontal movement
22
+ multiplyer = x_diff / x_diff.abs
23
+
24
+ ((x_diff.abs)-1).times do |aug|
25
+ aug = (aug + 1) * multiplyer
26
+ moves_between << [x + aug, y]
27
+ end
28
+ else # diagonal movement
29
+ y_multiplyer = y_diff / y_diff.abs
30
+ x_multiplyer = x_diff / x_diff.abs
31
+
32
+ ((x_diff.abs)-1).times do |aug|
33
+ y_aug = (aug + 1) * y_multiplyer
34
+ x_aug = (aug + 1) * x_multiplyer
35
+ moves_between << [x + x_aug, y + y_aug]
36
+ end
37
+ end
38
+
39
+ moves_between.any? {|coord| !board.at(*coord).empty?}
40
+ end
41
+ end
42
+
43
+ class BlackPiece < Piece
44
+
45
+ def initialize(x,y)
46
+ super
47
+ @color = :black
48
+ end
49
+
50
+ def enemy_of?(piece)
51
+ piece.color == :white
52
+ end
53
+
54
+ def friend_of?(piece)
55
+ piece.color == color
56
+ end
57
+ end
58
+
59
+ class WhitePiece < Piece
60
+
61
+ def initialize(x,y)
62
+ super
63
+ @color = :white
64
+ end
65
+
66
+ def enemy_of?(piece)
67
+ piece.color == :black
68
+ end
69
+
70
+ def friend_of?(piece)
71
+ piece.color == color
72
+ end
73
+ end
74
+
data/lib/queen.rb ADDED
@@ -0,0 +1,43 @@
1
+ module Queen
2
+
3
+ extend self
4
+ include Moves
5
+
6
+ def illegal(board,x,y)
7
+ possible_moves = []
8
+
9
+ 7.times do |coord|
10
+ possible_moves += Board.board_safe(
11
+ diagonal(coord) + horizontal(coord) + vertical(coord)
12
+ )
13
+ end
14
+
15
+ if not(possible_moves.any? {|move| move == [x,y]})
16
+ raise Game::IllegalMove, "#{x},#{y} is not a possible move"
17
+ elsif board.at(x,y).friend_of?(self)
18
+ raise Game::IllegalMove, "#{x},#{y} is occupied by a friend"
19
+ elsif jumped?(board,x,y)
20
+ raise Game::IllegalMove, "Queens cannot jump"
21
+ else
22
+ :legal_move
23
+ end
24
+ end
25
+ end
26
+
27
+ class WhiteQueen < WhitePiece
28
+
29
+ include Queen
30
+
31
+ def to_s
32
+ "WQ"
33
+ end
34
+ end
35
+
36
+ class BlackQueen < BlackPiece
37
+
38
+ include Queen
39
+
40
+ def to_s
41
+ "BQ"
42
+ end
43
+ end