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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 152e4d5131574d9b9e1df36fec0fddea92612cf9150cf46174b3c21d510469bd
4
+ data.tar.gz: 0cbf073ea9137c3d0371b13441aa44058410c68ef4b5cfb8a323d723d600f420
5
+ SHA512:
6
+ metadata.gz: c2ed66ac03afe8730cb46284020d7d0e80a27f17876d188acb90a653e4f419cde6c9de4cf66c28e28f9780ca6b9d40de24616f9b60284a280f6ca19944c0235b
7
+ data.tar.gz: 0a4b54e353cd8d7977daa9fd2517860062307c57e78a6049cf0c06073ebfb7f6d17aa8e1787949dd215f9971fab7b5e1b4968412b45711539ce5b433e91f8c4e
@@ -0,0 +1,4 @@
1
+ debug_en:
2
+ test_msg: "Hello, you are using the debug_en.yml text file."
3
+ test_msg2: "This is %{adj}!"
4
+ test_array: [obj: "nested", 123, ".yml"]
@@ -0,0 +1,424 @@
1
+ # Textfile for Chess
2
+ en:
3
+ cli:
4
+ ver: "|<<-───────────────|⟡ Ruby Arcade ⟡ v%{ver} by Ancient Nimbus|───────────────->>|"
5
+ guideline: |
6
+ ___________________ ⟡ Ruby Arcade ⟡ v0.0.0 by Ancient Nimbus ___________________
7
+ |<<-|For best experience, adjust the terminal to fit this in a single line |->>|
8
+ boot: |
9
+ * +──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+
10
+ * │ * │
11
+ * │ *** │
12
+ * │ ***** │
13
+ * │ ********* │
14
+ * │ ************* │
15
+ * │ ***************** │
16
+ * │ ************* │
17
+ * │ ********* │
18
+ * │ ***** │
19
+ * │ *** │
20
+ * │ * │
21
+ * │ ___ ___ ___ ___ ___ ___ │
22
+ * │ /\ \ /\ \ /\ \ /\ \ /\ \ /\ \ │
23
+ * │ /::\ \ /::\ \ /::\ \ /::\ \ /::\ \ /::\ \ │
24
+ * │ /::\:\__\ /::\:\__\ /:/\:\__\ /::\:\__\ /:/\:\__\ /::\:\__\ │
25
+ * │ \/\::/ / \;:::/ / \:\ \/__/ \/\::/ / \:\/:/ / \:\:\/ / │
26
+ * │ /:/ / |:\/__/ \:\__\ /:/ / \::/ / \:\/ / │
27
+ * │ \/__/ \|__| \/__/ \/__/ \/__/ \/__/ │
28
+ * +──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+
29
+ *
30
+ * To leave the arcade, type '--exit'
31
+ menu: |
32
+ * Games available in this release:
33
+ * +───────────────+───────────────+────────────────────────────────────────────+
34
+ * │ GAME │ LAUNCH CODE │ INFO │
35
+ * +───────────────+───────────────+────────────────────────────────────────────+
36
+ * │ Chess │ chess │ Strategy, Patience, Victory. │
37
+ * +───────────────+───────────────+────────────────────────────────────────────+
38
+ *
39
+ * Type '--help' will display a list of available commands.
40
+ * To run a game, type '--play <launch code>' to run a game.
41
+ * E.g., '--play chess'
42
+ help: |
43
+ * Ruby Arcade | Help Menu
44
+ * +──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+
45
+ * │ * Available commands at this page: | │
46
+ * │ * Help - [--help] | * Display all available commands │
47
+ * │ * Save - [--save] | * Save user profile to local disc │
48
+ * │ * Load - [--load] | * Load user profile from local disc │
49
+ * │ * Self - [--self] | * Retrieve player profile info │
50
+ * │ * Play - [--play] [<app name>] | * Run an app e.g., '--play chess' │
51
+ * │ * Info - [--info] | * Display system info │
52
+ * │ * Exit - [--exit | --ttfn] | * Safely close the program │
53
+ * +──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+
54
+ std_msg_prefix: "* "
55
+ std_err: "Invalid input, please try again: "
56
+ std_blank: "Press enter to continue "
57
+ std_help: |
58
+ * Ruby Arcade Intro | Help Menu
59
+ * +──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+
60
+ * │ * Available commands at this page: | │
61
+ * │ * Help - [--help] | * Display all available commands │
62
+ * │ * Info - [--info] | * Display system info │
63
+ * │ * Exit - [--exit | --ttfn] | * Safely close the program │
64
+ * +──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+
65
+ prompt_prefix: ">>> "
66
+ query_prefix: "? "
67
+ cmd_err: "Invalid command! type --help to view all available commands."
68
+ lobby:
69
+ msg: "Get started by typing a command: "
70
+ exit: "👾 Thank you for visiting Ruby Arcade 👾"
71
+ arcade: "Exiting %{app}... beaming you back to Ruby Arcade lobby."
72
+ new:
73
+ msg: |
74
+ * Welcome to ⟡ Ruby Arcade ⟡ T.O.P. Edition
75
+ *
76
+ * If this is your first visit to the Arcade, enter [1] to create a new profile.
77
+ * If you have already created a profile, enter [2] to load your profile.
78
+ msg2: "Please specify a profile loading mode: "
79
+ msg3: "Please provide a username (Default: Arcade Player): "
80
+ msg4: |
81
+ * 👋 Greetings %{name}!
82
+ load:
83
+ li_col1: "List of User Profiles"
84
+ li_col2: "Last saved date"
85
+ msg: "Fetching user profile..."
86
+ msg2: "Choose a profile by typing the corresponding number:"
87
+ msg3: "Profile loaded! Welcome back %{name}!"
88
+ input_err: "Invalid input, please try again! To load the first profile, type '1'"
89
+ bad_file_err: "The chosen user profile is corrupted and cannot be loaded, creating a new profile now..."
90
+ no_profile_err: "No profile found, creating a new profile now..."
91
+ no_profile_err2: "No user profile found, operation cancelled"
92
+ unknown_err: "Unknown error, creating a new profile now..."
93
+ save:
94
+ msg: "Your profile has been saved and can be found at the following directory: \n==> '%{dir}'"
95
+ self:
96
+ msg: |
97
+ * Your profile info:
98
+ * Player ID: %{uuid}
99
+ * Last profile saved date: %{date}
100
+ * Username: %{name}
101
+ * Ruby Arcade visit count: %{visit}
102
+ play:
103
+ run: "Launching %{app}..."
104
+ test: "Launching %{app}... %{msg}"
105
+ run_err: "Invalid launch code, please try again."
106
+ gm_err: "No game manager available"
107
+ lol:
108
+ title:
109
+ - "Wow! You've found the secret command!"
110
+ - "Wait, that worked??"
111
+ - "Entering the super special menu... Just kidding XD"
112
+ sub:
113
+ - "Loading... still loading..."
114
+ - "Almost ready..."
115
+ msgs:
116
+ - "You have found me."
117
+ - "Fact of the day: The first commit of this project was pushed on June 17, 2025."
118
+ - "Tip of the day: GitHub projects is a helpful tool to stay organised."
119
+ - "Shout out to The Odin Project for creating excellent open source learning material."
120
+ - "Quote of the day: Our greatest glory is not in never falling, but in rising every time we fall."
121
+ - "Quote of the day: It does not matter how slowly you go as long as you do not stop."
122
+ - "Tip of the day: The best error message is the one that never shows up."
123
+ - "Quote of the day: Programs must be written for people to read, and only incidentally for machines to execute."
124
+ app:
125
+ chess:
126
+ boot: |
127
+ * +──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+
128
+ * │ │
129
+ * │ ▓▓░░▓▓░░▓▓░░▓▓░░ AN/""""'\TT dP │
130
+ * │ ░░▓▓░░▓▓░░▓▓░░▓▓ N/ .cCc. \O 88 │
131
+ * │ ▓▓░░▓▓░░▓▓░░▓▓░░ C hHHHh `P 88 │
132
+ * │ ░░▓▓░░▓▓░░▓▓░░▓▓ I EEEEEeeO 88d888b. .d8888b. .d8888b. .d8888b. │
133
+ * │ ▓▓░░▓▓░░▓▓░░▓▓░░ E sSSSSSSD 88' `88 88ooood8 Y8ooooo. Y8ooooo. │
134
+ * │ ░░▓▓░░▓▓░░▓▓░░▓▓ N. `sSs' .I 88 88 88. ... 88 88 │
135
+ * │ ▓▓░░▓▓░░▓▓░░▓▓░░ TT. .sN dP dP `88888P' `88888P' `88888P' │
136
+ * │ ░░▓▓░░▓▓░░▓▓░░▓▓ NIMBUSCHESS │
137
+ * │ │
138
+ * +──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+
139
+ * │ Chess │
140
+ * +──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+
141
+ * │ A Command Line Game by Ancient Nimbus | ver: 0.0.0 │
142
+ * +──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+
143
+ intro: |
144
+ * **How to start**
145
+ * A series of prompts will help setup the game for you in no time.
146
+ * See below for what to expect:
147
+ * 1. Start a new game or load game from profile
148
+ * • [1] New Game
149
+ * • [2] Load Game
150
+ *
151
+ * 2. Select a game mode: (When New Game is selected)
152
+ * • [1] Player vs Player
153
+ * • [2] Player vs Computer (Level: Easy)
154
+ *
155
+ * 2. Select a game session to resume: (When Load Game is selected)
156
+ * • A list of session linked to you will be loaded
157
+ * • Please enter a number to indicate the session you wish to load
158
+ *
159
+ * 3. You can give Player a name, you can skip this by leaving the input empty.
160
+ * • Please name Player 1 (Default: Player 1)
161
+ * • Please name Player 2 (Default: Player 2) (Mode 1 only)
162
+ *
163
+ * 4. You can choose to play as White or Black, or let the game decide randomly.
164
+ * • [1] Play as White
165
+ * • [2] Play as Black
166
+ * • [3] Choose for me
167
+ *
168
+ * 5. A game save will be created, and it is stored locally at the ./data folder.
169
+ how_to: |
170
+ * **How to play**
171
+ * 1. In chess, White makes the first move, follow by Black.
172
+ * 2. This game uses Algebraic notation standard to process movements.
173
+ * 3. To make a move, type your move in notation format. (More info below)
174
+ * 4. The game features autosave, your progress is saved at the end of each move.
175
+ *
176
+ * Type '--help' will display a list of available commands.
177
+ *
178
+ * A quick input tutorial is available by typing the following command:
179
+ * Type '--help alg' will display Algebraic input examples
180
+ * Type '--help smith' will display Smith input examples
181
+ *
182
+ * To learn more about chess, see: ↴
183
+ * https://www.instructables.com/Playing-Chess/
184
+ * To learn more about Algebraic notation, see: ↴
185
+ * https://www.chessable.com/blog/chess-notation-for-beginners/
186
+ * To learn more about Smith notation, see: ↴
187
+ * https://en.wikipedia.org/wiki/Chess_notation#Notation_systems
188
+ help: |
189
+ * ♞ Ruby Chess by Ancient Nimbus | Help Menu
190
+ * +──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+
191
+ * │ * Available commands at this page: | │
192
+ * │ * Help - [--help] | * Display all available commands │
193
+ * │ * Algebraic Helper - [--help alg] | * Display Algebraic input examples │
194
+ * │ * Smith Helper - [--help smith] | * Display Smith input examples │
195
+ * │ * Info - [--info] | * Display game info │
196
+ * │ * Exit - [--exit | --ttfn] | * Safely exit the game. │
197
+ * +──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+
198
+ * │ * In-Game settings: │ │
199
+ * │ * Save - [--save] | * Save session to player profile │
200
+ * │ * Load - [--load] | * Load another session from profile │
201
+ * │ * Input Algebraic - [--alg] | * Use Algebraic notation as input │
202
+ * │ * Input Smith - [--smith] | * Use Smith notation as input │
203
+ * │ * Board size - [--board size] | * Set the board size to 1x or 2x │
204
+ * │ * Board flip - [--board flip] | * Enable/Disable board flipping │
205
+ * │ * Export game - [--export] | * Export current game as PGN file │
206
+ * +──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+──────+
207
+ alg_h: |
208
+ * **Algebraic notation: Input examples**
209
+ * Pieces Symbols:
210
+ * • ♚: K, ♛: Q, ♜: R, ♝: B, ♞: N, ♟: (P) No symbol
211
+ *
212
+ * Movements:
213
+ * • 'e4' => 'Pawn to e4'
214
+ * • 'Nh3' => 'Knight to h3'
215
+ *
216
+ * Capturing:
217
+ * • 'exd5' => 'Pawn captures d5'
218
+ * • 'Qxh7' => 'Queen captures h7'
219
+ *
220
+ * When multiple pieces can move/capture the same location (Disambiguation):
221
+ * Use file to specify (a to h):
222
+ * • 'Nbd2' => 'b-Knight to d2'
223
+ * • 'Rbxd4' => 'b-Rook captures d4'
224
+ *
225
+ * Castling:
226
+ * • 'O-O' => 'Castle Kingside'
227
+ * • 'O-O-O' => 'Castle Queenside'
228
+ *
229
+ * Pawn Promotion:
230
+ * Pawn can be prompted to a queen (Q), rook (R), bishop (B) or a knight (N).
231
+ * • 'e8=Q' => 'Pawn promotes to queen'
232
+ * • 'e8=R' => 'Pawn promotes to rook'
233
+ sm_h: |
234
+ * **Smith notation: Input examples**
235
+ * Pieces Symbols:
236
+ * • ♚: k, ♛: q, ♜: r, ♝: b, ♞: n, ♟: (p) No symbol
237
+ *
238
+ * Single Prompt Movement/Capturing:
239
+ * • 'e2e4' => 'Move a piece from e2 to e4'
240
+ *
241
+ * Dual Prompt Movement/Capturing:
242
+ * • 'e2' => Enter 'Preview mode' (Note: Once selected, you must play this piece)
243
+ * • 'e4' => Piece at 'e2' will move to 'e4'
244
+ *
245
+ * Castling:
246
+ * • 'e1g1' => 'Castle Kingside'
247
+ * • 'e1c1' => 'Castle Queenside'
248
+ *
249
+ * Pawn Promotion:
250
+ * Pawn can be prompted to a queen (q), rook (r), bishop (b) or a knight (n).
251
+ * • 'e7e8q' => 'Pawn promotes to queen'
252
+ * • 'e7e8r' => 'Pawn promotes to rook'
253
+ blanks:
254
+ enter: "Press enter to continue "
255
+ wait: "Please wait... "
256
+ load:
257
+ f1: |
258
+ * Start a new game or load game from profile/FEN
259
+ * • [1] New Game
260
+ * • [2] Load Game
261
+ * • [3] Import Game via FEN data
262
+ f1a: "Please select a load mode: "
263
+ f1a_err: "Invalid input, please choose between 1 to 3: "
264
+ f2a: "Please enter a number to indicate the session you wish to load:"
265
+ f2b: "Select a game session to resume:"
266
+ f2b_err: "Invalid input, please choose between 1 to %{max}"
267
+ err: "The session you wish to load appears to be corrupted, exiting load game menu."
268
+ new:
269
+ f1: |
270
+ * Select a game mode:
271
+ * • [1] Player vs Player
272
+ * • [2] Player vs Computer (Level: Easy)
273
+ f1a: "Please select a game mode: "
274
+ f1a_err: "Invalid input, please choose between 1 or 2"
275
+ f2: "Please name Player %{count} (Default: %{name}) "
276
+ f2a: "Name has been updated to '%{name}'."
277
+ f3: "Provide a valid FEN below, invalid FEN will cancel the operation and a new game will be setup instead."
278
+ err: "Sessions not found, entering new game creation mode..."
279
+ order:
280
+ f1: |
281
+ * You can choose to play as White or Black, or let the game decide randomly.
282
+ * • [1] Play as White
283
+ * • [2] Play as Black
284
+ * • [3] Choose for me
285
+ f1a: "Please enter a number: "
286
+ f1a_err: "Invalid input, please choose between 1 to 3"
287
+ f2: "All set! %{p1} will play as %{c1}, %{p2} will play as %{c2}."
288
+ session:
289
+ new: "Starting session: %{event}"
290
+ load: "Session: %{event} restored successfully. Welcome back %{p1}!"
291
+ restart: "Would you like to play another round of chess? "
292
+ cmd:
293
+ save: "This game features autosave. Progress is saved automatically at the end of each turn."
294
+ load: "Opening the list of available sessions..."
295
+ info: |
296
+ * ♞ Ruby Chess by Ancient Nimbus ver: %{ver}
297
+ info2: |
298
+ * Last saved date: %{date}
299
+ * FEN: %{fen}
300
+ * Event: %{event}
301
+ * White: %{w_player}
302
+ * Black: %{b_player}
303
+ * ♞ Ruby Chess by Ancient Nimbus ver: %{ver}
304
+ export: |
305
+ %{sep}
306
+
307
+ * Your Chess session has been exported as: '%{filename}'
308
+ * It can be found at the following directory:
309
+ * ==> '%{dir}'
310
+ * You can also select and copy the text below: ↴
311
+ * To import a game, visit chess site such as: https://lichess.org/paste
312
+
313
+ %{pgn_out}
314
+
315
+ %{sep}
316
+ input:
317
+ alg: "Press 'enter' to switch input detection to Algebraic notation"
318
+ smith: "Press 'enter' to switch input detection to Smith notation"
319
+ done: "Input scheme has been updated!"
320
+ err: "This is not a valid selection, please try again!"
321
+ board:
322
+ err: "'board' command is expecting an additional keyword, type --help to view all possible commands."
323
+ size1: "Board size is set to standard"
324
+ size2: "Board size is set to large"
325
+ flip_on: "Board flipping enabled"
326
+ flip_off: "Board flipping disabled"
327
+ exit: "♚ Thank you for playing Chess, see you soon ♚"
328
+ err: "Invalid command! Type --help to view all available commands."
329
+ disabled: "This command is disabled here! Type --help to view all available commands."
330
+ std_err: "Invalid input, please try again: "
331
+ soon: "This feature will be made available soon."
332
+ level:
333
+ white: "White"
334
+ Black: "Black"
335
+ turn: "It is %{player}'s turn"
336
+ action1: "Pick a piece to move: "
337
+ action2: "Make a move: "
338
+ preview: "Previewing %{name}'s move at %{info}"
339
+ move: "%{player} has moved %{name} to %{info}"
340
+ capture: "%{player} has captured a %{side} %{def} at %{info}"
341
+ promo: "Promoting to %{new_name} at %{info}"
342
+ promo_alert: "Your Pawn is ready for a promotion! "
343
+ promo_opt: "Stunning your Pawn has reached the other side!"
344
+ castle: "King is castling to %{info}"
345
+ en_passant: "%{name} is performing en passant maneuver at %{info}"
346
+ check:
347
+ "A check from the %{type}! %{king_side} king is under attack!"
348
+ # - "%{king_side} king is in check, he'll have to respond!"
349
+ # - "A check from the %{type}! %{king_side} king is under attack!"
350
+ # - "It appears the %{king_side} king is in deep water!"
351
+ # More checkmate related strings
352
+ # - "A beautiful checkmate to end the game, congratulation %{win_player}!"
353
+ # - "%{win_side} delivered mate, the %{lose_side} crown has fallen!"
354
+ endgame:
355
+ checkmate: "That's checkmate! %{win_player} is victorious!"
356
+ stalemate: "It's a stalemate, the game is a draw!"
357
+ insufficient: "The game is a draw by insufficient material!"
358
+ fifty_move: "Fifty moves without a capture or pawn move, the game is a draw!"
359
+ threefold: "Round and around, the game is a draw by Threefold repetition!"
360
+ err:
361
+ select: "%{input} is not a valid selection, please try again."
362
+ move: "%{input} is not a valid move, please try again."
363
+ notation: "This is not a valid move, please try again."
364
+ promo: "Invalid option for promotion!"
365
+ promo2: "Valid option: ♛: q, ♜: r, ♝: b, ♞: n"
366
+ end:
367
+ home: "Welcome back to the lobby! Hope you have fun playing chess."
368
+ fen:
369
+ err: "FEN error, '%{fen_str}' is not a valid sequence. Starting a new game..."
370
+ misc:
371
+ site: "Ruby Arcade Terminal Chess by Ancient Nimbus"
372
+ status:
373
+ ongoing: "In Progress"
374
+ stalemate: "Draw"
375
+ insufficient: "Draw"
376
+ fifty_move: "Draw"
377
+ threefold: "Draw"
378
+ checkmate: "Checkmate"
379
+ pieces:
380
+ k:
381
+ name: "King"
382
+ notation: "K"
383
+ style1: "♚"
384
+ style2: "♔"
385
+ q:
386
+ name: "Queen"
387
+ notation: "Q"
388
+ style1: "♛"
389
+ style2: "♕"
390
+ r:
391
+ name: "Rook"
392
+ notation: "R"
393
+ style1: "♜"
394
+ style2: "♖"
395
+ b:
396
+ name: "Bishop"
397
+ notation: "B"
398
+ style1: "♝"
399
+ style2: "♗"
400
+ n:
401
+ name: "Knight"
402
+ notation: "N"
403
+ style1: "♞"
404
+ style2: "♘"
405
+ p:
406
+ name: "Pawn"
407
+ notation: "P"
408
+ style1: "♟"
409
+ style2: "♙"
410
+ board:
411
+ frame:
412
+ h: "═"
413
+ v: "║"
414
+ vh: "╬"
415
+ ulc: "╔"
416
+ urc: "╗"
417
+ ut: "╦"
418
+ llc: "╚"
419
+ lrc: "╝"
420
+ lt: "╩"
421
+ decor:
422
+ d1: "◆"
423
+ d2: "◇"
424
+ d3: "⋄"
data/bin/chess_cli ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "chess_cli"
5
+
6
+ ChessCLI.run(lang: "en")
data/lib/chess_cli.rb ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "console_game/game_manager"
4
+
5
+ # Chess CLI Wrapper
6
+ class ChessCLI
7
+ # Run Chess CLI
8
+ def self.run(lang: "en")
9
+ ConsoleGame::GameManager.new(lang:).run
10
+ end
11
+ end
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "readline"
4
+
5
+ # Game display & input capturing for console game
6
+ # @author Ancient Nimbus
7
+ # @version v2.1.0
8
+ module Console
9
+ # Default message strings
10
+ D_MSG = { msg: "Using message printer", err_msg: "Invalid input, please try again: ",
11
+ prompt_prefix: ">>> ", query_prefix: "? ", warn_prefix: "! ", gear_icon: "⛭ ",
12
+ cmd_err: "Invalid command, type --help to view all available commands.",
13
+ cmd_pattern: "--(exit).*?", v_sep: "| " }.freeze
14
+
15
+ # prompt message helper
16
+ # @param msgs [Array<String>] message to print
17
+ # @param pre [String] message prefix
18
+ # @param suf [String] message suffix
19
+ # @param mode [Symbol] expecting the following symbols: `puts`, `print`, `p`
20
+ # @param delay [Integer] add delay between message print
21
+ # @param clear [Boolean] clear screen before print?
22
+ # @return [nil]
23
+ def print_msg(*msgs, pre: "", suf: "", mode: :puts, delay: 0, clear: false)
24
+ return ArgumentError("Invalid mode used for this method") unless %i[puts print p].include?(mode)
25
+
26
+ system("clear") if clear
27
+ msgs.each do |msg|
28
+ formatted_msg = "#{pre}#{msg}#{suf}"
29
+ method(mode).call(formatted_msg)
30
+ sleep(delay) if delay.positive?
31
+ end
32
+ nil
33
+ end
34
+
35
+ # process user input
36
+ # @param msg [String] first print
37
+ # @param cmds [Hash] expects a list of commands hash
38
+ # @param err_msg [String] second print
39
+ # @param reg [Regexp, String] pattern to match
40
+ # @param empty [Boolean] allow empty input value, default to false
41
+ # @return [String]
42
+ def ask(msg = "", cmds: { "exit" => method(:exit) }, err_msg: D_MSG[:err_msg], reg: /.*/, empty: false)
43
+ input = prompt_user(msg, err_msg: err_msg, reg: reg, empty: empty)
44
+ return input if input.empty?
45
+
46
+ input_arr = input.split(" ")
47
+ input_is_cmd, is_valid, cmd = command?(input_arr[0], cmds)
48
+
49
+ return input unless input_is_cmd
50
+
51
+ handle_command(cmd, input_arr[1..], cmds, is_valid)
52
+ ask(msg, cmds:, err_msg:, reg:, empty:)
53
+ end
54
+
55
+ # Build table
56
+ # @param data [Hash<String>] data
57
+ # @param head [String] the title of the table
58
+ # @return [Array<String>]
59
+ def build_table(data: {}, head: "Console Table")
60
+ data_values = data.values.map(&:values)
61
+ return "Non-string elements found, ops cancelled." unless data_values.all? { |_, v| v.is_a?(String) }
62
+
63
+ prefix_size, max_length = table_formatter(data_values)
64
+ build_header(data, head, prefix_size, max_length) + build_rows(data_values, max_length - prefix_size)
65
+ end
66
+
67
+ # Helper method to create regexp pattern
68
+ # @param cmd_pattern [String] command patterns
69
+ # @param reg [String] pattern to match
70
+ # @param pre [String] pattern prefix
71
+ # @param suf [String] pattern suffix
72
+ # @param flag [String, Regexp] regexp flag
73
+ # @return [Regexp]
74
+ def regexp_formatter(cmd_pattern = D_MSG[:cmd_pattern], reg = "reg", pre: '\A', suf: '\z', flag: "")
75
+ Regexp.new("#{pre}(#{reg}|#{cmd_pattern})#{suf}", flag)
76
+ end
77
+
78
+ # Shorthand method: handle range selections prompt, current number limit is up to 99.
79
+ # @param cmd_pattern [String] command patterns
80
+ # @param min [String, Integer] min range
81
+ # @param max [String, Integer] max range (inclusive)
82
+ # @param flag [String, Regexp] regexp flag
83
+ # @return [Regexp]
84
+ def regexp_range(cmd_pattern = D_MSG[:cmd_pattern], min: 1, max: 3, flag: "")
85
+ block = "[#{min}-#{max}]"
86
+ block = "[#{min}-9][0-#{max % 10}]?" if max.is_a?(Integer) && (max >= 10)
87
+
88
+ regexp_formatter(cmd_pattern, block, flag: flag)
89
+ end
90
+
91
+ # Helper method to build regexp capturing group
92
+ # @param reg [Array] elements
93
+ # @param pre [String] pattern prefix
94
+ # @param suf [String] pattern suffix
95
+ # @return [String]
96
+ def regexp_capturing_gp(reg = %w[reg abc], pre: "", suf: "") = "#{pre}(#{reg.join('|')})#{suf}"
97
+
98
+ private
99
+
100
+ # prompt user to collect input
101
+ # @param msg [String] first print
102
+ # @param err_msg [String] second print
103
+ # @param reg [Regexp] pattern to match
104
+ # @param empty [Boolean] allow empty input value, default to false
105
+ # @return [String] user input
106
+ def prompt_user(msg = "", err_msg: D_MSG[:err_msg], reg: /.*/, empty: false)
107
+ input = ""
108
+ query_symbol = Paint[D_MSG[:query_prefix], :green]
109
+ loop do
110
+ prompt_msg = "#{query_symbol}#{msg}#{D_MSG[:prompt_prefix]}"
111
+ input = Readline.readline(prompt_msg, true)
112
+ break if met_requirement?(input, reg, empty)
113
+
114
+ query_symbol = Paint[D_MSG[:warn_prefix], :red]
115
+ msg = err_msg
116
+ end
117
+ input.rstrip
118
+ end
119
+
120
+ # Handle prompt exit condition in a more precises manner
121
+ # @param input [String] user input to verify
122
+ # @param reg [Regexp] pattern to match
123
+ # @param empty [Boolean] allow empty input value, default to false
124
+ # @return [Boolean] true when requirement is met
125
+ def met_requirement?(input, reg, empty) = input.match?(reg) || (input.empty? if empty)
126
+
127
+ # returns true if user input matches available commands
128
+ # @param input [String] user input
129
+ # @param commands [Array<String>] command string keys
130
+ # @param flags [Array<String>] command pattern prefixes
131
+ # @return [Boolean, Array<Boolean, String>] whether it is a command or not
132
+ def command?(input, commands = %w[exit debug], flags: %w[--])
133
+ clean_input = nil
134
+ flags.each do |flag|
135
+ clean_input = input.delete_prefix(flag) if input[0...flag.size] == flag
136
+ break unless clean_input.nil?
137
+ end
138
+ return false if clean_input.nil?
139
+
140
+ [true, commands.include?(clean_input), clean_input]
141
+ end
142
+
143
+ # Handle command
144
+ # @param cmd [String]
145
+ # @param opt_arg [String]
146
+ # @param cmds [Array]
147
+ # @param is_valid [Boolean]
148
+ # @param cmd_err [String] custom error message
149
+ def handle_command(cmd, opt_arg, cmds, is_valid, cmd_err: D_MSG[:cmd_err])
150
+ return print_msg(cmd_err, pre: Paint[D_MSG[:warn_prefix], :red]) unless is_valid
151
+
152
+ begin
153
+ cmds.fetch(cmd).call(opt_arg)
154
+ rescue TypeError
155
+ cmd == "exit" ? cmds.fetch(cmd).call : raise(TypeError, "#{cmd} is missing optional argument: #{opt_arg}")
156
+ end
157
+ end
158
+
159
+ # == Build table helper ==
160
+
161
+ # Helper: Table formatter
162
+ # @param data_arr [Array<String>]
163
+ # @return [Array]
164
+ def table_formatter(data_arr)
165
+ row_counts = data_arr.size
166
+ prefix_size = ol_prefix(row_counts).size
167
+ max_length = 80
168
+ [prefix_size, max_length]
169
+ end
170
+
171
+ # Helper: Build Header row
172
+ # @return [Array<String>]
173
+ def build_header(data, head, prefix_size, row_length)
174
+ tb_col_heads = data.values[0].keys.map { |title| title.to_s.capitalize }
175
+ col_heads = tb_col_heads.join(D_MSG[:v_sep].rjust(row_length - prefix_size * 3))
176
+ separator = horizontal_line(row_length)
177
+ [head, separator, col_heads, separator]
178
+ end
179
+
180
+ # Helper: Build table row
181
+ # @param data_arr [Array<String>]
182
+ # @param tb_rows [Array<String>]
183
+ # @return [Array<String>]
184
+ def build_rows(data_arr, row_length, tb_rows = [])
185
+ data_arr.each_with_index do |entry, i|
186
+ last_col = entry[-1]
187
+ cols = entry[0].ljust(row_length - last_col.size)
188
+ tb_rows << "#{ol_prefix(i)}#{cols}#{last_col}"
189
+ end
190
+ tb_rows
191
+ end
192
+
193
+ # Helper: Ordered list prefix builder
194
+ # @param idx [Integer]
195
+ # @return [String]
196
+ def ol_prefix(idx) = "* [#{idx + 1}] - "
197
+
198
+ # Helper: Build horizontal line
199
+ # @param length [Integer]
200
+ # @return [String]
201
+ def horizontal_line(length) = "-" * length
202
+ end