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.
- checksums.yaml +7 -0
- data/.config/locale/debug_en.yml +4 -0
- data/.config/locale/en.yml +424 -0
- data/bin/chess_cli +6 -0
- data/lib/chess_cli.rb +11 -0
- data/lib/console/console.rb +202 -0
- data/lib/console/wait_utils.rb +25 -0
- data/lib/console_game/base_game.rb +74 -0
- data/lib/console_game/chess/board.rb +110 -0
- data/lib/console_game/chess/game.rb +184 -0
- data/lib/console_game/chess/input/algebraic_notation.rb +103 -0
- data/lib/console_game/chess/input/chess_input.rb +191 -0
- data/lib/console_game/chess/input/smith_notation.rb +38 -0
- data/lib/console_game/chess/launcher.rb +20 -0
- data/lib/console_game/chess/level.rb +276 -0
- data/lib/console_game/chess/logics/display.rb +182 -0
- data/lib/console_game/chess/logics/endgame_logic.rb +126 -0
- data/lib/console_game/chess/logics/logic.rb +137 -0
- data/lib/console_game/chess/logics/moves_simulation.rb +75 -0
- data/lib/console_game/chess/logics/piece_analysis.rb +76 -0
- data/lib/console_game/chess/logics/piece_lookup.rb +93 -0
- data/lib/console_game/chess/pieces/bishop.rb +18 -0
- data/lib/console_game/chess/pieces/chess_piece.rb +204 -0
- data/lib/console_game/chess/pieces/king.rb +200 -0
- data/lib/console_game/chess/pieces/knight.rb +46 -0
- data/lib/console_game/chess/pieces/pawn.rb +142 -0
- data/lib/console_game/chess/pieces/queen.rb +16 -0
- data/lib/console_game/chess/pieces/rook.rb +37 -0
- data/lib/console_game/chess/player/chess_computer.rb +25 -0
- data/lib/console_game/chess/player/chess_player.rb +211 -0
- data/lib/console_game/chess/utilities/chess_utils.rb +67 -0
- data/lib/console_game/chess/utilities/fen_export.rb +114 -0
- data/lib/console_game/chess/utilities/fen_import.rb +196 -0
- data/lib/console_game/chess/utilities/load_manager.rb +51 -0
- data/lib/console_game/chess/utilities/pgn_export.rb +97 -0
- data/lib/console_game/chess/utilities/pgn_utils.rb +134 -0
- data/lib/console_game/chess/utilities/player_builder.rb +74 -0
- data/lib/console_game/chess/utilities/session_builder.rb +48 -0
- data/lib/console_game/chess/version.rb +8 -0
- data/lib/console_game/console_menu.rb +68 -0
- data/lib/console_game/game_manager.rb +181 -0
- data/lib/console_game/input.rb +87 -0
- data/lib/console_game/player.rb +100 -0
- data/lib/console_game/user_profile.rb +65 -0
- data/lib/nimbus_file_utils/nimbus_file_utils.rb +194 -0
- data/user_data/.keep +0 -0
- data/user_data/dummy_user.json +124 -0
- data/user_data/pgn_export/.keep +0 -0
- 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,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
data/lib/chess_cli.rb
ADDED
@@ -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
|