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.
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Chess
4
+ module Chess
5
+ # Play all types of moves
6
+ module PlayMovesByType
7
+ include PlayPawnMoves
8
+ include PlayKingMoves
9
+
10
+ # play different move based on type of the piece
11
+ # @param piece any subclass of {Chess::Pieces::Piece}
12
+ # @param board [Chess::Board]
13
+ # @param move_pos [Array]
14
+ # @param inside_valid_moves_flag [Boolean]
15
+ # @return [void]
16
+ def play_move_by_type(piece, board, move_pos, inside_valid_moves_flag: false) # rubocop:disable Metrics/MethodLength
17
+ update_player_board_data(piece, board, inside_valid_moves_flag)
18
+ if piece.is_a?(Pieces::Pawn)
19
+ play_pawn_move(piece, board, move_pos, inside_valid_moves_flag)
20
+ board.reset_half_move unless inside_valid_moves_flag
21
+ elsif piece.is_a?(Pieces::King)
22
+ play_king_move(piece, board, move_pos)
23
+ else
24
+ play_normal_move(piece, board, move_pos)
25
+ end
26
+ change_player_turn(board)
27
+ remove_en_passant_from_board(board) if @remove_en_passant
28
+ end
29
+
30
+ # play normal move
31
+ # @param piece any subclass of {Chess::Pieces::Piece}
32
+ # @param board [Chess::Board]
33
+ # @param move_pos [Array]
34
+ # @return [void]
35
+ def play_normal_move(piece, board, move_pos)
36
+ board.remove_piece_at(piece.pos)
37
+ board.reset_half_move if board.piece_at(*move_pos).is_a?(Pieces::Piece)
38
+ board.remove_piece_at(move_pos)
39
+ board.insert_piece_at(piece, move_pos)
40
+ end
41
+
42
+ # update some player and board data
43
+ # @param piece any subclass of {Chess::Pieces::Piece}
44
+ # @param board [Chess::Board]
45
+ # @param inside_valid_moves_flag [Boolean]
46
+ # @return [void]
47
+ def update_player_board_data(piece, board, inside_valid_moves_flag)
48
+ board.half_move += 1 unless inside_valid_moves_flag
49
+ @remove_en_passant = true
50
+ board.full_move += 1 if !inside_valid_moves_flag && piece.black?
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Chess
4
+ module Chess
5
+ # PawnMoves
6
+ module PlayPawnMoves
7
+ include Display
8
+
9
+ # play appropriate type of pawn move
10
+ #
11
+ # @param piece any subclass of {Chess::Pieces::Piece}
12
+ # @param board [Chess::Board]
13
+ # @param move_pos [Array]
14
+ # @param inside_valid_moves_flag [Boolean]
15
+ # @return [void]
16
+ def play_pawn_move(piece, board, move_pos, inside_valid_moves_flag)
17
+ if pawn_is_promoting?(piece)
18
+ play_pawn_promotion(piece, board, move_pos, inside_valid_moves_flag)
19
+ elsif attacking_en_passant?(piece, board, move_pos)
20
+ play_normal_move(piece, board, move_pos)
21
+ remove_enemy_en_passant_pawn(piece, board, move_pos)
22
+ elsif pawn_two_step_move?(piece, board, move_pos)
23
+ move_two_step(piece, board, move_pos)
24
+ else
25
+ play_normal_move(piece, board, move_pos)
26
+ end
27
+ end
28
+
29
+ # play two step move
30
+ #
31
+ # @param piece any subclass of {Chess::Pieces::Piece}
32
+ # @param board [Chess::Board]
33
+ # @param move_pos [Array]
34
+ # @return [void]
35
+ def move_two_step(piece, board, move_pos)
36
+ play_normal_move(piece, board, move_pos)
37
+ set_board_possible_en_passant(piece, board)
38
+ @remove_en_passant = false
39
+ end
40
+
41
+ # check if move is attacking a enemy pawn at {Chess::Board#possible_en_passant_target}
42
+ # @param (see #move_two_step)
43
+ def attacking_en_passant?(piece, board, move_pos)
44
+ file = piece.pos.first
45
+ rank = piece.pos.last
46
+ moves = piece.attack_en_passant(board, file, rank)
47
+ moves.include?(move_pos)
48
+ end
49
+
50
+ # check if pawn is promoting
51
+ # @param piece any subclass of {Chess::Pieces::Piece}
52
+ def pawn_is_promoting?(piece)
53
+ rank = piece.pos.last
54
+ return true if rank == 1 && piece.black?
55
+
56
+ true if rank == 6 && piece.white?
57
+ end
58
+
59
+ # check if moving two step
60
+ #
61
+ # @param (see #move_two_step)
62
+ def pawn_two_step_move?(piece, board, move_pos)
63
+ file = piece.pos.first
64
+ rank = piece.pos.last
65
+ moves = piece.two_step_forward(board, file, rank)
66
+ moves.include?(move_pos)
67
+ end
68
+
69
+ # remove enemy pawn in attacking en passant
70
+ # @param (see #move_two_step)
71
+ # @return [void]
72
+ def remove_enemy_en_passant_pawn(piece, board, move_pos)
73
+ file = move_pos.first
74
+ rank = if piece.white?
75
+ move_pos.last - 1
76
+ else
77
+ move_pos.last + 1
78
+ end
79
+ board.remove_piece_at([file, rank])
80
+ end
81
+
82
+ # set board possible_en_passant_target
83
+ # @param piece any subclass of {Chess::Pieces::Piece}
84
+ # @param board [Chess::Board]
85
+ # @return [void]
86
+ def set_board_possible_en_passant(piece, board)
87
+ file = piece.pos.first
88
+ rank = if piece.white?
89
+ piece.pos.last - 1
90
+ else
91
+ piece.pos.last + 1
92
+ end
93
+ board.possible_en_passant_target = file + rank.to_s
94
+ end
95
+
96
+ # remove possible_en_passant_target pos from board
97
+ # @param board [Chess::Board]
98
+ # @return [void]
99
+ def remove_en_passant_from_board(board)
100
+ board.possible_en_passant_target = ''
101
+ end
102
+
103
+ # play pawn promotion move
104
+ # @param (see #play_pawn_move)
105
+ # @return [void]
106
+ def play_pawn_promotion(piece, board, move_pos, inside_valid_moves_flag)
107
+ board.remove_piece_at(piece.pos)
108
+ new_piece_letter = 'q'
109
+ new_piece_letter = prompt_pawn_promotion_choices unless inside_valid_moves_flag
110
+ new_piece_letter = new_piece_letter.upcase if piece.white?
111
+ piece = board.create_piece(new_piece_letter)
112
+ board.insert_piece_at(piece, move_pos)
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Chess
4
+ module Chess
5
+ # Player
6
+ class Player
7
+ include PlayMovesByType
8
+
9
+ attr_accessor :selected_piece
10
+
11
+ def initialize
12
+ @selected_piece = ''
13
+ end
14
+
15
+ # select a move
16
+ # @param board [Chess::Board]
17
+ # @param move_pos [Array]
18
+ # @return [void]
19
+ def select_move(board, move_pos)
20
+ if @selected_piece.valid_moves.include?(move_pos)
21
+ play_move_by_type(@selected_piece, board, move_pos)
22
+ else
23
+ pp 'invalid move'
24
+ end
25
+ end
26
+
27
+ # change player turn
28
+ # @param board [Chess::Board]
29
+ # @return [void]
30
+ def change_player_turn(board)
31
+ current_player_color = board.current_player
32
+ board.current_player = if current_player_color == 'w'
33
+ 'b'
34
+ else
35
+ 'w'
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Chess
4
+ module Chess
5
+ # create fen_code from board
6
+ module Save
7
+ # Fen code from Board
8
+ module FenCodeFromBoard
9
+ # generate FEN code from given board
10
+ # @param board [Chess::Board]
11
+ def generate_fen_code(board)
12
+ piece_placement = fen_piece_placement(board.data)
13
+ current_player = board.current_player
14
+ castling_rights = board.castling_rights
15
+ en_passant = board.possible_en_passant_target
16
+ half_move = board.half_move
17
+ full_move = board.full_move
18
+
19
+ "#{piece_placement} #{current_player} #{castling_rights} #{en_passant} #{half_move} #{full_move}"
20
+ end
21
+
22
+ # create fen piece placement for given board_data
23
+ # @param board_data [Hash]
24
+ # @return [String] piece_placement
25
+ def fen_piece_placement(board_data)
26
+ string = ''
27
+ (0..7).reverse_each do |rank|
28
+ string += fen_piece_placement_by_rank(board_data, rank)
29
+ string += '/' unless rank.zero?
30
+ end
31
+ string
32
+ end
33
+
34
+ # helper method for {#fen_piece_placement}
35
+ # @param board_data [Hash]
36
+ # @param rank [Integer]
37
+ # @return [String] piece_placement_part
38
+ def fen_piece_placement_by_rank(board_data, rank) # rubocop:disable Metrics/MethodLength
39
+ string = ''
40
+ empty_space = 0
41
+ ('a'..'h').each do |file|
42
+ piece = board_data[file][rank]
43
+ if piece == ''
44
+ empty_space += 1
45
+ string += empty_space.to_s if file == 'h'
46
+ else
47
+ string += empty_space.to_s unless empty_space.zero?
48
+ string += letter_of(piece)
49
+ empty_space = 0
50
+ end
51
+ end
52
+ string
53
+ end
54
+
55
+ # return letter of piece
56
+ #
57
+ # @param piece any subclass of {Chess::Pieces::Piece}
58
+ # @return [String] piece_letter
59
+ def letter_of(piece) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
60
+ letter = case piece
61
+ when Pieces::King
62
+ 'k'
63
+ when Pieces::Bishop
64
+ 'b'
65
+ when Pieces::Rook
66
+ 'r'
67
+ when Pieces::Pawn
68
+ 'p'
69
+ when Pieces::Queen
70
+ 'q'
71
+ when Pieces::Knight
72
+ 'n'
73
+ end
74
+ if piece.white?
75
+ letter.upcase
76
+ else
77
+ letter
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Chess
4
+ module Chess
5
+ # Save file commands
6
+ module Save
7
+ include Serializer
8
+ include FenCodeFromBoard
9
+
10
+ # writes a save to the save file
11
+ #
12
+ # @param name [String]
13
+ # @param data [String]
14
+ # @return [void]
15
+ def save(name, data)
16
+ file_data = read
17
+ file_data[name] = data
18
+ new_data_serialized = serialize(file_data)
19
+ File.write(SAVE_PATH, new_data_serialized)
20
+ end
21
+
22
+ # reads and returns saves from the save file.
23
+ # {Serializer#unserialize} is used to unserialize the data.
24
+ #
25
+ # @return [Hash]
26
+ def read
27
+ contents = File.read(SAVE_PATH)
28
+ unserialize(contents)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Chess
4
+ module Chess
5
+ # Save file commands
6
+ module Save
7
+ # Serializer for saving to file and reading from file.
8
+ module Serializer
9
+ # Serializing format used for save and load
10
+ SERIALIZER = JSON
11
+
12
+ # serializes the save data with save_name as keys and FEN as values
13
+ #
14
+ # @param data [Hash]
15
+ # @return [String]
16
+ def serialize(data)
17
+ SERIALIZER.dump data
18
+ end
19
+
20
+ # unserializes the given string and returns the Hash
21
+ # with save's name as keys and FEN code as values.
22
+ #
23
+ # @return [Hash]
24
+ def unserialize(string)
25
+ SERIALIZER.parse string
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chess
4
+ # VERSION following https://semver.org
5
+ VERSION = '0.41.4'
6
+ end
data/lib/chess.rb ADDED
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ # the order of requiring here is important.
4
+ # requiring any gems/modules/class called in Game before requiring
5
+ # Game itself for example
6
+ require 'tty/prompt'
7
+ require 'json'
8
+ require_relative 'chess/display/color'
9
+ require_relative 'chess/display/prompt'
10
+ require_relative 'chess/display/board'
11
+ require_relative 'chess/mouse/mouse_position'
12
+ require_relative 'chess/mouse/mouse_input'
13
+ require_relative 'chess/save/fen_from_board'
14
+ require_relative 'chess/save/serializer'
15
+ require_relative 'chess/save/save'
16
+ require_relative 'chess/pieces/piece'
17
+ require_relative 'chess/pieces/bishop'
18
+ require_relative 'chess/pieces/king'
19
+ require_relative 'chess/pieces/knight'
20
+ require_relative 'chess/pieces/pawn'
21
+ require_relative 'chess/pieces/rook'
22
+ require_relative 'chess/pieces/queen'
23
+ require_relative 'chess/board/board_pos'
24
+ require_relative 'chess/board/board_from_fen'
25
+ require_relative 'chess/board/board'
26
+ require_relative 'chess/play_moves/play_king_moves'
27
+ require_relative 'chess/play_moves/play_pawn_moves'
28
+ require_relative 'chess/play_moves/play_moves_by_type'
29
+ require_relative 'chess/player/player'
30
+ require_relative 'chess/game/valid_moves'
31
+ require_relative 'chess/game/win_and_draw'
32
+ require_relative 'chess/game/game'
33
+ require_relative 'chess/play_game'
34
+
35
+ # Chess
36
+ module Chess
37
+ SAVE_PATH = File.expand_path('../save.json', __dir__)
38
+ # calls {start_game} the script file at chess/play_game
39
+ #
40
+ # @return [void]
41
+ def self.start
42
+ start_game
43
+ end
44
+ end
data/save.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "default": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
3
+ "test_king": "r3k1nr/pppp1ppp/4p3/6Q1/8/8/PPP2PPP/R3K2R w KQkq - 0 1",
4
+ "test_queen": "rnb1kbnr/pppp1ppp/5q2/QQ2p3/2P3Q1/2Q5/PP1PPPPP/RNBQKBNR w KQkq - 0 1",
5
+ "test_rook_bishop": "r1kbB2R/8/8/P2R2pp/pB2r2P/8/4B3/R1Kb3r w - - 0 1",
6
+ "test_pawn": "3k4/ppp1pppp/1P2P1P1/4P3/4p3/1p2p1p1/PPP1PPPP/3K4 w - - 0 1",
7
+ "test_knight": "2nkN3/P4P2/7n/n1pN3N/N3nP2/8/8/1p1KNnp1 w - - 0 1",
8
+ "test_checkmate_stalemate": "8/P7/8/8/5Q1K/6B1/8/4R1bk w - - 2 2"
9
+ }
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chess_tui
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.41.4
5
+ platform: ruby
6
+ authors:
7
+ - X_AJ_X
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: tty-prompt
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: 0.23.1
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: 0.23.1
26
+ description: |-
27
+ Chess game with terminal UI written in ruby, check out docs: https://xajx179.github.io/Chess \
28
+ & source: https://github.com/XAJX179/Chess
29
+ email:
30
+ - xajx179@gmail.com
31
+ executables:
32
+ - chess_tui
33
+ extensions: []
34
+ extra_rdoc_files:
35
+ - README.md
36
+ files:
37
+ - README.md
38
+ - bin/chess_tui
39
+ - lib/chess.rb
40
+ - lib/chess/board/board.rb
41
+ - lib/chess/board/board_from_fen.rb
42
+ - lib/chess/board/board_pos.rb
43
+ - lib/chess/display/board.rb
44
+ - lib/chess/display/color.rb
45
+ - lib/chess/display/prompt.rb
46
+ - lib/chess/game/game.rb
47
+ - lib/chess/game/valid_moves.rb
48
+ - lib/chess/game/win_and_draw.rb
49
+ - lib/chess/mouse/mouse_input.rb
50
+ - lib/chess/mouse/mouse_position.rb
51
+ - lib/chess/pieces/bishop.rb
52
+ - lib/chess/pieces/king.rb
53
+ - lib/chess/pieces/knight.rb
54
+ - lib/chess/pieces/pawn.rb
55
+ - lib/chess/pieces/piece.rb
56
+ - lib/chess/pieces/queen.rb
57
+ - lib/chess/pieces/rook.rb
58
+ - lib/chess/play_game.rb
59
+ - lib/chess/play_moves/play_king_moves.rb
60
+ - lib/chess/play_moves/play_moves_by_type.rb
61
+ - lib/chess/play_moves/play_pawn_moves.rb
62
+ - lib/chess/player/player.rb
63
+ - lib/chess/save/fen_from_board.rb
64
+ - lib/chess/save/save.rb
65
+ - lib/chess/save/serializer.rb
66
+ - lib/chess/version.rb
67
+ - save.json
68
+ homepage: https://xajx179.github.io/Chess
69
+ licenses:
70
+ - MIT
71
+ metadata:
72
+ homepage_uri: https://xajx179.github.io/Chess
73
+ source_code_uri: https://github.com/XAJX179/Chess
74
+ rubygems_mfa_required: 'true'
75
+ post_install_message: Thanks for installing! by X_AJ_X
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: 3.2.0
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubygems_version: 3.7.1
91
+ specification_version: 4
92
+ summary: Chess game with terminal display and mouse input , load games with FEN code
93
+ or start new!
94
+ test_files: []