chess_tui 0.41.4
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/README.md +39 -0
- data/bin/chess_tui +6 -0
- data/lib/chess/board/board.rb +92 -0
- data/lib/chess/board/board_from_fen.rb +136 -0
- data/lib/chess/board/board_pos.rb +65 -0
- data/lib/chess/display/board.rb +168 -0
- data/lib/chess/display/color.rb +38 -0
- data/lib/chess/display/prompt.rb +79 -0
- data/lib/chess/game/game.rb +165 -0
- data/lib/chess/game/valid_moves.rb +131 -0
- data/lib/chess/game/win_and_draw.rb +106 -0
- data/lib/chess/mouse/mouse_input.rb +87 -0
- data/lib/chess/mouse/mouse_position.rb +74 -0
- data/lib/chess/pieces/bishop.rb +78 -0
- data/lib/chess/pieces/king.rb +166 -0
- data/lib/chess/pieces/knight.rb +79 -0
- data/lib/chess/pieces/pawn.rb +163 -0
- data/lib/chess/pieces/piece.rb +29 -0
- data/lib/chess/pieces/queen.rb +44 -0
- data/lib/chess/pieces/rook.rb +78 -0
- data/lib/chess/play_game.rb +55 -0
- data/lib/chess/play_moves/play_king_moves.rb +58 -0
- data/lib/chess/play_moves/play_moves_by_type.rb +53 -0
- data/lib/chess/play_moves/play_pawn_moves.rb +115 -0
- data/lib/chess/player/player.rb +39 -0
- data/lib/chess/save/fen_from_board.rb +82 -0
- data/lib/chess/save/save.rb +31 -0
- data/lib/chess/save/serializer.rb +29 -0
- data/lib/chess/version.rb +6 -0
- data/lib/chess.rb +44 -0
- data/save.json +9 -0
- metadata +94 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Chess
|
|
4
|
+
module Chess
|
|
5
|
+
# Pieces
|
|
6
|
+
module Pieces
|
|
7
|
+
# King
|
|
8
|
+
class King < Piece
|
|
9
|
+
include Chess::Save::FenCodeFromBoard
|
|
10
|
+
|
|
11
|
+
attr_accessor :in_check
|
|
12
|
+
|
|
13
|
+
def initialize(color)
|
|
14
|
+
super
|
|
15
|
+
@in_check = false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# all possible_moves of Bishop
|
|
19
|
+
#
|
|
20
|
+
# @param board [Chess::Board]
|
|
21
|
+
# @return [Array] possible_moves_arr
|
|
22
|
+
def possible_moves(board)
|
|
23
|
+
file = @pos[0]
|
|
24
|
+
rank = @pos[1]
|
|
25
|
+
moves = []
|
|
26
|
+
moves += one_step_all_direction_moves(board, file, rank)
|
|
27
|
+
moves += castling_moves(board)
|
|
28
|
+
moves
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# gets one step possible move in all direction for king
|
|
32
|
+
#
|
|
33
|
+
# @param board [Chess::Board]
|
|
34
|
+
# @param file [String]
|
|
35
|
+
# @param rank [Integer]
|
|
36
|
+
# @return [Array] all_dir_possible_moves_arr
|
|
37
|
+
def one_step_all_direction_moves(board, file, rank)
|
|
38
|
+
north = board.north_pos(file, rank)
|
|
39
|
+
south = board.south_pos(file, rank)
|
|
40
|
+
west = board.west_pos(file, rank)
|
|
41
|
+
east = board.east_pos(file, rank)
|
|
42
|
+
north_west = board.north_west_pos(file, rank)
|
|
43
|
+
south_east = board.south_east_pos(file, rank)
|
|
44
|
+
south_west = board.south_west_pos(file, rank)
|
|
45
|
+
north_east = board.north_east_pos(file, rank)
|
|
46
|
+
directions = north, south, west, east, north_west, south_east, south_west, north_east
|
|
47
|
+
|
|
48
|
+
get_moves_on_direction(board, directions)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# helper method for {#one_step_all_direction_moves}
|
|
52
|
+
#
|
|
53
|
+
# @param board [Chess::Board]
|
|
54
|
+
# @param directions [Array]
|
|
55
|
+
# @return [Array] all_dir_possible_moves_arr
|
|
56
|
+
def get_moves_on_direction(board, directions)
|
|
57
|
+
moves = []
|
|
58
|
+
directions.each do |direction|
|
|
59
|
+
if board.pos_in_range?(direction) && (board.empty_at?(*direction) || board.enemy_at?(*direction))
|
|
60
|
+
moves << direction
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
moves
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# finds possible castling moves
|
|
67
|
+
#
|
|
68
|
+
# @param board [Chess::Board]
|
|
69
|
+
# @return [Array] castling_moves_arr
|
|
70
|
+
def castling_moves(board)
|
|
71
|
+
moves = []
|
|
72
|
+
if valid_castling_rights?(board)
|
|
73
|
+
if white?
|
|
74
|
+
moves += white_castling_moves(board)
|
|
75
|
+
elsif black?
|
|
76
|
+
moves += black_castling_moves(board)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
moves
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# check if valid_castling_rights exits
|
|
83
|
+
#
|
|
84
|
+
# @param board [Chess::Board]
|
|
85
|
+
def valid_castling_rights?(board)
|
|
86
|
+
return false if board.castling_rights == '-'
|
|
87
|
+
|
|
88
|
+
board.castling_rights.include?('K') ||
|
|
89
|
+
board.castling_rights.include?('Q') ||
|
|
90
|
+
board.castling_rights.include?('k') ||
|
|
91
|
+
board.castling_rights.include?('q')
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# helper method for {#castling_moves}
|
|
95
|
+
# @param (see #castling_moves)
|
|
96
|
+
# @return (see #castling_moves)
|
|
97
|
+
def white_castling_moves(board)
|
|
98
|
+
moves = []
|
|
99
|
+
moves << ['g', 0] if board.castling_rights.include?('K') && can_castle?(board, 'K')
|
|
100
|
+
moves << ['c', 0] if board.castling_rights.include?('Q') && can_castle?(board, 'Q')
|
|
101
|
+
moves
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# helper method for {#castling_moves}
|
|
105
|
+
# @param (see #castling_moves)
|
|
106
|
+
# @return (see #castling_moves)
|
|
107
|
+
def black_castling_moves(board)
|
|
108
|
+
moves = []
|
|
109
|
+
moves << ['g', 7] if board.castling_rights.include?('k') && can_castle?(board, 'k')
|
|
110
|
+
moves << ['c', 7] if board.castling_rights.include?('q') && can_castle?(board, 'q')
|
|
111
|
+
moves
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# check if can castle(no_piece_in_between) for the given castling_right
|
|
115
|
+
#
|
|
116
|
+
# @param board [Chess::Board]
|
|
117
|
+
# @param castling_right [String]
|
|
118
|
+
def can_castle?(board, castling_right)
|
|
119
|
+
case castling_right
|
|
120
|
+
when 'Q'
|
|
121
|
+
no_piece_in_between?(board, ['a', 0], ['e', 0])
|
|
122
|
+
when 'K'
|
|
123
|
+
no_piece_in_between?(board, ['e', 0], ['h', 0])
|
|
124
|
+
when 'q'
|
|
125
|
+
no_piece_in_between?(board, ['a', 7], ['e', 7])
|
|
126
|
+
when 'k'
|
|
127
|
+
no_piece_in_between?(board, ['e', 7], ['h', 7])
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# checks if no piece in between start and finish
|
|
132
|
+
# helper method for {#can_castle?}
|
|
133
|
+
#
|
|
134
|
+
# @param board [Chess::Board]
|
|
135
|
+
# @param start [Array]
|
|
136
|
+
# @param finish [Array]
|
|
137
|
+
def no_piece_in_between?(board, start, finish)
|
|
138
|
+
curr_file = start.first
|
|
139
|
+
rank = start.last
|
|
140
|
+
pieces = []
|
|
141
|
+
loop do
|
|
142
|
+
curr_file = (curr_file.ord + 1).chr
|
|
143
|
+
break if curr_file == finish.first
|
|
144
|
+
|
|
145
|
+
pieces << board.piece_at(curr_file, rank)
|
|
146
|
+
end
|
|
147
|
+
pieces.all?('')
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# checks if king is in check by one or more opponent_piece
|
|
151
|
+
#
|
|
152
|
+
# @param board [Chess::Board]
|
|
153
|
+
# @param opponent_pieces [Chess::Board#white_pieces, Chess::Board#black_pieces]
|
|
154
|
+
def in_check?(board, opponent_pieces)
|
|
155
|
+
@in_check = false
|
|
156
|
+
opponent_pieces.each do |piece|
|
|
157
|
+
break if @in_check
|
|
158
|
+
|
|
159
|
+
possible_moves = piece.possible_moves(board)
|
|
160
|
+
@in_check = true if possible_moves.include?(@pos)
|
|
161
|
+
end
|
|
162
|
+
@in_check
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Chess
|
|
4
|
+
module Chess
|
|
5
|
+
# Pieces
|
|
6
|
+
module Pieces
|
|
7
|
+
# Knight
|
|
8
|
+
class Knight < Piece
|
|
9
|
+
# all possible_moves of Knight
|
|
10
|
+
#
|
|
11
|
+
# @param board [Chess::Board]
|
|
12
|
+
# @return [Array] possible_moves_arr
|
|
13
|
+
def possible_moves(board)
|
|
14
|
+
file = @pos[0]
|
|
15
|
+
rank = @pos[1]
|
|
16
|
+
moves = []
|
|
17
|
+
moves += north_moves(board, file, rank)
|
|
18
|
+
moves += south_moves(board, file, rank)
|
|
19
|
+
moves += west_moves(board, file, rank)
|
|
20
|
+
moves += east_moves(board, file, rank)
|
|
21
|
+
moves
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def north_moves(board, file, rank)
|
|
25
|
+
moves = []
|
|
26
|
+
north_steps = 2
|
|
27
|
+
two_step_north = [file, rank + north_steps]
|
|
28
|
+
|
|
29
|
+
east = board.east_pos(*two_step_north)
|
|
30
|
+
west = board.west_pos(*two_step_north)
|
|
31
|
+
moves << east if board.pos_in_range?(east) && (board.empty_at?(*east) || board.enemy_at?(*east))
|
|
32
|
+
moves << west if board.pos_in_range?(west) && (board.empty_at?(*west) || board.enemy_at?(*west))
|
|
33
|
+
moves
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def south_moves(board, file, rank)
|
|
37
|
+
moves = []
|
|
38
|
+
south_steps = 2
|
|
39
|
+
two_step_south = [file, rank - south_steps]
|
|
40
|
+
|
|
41
|
+
return moves unless board.pos_in_range?(two_step_south)
|
|
42
|
+
|
|
43
|
+
east = board.east_pos(*two_step_south)
|
|
44
|
+
west = board.west_pos(*two_step_south)
|
|
45
|
+
[east, west].each do |pos|
|
|
46
|
+
moves << pos if board.pos_in_range?(pos) && (board.empty_at?(*pos) || board.enemy_at?(*pos))
|
|
47
|
+
end
|
|
48
|
+
moves
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def west_moves(board, file, rank)
|
|
52
|
+
moves = []
|
|
53
|
+
west_steps = 2
|
|
54
|
+
two_step_west = [(file.ord - west_steps).chr, rank]
|
|
55
|
+
|
|
56
|
+
return moves unless board.pos_in_range?(two_step_west)
|
|
57
|
+
|
|
58
|
+
north = board.north_pos(*two_step_west)
|
|
59
|
+
south = board.south_pos(*two_step_west)
|
|
60
|
+
[north, south].each do |pos|
|
|
61
|
+
moves << pos if board.pos_in_range?(pos) && (board.empty_at?(*pos) || board.enemy_at?(*pos))
|
|
62
|
+
end
|
|
63
|
+
moves
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def east_moves(board, file, rank)
|
|
67
|
+
moves = []
|
|
68
|
+
east_steps = 2
|
|
69
|
+
two_step_east = [(file.ord + east_steps).chr, rank]
|
|
70
|
+
north = board.north_pos(*two_step_east)
|
|
71
|
+
south = board.south_pos(*two_step_east)
|
|
72
|
+
[north, south].each do |pos|
|
|
73
|
+
moves << pos if board.pos_in_range?(pos) && (board.empty_at?(*pos) || board.enemy_at?(*pos))
|
|
74
|
+
end
|
|
75
|
+
moves
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Chess
|
|
4
|
+
module Chess
|
|
5
|
+
# Pieces
|
|
6
|
+
module Pieces
|
|
7
|
+
# Pawn
|
|
8
|
+
class Pawn < Piece
|
|
9
|
+
# possible_moves of the pawn
|
|
10
|
+
# @param board [Chess::Board]
|
|
11
|
+
# @return [Array] moves
|
|
12
|
+
def possible_moves(board)
|
|
13
|
+
moves = []
|
|
14
|
+
file = @pos[0]
|
|
15
|
+
rank = @pos[1]
|
|
16
|
+
moves += pawn_moves_by_color(board, file, rank)
|
|
17
|
+
moves
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# checks if it's first move of pawn
|
|
21
|
+
# @param rank [Integer]
|
|
22
|
+
def first_move?(rank)
|
|
23
|
+
(rank == 1 && white?) || (rank == 6 && black?)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# creates all the possible first moves
|
|
27
|
+
# @param board [Chess::Board]
|
|
28
|
+
# @param file [String]
|
|
29
|
+
# @param rank [Integer]
|
|
30
|
+
# @return [Array] moves
|
|
31
|
+
def pawn_moves_by_color(board, file, rank)
|
|
32
|
+
moves = []
|
|
33
|
+
moves += one_step_forward(board, file, rank)
|
|
34
|
+
moves += two_step_forward(board, file, rank) if first_move?(rank)
|
|
35
|
+
moves += attack_en_passant(board, file, rank) unless first_move?(rank)
|
|
36
|
+
moves += cross_side_attack(board, file, rank)
|
|
37
|
+
moves.reject!(&:empty?)
|
|
38
|
+
moves
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# one step move of pawn
|
|
42
|
+
# @param board [Chess::Board]
|
|
43
|
+
# @param file [String]
|
|
44
|
+
# @param rank [Integer]
|
|
45
|
+
# @return [Array] moves
|
|
46
|
+
def one_step_forward(board, file, rank)
|
|
47
|
+
moves = []
|
|
48
|
+
if white?
|
|
49
|
+
north = board.north_pos(file, rank)
|
|
50
|
+
moves << north if board.empty_at?(*north)
|
|
51
|
+
else
|
|
52
|
+
south = board.south_pos(file, rank)
|
|
53
|
+
moves << south if board.empty_at?(*south) && board.pos_in_range?(south)
|
|
54
|
+
end
|
|
55
|
+
moves
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# two step move of pawn
|
|
59
|
+
# @param board [Chess::Board]
|
|
60
|
+
# @param file [String]
|
|
61
|
+
# @param rank [Integer]
|
|
62
|
+
# @return [Array] moves
|
|
63
|
+
def two_step_forward(board, file, rank)
|
|
64
|
+
moves = []
|
|
65
|
+
moves << if white?
|
|
66
|
+
north_two_step(board, file, rank)
|
|
67
|
+
else
|
|
68
|
+
south_two_step(board, file, rank)
|
|
69
|
+
end
|
|
70
|
+
moves
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# helper method for {#two_step_forward}
|
|
74
|
+
# @param (see #two_step_forward)
|
|
75
|
+
# @return (see #two_step_forward)
|
|
76
|
+
def north_two_step(board, file, rank)
|
|
77
|
+
north_one_step = board.north_pos(file, rank)
|
|
78
|
+
north_two_step = board.north_pos(*north_one_step)
|
|
79
|
+
return [] unless board.empty_at?(*north_one_step) && board.empty_at?(*north_two_step)
|
|
80
|
+
|
|
81
|
+
north_two_step
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# helper method for {#two_step_forward}
|
|
85
|
+
# @param (see #two_step_forward)
|
|
86
|
+
# @return (see #two_step_forward)
|
|
87
|
+
def south_two_step(board, file, rank)
|
|
88
|
+
south_one_step = board.south_pos(file, rank)
|
|
89
|
+
south_two_step = board.south_pos(*south_one_step)
|
|
90
|
+
unless board.empty_at?(*south_one_step) && board.empty_at?(*south_two_step) &&
|
|
91
|
+
board.pos_in_range?(south_one_step) && board.pos_in_range?(south_two_step)
|
|
92
|
+
|
|
93
|
+
return []
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
south_two_step
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# cross attack moves of pawn
|
|
100
|
+
# @param board [Chess::Board]
|
|
101
|
+
# @param file [String]
|
|
102
|
+
# @param rank [Integer]
|
|
103
|
+
# @return [Array] moves
|
|
104
|
+
def cross_side_attack(board, file, rank)
|
|
105
|
+
moves = []
|
|
106
|
+
moves += if white?
|
|
107
|
+
north_attack(board, file, rank)
|
|
108
|
+
else
|
|
109
|
+
south_attack(board, file, rank)
|
|
110
|
+
end
|
|
111
|
+
moves
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# helper method for {#cross_side_attack}
|
|
115
|
+
# @param (see #cross_side_attack)
|
|
116
|
+
# @return (see #cross_side_attack)
|
|
117
|
+
def north_attack(board, file, rank)
|
|
118
|
+
moves = []
|
|
119
|
+
north_east = board.north_east_pos(file, rank)
|
|
120
|
+
north_west = board.north_west_pos(file, rank)
|
|
121
|
+
[north_east, north_west].each do |pos|
|
|
122
|
+
moves << pos if !(board.empty_at?(*pos) || board.pos_nil?(pos)) && board.piece_at(*pos).black?
|
|
123
|
+
end
|
|
124
|
+
moves
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# helper method for {#cross_side_attack}
|
|
128
|
+
# @param (see #cross_side_attack)
|
|
129
|
+
# @return (see #cross_side_attack)
|
|
130
|
+
def south_attack(board, file, rank)
|
|
131
|
+
moves = []
|
|
132
|
+
south_east = board.south_east_pos(file, rank)
|
|
133
|
+
south_west = board.south_west_pos(file, rank)
|
|
134
|
+
[south_east, south_west].each do |pos|
|
|
135
|
+
moves << pos if !(board.empty_at?(*pos) || board.pos_nil?(pos)) &&
|
|
136
|
+
board.piece_at(*pos).white? &&
|
|
137
|
+
board.pos_in_range?(pos)
|
|
138
|
+
end
|
|
139
|
+
moves
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# moves for attacking an pawn which is a {Chess::Board#possible_en_passant_target}
|
|
143
|
+
#
|
|
144
|
+
# @param (see #cross_side_attack)
|
|
145
|
+
# @return (see #cross_side_attack)
|
|
146
|
+
def attack_en_passant(board, file, rank) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
147
|
+
moves = []
|
|
148
|
+
if white?
|
|
149
|
+
north_east = board.north_east_pos(file, rank)
|
|
150
|
+
moves << north_east if (north_east.first + north_east.last.to_s) == board.possible_en_passant_target
|
|
151
|
+
north_west = board.north_west_pos(file, rank)
|
|
152
|
+
moves << north_west if (north_west.first + north_west.last.to_s) == board.possible_en_passant_target
|
|
153
|
+
else
|
|
154
|
+
south_east = board.south_east_pos(file, rank)
|
|
155
|
+
moves << south_east if (south_east.first + south_east.last.to_s) == board.possible_en_passant_target
|
|
156
|
+
south_west = board.south_west_pos(file, rank)
|
|
157
|
+
moves << south_west if (south_west.first + south_west.last.to_s) == board.possible_en_passant_target
|
|
158
|
+
end
|
|
159
|
+
moves
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Chess
|
|
4
|
+
module Chess
|
|
5
|
+
# Pieces
|
|
6
|
+
module Pieces
|
|
7
|
+
# Piece
|
|
8
|
+
class Piece
|
|
9
|
+
attr_reader :color
|
|
10
|
+
attr_accessor :pos, :valid_moves
|
|
11
|
+
|
|
12
|
+
def initialize(color)
|
|
13
|
+
@color = color
|
|
14
|
+
@pos = []
|
|
15
|
+
@valid_moves = []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# checks if piece color is black
|
|
19
|
+
def black?
|
|
20
|
+
@color == 'black'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# checks if piece color is white
|
|
24
|
+
def white?
|
|
25
|
+
@color == 'white'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Chess
|
|
4
|
+
module Chess
|
|
5
|
+
# Pieces
|
|
6
|
+
module Pieces
|
|
7
|
+
# Queen
|
|
8
|
+
class Queen < Piece
|
|
9
|
+
include RookMoves
|
|
10
|
+
include BishopMoves
|
|
11
|
+
|
|
12
|
+
# all possible_moves of Queen
|
|
13
|
+
#
|
|
14
|
+
# @param board [Chess::Board]
|
|
15
|
+
# @return [Array] possible_moves_arr
|
|
16
|
+
def possible_moves(board)
|
|
17
|
+
file = @pos[0]
|
|
18
|
+
rank = @pos[1]
|
|
19
|
+
moves = []
|
|
20
|
+
moves += rook_moves(board, file, rank)
|
|
21
|
+
moves += bishop_moves(board, file, rank)
|
|
22
|
+
moves
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def rook_moves(board, file, rank)
|
|
26
|
+
moves = []
|
|
27
|
+
moves += north_moves(board, file, rank)
|
|
28
|
+
moves += south_moves(board, file, rank)
|
|
29
|
+
moves += west_moves(board, file, rank)
|
|
30
|
+
moves += east_moves(board, file, rank)
|
|
31
|
+
moves
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def bishop_moves(board, file, rank)
|
|
35
|
+
moves = []
|
|
36
|
+
moves += north_west_moves(board, file, rank)
|
|
37
|
+
moves += north_east_moves(board, file, rank)
|
|
38
|
+
moves += south_west_moves(board, file, rank)
|
|
39
|
+
moves += south_east_moves(board, file, rank)
|
|
40
|
+
moves
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Chess
|
|
4
|
+
module Chess
|
|
5
|
+
# Pieces
|
|
6
|
+
module Pieces
|
|
7
|
+
# RookMoves
|
|
8
|
+
module RookMoves
|
|
9
|
+
def north_moves(board, file, rank)
|
|
10
|
+
north = board.north_pos(file, rank)
|
|
11
|
+
|
|
12
|
+
moves = []
|
|
13
|
+
while board.empty_at?(*north)
|
|
14
|
+
moves << north if board.pos_in_range?(north)
|
|
15
|
+
north = board.north_pos(*north)
|
|
16
|
+
end
|
|
17
|
+
moves << north if board.pos_in_range?(north) && board.enemy_at?(*north)
|
|
18
|
+
moves
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def south_moves(board, file, rank)
|
|
22
|
+
south = board.south_pos(file, rank)
|
|
23
|
+
|
|
24
|
+
moves = []
|
|
25
|
+
while board.empty_at?(*south)
|
|
26
|
+
moves << south if board.pos_in_range?(south)
|
|
27
|
+
south = board.south_pos(*south)
|
|
28
|
+
end
|
|
29
|
+
moves << south if board.pos_in_range?(south) && board.enemy_at?(*south)
|
|
30
|
+
moves
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def east_moves(board, file, rank)
|
|
34
|
+
east = board.east_pos(file, rank)
|
|
35
|
+
|
|
36
|
+
moves = []
|
|
37
|
+
while board.empty_at?(*east)
|
|
38
|
+
moves << east if board.pos_in_range?(east)
|
|
39
|
+
east = board.east_pos(*east)
|
|
40
|
+
end
|
|
41
|
+
moves << east if board.pos_in_range?(east) && board.enemy_at?(*east)
|
|
42
|
+
moves
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def west_moves(board, file, rank)
|
|
46
|
+
west = board.west_pos(file, rank)
|
|
47
|
+
|
|
48
|
+
moves = []
|
|
49
|
+
while board.empty_at?(*west)
|
|
50
|
+
moves << west if board.pos_in_range?(west)
|
|
51
|
+
west = board.west_pos(*west)
|
|
52
|
+
end
|
|
53
|
+
moves << west if board.pos_in_range?(west) && board.enemy_at?(*west)
|
|
54
|
+
moves
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Rook
|
|
59
|
+
class Rook < Piece
|
|
60
|
+
include RookMoves
|
|
61
|
+
|
|
62
|
+
# all possible_moves of Rook
|
|
63
|
+
#
|
|
64
|
+
# @param board [Chess::Board]
|
|
65
|
+
# @return [Array] possible_moves_arr
|
|
66
|
+
def possible_moves(board)
|
|
67
|
+
file = @pos[0]
|
|
68
|
+
rank = @pos[1]
|
|
69
|
+
moves = []
|
|
70
|
+
moves += north_moves(board, file, rank)
|
|
71
|
+
moves += south_moves(board, file, rank)
|
|
72
|
+
moves += west_moves(board, file, rank)
|
|
73
|
+
moves += east_moves(board, file, rank)
|
|
74
|
+
moves
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This is a script file handling procedular stuff of this game.
|
|
4
|
+
|
|
5
|
+
# Starts a Chess Game
|
|
6
|
+
#
|
|
7
|
+
# see {Chess::Display#prompt_start_choices} for start choices.
|
|
8
|
+
#
|
|
9
|
+
# @return [void]
|
|
10
|
+
def start_game
|
|
11
|
+
game = Chess::Game.new
|
|
12
|
+
|
|
13
|
+
choice = game.prompt_start_choices
|
|
14
|
+
case choice
|
|
15
|
+
when 1 then load_game_with_code(
|
|
16
|
+
game,
|
|
17
|
+
'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'
|
|
18
|
+
) # new game FEN code
|
|
19
|
+
when 2 then select_save(game)
|
|
20
|
+
when 3 then enter_code(game)
|
|
21
|
+
else start_game end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# choice to resume a saved game.
|
|
25
|
+
#
|
|
26
|
+
# {Chess::Save#read} is used to read the file data
|
|
27
|
+
# @param game [Chess::Game]
|
|
28
|
+
# @return [void]
|
|
29
|
+
def select_save(game)
|
|
30
|
+
fen_code = game.prompt_select_save(game.read)
|
|
31
|
+
load_game_with_code(game, fen_code)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# choice to start a game using FEN code
|
|
35
|
+
#
|
|
36
|
+
# {Chess::Display#prompt_enter_code} is used for prompting user.
|
|
37
|
+
# @param game [Chess::Game]
|
|
38
|
+
# @return [void]
|
|
39
|
+
def enter_code(game)
|
|
40
|
+
fen_code = game.prompt_enter_code
|
|
41
|
+
load_game_with_code(game, fen_code)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# loads a game with given code
|
|
45
|
+
#
|
|
46
|
+
# @param game [Chess::Game]
|
|
47
|
+
# @param fen_code [String] Fen notation for chess board.
|
|
48
|
+
# @return [void]
|
|
49
|
+
def load_game_with_code(game, fen_code)
|
|
50
|
+
board = Chess::Board.new(fen_code)
|
|
51
|
+
system 'clear'
|
|
52
|
+
game.display_board(board.data)
|
|
53
|
+
game.display_buttons
|
|
54
|
+
game.start_mouse_input(board)
|
|
55
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Chess
|
|
4
|
+
module Chess
|
|
5
|
+
# PlayKingMoves
|
|
6
|
+
module PlayKingMoves
|
|
7
|
+
# plays the given castling move or normal move for king
|
|
8
|
+
#
|
|
9
|
+
# @param piece [Chess::Pieces::King]
|
|
10
|
+
# @param board [Chess::Board]
|
|
11
|
+
# @param move_pos [Array]
|
|
12
|
+
# @return [void]
|
|
13
|
+
def play_king_move(piece, board, move_pos)
|
|
14
|
+
if castling_move?(piece, board, move_pos)
|
|
15
|
+
play_castling_move(piece, board, move_pos)
|
|
16
|
+
else
|
|
17
|
+
play_normal_move(piece, board, move_pos)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# check if move is a castling_move
|
|
22
|
+
#
|
|
23
|
+
# @param (see #play_king_move)
|
|
24
|
+
def castling_move?(piece, board, move_pos)
|
|
25
|
+
castling_moves = piece.castling_moves(board)
|
|
26
|
+
castling_moves.include?(move_pos)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# plays the castling move (a special move which moves 2 pieces a king and a rook)
|
|
30
|
+
#
|
|
31
|
+
# @param (see #play_king_move)
|
|
32
|
+
# @return [void]
|
|
33
|
+
def play_castling_move(piece, board, move_pos)
|
|
34
|
+
play_normal_move(piece, board, move_pos)
|
|
35
|
+
play_rook_jump_move(piece, board, move_pos)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# plays the move of jumping rook over to the other side of king
|
|
39
|
+
#
|
|
40
|
+
# @param piece [Chess::Pieces::King]
|
|
41
|
+
# @param board [Chess::Board]
|
|
42
|
+
# @param rook_move_pos [Array]
|
|
43
|
+
# @return [void]
|
|
44
|
+
def play_rook_jump_move(piece, board, rook_move_pos) # rubocop:disable Metrics/MethodLength
|
|
45
|
+
king_file = piece.pos.first
|
|
46
|
+
king_rank = piece.pos.last
|
|
47
|
+
if king_file == 'c'
|
|
48
|
+
rook = board.piece_at('a', king_rank)
|
|
49
|
+
rook_move_pos = ['d', king_rank]
|
|
50
|
+
play_normal_move(rook, board, rook_move_pos)
|
|
51
|
+
elsif king_file == 'g'
|
|
52
|
+
rook = board.piece_at('h', king_rank)
|
|
53
|
+
rook_move_pos = ['f', king_rank]
|
|
54
|
+
play_normal_move(rook, board, rook_move_pos)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|