From fee1ed9a89704b2546a7ff59d1315dadb873874c Mon Sep 17 00:00:00 2001 From: Mendz Date: Thu, 16 Nov 2023 05:56:34 -0500 Subject: [PATCH] Release v1.1.0 --- README.md | 65 +++++++++++++++++++----- boilerplate/Game.cs.txt | 16 ++---- boilerplate/Program.cs.txt | 2 +- examples/GameConsole3Guesses/Game.cs | 2 +- examples/GameConsole3Guesses/GamePlay.cs | 8 +-- examples/GameConsole3Guesses/GameUI.cs | 23 +++++---- examples/GameConsole3Seconds/Game.cs | 2 +- examples/GameConsole3Seconds/GamePlay.cs | 4 +- examples/GameConsole3Seconds/GameUI.cs | 18 ++++--- src/GameLibrary/GameActionInfo.cs | 61 ++++++++++++++++++++++ src/GameLibrary/GameActionMode.cs | 20 ++++++++ src/GameLibrary/GameActionType.cs | 24 +++++++++ src/GameLibrary/GameConsole.cs | 52 +++++++++++-------- src/GameLibrary/GameConsoleUX.cs | 59 ++++++++++++++++----- src/GameLibrary/GameLibrary.csproj | 27 +++++----- src/GameLibrary/GameRandomizer.cs | 32 ------------ src/GameLibrary/IGameFlowAsync.cs | 33 ++++++++++++ src/GameLibrary/IGamePlay.cs | 10 ++-- src/GameLibrary/IGamePlayAsync.cs | 45 ++++++++++++++++ src/GameLibrary/IGameUI.cs | 8 ++- src/GameLibrary/IGameUIAsync.cs | 60 ++++++++++++++++++++++ 21 files changed, 432 insertions(+), 139 deletions(-) create mode 100644 src/GameLibrary/GameActionInfo.cs create mode 100644 src/GameLibrary/GameActionMode.cs create mode 100644 src/GameLibrary/GameActionType.cs delete mode 100644 src/GameLibrary/GameRandomizer.cs create mode 100644 src/GameLibrary/IGameFlowAsync.cs create mode 100644 src/GameLibrary/IGamePlayAsync.cs create mode 100644 src/GameLibrary/IGameUIAsync.cs diff --git a/README.md b/README.md index d28c8e3..1bc9e9f 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ The design allows the game developer to focus on implementing the game play and Thus, for example, in a game console app's program main entry point, the developer can simply code: - new GameConsole( + new GameConsole( "Game name", "All rights reserved.", "Game description.", @@ -32,16 +32,31 @@ Thus, for example, in a game console app's program main entry point, the develop GamePlayReadyMode.WhileReady ).Play(); -## IGamePlay -Defines a game play designed to align with the basic construct's Start(), Action(), Continue(), GameOver() and End() methods. +## GameActionMode +Specifies the game action mode if for GamePlay, GamePause or GameStop. + +## GameActionType +Specifies the game action type if for Control, Response, Navigation or Other. + +## GameActionInfo +Represents a game action, which has an input, an output and/or a result for the given game action mode and game action type. + +The default game action mode is GamePlay. -The game play Action() is defined by the *in* and *out* generic types passed to IGamePlay. +The default game action type is Control. + +Typically, an IGameUI.Action() creates an instance of a GameActionInfo to pass to IGamePlay.Action() for execution, evaluation or processing. + +## IGamePlay and IGamePlayAsync +Defines a game play designed to align with the basic construct's Start(), Action(), Continue(), GameOver() and End() methods. The game play is essentially the actual game. When implemented sans any UI-specific aspects, the game play can be re-used in different UI/UX platforms. -## IGameUI +IGamePlayAsync is provided for async implementations. + +## IGameUI\ and IGameUIAsync\ Defines a game UI designed to align with the basic construct's Start(), Action(), Continue(), GameOver() and End() methods. IGameUI accepts a type that implements IGamePlay, and provides additional methods to Render() and Refresh(). @@ -50,7 +65,9 @@ An implementation must define a parameterless constructor that can initialize an Implementations can be UI/UX platform specific. -## IGameFlow +IGameUIAsync is provided for async implementations. + +## IGameFlow and IGameFlowAsync Defines a game flow designed to align with the basic flow's Play(), Ready(), Set() and Go() methods. Play() implements the basic flow. @@ -59,10 +76,7 @@ Go() implements the basic construct. Implementations can be UI/UX platform specific. -## GameRandomizer -Defines a game randomizer to generate random numbers. - -Methods are provided to get the next random number, the next random number below a limit, and the next random number within a minimum/maximum range. +IGameFlowAsync is provided for async implementations. ## GamePlayReadyMode Specifies the game play ready mode or how the game flows to consume the Ready(), Set() and Go() methods. @@ -79,6 +93,8 @@ An example is a dice guessing game, where the player guesses and rolls the dice: - Opting to continue simply continues the same game; - Otherwise, the game ends and closes. +See: [GameConsoleDice](https://github.com/etmendz/game-console-dice) + ### WhileReady Allow starting more than one game UI instance per launch. @@ -92,7 +108,9 @@ An example is 2048, where the player performs moves to reach a goal: 2. If the player runs out of moves, then the game is over: - Then the game prompts the player to start a new game. -## GameConsole +See: [GameConsole2048](https://github.com/etmendz/game-console-2048) + +## GameConsole\ GameConsole implements IGameFlow for game console apps. GameConsole accepts a type that implements IGameUI, which requires a type that implements IGamePlay. @@ -157,7 +175,7 @@ The code pattern can be described as follows: - Action() refreshes the UI, prompts for action input and processes the result - Continue() evaluates the game result and prompts for control input (to try again?) - GameOver() checks for the game over state -- End() shows the game result. +- End() shows the game result and ends the game. ## GameConsole3Seconds [GameConsole3Seconds](https://github.com/etmendz/game-library/tree/main/examples/GameConsole3Seconds) challenges the player to stop the clock at 3 seconds flat. @@ -176,11 +194,32 @@ The code pattern can be described as follows: - Action() refreshes the UI, prompts for action input and processes the result - Continue() evaluates the game result, shows the game result and prompts for control input (to try again?); if the player opts to continue, the clock is restarted and the UI is re-rendered. - GameOver() checks for the game over state (if the player opted to quit) -- End() in effect, "acknowledges" that the player quits; essentially, does nothing. +- End() ends the game. # Boilerplate Boilerplate template codes are available in the repo's [boilerplate/](https://github.com/etmendz/game-library/tree/main/boilerplate) subfolder. +# Release Notes + +## v1.1.0 +This is the first release version, published after Microsoft released .NET 8. + +- Breaking: GameRandomizer deprecated. Directly use [System.Random.Shared](https://learn.microsoft.com/en-us/dotnet/api/system.random.shared) instead. +- Breaking: IGamePlay.Action() is no longer variant, instead defined as: bool Action(GameActionInfo). +- Breaking: IGamePlay no longer needs variant types for Action(). +- Breaking: IGameUI no longer needs variant types for IGamePlay. +- Breaking: GameConsole no longer needs variant types for TGameUI and TGamePlay. +- New: Introduced GameActionMode and GameActionType enumerations. +- New: Introduced GameActionInfo, a class representing a game action. +- New: Introduced IGamePlayAsync, IGameUIAsync and IGameFlowAsync. +- New: GameConsoleUX adds GetParsableEntry(), which can block/loop until a valid date or numeric value is entered. +- Updated: Game.cs.txt boilerplate template updated in line with GameConsole's breaking changes. +- Updated: Example projects codes updated in line with the breaking changes listed above. +- Updated: Documentation updated. + +## v1.0.x +These are the preview versions, implemented using .NET 8 preview versions. + --- (c) Mendz, etmendz. All rights reserved. \ No newline at end of file diff --git a/boilerplate/Game.cs.txt b/boilerplate/Game.cs.txt index e7c3f7a..d9bd650 100644 --- a/boilerplate/Game.cs.txt +++ b/boilerplate/Game.cs.txt @@ -6,10 +6,7 @@ * Replace "game-console-app-name" with your game console app project's namespace. * Find and replace the appropriate generic types with actual types in the game project. * Use this template to wrap GameConsole with game initialization and setup codes. -* -* Thus, the program main entry point can use the following boilerplate template to call Play(): -* -* new Game().Play(); +* This works with Program.cs.txt. */ namespace game-console-app-name; @@ -17,17 +14,10 @@ namespace game-console-app-name; /// /// Wraps GameConsole with game initialization and setup codes. /// -/// -/// The program main entry point can use the following boilerplate template to call Play(): -/// -/// new Game().Play(); -/// -/// -internal class Game : GameConsole +internal class Game : GameConsole { public Game() - : base( - "Game name", + : base("Game name", "All rights reserved.", "Game description.", "Press [Esc] anytime to exit.", diff --git a/boilerplate/Program.cs.txt b/boilerplate/Program.cs.txt index 610b105..b693cea 100644 --- a/boilerplate/Program.cs.txt +++ b/boilerplate/Program.cs.txt @@ -4,7 +4,7 @@ * * To use, save this template to a new .cs file (ex. Program.cs) in your project. * Replace "game-console-app-name" with your game console app project's namespace. -* This works only if a Game class is also implemented (see Game.cs.txt). +* This works with Game.cs.txt. */ namespace game-console-app-name; diff --git a/examples/GameConsole3Guesses/Game.cs b/examples/GameConsole3Guesses/Game.cs index ac0a64f..ad76588 100644 --- a/examples/GameConsole3Guesses/Game.cs +++ b/examples/GameConsole3Guesses/Game.cs @@ -6,7 +6,7 @@ namespace GameConsole3Guesses; -internal class Game : GameConsole +internal class Game : GameConsole { public Game() : base("GameConsole3Guesses", diff --git a/examples/GameConsole3Guesses/GamePlay.cs b/examples/GameConsole3Guesses/GamePlay.cs index 8c504c3..622dc9a 100644 --- a/examples/GameConsole3Guesses/GamePlay.cs +++ b/examples/GameConsole3Guesses/GamePlay.cs @@ -6,7 +6,7 @@ namespace GameConsole3Guesses; -internal class GamePlay : IGamePlay +internal class GamePlay : IGamePlay { public int Secret { get; private set; } @@ -16,15 +16,15 @@ internal class GamePlay : IGamePlay public bool Start() { - Secret = GameRandomizer.Next(1, 11); + Secret = Random.Shared.Next(1, 11); IsWon = false; Tries = 0; return true; } - public bool Action(int action) + public bool Action(GameActionInfo gameActionInfo) { - if (action == Secret) IsWon = true; + if (gameActionInfo.GetInputAs() == Secret) IsWon = true; Tries++; return true; } diff --git a/examples/GameConsole3Guesses/GameUI.cs b/examples/GameConsole3Guesses/GameUI.cs index 802e0f3..03ad145 100644 --- a/examples/GameConsole3Guesses/GameUI.cs +++ b/examples/GameConsole3Guesses/GameUI.cs @@ -6,8 +6,14 @@ namespace GameConsole3Guesses; -internal class GameUI : IGameUI +internal class GameUI : IGameUI { + /// + /// Note that a game can have different instances of GameConsoleUX for different game action mode and type combinations. + /// For example, one instance can have EscExit = true, and another instance with EscExit = false. + /// + private readonly GameConsoleUX _gameConsoleUX = new(); + public GamePlay GamePlay { get; set; } public GameUI() => GamePlay = new(); @@ -25,25 +31,24 @@ public bool Action() Console.WriteLine(); Console.Write($"Guess #{GamePlay.Tries + 1}: "); Console.CursorVisible = true; - int guess; - while (true) + GameActionInfo gameActionInfo = new() { - if (Int32.TryParse(Console.ReadLine(), out guess)) break; - } + ActionType = GameActionType.Response, + Input = GameConsoleUX.GetParsableEntry() + }; Console.CursorVisible = false; - return GamePlay.Action(guess); + return GamePlay.Action(gameActionInfo); } public bool Continue() { - bool isOK = false; if (GamePlay.Continue()) { Console.WriteLine(); Console.WriteLine("Try again? (Y/N): "); - isOK = new GameConsoleUX().GetYN() == ConsoleKey.Y; + return _gameConsoleUX.GetYN() == ConsoleKey.Y; } - return isOK; + return true; } public void End() diff --git a/examples/GameConsole3Seconds/Game.cs b/examples/GameConsole3Seconds/Game.cs index 4e6612e..a596bea 100644 --- a/examples/GameConsole3Seconds/Game.cs +++ b/examples/GameConsole3Seconds/Game.cs @@ -6,7 +6,7 @@ namespace GameConsole3Seconds; -internal class Game : GameConsole +internal class Game : GameConsole { public Game() : base("GameConsole3Seconds", diff --git a/examples/GameConsole3Seconds/GamePlay.cs b/examples/GameConsole3Seconds/GamePlay.cs index 7e3d8ca..ce4c535 100644 --- a/examples/GameConsole3Seconds/GamePlay.cs +++ b/examples/GameConsole3Seconds/GamePlay.cs @@ -7,7 +7,7 @@ namespace GameConsole3Seconds; -internal class GamePlay : IGamePlay +internal class GamePlay : IGamePlay { public Stopwatch Stopwatch { get; private set; } = new(); @@ -21,7 +21,7 @@ public bool Start() return Stopwatch.IsRunning; } - public bool Action(ConsoleKey action) + public bool Action(GameActionInfo gameActionInfo) { Stopwatch.Stop(); long elapsed = Stopwatch.ElapsedMilliseconds; diff --git a/examples/GameConsole3Seconds/GameUI.cs b/examples/GameConsole3Seconds/GameUI.cs index e21057e..e7fdc9d 100644 --- a/examples/GameConsole3Seconds/GameUI.cs +++ b/examples/GameConsole3Seconds/GameUI.cs @@ -6,8 +6,16 @@ namespace GameConsole3Seconds; -internal class GameUI : IGameUI +internal class GameUI : IGameUI { + /// + /// Note that a game can have different instances of GameConsoleUX for different game action mode and type combinations. + /// For example, one instance can have EscExit = true, and another instance with EscExit = false. + /// + private readonly GameConsoleUX _gameConsoleUX = new(); + + private readonly HashSet _validKeyInfos = [new(' ', ConsoleKey.Spacebar, false, false, false)]; + private bool _refresh = false; public GamePlay GamePlay { get; set; } @@ -34,15 +42,13 @@ public void Render() public bool Action() { ConsoleKeyInfo ? cki = null; - GameConsoleUX gameUX = new(); - HashSet validKeyInfos = [new(' ', ConsoleKey.Spacebar, false, false, false)]; while (cki is null) { Refresh(); - cki = gameUX.GetKeyInfo(validKeyInfos); + cki = _gameConsoleUX.GetKeyInfo(_validKeyInfos); } Console.WriteLine(); - return GamePlay.Action(cki.Value.Key); + return GamePlay.Action(new() { Input = cki.Value.Key }); } public bool Continue() @@ -62,7 +68,7 @@ public bool Continue() Console.ForegroundColor = foregroundColor; Console.WriteLine(); Console.WriteLine("Try again? (Y/N): "); - if (new GameConsoleUX().GetYN() == ConsoleKey.Y) + if (_gameConsoleUX.GetYN() == ConsoleKey.Y) { _refresh = false; // Turn off refresh mode... GamePlay.Continue(); diff --git a/src/GameLibrary/GameActionInfo.cs b/src/GameLibrary/GameActionInfo.cs new file mode 100644 index 0000000..9bcceb1 --- /dev/null +++ b/src/GameLibrary/GameActionInfo.cs @@ -0,0 +1,61 @@ +namespace GameLibrary; + +/// +/// Represents a game action. +/// +public class GameActionInfo +{ + /// + /// Gets or sets the action mode. + /// + public GameActionMode ActionMode { get; set; } = GameActionMode.GamePlay; + + /// + /// Gets or sets the action type. + /// + public GameActionType ActionType { get; set; } = GameActionType.Control; + + /// + /// Gets or sets the game action input. + /// + public object? Input { get; set; } + + /// + /// Gets or sets the game action output. + /// + public object? Output { get; set; } + + /// + /// Gets or sets the game action result. + /// + public object? Result { get; set; } + + /// + /// Gets the input as the type specified. Basically, a casting mnemonic. + /// + /// The type to cast to. + /// The input cast to type T. + public virtual T? GetInputAs() => GetValueAs(Input); + + /// + /// Gets the output as the type specified. Basically, a casting mnemonic. + /// + /// The type to cast to. + /// The output cast to type T. + public virtual T? GetOutputAs() => GetValueAs(Output); + + /// + /// Gets the result as the type specified. Basically, a casting mnemonic. + /// + /// The type to cast to. + /// The result cast to type T. + public virtual T? GetResultAs() => GetValueAs(Result); + + /// + /// Gets the value as the type specified. Basically, a casting mnemonic. + /// + /// The type to cast to. + /// The value to cast. + /// The value cast to type T. + protected static T? GetValueAs(object? value) => (T?)value; +} \ No newline at end of file diff --git a/src/GameLibrary/GameActionMode.cs b/src/GameLibrary/GameActionMode.cs new file mode 100644 index 0000000..8b0f811 --- /dev/null +++ b/src/GameLibrary/GameActionMode.cs @@ -0,0 +1,20 @@ +namespace GameLibrary; + +/// +/// Specifies the game action mode. +/// +public enum GameActionMode +{ + /// + /// In-gameplay actions. + /// + GamePlay, + /// + /// Non-gameplay actions while the game is paused. Example, the action to resume the game. + /// + GamePause, + /// + /// Non-gameplay actions while the game is stopped. Example, the action to undo the last move that led to a gameover. + /// + GameStop +} \ No newline at end of file diff --git a/src/GameLibrary/GameActionType.cs b/src/GameLibrary/GameActionType.cs new file mode 100644 index 0000000..684a18e --- /dev/null +++ b/src/GameLibrary/GameActionType.cs @@ -0,0 +1,24 @@ +namespace GameLibrary; + +/// +/// Specifies the game action type. +/// +public enum GameActionType +{ + /// + /// Gameplay controls like moves, attacks, selections, etc. + /// + Control, + /// + /// Responses or data entries to gameplay prompts. + /// + Response, + /// + /// Navigation of non-gameplay elements like menus, configurations, settings, etc. + /// + Navigation, + /// + /// Other actions. + /// + Other +} \ No newline at end of file diff --git a/src/GameLibrary/GameConsole.cs b/src/GameLibrary/GameConsole.cs index 75b0881..54e249d 100644 --- a/src/GameLibrary/GameConsole.cs +++ b/src/GameLibrary/GameConsole.cs @@ -11,17 +11,15 @@ namespace GameLibrary; /// /// The game UI. /// The game play. -/// The action input type. -/// The action output or result type. /// The game's name. /// The game copyright information. /// The game description. -/// The game splash text. -/// The game play ready mode. +/// The game splash text. Default is null. +/// The game play ready mode. Default is . /// /// The program main entry point can use the following boilerplate template to call Play(): /// -/// new GameConsole<GameUI, GamePlay, ConsoleKey, bool>( +/// new GameConsole<GameUI, GamePlay>( /// "Game name", /// "All rights reserved.", /// "Game description.", @@ -30,9 +28,15 @@ namespace GameLibrary; /// ).Play(); /// /// -public class GameConsole(string name, string copyright, string description, string? splashText = null, GamePlayReadyMode gamePlayReadyMode = GamePlayReadyMode.IfReady) : IGameFlow - where TGameUI : IGameUI, new() - where TGamePlay : IGamePlay +public class GameConsole( + string name, + string copyright, + string description, + string? splashText = null, + GamePlayReadyMode gamePlayReadyMode = GamePlayReadyMode.IfReady + ) : IGameFlow + where TGameUI : IGameUI, new() + where TGamePlay : IGamePlay { /// /// Gets the game's name. @@ -80,7 +84,7 @@ public class GameConsole(string name, public string? GoText { get; set; } /// - /// Plays the game. + /// Plays the game. Implements the basic flow. /// /// The default implementation uses GamePlayReadyMode to switch (if|while) Ready(), Set(), Go()! public virtual void Play() @@ -128,13 +132,9 @@ public virtual void Play() /// public virtual void Splash() { - Console.WriteLine($"{Name} {Assembly.GetExecutingAssembly().GetCustomAttribute()?.InformationalVersion} (c) {DateTime.Now.Year} {Copyright}"); + Console.WriteLine($"{Name} {Assembly.GetEntryAssembly()?.GetName().Version?.ToString()} (c) {DateTime.Now.Year} {Copyright}"); Console.WriteLine(Description); - if (!string.IsNullOrEmpty(SplashText)) - { - Console.WriteLine(); - Console.WriteLine(SplashText); - } + GameConsole.ShowText(SplashText); } /// @@ -157,7 +157,7 @@ public virtual bool Ready() if (!IsReady) { Splash(); - if (!string.IsNullOrEmpty(ReadyText)) Console.WriteLine(ReadyText); + GameConsole.ShowText(ReadyText); IsReady = true; } return IsReady; @@ -169,13 +169,12 @@ public virtual bool Ready() /// The default implementation prompts the player to press the [Enter] key to start playing. public virtual void Set() { - Console.WriteLine(); - if (!string.IsNullOrEmpty(SetText)) Console.WriteLine(SetText); + GameConsole.ShowText(SetText); new GameConsoleUX().GetKey(ConsoleKey.Enter); } /// - /// Go! + /// Go! Implements the basic construct. /// /// /// The default implementation is as follows: @@ -196,7 +195,7 @@ public virtual void Set() /// public virtual void Go() { - if (!string.IsNullOrEmpty(GoText)) Console.WriteLine(GoText); + GameConsole.ShowText(GoText); TGameUI gameUI = new(); if (gameUI.Start()) { @@ -210,4 +209,17 @@ public virtual void Go() } gameUI.End(); } + + /// + /// If the text is not empty, writes an empty line followed by the text value. + /// + /// The text to show. + protected static void ShowText(string? text) + { + if (!string.IsNullOrEmpty(text)) + { + Console.WriteLine(); + Console.WriteLine(text); + } + } } \ No newline at end of file diff --git a/src/GameLibrary/GameConsoleUX.cs b/src/GameLibrary/GameConsoleUX.cs index be521ed..4879b85 100644 --- a/src/GameLibrary/GameConsoleUX.cs +++ b/src/GameLibrary/GameConsoleUX.cs @@ -37,13 +37,13 @@ public class GameConsoleUX(bool escExit = true) /// Gets a move (arrow) key input. /// /// The key input. - public ConsoleKey GetMove() => GetKey(new HashSet() { ConsoleKey.UpArrow, ConsoleKey.RightArrow, ConsoleKey.LeftArrow, ConsoleKey.DownArrow }); + public ConsoleKey GetMove() => GetKey([ConsoleKey.UpArrow, ConsoleKey.RightArrow, ConsoleKey.LeftArrow, ConsoleKey.DownArrow]); /// /// Gets a Y or N key input. /// /// The key input. - public ConsoleKey GetYN() => GetKey(new HashSet() { ConsoleKey.Y, ConsoleKey.N }); + public ConsoleKey GetYN() => GetKey([ConsoleKey.Y, ConsoleKey.N]); /// /// Gets a key input. @@ -51,8 +51,8 @@ public class GameConsoleUX(bool escExit = true) /// The valid key input. /// Indicates to evaluate EscExit or not. Default is true. /// The key input. - /// Pass evalEscExit = false to ignore . - public ConsoleKey GetKey(ConsoleKey validKey, bool evalEscExit = true) => GetKey(new HashSet { validKey }, evalEscExit); + /// Pass evalEscExit = false to ignore . This can be used for example when [Esc] is a valid key input. + public ConsoleKey GetKey(ConsoleKey validKey, bool evalEscExit = true) => GetKey([validKey], evalEscExit); /// /// Gets a key input. @@ -60,7 +60,7 @@ public class GameConsoleUX(bool escExit = true) /// The valid key inputs. /// Indicates to evaluate EscExit or not. Default is true. /// The key input. - /// Pass evalEscExit = false to ignore . + /// Pass evalEscExit = false to ignore . This can be used for example when [Esc] is a valid key input. public virtual ConsoleKey GetKey(HashSet validKeys, bool evalEscExit = true) { ConsoleKey key; @@ -79,7 +79,7 @@ public virtual ConsoleKey GetKey(HashSet validKeys, bool evalEscExit /// Indicates to evaluate EscExit or not. Default is true. /// The key input. /// - /// Pass evalEscExit = false to ignore . + /// Pass evalEscExit = false to ignore . This can be used for example when [Esc] is a valid key input. /// Use when there are more keys allowed than not, that it makes sense to list the invalid keys instead. /// public virtual ConsoleKey GetKeyExcept(HashSet invalidKeys, bool evalEscExit = true) @@ -100,21 +100,23 @@ public virtual ConsoleKey GetKeyExcept(HashSet invalidKeys, bool eva /// Indicates to evaluate EscExit or not. Default is true. /// If valid, returns the input key info. Else, or if there's no key available to check, returns null. /// - /// Pass evalEscExit = false to ignore . + /// Pass evalEscExit = false to ignore . This can be used for example when [Esc] is a valid key input. /// As a non-blocking method, use in a loop that can perform tasks until a valid input key info is returned. /// - /// // An example, from an IGameUI implementation... + /// // Example usage in an IGameUI* implementation; to accept a press on the [Spacebar]... + /// private readonly GameConsoleUX _gameConsoleUX = new(); + /// + /// private readonly HashSet _validKeyInfos = [new(' ', ConsoleKey.Spacebar, false, false, false)]; + /// /// public bool Action() /// { /// ConsoleKeyInfo? cki = null; - /// GameConsoleUX gameUX = new(); - /// HashSet<ConsoleKeyInfo> validKeyInfos = [new(' ', ConsoleKey.Spacebar, false, false, false)]; /// while (cki is null) /// { /// Refresh(); - /// cki = gameUX.GetKeyInfo(validKeyInfos); + /// cki = _gameConsoleUX.GetKeyInfo(_validKeyInfos); /// } - /// return GamePlay.Action(cki.Value.Key); + /// return GamePlay.Action(new() { Input = cki.Value.Key }); /// } /// /// @@ -128,4 +130,37 @@ public virtual ConsoleKey GetKeyExcept(HashSet invalidKeys, bool eva } return null; } + + /// + /// Gets an entry of type T. + /// + /// A type that implements . + /// The valid entry of type T. + /// Loops until an entry that can be parsed to type T is entered. + /// + /// // Example usage in an IGameUI* implementation; to accept a numeric entry... + /// private readonly GameConsoleUX _gameConsoleUX = new(); + /// + /// public bool Action() + /// { + /// Console.CursorVisible = true; + /// GameActionInfo gameActionInfo = new() + /// { + /// ActionType = GameActionType.Response, + /// Input = GameConsoleUX.GetParsableEntry() + /// }; + /// Console.CursorVisible = false; + /// return GamePlay.Action(gameActionInfo); + /// } + /// + public static T GetParsableEntry() + where T : IParsable + { + T? entry; + while (true) + { + if (T.TryParse(Console.ReadLine(), null, out entry)) break; + } + return entry; + } } \ No newline at end of file diff --git a/src/GameLibrary/GameLibrary.csproj b/src/GameLibrary/GameLibrary.csproj index 13ab367..87cc3a5 100644 --- a/src/GameLibrary/GameLibrary.csproj +++ b/src/GameLibrary/GameLibrary.csproj @@ -2,21 +2,20 @@ net8.0 - true + true enable - enable - preview - 1.0.8 - True - True - © Mendz, etmendz. All rights reserved. - GameLibrary - A game library. - https://github.com/etmendz/game-library/wiki - README.md - https://github.com/etmendz/game-library - LICENSE - Mendz + enable + 1.1.0 + True + True + © Mendz, etmendz. All rights reserved. + GameLibrary + A game library. + https://github.com/etmendz/game-library/wiki + README.md + https://github.com/etmendz/game-library + LICENSE + Mendz diff --git a/src/GameLibrary/GameRandomizer.cs b/src/GameLibrary/GameRandomizer.cs deleted file mode 100644 index c9b9ce6..0000000 --- a/src/GameLibrary/GameRandomizer.cs +++ /dev/null @@ -1,32 +0,0 @@ -/* -* GameLibrary (c) Mendz, etmendz. All rights reserved. -* SPDX-License-Identifier: MIT -*/ -namespace GameLibrary; - -/// -/// Defines a game randomizer. -/// -public static class GameRandomizer -{ - /// - /// Gets a random number. - /// - /// A random number. - public static int Next() => Random.Shared.Next(); - - /// - /// Gets a random number less than the given limit. - /// - /// The limit. - /// A random number less than the given limit. - public static int Next(int limit) => Random.Shared.Next(limit); - - /// - /// Gets a random number within a given range. - /// - /// The inclusive minimum value. - /// The exclusive maximum value. - /// A random number within a given range. - public static int Next(int min, int max) => Random.Shared.Next(min, max); -} \ No newline at end of file diff --git a/src/GameLibrary/IGameFlowAsync.cs b/src/GameLibrary/IGameFlowAsync.cs new file mode 100644 index 0000000..19412d5 --- /dev/null +++ b/src/GameLibrary/IGameFlowAsync.cs @@ -0,0 +1,33 @@ +/* +* GameLibrary (c) Mendz, etmendz. All rights reserved. +* SPDX-License-Identifier: MIT +*/ +namespace GameLibrary; + +/// +/// Defines the game flow -- with async methods. +/// +/// Implementations can be UI/UX platform specific. +public interface IGameFlowAsync +{ + /// + /// Plays the game. + /// + public Task Play(); + + /// + /// Ready. + /// + /// True of ready, else false. + public Task Ready(); + + /// + /// Set. + /// + public Task Set(); + + /// + /// Go! + /// + public Task Go(); +} \ No newline at end of file diff --git a/src/GameLibrary/IGamePlay.cs b/src/GameLibrary/IGamePlay.cs index 1e2a6ff..e781036 100644 --- a/src/GameLibrary/IGamePlay.cs +++ b/src/GameLibrary/IGamePlay.cs @@ -7,13 +7,11 @@ namespace GameLibrary; /// /// Defines the game play. /// -/// The action input type. -/// The action output or result type. /// /// The game play is essentially the actual game. /// When implemented separate from any UI-specific aspects, the game play can be re-used in different UI/UX platforms. /// -public interface IGamePlay +public interface IGamePlay { /// /// Starts the game play. @@ -24,9 +22,9 @@ public interface IGamePlay /// /// Executes the game play action. /// - /// The action input. - /// The action output or result. - public TActionOut Action(TActionIn action); + /// The game action info to execute, evaluate or process. + /// True if the game play action is executed, else false. + public bool Action(GameActionInfo gameActionInfo); /// /// Continues the game play. diff --git a/src/GameLibrary/IGamePlayAsync.cs b/src/GameLibrary/IGamePlayAsync.cs new file mode 100644 index 0000000..ca444bc --- /dev/null +++ b/src/GameLibrary/IGamePlayAsync.cs @@ -0,0 +1,45 @@ +/* +* GameLibrary (c) Mendz, etmendz. All rights reserved. +* SPDX-License-Identifier: MIT +*/ +namespace GameLibrary; + +/// +/// Defines the game play -- with async methods. +/// +/// +/// The game play is essentially the actual game. +/// When implemented separate from any UI-specific aspects, the game play can be re-used in different UI/UX platforms. +/// +public interface IGamePlayAsync +{ + /// + /// Starts the game play. + /// + /// True if the game play is started, else false. + public Task Start(); + + /// + /// Executes the game play action. + /// + /// The game action info to execute, evaluate or process. + /// True if the game play action is executed, else false. + public Task Action(GameActionInfo gameActionInfo); + + /// + /// Continues the game play. + /// + /// True to continue, else false. + public Task Continue(); + + /// + /// Detects if it's game over. + /// + /// True if game over, else false. + public Task GameOver(); + + /// + /// Ends the game play. + /// + public Task End(); +} \ No newline at end of file diff --git a/src/GameLibrary/IGameUI.cs b/src/GameLibrary/IGameUI.cs index b0751e5..8075c53 100644 --- a/src/GameLibrary/IGameUI.cs +++ b/src/GameLibrary/IGameUI.cs @@ -8,11 +8,9 @@ namespace GameLibrary; /// Defines the game UI. /// /// The game play. -/// The action input type. -/// The action output or result type. /// Implementations can be UI/UX platform specific. -public interface IGameUI - where TGamePlay : IGamePlay +public interface IGameUI + where TGamePlay : IGamePlay { /// /// Gets or sets the game play. @@ -61,7 +59,7 @@ public bool Start() /// Executes the game action. /// /// True if the game play action is executed, else false. - public bool Action(); // => GamePlay.Action(); + public bool Action(); /// /// Continues the game. diff --git a/src/GameLibrary/IGameUIAsync.cs b/src/GameLibrary/IGameUIAsync.cs new file mode 100644 index 0000000..1648bb7 --- /dev/null +++ b/src/GameLibrary/IGameUIAsync.cs @@ -0,0 +1,60 @@ +/* +* GameLibrary (c) Mendz, etmendz. All rights reserved. +* SPDX-License-Identifier: MIT +*/ +namespace GameLibrary; + +/// +/// Defines the game UI -- with async methods. +/// +/// The game play -- with async methods. +/// Implementations can be UI/UX platform specific. +public interface IGameUIAsync + where TGamePlay : IGamePlayAsync +{ + /// + /// Gets or sets the game play. + /// + public TGamePlay GamePlay { get; set; } + + /// + /// Starts the game. + /// + /// True if the game is started, else false. + public Task Start(); + + /// + /// Renders the game UI. + /// + public Task Render(); + + /// + /// Refreshes the game UI. + /// + public Task Refresh(); + + /// + /// Executes the game action. + /// + /// True if the game play action is executed, else false. + public Task Action(); + + /// + /// Continues the game. + /// + /// True to continue, else false. + public Task Continue(); // => GamePlay.Continue(); + + /// + /// Detects if it's game over. + /// + /// True if game over, else false. + /// The default implementation awaits GamePlay.GameOver(). + public async Task GameOver() => await GamePlay.GameOver(); + + /// + /// Ends the game. + /// + /// The default implementation awaits GamePlay.End(). + public async Task End() => await GamePlay.End(); +} \ No newline at end of file