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
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "chess_utils"
|
4
|
+
require_relative "../player/chess_player"
|
5
|
+
require_relative "../player/chess_computer"
|
6
|
+
|
7
|
+
module ConsoleGame
|
8
|
+
module Chess
|
9
|
+
# The Player Builder class helps create new chess players for each session
|
10
|
+
# @author Ancient Nimbus
|
11
|
+
class PlayerBuilder
|
12
|
+
include ChessUtils
|
13
|
+
|
14
|
+
attr_reader :game, :session, :controller, :human_class, :ai_class
|
15
|
+
|
16
|
+
# @param game [Game] expects chess game manager object
|
17
|
+
def initialize(game)
|
18
|
+
@game = game
|
19
|
+
@controller = game.controller
|
20
|
+
@human_class = ChessPlayer
|
21
|
+
@ai_class = ChessComputer
|
22
|
+
end
|
23
|
+
|
24
|
+
# Create players based on load mode
|
25
|
+
# @param session [Hash] game session to load
|
26
|
+
# @return [Array<ChessPlayer, ChessComputer>]
|
27
|
+
def build_players(session)
|
28
|
+
@session = session
|
29
|
+
case session[:mode]
|
30
|
+
when 1 then pvp_mode
|
31
|
+
when 2 then pve_mode
|
32
|
+
else raise KeyError
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Create a player
|
37
|
+
# @param name [String] expects a name
|
38
|
+
# @param side [Symbol, nil] expects :black or :white
|
39
|
+
# @param type [Symbol] expects :human or :ai
|
40
|
+
# @param m_history [Array<String>]
|
41
|
+
# @return [ChessPlayer, ChessComputer]
|
42
|
+
def create_player(name = "", side = nil, type: :human, m_history: [])
|
43
|
+
new_player = type == :human ? human_class : ai_class
|
44
|
+
new_player.new(name, controller, side, m_history:)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# When it is player vs player
|
50
|
+
# @return [Array<ChessPlayer>]
|
51
|
+
def pvp_mode
|
52
|
+
SIDES_SYM.map { |side| create_player(session[side], side, m_history: m_history_data(side)) }
|
53
|
+
end
|
54
|
+
|
55
|
+
# When it is player vs computer
|
56
|
+
# @return [Array<ChessPlayer, ChessComputer>]
|
57
|
+
def pve_mode
|
58
|
+
computer_side = session.key("Computer")
|
59
|
+
raise KeyError if computer_side.nil?
|
60
|
+
|
61
|
+
player_side = opposite_of(computer_side)
|
62
|
+
[create_player(session[player_side], player_side, m_history: m_history_data(player_side)),
|
63
|
+
create_player(session[computer_side], computer_side, type: :ai, m_history: m_history_data(computer_side))]
|
64
|
+
end
|
65
|
+
|
66
|
+
# Helper to return moves history depending on side
|
67
|
+
# @return [Array<String>]
|
68
|
+
def m_history_data(side)
|
69
|
+
data = session["#{side}_moves".to_sym]
|
70
|
+
data.nil? ? [] : data
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "chess_utils"
|
4
|
+
|
5
|
+
module ConsoleGame
|
6
|
+
module Chess
|
7
|
+
# The Session Builder class helps create new session data hash
|
8
|
+
# @author Ancient Nimbus
|
9
|
+
class SessionBuilder
|
10
|
+
include ChessUtils
|
11
|
+
|
12
|
+
# Create session data
|
13
|
+
# @return [Array<Integer, Hash>] session data
|
14
|
+
def self.build_session(...) = new(...).build_session
|
15
|
+
|
16
|
+
attr_reader :sessions, :mode, :sides, :site_txt, :ongoing_txt
|
17
|
+
|
18
|
+
# @param game [Game] expects chess game manager object
|
19
|
+
def initialize(game)
|
20
|
+
@sessions = game.sessions
|
21
|
+
@mode = game.mode
|
22
|
+
@sides = game.sides
|
23
|
+
@site_txt = game.s("misc.site")
|
24
|
+
@ongoing_txt = game.s("status.ongoing")
|
25
|
+
end
|
26
|
+
|
27
|
+
# Build session data
|
28
|
+
# @return [Array<Integer, Hash>] session data
|
29
|
+
def build_session
|
30
|
+
id = sessions.size + 1
|
31
|
+
set_player_side
|
32
|
+
wp_name, bp_name = sides.values_at(w_sym, b_sym).map(&:name)
|
33
|
+
session = { mode: mode,
|
34
|
+
white: wp_name,
|
35
|
+
black: bp_name,
|
36
|
+
event: "#{wp_name} vs #{bp_name} Status #{ongoing_txt}",
|
37
|
+
site: site_txt,
|
38
|
+
date: Time.new.ceil.strftime(STR_TIME) }
|
39
|
+
[id, session]
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# Set internal side value for new player object
|
45
|
+
def set_player_side = sides.map { |side, player| player.side = side }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "input"
|
4
|
+
|
5
|
+
module ConsoleGame
|
6
|
+
# Input controller for console menu
|
7
|
+
class ConsoleMenu < Input
|
8
|
+
# == Console Commands ==
|
9
|
+
|
10
|
+
# Exit sequences | command patterns: `exit`, `ttfn`
|
11
|
+
def quit(_args = [])
|
12
|
+
print_msg(s("cli.lobby.exit"))
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
# Display help string | command pattern: `help`
|
17
|
+
def help(_args = []) = print_msg(s("cli.help"))
|
18
|
+
|
19
|
+
# Display system info | command pattern: `info`
|
20
|
+
def info(_args = []) = print_msg(s("cli.menu"))
|
21
|
+
|
22
|
+
# Save user profile to disk | command pattern: `save`
|
23
|
+
def save(_args = []) = game_manager.save_user_profile
|
24
|
+
|
25
|
+
# Load user profile from disk | command pattern: `load`
|
26
|
+
def load(_args = []) = game_manager.switch_user_profile
|
27
|
+
|
28
|
+
# Launch a game | command pattern: `play <launch code>`
|
29
|
+
# @param args [Array<String>] optional arguments
|
30
|
+
def play(args = [])
|
31
|
+
return print_msg(s("cli.play.gm_err")) unless game_manager
|
32
|
+
|
33
|
+
app = args[0]
|
34
|
+
game_manager.apps.key?(app) ? game_manager.launch(app) : print_msg(s("cli.play.run_err"), pre: "! ")
|
35
|
+
end
|
36
|
+
|
37
|
+
# Display user info | command pattern: `self`
|
38
|
+
def self(_args = [])
|
39
|
+
profile = game_manager.user.profile
|
40
|
+
user_color = :yellow
|
41
|
+
# p "Debug: #{profile}"
|
42
|
+
print_msg(s("cli.self.msg",
|
43
|
+
{ uuid: [profile[:uuid], user_color],
|
44
|
+
date: [profile[:saved_date].strftime("%m/%d/%Y %I:%M %p"), user_color],
|
45
|
+
name: [profile[:username], user_color],
|
46
|
+
visit: [profile[:stats][:launch_count], user_color] }))
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# Easter egg | command pattern: `lol`
|
52
|
+
def lol(_args = [])
|
53
|
+
Whirly.start spinner: "random_dots", status: s("cli.lol.title").sample do
|
54
|
+
sleep 3
|
55
|
+
Whirly.status = s("cli.lol.sub").sample
|
56
|
+
sleep 1.5
|
57
|
+
msgs = s("cli.lol.msgs")
|
58
|
+
print_msg(msgs[command_usage[:lol] % msgs.size], pre: "⠗ ")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# == Unities ==
|
63
|
+
|
64
|
+
# Setup input commands
|
65
|
+
def setup_commands = super.merge({ "save" => method(:save), "load" => method(:load), "play" => method(:play),
|
66
|
+
"self" => method(:self), "lol" => method(:lol) })
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../nimbus_file_utils/nimbus_file_utils"
|
4
|
+
require_relative "../console/console"
|
5
|
+
require_relative "input"
|
6
|
+
require_relative "console_menu"
|
7
|
+
require_relative "user_profile"
|
8
|
+
require_relative "player"
|
9
|
+
require_relative "base_game"
|
10
|
+
require_relative "chess/launcher"
|
11
|
+
|
12
|
+
# Console Game System v2.0.0
|
13
|
+
# @author Ancient Nimbus
|
14
|
+
# @version 2.0.0
|
15
|
+
module ConsoleGame
|
16
|
+
# Game Manager for Console game
|
17
|
+
class GameManager
|
18
|
+
include Console
|
19
|
+
include ::NimbusFileUtils
|
20
|
+
include Chess::Launcher
|
21
|
+
|
22
|
+
# Alias for NimbusFileUtils
|
23
|
+
F = NimbusFileUtils
|
24
|
+
|
25
|
+
attr_reader :base_input, :cli, :apps, :user, :ver
|
26
|
+
attr_accessor :running, :active_game
|
27
|
+
|
28
|
+
def initialize(lang: "en")
|
29
|
+
F.set_locale(lang)
|
30
|
+
@ver = "2.0.0"
|
31
|
+
@running = true
|
32
|
+
@base_input = Input.new(self)
|
33
|
+
@cli = ConsoleMenu.new(self)
|
34
|
+
@apps = load_app_list
|
35
|
+
@user = nil
|
36
|
+
@active_game = nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# run the console game manager
|
40
|
+
def run
|
41
|
+
greet
|
42
|
+
assign_user_profile
|
43
|
+
lobby
|
44
|
+
end
|
45
|
+
|
46
|
+
# Run a game
|
47
|
+
# @param app [String]
|
48
|
+
def launch(app)
|
49
|
+
self.active_game = apps[app].call(self)
|
50
|
+
active_game.start
|
51
|
+
print_msg(s(shut_down_msg(app)))
|
52
|
+
self.active_game = nil
|
53
|
+
end
|
54
|
+
|
55
|
+
# Exit Arcade
|
56
|
+
def exit_arcade
|
57
|
+
self.running = false
|
58
|
+
user&.save_profile
|
59
|
+
exit
|
60
|
+
end
|
61
|
+
|
62
|
+
# Save user profile
|
63
|
+
# @param mute [Boolean] bypass printing when use at the background
|
64
|
+
def save_user_profile(mute: false)
|
65
|
+
return load_err(:no_profile2) if user.nil?
|
66
|
+
|
67
|
+
user.save_profile
|
68
|
+
print_msg(s("cli.save.msg", { dir: [user.filepath, :yellow] })) unless mute
|
69
|
+
end
|
70
|
+
|
71
|
+
# Load another profile when using is at the lobby
|
72
|
+
def switch_user_profile
|
73
|
+
return load_err(:no_profile2) if user.nil?
|
74
|
+
|
75
|
+
@user = load_profile
|
76
|
+
launch_counter
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
# Greet user
|
82
|
+
def greet
|
83
|
+
tf_fetcher("", *%w[.guideline .boot], root: "cli").each do |msg|
|
84
|
+
print_msg(msg.sub("0.0.0", ver))
|
85
|
+
base_input.ask(s("cli.std_blank"), empty: true)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Arcade lobby
|
90
|
+
def lobby
|
91
|
+
print_msg(s("cli.menu"))
|
92
|
+
cli.ask(empty: true) while running
|
93
|
+
end
|
94
|
+
|
95
|
+
# Define the app list
|
96
|
+
def load_app_list = {}.merge(load_chess)
|
97
|
+
|
98
|
+
# End game message
|
99
|
+
# @param app [String] expects app name
|
100
|
+
# @return [String] textfile key
|
101
|
+
def shut_down_msg(app) = "app.#{app}.end.home"
|
102
|
+
|
103
|
+
# Setup user profile
|
104
|
+
def assign_user_profile
|
105
|
+
print_msg(s("cli.new.msg"))
|
106
|
+
mode = base_input.ask(s("cli.new.msg2"), reg: [1, 2], input_type: :range).to_i
|
107
|
+
@user = mode == 1 ? new_profile : load_profile
|
108
|
+
# Create stats data
|
109
|
+
launch_counter
|
110
|
+
# Save to disk
|
111
|
+
save_user_profile
|
112
|
+
end
|
113
|
+
|
114
|
+
# Handle new user
|
115
|
+
# @param username [String] username for UserProfile, skip prompt stage if it is provided
|
116
|
+
# @return [UserProfile] user profile
|
117
|
+
def new_profile(username = "")
|
118
|
+
# Get username
|
119
|
+
username = username.empty? ? grab_username : username
|
120
|
+
# Create user profile
|
121
|
+
profile = UserProfile.new(username)
|
122
|
+
# Welcome user
|
123
|
+
print_msg(s("cli.new.msg4", { name: [profile.username, :yellow] }))
|
124
|
+
profile
|
125
|
+
end
|
126
|
+
|
127
|
+
# Handle returning user
|
128
|
+
# @param extname [String] extension name
|
129
|
+
# @return [UserProfile] user profile
|
130
|
+
def load_profile(extname: ".json")
|
131
|
+
f_path = select_profile(extname: extname)
|
132
|
+
# Load ops
|
133
|
+
profile = f_path.nil? ? load_err(:no_profile) : UserProfile.load_profile(F.load_file(f_path, extname: extname))
|
134
|
+
profile = profile.nil? ? load_err(:bad_profile) : UserProfile.new(profile[:username], profile)
|
135
|
+
# Post-load ops
|
136
|
+
print_msg(s("cli.load.msg3", { name: [profile.username, :yellow] }))
|
137
|
+
profile
|
138
|
+
end
|
139
|
+
|
140
|
+
# Handle profile selection
|
141
|
+
# @param extname [String] extension name
|
142
|
+
# @return [String] filepath
|
143
|
+
def select_profile(extname: ".json")
|
144
|
+
print_msg(s("cli.load.msg"))
|
145
|
+
|
146
|
+
folder_path = F.filepath("", "user_data")
|
147
|
+
profiles = F.file_list(folder_path, extname: extname)
|
148
|
+
return nil if profiles.empty?
|
149
|
+
|
150
|
+
# Print the list
|
151
|
+
print_msg(*build_file_list(folder_path, profiles, col1: s("cli.load.li_col1"), col2: s("cli.load.li_col2")))
|
152
|
+
# Handle selection
|
153
|
+
profile = base_input.pick_from(profiles, msg: s("cli.load.msg2"), err_msg: s("cli.load.input_err"))
|
154
|
+
# Returns a valid filename
|
155
|
+
folder_path + profile
|
156
|
+
end
|
157
|
+
|
158
|
+
# Edge cases handling when there are issue loading a profile
|
159
|
+
# @param err_label [Symbol] control which error message to print
|
160
|
+
# @return [UserProfile]
|
161
|
+
def load_err(err_label = :no_profile)
|
162
|
+
err_msgs ||= { no_profile: "cli.load.no_profile_err", no_profile2: "cli.load.no_profile_err2",
|
163
|
+
bad_profile: "cli.load.bad_file_err" }
|
164
|
+
keypath = err_msgs.fetch(err_label, "cli.load.unknown_err")
|
165
|
+
print_msg(s(keypath), pre: "! ")
|
166
|
+
new_profile
|
167
|
+
end
|
168
|
+
|
169
|
+
# Get username from prompt
|
170
|
+
# @return [String] username
|
171
|
+
def grab_username = base_input.ask(s("cli.new.msg3"), reg: COMMON_REG[:filename], input_type: :custom, empty: true)
|
172
|
+
|
173
|
+
# Simple usage stats counting
|
174
|
+
def launch_counter
|
175
|
+
return load_err(:no_profile2) if user.nil?
|
176
|
+
|
177
|
+
user.profile[:stats][:launch_count] ||= 0
|
178
|
+
user.profile[:stats][:launch_count] += 1
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "whirly"
|
4
|
+
require "paint"
|
5
|
+
require_relative "../nimbus_file_utils/nimbus_file_utils"
|
6
|
+
require_relative "../console/console"
|
7
|
+
|
8
|
+
module ConsoleGame
|
9
|
+
# Input class is a base class for various control layouts for ConsoleGame
|
10
|
+
class Input
|
11
|
+
include Console
|
12
|
+
include ::NimbusFileUtils
|
13
|
+
|
14
|
+
attr_reader :game_manager, :commands, :cmd_pattern, :command_usage
|
15
|
+
|
16
|
+
# @param game_manager [ConsoleGame::GameManager]
|
17
|
+
def initialize(game_manager = nil)
|
18
|
+
@game_manager = game_manager
|
19
|
+
@commands = setup_commands
|
20
|
+
@cmd_pattern = regexp_capturing_gp(commands.keys, pre: "--", suf: '(\s.*)?')
|
21
|
+
@command_usage = Hash.new { |h, k| h[k] = 0 }
|
22
|
+
end
|
23
|
+
|
24
|
+
# == Core methods ==
|
25
|
+
|
26
|
+
# Override: process user input
|
27
|
+
# @param msg [String] first print
|
28
|
+
# @param cmds [Hash] expects a list of commands hash
|
29
|
+
# @param err_msg [String] second print
|
30
|
+
# @param reg [Regexp, String, Array<String>] pattern to match, use an Array when input type is :range
|
31
|
+
# @param empty [Boolean] allow empty input value, default to false
|
32
|
+
# @param input_type [Symbol] expects the following option: :any, :range, :custom
|
33
|
+
# @return [String]
|
34
|
+
def ask(msg = "", cmds: commands, err_msg: s("cli.std_err"), reg: ".*", empty: false, input_type: :any)
|
35
|
+
reg = case input_type
|
36
|
+
when :range then regexp_range(cmd_pattern, min: reg[0], max: reg[1])
|
37
|
+
when :custom then regexp_formatter(cmd_pattern, reg)
|
38
|
+
else reg
|
39
|
+
end
|
40
|
+
# p "location: #{self.class}, reg: #{reg}"
|
41
|
+
# p reg
|
42
|
+
super(msg, cmds:, err_msg:, reg:, empty:)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Process user input where bound checks are required
|
46
|
+
# @param options [Array] a list of options
|
47
|
+
# @param msg [String] first print
|
48
|
+
# @param err_msg [String] second print
|
49
|
+
# @return [Any] a valid element within the given array
|
50
|
+
def pick_from(options, msg: "Pick from the following options: ", err_msg: "Not a valid option, try again.")
|
51
|
+
until options.fetch(opt = ask(msg, reg: COMMON_REG[:digits], input_type: :custom, err_msg: err_msg).to_i - 1, nil)
|
52
|
+
print_msg(err_msg, pre: Paint["! ", :red])
|
53
|
+
end
|
54
|
+
options[opt]
|
55
|
+
end
|
56
|
+
|
57
|
+
# == Console Commands ==
|
58
|
+
|
59
|
+
# Exit sequences | command patterns: `exit`
|
60
|
+
def quit(_args = []) = game_manager.nil? ? exit : game_manager.exit_arcade
|
61
|
+
|
62
|
+
# Display help string | command pattern: `help`
|
63
|
+
def help(_args = []) = print_msg(s("cli.std_help"))
|
64
|
+
|
65
|
+
# Display system info | command pattern: `info`
|
66
|
+
def info(_args = []) = print_msg(s("cli.ver", { ver: game_manager.ver }))
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
# == Unities ==
|
71
|
+
|
72
|
+
# Setup input commands
|
73
|
+
def setup_commands = { "exit" => method(:quit), "ttfn" => method(:quit), "help" => method(:help),
|
74
|
+
"info" => method(:info) }
|
75
|
+
|
76
|
+
# Override: Handle command
|
77
|
+
# @param cmd [String]
|
78
|
+
# @param opt_arg [String]
|
79
|
+
# @param cmds [Array]
|
80
|
+
# @param is_valid [Boolean]
|
81
|
+
# @param cmd_err [String] custom error message
|
82
|
+
def handle_command(cmd, opt_arg, cmds, is_valid, cmd_err: s("cli.cmd_err"))
|
83
|
+
command_usage[cmd.to_sym] += 1 if cmds.key?(cmd)
|
84
|
+
super
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "paint"
|
4
|
+
|
5
|
+
module ConsoleGame
|
6
|
+
# Player class
|
7
|
+
class Player
|
8
|
+
@number_of_player = 0
|
9
|
+
# @colors = String.colors[8..15]
|
10
|
+
class << self
|
11
|
+
# attr_reader :colors
|
12
|
+
|
13
|
+
# Add Player count
|
14
|
+
def add_player
|
15
|
+
@number_of_player += 1
|
16
|
+
end
|
17
|
+
|
18
|
+
# Set global player count
|
19
|
+
def player_count(value)
|
20
|
+
@number_of_player = value
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return number of active players
|
24
|
+
def total_player
|
25
|
+
@number_of_player
|
26
|
+
end
|
27
|
+
|
28
|
+
# Setup color array and remove unwanted color options
|
29
|
+
# def setup_color
|
30
|
+
# %i[default gray].each { |elem| remove_color(elem) }
|
31
|
+
# end
|
32
|
+
|
33
|
+
# Remove color option to avoid two players share the same color tag
|
34
|
+
# @param color [Symbol]
|
35
|
+
# def remove_color(color)
|
36
|
+
# @colors.delete(color)
|
37
|
+
# end
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_reader :data, :name, :player_color, :controller
|
41
|
+
|
42
|
+
# @param name [String]
|
43
|
+
# @param controller [Input]
|
44
|
+
def initialize(name = "", controller = nil)
|
45
|
+
@name = name
|
46
|
+
@controller = controller
|
47
|
+
# Player.setup_color
|
48
|
+
Player.add_player
|
49
|
+
@player_color = Paint.random
|
50
|
+
edit_name(name)
|
51
|
+
init_data
|
52
|
+
end
|
53
|
+
|
54
|
+
# Initialise player save data
|
55
|
+
def init_data
|
56
|
+
@data = { name: name }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Edit player name
|
60
|
+
# @param new_name [String]
|
61
|
+
def edit_name(new_name = "")
|
62
|
+
if new_name.empty?
|
63
|
+
new_name = name.empty? ? "Player #{Player.total_player}" : name
|
64
|
+
end
|
65
|
+
@name = Paint[new_name, player_color]
|
66
|
+
end
|
67
|
+
|
68
|
+
# Store player's move
|
69
|
+
# @param value [Integer] Positional value of the grid
|
70
|
+
# def store_move(value)
|
71
|
+
# return nil if value.nil?
|
72
|
+
|
73
|
+
# data.fetch(:moves) << value
|
74
|
+
# end
|
75
|
+
|
76
|
+
# Update player turn count
|
77
|
+
# def update_turn_count
|
78
|
+
# data[:turn] = data.fetch(:moves).size
|
79
|
+
# end
|
80
|
+
|
81
|
+
# == Utilities ==
|
82
|
+
end
|
83
|
+
|
84
|
+
# Computer player class
|
85
|
+
# class Computer < Player
|
86
|
+
# def initialize(game_manager = nil, name = "Computer")
|
87
|
+
# super(game_manager, name)
|
88
|
+
# end
|
89
|
+
|
90
|
+
# # Returns a random integer between 1 to 7
|
91
|
+
# # @param empty_slots [Array<Integer>]
|
92
|
+
# # @param bound [Array<Integer>]
|
93
|
+
# def random_move(empty_slots, bound)
|
94
|
+
# row, = bound
|
95
|
+
# value = (empty_slots.sample % row) + 1
|
96
|
+
# print "#{value}\n"
|
97
|
+
# value
|
98
|
+
# end
|
99
|
+
# end
|
100
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "securerandom"
|
4
|
+
require_relative "../nimbus_file_utils/nimbus_file_utils"
|
5
|
+
|
6
|
+
module ConsoleGame
|
7
|
+
# User Profile class
|
8
|
+
class UserProfile
|
9
|
+
include ::NimbusFileUtils
|
10
|
+
|
11
|
+
# Alias for NimbusFileUtils
|
12
|
+
F = NimbusFileUtils
|
13
|
+
# Expected user profile structure
|
14
|
+
PROFILE = { uuid: "", username: "", saved_date: Time, appdata: {}, stats: {} }.freeze
|
15
|
+
|
16
|
+
class << self
|
17
|
+
# load user profile
|
18
|
+
def load_profile(user_profile, _extname: ".json")
|
19
|
+
integrity_check(user_profile)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Profile integrity check
|
25
|
+
# @param imported_profile [Hash]
|
26
|
+
def integrity_check(imported_profile)
|
27
|
+
return nil unless imported_profile.keys == PROFILE.keys
|
28
|
+
|
29
|
+
imported_profile[:saved_date] = to_time(imported_profile[:saved_date])
|
30
|
+
imported_profile
|
31
|
+
end
|
32
|
+
|
33
|
+
# convert string time field to TIme object for internal use
|
34
|
+
# @param str_date [String] expects a valid date format
|
35
|
+
def to_time(str_date)
|
36
|
+
return str_date if str_date.is_a?(Time)
|
37
|
+
|
38
|
+
Time.new(str_date)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_reader :profile, :filepath
|
43
|
+
attr_accessor :username
|
44
|
+
|
45
|
+
def initialize(username = "", user_profile = nil)
|
46
|
+
@username = username.empty? ? "Arcade Player" : username
|
47
|
+
@profile = user_profile.nil? ? create_profile : user_profile
|
48
|
+
end
|
49
|
+
|
50
|
+
# Create a user profile
|
51
|
+
def create_profile
|
52
|
+
{ uuid: SecureRandom.uuid, username: username, saved_date: Time.now.ceil,
|
53
|
+
appdata: Hash.new { |hash, key| hash[key] = {} }, stats: {} }
|
54
|
+
end
|
55
|
+
|
56
|
+
# Save user profile
|
57
|
+
def save_profile(extname: ".json")
|
58
|
+
filename ||= F.formatted_filename(username, extname)
|
59
|
+
@filepath ||= F.filepath(filename, "user_data")
|
60
|
+
|
61
|
+
profile[:saved_date] = Time.now.ceil
|
62
|
+
F.write_to_disk(filepath, profile)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|