bchess 0.1.1

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.
@@ -0,0 +1,21 @@
1
+ module Bchess
2
+ class Game
3
+ attr_reader :fen, :board, :moves
4
+
5
+ def initialize(fen='')
6
+ @fen = fen
7
+ @board = Board.new(fen)
8
+ @moves = []
9
+ end
10
+
11
+ def add_move(move)
12
+ moves << move
13
+ end
14
+
15
+ def validate_game
16
+ moves.all? do |move|
17
+ board.execute(move)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,43 @@
1
+ module Bchess
2
+ module BoardHelpers
3
+ def field(column, row)
4
+ (column+97).chr + (row+1).to_s
5
+ end
6
+
7
+ def invalid_data?(piece, column, row)
8
+ piece.nil? || !(0..7).include?(column) || !(0..7).include?(row)
9
+ end
10
+
11
+ def castle_detected?(piece, column)
12
+ piece.kind_of?(Bchess::King) && piece.column == 4 && (column - piece.column).abs == 2
13
+ end
14
+
15
+ def promotion_detected?(piece, row)
16
+ piece.kind_of?(Bchess::Pawn) && (row == 0 || row == 7)
17
+ end
18
+
19
+ def en_passant_detected?(piece, column, row)
20
+ piece.kind_of?(Bchess::Pawn) && piece.row != row && piece.column != column && at(column, row).nil? && piece.can_take_on_field?(column, row)
21
+ end
22
+
23
+ def pawn_long_move_detected?(piece, row)
24
+ piece.kind_of?(Bchess::Pawn) && (piece.row - row).abs == 2
25
+ end
26
+
27
+ def kings_present?
28
+ !!king(Bchess::WHITE) && !!king(Bchess::BLACK)
29
+ end
30
+
31
+ def to_row(row)
32
+ row.to_i - 1
33
+ end
34
+
35
+ def to_column(column)
36
+ column.bytes.first - 97
37
+ end
38
+
39
+ def field(column, row)
40
+ (column+97).chr + (row+1).to_s
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,58 @@
1
+ module CastleHelpers
2
+ def castle(piece, column, row)
3
+ validate_castle(piece, column)
4
+ update_castles_after_king_move(piece.color)
5
+ execute_castle(piece, column, row)
6
+ end
7
+
8
+ def validate_castle(piece, column)
9
+ !piece.moved && !rook_moved?(piece, column) && fen_allows?(piece,column)
10
+ end
11
+
12
+ def execute_castle(piece, column, row)
13
+ piece.move(column, row)
14
+ lngth = @short_castle ? 2 : 3
15
+ @castling_rook = castling_rook(piece, column)
16
+ @castling_rook.move((@castling_rook.column - lngth).abs, row)
17
+ end
18
+
19
+ def white_castle(column)
20
+ if short_castle?(column)
21
+ @short_castle = true
22
+ at(7,0)
23
+ else
24
+ @short_castle = false
25
+ at(0,0)
26
+ end
27
+ end
28
+
29
+ def black_castle(column)
30
+ if short_castle?(column)
31
+ @short_castle = true
32
+ at(7,7)
33
+ else
34
+ @short_castle = false
35
+ at(0,7)
36
+ end
37
+ end
38
+
39
+ def rook_moved?(piece, column)
40
+ castling_rook(piece, column)&.moved
41
+ end
42
+
43
+ def castling_rook(piece, column)
44
+ select_rook(piece, column)
45
+ end
46
+
47
+ def select_rook(piece, column)
48
+ if piece.row == 0
49
+ white_castle(column)
50
+ elsif piece.row == 7
51
+ black_castle(column)
52
+ end
53
+ end
54
+
55
+ def short_castle?(column)
56
+ column == 6
57
+ end
58
+ end
@@ -0,0 +1,22 @@
1
+ module EnPassantHelpers
2
+ def validate_en_passant(piece, column, row)
3
+ #TODO
4
+ true
5
+ end
6
+
7
+ def execute_en_passant(piece, row, column)
8
+ piece.move(row, column)
9
+ remove_old_piece(*remove_en_passant(piece, *transform_field(@en_passant)), piece.color)
10
+ end
11
+
12
+ def remove_en_passant(piece, column, row)
13
+ direction = piece.color == Bchess::WHITE ? 1 : -1
14
+ [column, row - direction]
15
+ end
16
+
17
+ def long_pawn_move(piece, column, row)
18
+ piece.move(column, row)
19
+ direction = piece.color == Bchess::WHITE ? 1 : -1
20
+ @en_passant = field(column, (row + piece.row)/2 - direction)
21
+ end
22
+ end
@@ -0,0 +1,141 @@
1
+ module Bchess
2
+ module FenHelpers
3
+ def fen_hash
4
+ {
5
+ 'k': {klass: Bchess::King, color: Bchess::BLACK},
6
+ 'q': {klass: Bchess::Queen, color: Bchess::BLACK},
7
+ 'r': {klass: Bchess::Rook, color: Bchess::BLACK},
8
+ 'b': {klass: Bchess::Bishop, color: Bchess::BLACK},
9
+ 'n': {klass: Bchess::Knight, color: Bchess::BLACK},
10
+ 'p': {klass: Bchess::Pawn, color: Bchess::BLACK},
11
+ 'K': {klass: Bchess::King, color: Bchess::WHITE},
12
+ 'Q': {klass: Bchess::Queen, color: Bchess::WHITE},
13
+ 'R': {klass: Bchess::Rook, color: Bchess::WHITE},
14
+ 'B': {klass: Bchess::Bishop, color: Bchess::WHITE},
15
+ 'N': {klass: Bchess::Knight, color: Bchess::WHITE},
16
+ 'P': {klass: Bchess::Pawn, color: Bchess::WHITE},
17
+ }
18
+ end
19
+
20
+ def write_fen
21
+ result = ''
22
+ 7.downto(0) do |i|
23
+ line_pieces = pieces.select{ |p| p.row == i }
24
+ one_line = create_fen_line(line_pieces)
25
+ result << one_line
26
+ result << '/' unless i == 0
27
+ end
28
+ result.concat(additional_info)
29
+ end
30
+
31
+ def create_fen_line(pieces)
32
+ line = ''
33
+ counter = 0
34
+
35
+ 0.upto(7) do |i|
36
+ piece = pieces.select{ |p| p.column == i }.first
37
+ if !!piece
38
+ if counter > 0
39
+ line.concat(counter.to_s)
40
+ counter = 0
41
+ line.concat(to_fen(piece))
42
+ else
43
+ line.concat(to_fen(piece))
44
+ end
45
+ else
46
+ counter += 1
47
+ end
48
+ end
49
+ line.concat(counter.to_s) if counter > 0
50
+ line
51
+ end
52
+
53
+ def set_pieces(board)
54
+ pieces.clear
55
+ board.split("/").each_with_index do |line, index|
56
+ column = 0
57
+ line.each_char do |char|
58
+ if char.to_i != 0
59
+ column += char.to_i - 1
60
+ else
61
+ pieces << fen_hash[char.to_sym][:klass].new(fen_hash[char.to_sym][:color], column, 7-index)
62
+ end
63
+ column += 1
64
+ end
65
+ end
66
+ end
67
+
68
+ def fen_allows?(piece, column)
69
+ if piece.color == Bchess::WHITE
70
+ if column == 6
71
+ castles.chars.include?('k')
72
+ else
73
+ castles.chars.include?('q')
74
+ end
75
+ else
76
+ if column == 6
77
+ castles.chars.include?('K')
78
+ else
79
+ castles.chars.include?('Q')
80
+ end
81
+ end
82
+ end
83
+
84
+ def to_fen(piece)
85
+ fen_hash.key({ :klass => piece.class, :color => piece.color }).to_s
86
+ end
87
+
88
+ def change_halfmove_clock(piece)
89
+ if piece.kind_of?(Bchess::Pawn)
90
+ @halfmove_clock = 0
91
+ else
92
+ @halfmove_clock = halfmove_clock + 1
93
+ end
94
+ end
95
+
96
+ def update_castles_after_move(piece)
97
+ if piece == Bchess::King
98
+ update_castles_after_king_move(piece.color)
99
+ elsif piece == Bchess::Rook
100
+ update_castles_after_rook_move(piece)
101
+ end
102
+ @castles = '-' if @castles == ''
103
+ end
104
+
105
+ def update_castles_after_king_move(color)
106
+ if color == Bchess::WHITE
107
+ @castles.gsub!('K', '').gsub!('Q', '')
108
+ else
109
+ @castles.gsub!('k', '').gsub!('q', '')
110
+ end
111
+ end
112
+
113
+ def change_move_number
114
+ @move_number = move_number + 1
115
+ end
116
+
117
+ def set_to_move(fen_colors)
118
+ @to_move = fen_colors == 'w' ? Bchess::WHITE : Bchess::BLACK
119
+ end
120
+
121
+ def set_castles(fen_castles)
122
+ @castles = fen_castles
123
+ end
124
+
125
+ def set_en_passant(fen_en_passant)
126
+ @en_passant = fen_en_passant
127
+ end
128
+
129
+ def set_halfmove_clock(fen_halfmove_clock)
130
+ @halfmove_clock = fen_halfmove_clock.to_i
131
+ end
132
+
133
+ def set_move_number(fen_move_number)
134
+ @move_number = fen_move_number.to_i
135
+ end
136
+
137
+ def additional_info
138
+ " #{to_move} #{castles} #{en_passant} #{halfmove_clock} #{move_number}"
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,50 @@
1
+ module FieldBetweenHelpers
2
+ def row_fields(dcolumn, drow)
3
+ smaller, bigger = [column, dcolumn].sort
4
+ (smaller+1..bigger-1).map{|c| [c, row] }
5
+ end
6
+
7
+ def column_fields(dcolumn, drow)
8
+ smaller, bigger = [row, drow].sort
9
+ (smaller+1..bigger-1).map{|r| [column, r] }
10
+ end
11
+
12
+ def diagonal_fields(dcolumn, drow)
13
+ fields = []
14
+ if dcolumn > column
15
+ if drow > row
16
+ (dcolumn - column - 1).times do |i|
17
+ fields << [dcolumn-(i+1), drow-(i+1)]
18
+ end
19
+ else
20
+ (dcolumn - column - 1).times do |i|
21
+ fields << [dcolumn-(i+1), drow+(i+1)]
22
+ end
23
+ end
24
+ else
25
+ if drow > row
26
+ (column - dcolumn - 1).times do |i|
27
+ fields << [dcolumn+(i+1), drow-(i+1)]
28
+ end
29
+ else
30
+ (column - dcolumn - 1).times do |i|
31
+ fields << [dcolumn+(i+1), drow+(i+1)]
32
+ end
33
+ end
34
+ end
35
+ fields
36
+ end
37
+
38
+ def fields_between(dcolumn, drow)
39
+ return [] unless can_move_to_field?(dcolumn, drow)
40
+ if same_row?(drow)
41
+ row_fields(dcolumn, drow)
42
+ elsif same_column?(dcolumn)
43
+ column_fields(dcolumn, drow)
44
+ elsif same_diagonal?(dcolumn, drow)
45
+ diagonal_fields(dcolumn, drow)
46
+ else
47
+ []
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,16 @@
1
+ module Validations
2
+ def validate_move
3
+ if !valid_position?
4
+ read_fen
5
+ false
6
+ else
7
+ @fen = write_fen
8
+ true
9
+ end
10
+ end
11
+
12
+ def valid_position?
13
+ kings_present? &&
14
+ !king_attacked(just_moved)
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ module Bchess
2
+ class King < Piece
3
+
4
+ KINGS_REACH = 1
5
+
6
+ attr_reader :moved
7
+
8
+ def initiialize(*args)
9
+ super(args)
10
+ @moved = false
11
+ end
12
+
13
+ def name
14
+ 'K'
15
+ end
16
+
17
+ def move(dcolumn, drow)
18
+ super(dcolumn, drow)
19
+ @moved = true
20
+ end
21
+
22
+ def can_move_to_field?(dcolumn, drow)
23
+ super &&
24
+ (
25
+ by_line(dcolumn, drow, KINGS_REACH) ||
26
+ by_diagonal(dcolumn, drow, KINGS_REACH)
27
+ )
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,27 @@
1
+ module Bchess
2
+ class Knight < Piece
3
+
4
+ def initiialize(*args)
5
+ super(args)
6
+ end
7
+
8
+ def name
9
+ 'N'
10
+ end
11
+
12
+ def can_move_to_field?(dcolumn, drow)
13
+ super && by_jump(dcolumn, drow)
14
+ end
15
+
16
+ def fields_between(column, row)
17
+ []
18
+ end
19
+
20
+ private
21
+
22
+ def by_jump(dcolumn, drow)
23
+ (row - drow).abs + (column - dcolumn).abs == 3 &&
24
+ (row - drow).abs != 3 && (column - dcolumn).abs != 3
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,54 @@
1
+ module Bchess
2
+ class Pawn < Piece
3
+
4
+ def initiialize(*args)
5
+ super(args)
6
+ end
7
+
8
+ def name
9
+ ''
10
+ end
11
+
12
+ def valid?
13
+ super && pawn_position?
14
+ end
15
+
16
+ def can_move_to_field?(dcolumn, drow)
17
+ super &&
18
+ pawn_move(dcolumn, drow)
19
+ end
20
+
21
+ def can_take_on_field?(dcolumn, drow)
22
+ direction = white? ? 1 : -1
23
+ by_diagonal(dcolumn, drow, 1) && (drow - row) == direction
24
+ end
25
+
26
+ private
27
+
28
+ def pawn_move(dcolumn, drow)
29
+ row_diff?(dcolumn, drow) &&
30
+ direction_kept?(dcolumn, drow) &&
31
+ column_kept?(dcolumn, column)
32
+ end
33
+
34
+ def column_kept?(dcolumn, column)
35
+ column == dcolumn
36
+ end
37
+
38
+ def direction_kept?(dcolumn, drow)
39
+ white? ? drow > row : row > drow
40
+ end
41
+
42
+ def row_diff?(dcolumn, drow)
43
+ (row - drow).abs <= (starting_position? ? 2 : 1)
44
+ end
45
+
46
+ def starting_position?
47
+ row == (white? ? 1 : 6)
48
+ end
49
+
50
+ def pawn_position?
51
+ (1..6).include?(row)
52
+ end
53
+ end
54
+ end