chess_cli 0.9.2

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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.config/locale/debug_en.yml +4 -0
  3. data/.config/locale/en.yml +424 -0
  4. data/bin/chess_cli +6 -0
  5. data/lib/chess_cli.rb +11 -0
  6. data/lib/console/console.rb +202 -0
  7. data/lib/console/wait_utils.rb +25 -0
  8. data/lib/console_game/base_game.rb +74 -0
  9. data/lib/console_game/chess/board.rb +110 -0
  10. data/lib/console_game/chess/game.rb +184 -0
  11. data/lib/console_game/chess/input/algebraic_notation.rb +103 -0
  12. data/lib/console_game/chess/input/chess_input.rb +191 -0
  13. data/lib/console_game/chess/input/smith_notation.rb +38 -0
  14. data/lib/console_game/chess/launcher.rb +20 -0
  15. data/lib/console_game/chess/level.rb +276 -0
  16. data/lib/console_game/chess/logics/display.rb +182 -0
  17. data/lib/console_game/chess/logics/endgame_logic.rb +126 -0
  18. data/lib/console_game/chess/logics/logic.rb +137 -0
  19. data/lib/console_game/chess/logics/moves_simulation.rb +75 -0
  20. data/lib/console_game/chess/logics/piece_analysis.rb +76 -0
  21. data/lib/console_game/chess/logics/piece_lookup.rb +93 -0
  22. data/lib/console_game/chess/pieces/bishop.rb +18 -0
  23. data/lib/console_game/chess/pieces/chess_piece.rb +204 -0
  24. data/lib/console_game/chess/pieces/king.rb +200 -0
  25. data/lib/console_game/chess/pieces/knight.rb +46 -0
  26. data/lib/console_game/chess/pieces/pawn.rb +142 -0
  27. data/lib/console_game/chess/pieces/queen.rb +16 -0
  28. data/lib/console_game/chess/pieces/rook.rb +37 -0
  29. data/lib/console_game/chess/player/chess_computer.rb +25 -0
  30. data/lib/console_game/chess/player/chess_player.rb +211 -0
  31. data/lib/console_game/chess/utilities/chess_utils.rb +67 -0
  32. data/lib/console_game/chess/utilities/fen_export.rb +114 -0
  33. data/lib/console_game/chess/utilities/fen_import.rb +196 -0
  34. data/lib/console_game/chess/utilities/load_manager.rb +51 -0
  35. data/lib/console_game/chess/utilities/pgn_export.rb +97 -0
  36. data/lib/console_game/chess/utilities/pgn_utils.rb +134 -0
  37. data/lib/console_game/chess/utilities/player_builder.rb +74 -0
  38. data/lib/console_game/chess/utilities/session_builder.rb +48 -0
  39. data/lib/console_game/chess/version.rb +8 -0
  40. data/lib/console_game/console_menu.rb +68 -0
  41. data/lib/console_game/game_manager.rb +181 -0
  42. data/lib/console_game/input.rb +87 -0
  43. data/lib/console_game/player.rb +100 -0
  44. data/lib/console_game/user_profile.rb +65 -0
  45. data/lib/nimbus_file_utils/nimbus_file_utils.rb +194 -0
  46. data/user_data/.keep +0 -0
  47. data/user_data/dummy_user.json +124 -0
  48. data/user_data/pgn_export/.keep +0 -0
  49. metadata +147 -0
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require "json"
5
+ require "paint"
6
+
7
+ # File operations helper module
8
+ # @author Ancient Nimbus
9
+ # @version 0.6.0
10
+ module NimbusFileUtils
11
+ # Common reg pattern
12
+ # - :filename [String] Valid filename pattern
13
+ # - :digits [Regexp] Any digits
14
+ COMMON_REG = { filename: '[\sa-zA-Z0-9._-]+', digits: /\A\d+\z/, yesno: "(?<yes>yes|y)|(?<no>no|n)" }.freeze
15
+
16
+ class << self
17
+ attr_accessor :locale, :locale_filename
18
+
19
+ # Set program language
20
+ # @param lang [String] default to English(en)
21
+ # @param extname [String] default to `.yml`
22
+ def set_locale(lang = "en", extname: ".yml")
23
+ # localization target
24
+ @locale = lang
25
+ @locale_filename = formatted_filename(lang, extname)
26
+ end
27
+
28
+ # Return the root path.
29
+ # @return [String] project root path
30
+ def proj_root = @proj_root ||= File.expand_path("../..", __dir__)
31
+
32
+ # Return cross-system friendly filename.
33
+ # @param filename [String] file name
34
+ # @param extname [String] file extension name
35
+ # @return [String] Formatted filename
36
+ def formatted_filename(filename, extname = "")
37
+ raise ArgumentError, "Forbidden character detected" unless filename.match?(/\A[\sa-zA-Z0-9._-]+\z/)
38
+
39
+ filename.downcase.tr(" ", "_") + extname
40
+ end
41
+
42
+ # Return the full path of a specific file.
43
+ # @param filename [String]
44
+ # @param dirs [Array<String>] `filepath("en", ".config", "locale")` will return <root...>/.config/locale/en
45
+ # @return [String] full file path
46
+ def filepath(filename, *dirs) = File.join(proj_root, *dirs, filename)
47
+
48
+ # Check if a file exist in the given file path.
49
+ # @param filepath [String]
50
+ # @param use_filetype [Boolean]
51
+ # @param extname [String] file extension name
52
+ def file_exist?(filepath, use_filetype: true, extname: ".yml")
53
+ extname = "" unless use_filetype
54
+ filepath += extname
55
+ File.exist?(filepath)
56
+ end
57
+
58
+ # Returns a list of files within a given directory and file extensions
59
+ # @param folder_path [String] folder path
60
+ # @param extname [String] file extension name
61
+ # @return [Array<String>]
62
+ def file_list(folder_path, extname: ".yml")
63
+ filenames = []
64
+ Dir.new(folder_path).each_child { |entry| filenames << entry if File.extname(entry) == extname }
65
+ filenames
66
+ end
67
+
68
+ # Writes the given data and save it to the specified file path.
69
+ # @param filepath [String] The base path of the file to write (expects complete filepath with extension).
70
+ # @param data [Object] The data to serialize and write.
71
+ def write_to_disk(filepath, data)
72
+ extname = File.extname(filepath)
73
+ return "Operation error! File extension is missing." if extname.empty?
74
+
75
+ File.open(filepath, "w") do |output|
76
+ case extname
77
+ when ".yml" then output.puts data.to_yaml
78
+ when ".json" then output.puts JSON.pretty_generate(data)
79
+ else output.puts data
80
+ end
81
+ end
82
+ end
83
+
84
+ # Load file in YAML or JSON extension.
85
+ # @param filepath [String] the base path of the file to write (extension is added automatically).
86
+ # @param extname [String] set target file extension, default: `.yml`
87
+ # @param symbols [Boolean] set whether to use symbols as key, default: true
88
+ # @return [Hash]
89
+ def load_file(filepath, extname: ".yml", symbols: true)
90
+ raise ArgumentError, "Invalid extension: only .yml or .json is accepted" unless %w[.yml .json].include?(extname)
91
+
92
+ return puts "File not found: #{filepath}" unless File.exist?(filepath)
93
+
94
+ File.open(filepath, "r") do |file|
95
+ data = extname == ".yml" ? handle_yaml(file) : handle_json(file)
96
+ return symbols ? to_symbols(data) : data
97
+ end
98
+ end
99
+
100
+ # Retrieves a localized string by key path from the specified locale file.
101
+ # Returns a missing message if the locale or key is not found.
102
+ # @param key_path [String] e.g., "welcome.greeting"
103
+ # @param extname [String] set target file extension, default: `:yml`
104
+ # @return [String]
105
+ def get_string(key_path, extname: ".yml")
106
+ path = filepath(locale_filename, ".config", "locale")
107
+ @strings ||= load_file(path, extname: extname, symbols: false)
108
+
109
+ locale_strings = @strings[locale]
110
+ return "Missing locale: #{locale}" unless locale_strings
111
+
112
+ keys = key_path.to_s.split(".")
113
+ result = keys.reduce(locale_strings) do |val, key|
114
+ val&.[](key)
115
+ end
116
+
117
+ result || "Missing string: '#{key_path}'"
118
+ end
119
+
120
+ private
121
+
122
+ # Helper to handle yaml data
123
+ # @param file [File]
124
+ def handle_yaml(file)
125
+ YAML.safe_load(file, permitted_classes: [Time, Symbol], aliases: true, freeze: true)
126
+ rescue Psych::SyntaxError => e
127
+ puts "Textfile parsing error: '#{e.message}.'\nPlease verify the following file: \n#{File.path(file)}"
128
+ exit(1)
129
+ end
130
+
131
+ # Helper to handle json data
132
+ # @param file [File]
133
+ # @return [Hash]
134
+ def handle_json(file) = JSON.parse(file.read)
135
+
136
+ # Convert string keys to symbol keys.
137
+ # @param obj [Object]
138
+ # @return [Hash]
139
+ def to_symbols(obj)
140
+ case obj
141
+ when Hash then obj.transform_keys(&:to_sym).transform_values { |v| to_symbols(v) }
142
+ when Array then obj.map { |e| to_symbols(e) }
143
+ else obj
144
+ end
145
+ end
146
+ end
147
+
148
+ # Build a list of files with last modified date as metadata field
149
+ # @param folder_path [String] folder path
150
+ # @param filenames [Array<String>] filenames within the given directory
151
+ # @param col1 [String] header name for the file name col
152
+ # @param col2 [String] header name for the last modified name col
153
+ # @param list_width [Integer] the width of the table
154
+ # @return [Array<String>]
155
+ def build_file_list(folder_path, filenames, col1: "List of Files", col2: "Last modified date", list_width: 80)
156
+ file_list = file_list_head(col1:, col2:)
157
+ filenames.each_with_index do |entry, i|
158
+ prefix = "* [#{i + 1}] - "
159
+ filename = File.basename(entry, File.extname(entry)).ljust(list_width * 0.6 - (prefix.size % 8))
160
+ mod_time = File.new(folder_path + entry).mtime.strftime("%m/%d/%Y %I:%M %p")
161
+ file_list.push("#{prefix}#{filename} | #{mod_time}")
162
+ end
163
+ file_list
164
+ end
165
+
166
+ # Retrieves a localized string and perform String interpolation and paint text if needed.
167
+ # @param key_path [String] textfile keypath
168
+ # @param subs [Hash] `{ demo: ["some text", :red] }`
169
+ # @param paint_str [Array<Symbol, String, nil>]
170
+ # @param extname [String]
171
+ # @return [String, Array<String>] the translated and interpolated string
172
+ def s(key_path, subs = {}, paint_str: %i[default default], extname: ".yml")
173
+ str = NimbusFileUtils.get_string(key_path, extname: extname)
174
+ return str if str.is_a?(Array)
175
+
176
+ output = Paint % [str, *paint_str, subs]
177
+ paint_str.uniq.all?(:default) && subs.values.flatten[1..].nil? ? Paint.unpaint(output) : output
178
+ end
179
+
180
+ # Textfile strings fetcher
181
+ # @param sub [String] sub-head
182
+ # @param keys [Array<String>] key
183
+ # @return [Array<String>] array of textfile strings
184
+ def tf_fetcher(sub, *keys, root: "") = keys.map { |key| s("#{root}#{sub}#{key}") }
185
+
186
+ private
187
+
188
+ # Helper: Build the header for file list
189
+ # @param col1 [String]
190
+ # @param col2 [String]
191
+ # @param list_width [Integer]
192
+ # @return [Array<String>]
193
+ def file_list_head(col1:, col2:, list_width: 80) = ["#{col1.ljust(list_width * 0.7)} | #{col2}", "-" * list_width]
194
+ end
data/user_data/.keep ADDED
File without changes
@@ -0,0 +1,124 @@
1
+ {
2
+ "uuid": "9e39f092-534e-4dbb-ac3c-64846b78d727",
3
+ "username": "Dummy User",
4
+ "saved_date": "2025-08-25 17:18:33 +0100",
5
+ "appdata": {
6
+ "chess": {
7
+ "1": {
8
+ "event": "Dummy User vs Legal Mate",
9
+ "site": "Ruby Arcade Terminal Chess by Ancient Nimbus",
10
+ "date": "08/04/2025 06:37 PM",
11
+ "round": null,
12
+ "white": "Dummy User",
13
+ "black": "Legal Mate",
14
+ "result": null,
15
+ "mode": 1,
16
+ "moves": {
17
+ "1": [
18
+ "e4",
19
+ "e5"
20
+ ],
21
+ "2": [
22
+ "Nf3",
23
+ "Nc6"
24
+ ],
25
+ "3": [
26
+ "Bc4",
27
+ "d6"
28
+ ],
29
+ "4": [
30
+ "Nc3",
31
+ "Bg4"
32
+ ],
33
+ "5": [
34
+ "h3",
35
+ "Bh5"
36
+ ],
37
+ "6": [
38
+ "Nfxe5",
39
+ "Bhxd1"
40
+ ],
41
+ "7": [
42
+ "Bcxf7",
43
+ "Ke7"
44
+ ]
45
+ },
46
+ "white_moves": [
47
+ "e4",
48
+ "Nf3",
49
+ "Bc4",
50
+ "Nc3",
51
+ "h3",
52
+ "Nfxe5",
53
+ "Bcxf7",
54
+ "Nd5+"
55
+ ],
56
+ "black_moves": [
57
+ "e5",
58
+ "Nc6",
59
+ "d6",
60
+ "Bg4",
61
+ "Bh5",
62
+ "Bhxd1",
63
+ "Ke7"
64
+ ],
65
+ "fens": [
66
+ "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
67
+ "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1",
68
+ "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2",
69
+ "rnbqkbnr/pppp1ppp/8/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2",
70
+ "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3",
71
+ "r1bqkbnr/pppp1ppp/2n5/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R b KQkq - 3 3",
72
+ "r1bqkbnr/ppp2ppp/2np4/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 4",
73
+ "r1bqkbnr/ppp2ppp/2np4/4p3/2B1P3/2N2N2/PPPP1PPP/R1BQK2R b KQkq - 1 4",
74
+ "r2qkbnr/ppp2ppp/2np4/4p3/2B1P1b1/2N2N2/PPPP1PPP/R1BQK2R w KQkq - 2 5",
75
+ "r2qkbnr/ppp2ppp/2np4/4p3/2B1P1b1/2N2N1P/PPPP1PP1/R1BQK2R b KQkq - 0 5",
76
+ "r2qkbnr/ppp2ppp/2np4/4p2b/2B1P3/2N2N1P/PPPP1PP1/R1BQK2R w KQkq - 1 6",
77
+ "r2qkbnr/ppp2ppp/2np4/4N2b/2B1P3/2N4P/PPPP1PP1/R1BQK2R b KQkq - 2 6",
78
+ "r2qkbnr/ppp2ppp/2np4/4N3/2B1P3/2N4P/PPPP1PP1/R1BbK2R w KQkq - 3 7",
79
+ "r2qkbnr/ppp2Bpp/2np4/4N3/4P3/2N4P/PPPP1PP1/R1BbK2R b KQkq - 4 7",
80
+ "r2q1bnr/ppp1kBpp/2np4/4N3/4P3/2N4P/PPPP1PP1/R1BbK2R w KQ - 5 8",
81
+ "r2q1bnr/ppp1kBpp/2np4/3NN3/4P3/7P/PPPP1PP1/R1BbK2R b KQ - 6 8"
82
+ ]
83
+ },
84
+ "2": {
85
+ "event": "Dummy User vs Master Yo status-Checkmate",
86
+ "site": "Ruby Arcade Terminal Chess by Ancient Nimbus",
87
+ "date": "08/04/2025 06:37 PM",
88
+ "round": null,
89
+ "white": "Dummy User",
90
+ "black": "Master Yo",
91
+ "result": null,
92
+ "mode": 1,
93
+ "moves": {
94
+ "1": [
95
+ "f4",
96
+ "e5"
97
+ ],
98
+ "2": [
99
+ "g4",
100
+ "Qh4#"
101
+ ]
102
+ },
103
+ "white_moves": [
104
+ "f4",
105
+ "g4"
106
+ ],
107
+ "black_moves": [
108
+ "e5",
109
+ "Qh4"
110
+ ],
111
+ "fens": [
112
+ "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
113
+ "rnbqkbnr/pppppppp/8/8/5P2/8/PPPPP1PP/RNBQKBNR b KQkq - 0 1",
114
+ "rnbqkbnr/pppp1ppp/8/4p3/5P2/8/PPPPP1PP/RNBQKBNR w KQkq - 0 2",
115
+ "rnbqkbnr/pppp1ppp/8/4p3/5PP1/8/PPPPP2P/RNBQKBNR b KQkq - 0 2",
116
+ "rnb1kbnr/pppp1ppp/8/4p3/5PPq/8/PPPPP2P/RNBQKBNR w KQkq - 1 3"
117
+ ]
118
+ }
119
+ }
120
+ },
121
+ "stats": {
122
+ "launch_count": 374
123
+ }
124
+ }
File without changes
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chess_cli
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.2
5
+ platform: ruby
6
+ authors:
7
+ - Ancient Nimbus
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: paint
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.3'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.3'
26
+ - !ruby/object:Gem::Dependency
27
+ name: reline
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: 0.6.2
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 0.6.2
40
+ - !ruby/object:Gem::Dependency
41
+ name: whirly
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 0.3.0
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 0.3.0
54
+ description: |
55
+ A colourful terminal Chess, support Smith & Algebraic Notation input.
56
+
57
+ Core feature:
58
+ - FEN session import / export
59
+ - PGN file export
60
+ - User Profile Management
61
+ - Multiple chess session per profile
62
+ - Input hot-swapping
63
+ - Session hot-swapping
64
+ - Chessboard customization
65
+ email:
66
+ - rb@ttfn.lol
67
+ executables:
68
+ - chess_cli
69
+ extensions: []
70
+ extra_rdoc_files: []
71
+ files:
72
+ - ".config/locale/debug_en.yml"
73
+ - ".config/locale/en.yml"
74
+ - bin/chess_cli
75
+ - lib/chess_cli.rb
76
+ - lib/console/console.rb
77
+ - lib/console/wait_utils.rb
78
+ - lib/console_game/base_game.rb
79
+ - lib/console_game/chess/board.rb
80
+ - lib/console_game/chess/game.rb
81
+ - lib/console_game/chess/input/algebraic_notation.rb
82
+ - lib/console_game/chess/input/chess_input.rb
83
+ - lib/console_game/chess/input/smith_notation.rb
84
+ - lib/console_game/chess/launcher.rb
85
+ - lib/console_game/chess/level.rb
86
+ - lib/console_game/chess/logics/display.rb
87
+ - lib/console_game/chess/logics/endgame_logic.rb
88
+ - lib/console_game/chess/logics/logic.rb
89
+ - lib/console_game/chess/logics/moves_simulation.rb
90
+ - lib/console_game/chess/logics/piece_analysis.rb
91
+ - lib/console_game/chess/logics/piece_lookup.rb
92
+ - lib/console_game/chess/pieces/bishop.rb
93
+ - lib/console_game/chess/pieces/chess_piece.rb
94
+ - lib/console_game/chess/pieces/king.rb
95
+ - lib/console_game/chess/pieces/knight.rb
96
+ - lib/console_game/chess/pieces/pawn.rb
97
+ - lib/console_game/chess/pieces/queen.rb
98
+ - lib/console_game/chess/pieces/rook.rb
99
+ - lib/console_game/chess/player/chess_computer.rb
100
+ - lib/console_game/chess/player/chess_player.rb
101
+ - lib/console_game/chess/utilities/chess_utils.rb
102
+ - lib/console_game/chess/utilities/fen_export.rb
103
+ - lib/console_game/chess/utilities/fen_import.rb
104
+ - lib/console_game/chess/utilities/load_manager.rb
105
+ - lib/console_game/chess/utilities/pgn_export.rb
106
+ - lib/console_game/chess/utilities/pgn_utils.rb
107
+ - lib/console_game/chess/utilities/player_builder.rb
108
+ - lib/console_game/chess/utilities/session_builder.rb
109
+ - lib/console_game/chess/version.rb
110
+ - lib/console_game/console_menu.rb
111
+ - lib/console_game/game_manager.rb
112
+ - lib/console_game/input.rb
113
+ - lib/console_game/player.rb
114
+ - lib/console_game/user_profile.rb
115
+ - lib/nimbus_file_utils/nimbus_file_utils.rb
116
+ - user_data/.keep
117
+ - user_data/dummy_user.json
118
+ - user_data/pgn_export/.keep
119
+ homepage: https://github.com/AncientNimbus/rb-chess
120
+ licenses:
121
+ - BSD-3-Clause
122
+ metadata:
123
+ changelog_uri: https://github.com/AncientNimbus/rb-chess/blob/main/CHANGELOG.md
124
+ documentation_uri: https://ancientnimbus.github.io/rb-chess/doc/
125
+ homepage_uri: https://github.com/AncientNimbus/rb-chess
126
+ source_code_uri: https://github.com/AncientNimbus/rb-chess
127
+ code_coverage_uri: https://ancientnimbus.github.io/rb-chess/coverage/#_AllFiles
128
+ post_install_message: Thanks for installing Chess powered by Ruby Arcade!
129
+ rdoc_options: []
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '3.4'
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ requirements: []
143
+ rubygems_version: 3.7.1
144
+ specification_version: 4
145
+ summary: A colourful terminal Chess, support Smith & Algebraic input, FEN I/O, user
146
+ profile management and more.
147
+ test_files: []