I'm currently trying to understand events in C # by writing my own custom event. My goal is to trigger an event after the user enters something into the console. if the string equals to "--start" something should happen. I am currently not reaching my breakpoint in the constructor of my custom event. I hope you can help me.
here is my code:
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"Welcome to the BlackJack Console Game!{Environment.NewLine}" );
Console.WriteLine($"You get 500 Credits. The minimal Bet is 10 and the maximal 50!{Environment.NewLine}");
Console.WriteLine($"You can check your Creditcount with --credits{Environment.NewLine}");
Console.WriteLine($"To Start the Game write --start in the command line{Environment.NewLine}");
string userInput = Console.ReadLine();
Game game = new Game();
game.UserInput = userInput;
}
}
public class Game
{
public event EventHandler<UserInputEvent> UserWritedInput;
private string _userInput;
public string UserInput
{
get { return _userInput; }
set
{
_userInput = value;
OnUserWritedInput();
}
}
public void OnUserWritedInput()
{
UserWritedInput?.Invoke(this, new UserInputEvent(_userInput));
}
}
public class UserInputEvent : EventArgs
{
private string _userInput;
public UserInputEvent(string userInput)
{
this._userInput = userInput;
if (_userInput.Equals("--start"))
{
Console.WriteLine("game started!");
}
}
}
You haven't subscribed to the event:
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"Welcome to the BlackJack Console Game!{Environment.NewLine}" );
Console.WriteLine($"You get 500 Credits. The minimal Bet is 10 and the maximal 50!{Environment.NewLine}");
Console.WriteLine($"You can check your Creditcount with --credits{Environment.NewLine}");
Console.WriteLine($"To Start the Game write --start in the command line{Environment.NewLine}");
string userInput = Console.ReadLine();
Game game = new Game();
game.UserWritedInput += OnUserWritedInput;
game.UserInput = userInput;
}
private void OnUserWritedInput(object sender, UserInputEvent args)
{
if (args.UserInput.Equals("--start"))
{
Console.WriteLine("game started!");
}
}
}
public class UserInputEvent : EventArgs
{
public string UserInput {get;}
public UserInputEvent(string userInput)
{
UserInput = userInput;
}
}
Related
I'm writing a simple text adventure, and would like to have a main menu, which can be accessed at any time, while any other method is running just by typing "menu" in the console
Here's some code that I wrote (sorry, if it's not very good, I'm only starting to learn), but in this example the menu can be accessed only once at the beginning, while I'd like it to run anytime i type "menu", no matter which part of the program is currently running
So, is there any simple and efficient way to check for certain input ("menu" in this case) without copy pasting menu call method everywhere?
Any help would be appreciated, thank you in advance
(This is my previous attempt, which doesn't work, my new approach is in UPDATE1)
class Program
{
static int menuSwitch = 0;
static void Main(string[] args)
{
Console.WriteLine("Welcome to the test program");
string menu = "menu";
string trymenu = Console.ReadLine();
do
{
Console.WriteLine("Main menu");
Console.WriteLine("1.Entry1 \n2.Entry2");
Int32.TryParse(Console.ReadLine(), out menuSwitch);
switch (menuSwitch)
{
case 1:
Console.WriteLine("Entry1");
break;
case 2:
Console.WriteLine("Entry2");
break;
default:
Console.WriteLine("Exiting menu");
break;
}
break;
} while (trymenu == menu);
{
Start();
Continue();
End();
}
}
}
UPDATE1
So, I've done some tinkering and managed to get it working as intended, keeping in mind Alejandro's advice, however as I expected, now I have to call the Menu method after every step (console messages in this case) of another (Start) method
I've been wondering if there is any workaround for this, or any other efficient method to check if there is a "menu" input, as it seems kind of excessive and tedious to work with the way it is now
class Program
{
static void Main(string[] args)
{
Start();
}
static void Menu()
{
int menuSwitch = 0;
string menu = "menu";
string trymenu = Console.ReadLine();
if (trymenu == menu)
{
Console.WriteLine("Main menu");
Console.WriteLine("1.Entry1 \n2.Entry2");
Int32.TryParse(Console.ReadLine(), out menuSwitch);
switch (menuSwitch)
{
case 1:
Console.WriteLine("Entry1");
break;
case 2:
Console.WriteLine("Entry2");
break;
default:
Console.WriteLine("Exiting menu");
break;
}
}
}
static void Start()
{
Console.WriteLine("Welcome to the test program");
Menu();
Console.WriteLine("Type 'menu' to access the main menu");
Menu();
Console.WriteLine("Message1");
Menu();
Console.WriteLine("Message2");
Menu();
Console.WriteLine("Message3");
Menu();
}
}
The simplest way would probably be to move the common code to a method. You might however want to model your game as a state machine.
This allows you to separate most of the content from the game logic. This is very useful since it allows you to store the content in some file that can be loaded by the game engine. A very simple state could look something like this:
public interface IState
{
string Description { get; }
IEnumerable<ITransition> Transitions { get; }
void OnActivated(IState from);
}
public interface ITransition
{
string Command { get; }
IState TargetState { get; }
}
In this model commands are modeled as transitions between states. There are several ways to have transitions that are global:
Make a base class that contains global transitions that all states should have
Add the global states explicitly when creating each state (probably with some helper method)
Built the global transitions directly into the main game loop
An example of the last alternative could be something like this:
public class Game
{
private readonly IState initialState;
private readonly IEnumerable<ITransition> globalCommands;
private readonly IState exitState;
public Game(IState initialState, IEnumerable<ITransition> globalCommands, IState exitState)
{
this.initialState = initialState;
this.globalCommands = globalCommands;
this.exitState = exitState;
}
private IEnumerable<ITransition> GetTransitions(IState state) => state.Transitions.Concat(globalCommands);
public void Loop()
{
var currentState = initialState;
while (currentState != exitState)
{
Console.WriteLine(currentState.Description);
var transitions = GetTransitions(currentState).ToList();
foreach (var transition in transitions)
{
Console.WriteLine(transition.Command);
}
ITransition nextTransition;
do
{
var command = Console.ReadLine();
nextTransition = transitions.FirstOrDefault(t => t.Command.Equals(command));
} while (nextTransition == null);
nextTransition.TargetState.OnActivated(currentState);
currentState = nextTransition.TargetState;
}
}
}
One complexity with this is that you probably want to return to whatever state you left when you exit the menu. One way to do this could be to save the state you entered the menu from in a "exit" transition:
public class MenuState : IState
{
public string Description { get; }
IEnumerable<ITransition> IState.Transitions => Transitions.Concat(new[] {exit});
public List<ITransition> Transitions { get; } = new List<ITransition>();
private Transition exit;
public virtual void OnActivated(IState from) => exit = new Transition("exit", from);
public MenuState(string description) => Description = description;
}
class Program
{
static void Main(string[] args)
{
Brain b = new Brain();
b.start();
}
}
public class Map
{
public string key;
public string value;
public Map(string key, string value)
{
this.key = key;
this.value = value;
}
}
public class Ears
{
private SpeechRecognitionEngine ears;
public Ears(List<Map> knowledge, EventHandler<SpeechRecognizedEventArgs> onRecognise, EventHandler<SpeechRecognitionRejectedEventArgs> onReject)
{
this.ears = new SpeechRecognitionEngine();
Choices commands = new Choices();
foreach (var item in knowledge)
{
commands.Add(item.key);
}
GrammarBuilder gBuilder = new GrammarBuilder();
gBuilder.Append(commands);
Grammar grammar = new Grammar(gBuilder);
ears.LoadGrammar(grammar);
ears.SetInputToDefaultAudioDevice();
ears.RecognizeAsync(RecognizeMode.Multiple);
ears.SpeechRecognized += onRecognise;
ears.SpeechRecognitionRejected += onReject;
}
public void stop()
{
ears.RecognizeAsyncCancel();
}
public void start()
{
ears.RecognizeAsync(RecognizeMode.Multiple);
}
}
public class Brain
{
protected List<Map> commands;
protected List<Map> answers;
private readonly Ears ears;
public Brain()
{
commands = new List<Map>();
commands.Add(new Map("list",""));
commands.Add(new Map("somethingElse","someValue"));
ears = new Ears(commands, onRecognized, onRejected);
}
private void onRecognized(object sender, SpeechRecognizedEventArgs e)
{
Console.WriteLine(e.Result.Text);
terminateCommands(new Map(e.Result.Text.Trim().ToLower(), null));
}
public void start() {
while (true) {
string answer = Console.ReadLine();
if (answer!="")
{
terminateCommands(new Map(answer, null));
}
}
}
private void onRejected(object sender, SpeechRecognitionRejectedEventArgs e)
{
}
private void terminateCommands(Map cmd) {
Console.WriteLine("need user input");
var answer = Console.ReadLine();
Console.WriteLine("need user input again");
var answer2 = Console.ReadLine();
}
}
The 'list' method works just fine (called by terminateCommands), if I use my keyboard. So terminateCommands is called from the 'start' method. However, if I use VoiceRecognition (basic Mycrosoft.Speech), and terminateCommands is called from the eventHandler, every time, when the list method needs a user input, I have to press Enter, write sg, and then press Enter again. It seems like that there's another Console.ReadLine() before every Console.ReadLine() in the 'list' method.
ears.stop() just stops the voice recognition.
Map is a class, containing 2 strings(value,key).
I'm kinda confused. Any idea?
Look at my code pls. I have ThirdClass to triger an event. In Second Class I Handle that event. But how to handle that event in my Program Class. In this class I have no ThirdClass object to subscribe an event. Do I have to declerate another event in Second class In order to triger MyPurpose() method?
public class Program
{
static void Main(string[] ars)
{
Program myProgram = new Program();
SecondClass second = new SecondClass();
second.LaunchSecondClass();
//A want to run this method when OnCounted event fired
//...
//myProgram.MyPurpose();
//...
}
public void MyPurpose()
{
Console.WriteLine("Program Class here!");
Console.ReadLine();
}
}
public class SecondClass
{
public void LaunchSecondClass()
{
ThirdClass third = new ThirdClass();
third.myEvent += this.OnCounted;
third.count();
}
private void OnCounted(object sender, EventArgs e)
{
Console.WriteLine("Second Class Here.");
//Console.ReadLine();
}
}
public class ThirdClass
{
public event EventHandler myEvent;
public void count()
{
for (int i = 0; i < 3; i++)
{
//Only for testing
Thread.Sleep(1000);
}
OnCounted();
}
protected virtual void OnCounted()
{
if (myEvent != null)
myEvent(this, EventArgs.Empty);
}
}
There are a lot of way to do what you want, as I said. The goal can be achieved, despite this, I highly recommend you to pay attention to #Glubus 's comment, .
Here's three of this ways:
Option 1 - Expose a new Event on SecondClass
public class SecondClass
{
public event EventHandler SecondEvent;
public void LaunchSecondClass()
{
ThirdClass third = new ThirdClass();
third.myEvent += this.OnCounted;
third.count();
}
private void OnCounted(object sender, EventArgs e)
{
Console.WriteLine("Second Class Here.");
//Console.ReadLine();
SecondEvent?.Invoke(); // Based on C# 6
}
}
And in your main program:
static void Main(string[] ars)
{
Program myProgram = new Program();
SecondClass second = new SecondClass();
second.SecondEvent += MyPurpose;
second.LaunchSecondClass();
}
Option 2 - Expose the ThirdClass on SecondClass as a property
public class SecondClass
{
public ThirdClass ThirdClass { get; }
public SecondClass()
{
ThirdClass = new ThirdClass();
ThirdClass.myEvent += this.OnCounted;
}
public void LaunchSecondClass()
{
if(ThirdClass == null)
ThirdClass = new ThirdClass();
ThirdClass.count();
}
private void OnCounted(object sender, EventArgs e)
{
Console.WriteLine("Second Class Here.");
//Console.ReadLine();
}
}
And in your main program:
static void Main(string[] ars)
{
Program myProgram = new Program();
SecondClass second = new SecondClass();
second.ThirdClass.myEvent += MyPurpose;
second.LaunchSecondClass();
}
Option 3 - Pass and Action (Delegate) to be executed by the SecondClass's method
public class SecondClass
{
public void LaunchSecondClass(Action action)
{
ThirdClass third = new ThirdClass();
third.myEvent += this.OnCounted;
if(action != null)
third.myEvent += (o, a) => action.Invoke();
third.count();
}
private void OnCounted(object sender, EventArgs e)
{
Console.WriteLine("Second Class Here.");
//Console.ReadLine();
}
}
And in your main program:
static void Main(string[] ars)
{
Program myProgram = new Program();
SecondClass second = new SecondClass();
second.LaunchSecondClass(MyPurpose);
}
Remember that there are no guarantees of a best practicle choice without know the real scenario where you're intent to apply it. So, maybe you must search up a design pattern to your problem and follow up the SOLID principles when planning your solutions
This is the best way to get a clean and efficient code .
Here's a good read about this topics:
Design Patterns
SOLID Principles
I Hope it help you, and sorry for my bad english
I have a class with 2 queues - a and b.
When I insert values to the second queue(queue b) all values from the first one(queue a) are being removed.
Here is part of the class:
class SuperMarket
{
private Queue<string> a;
private Queue<string> b;
public SuperMarket()
{
this.a = new Queue<string>();
this.b = new Queue<string>();
}
public void InsertToA(string name)
{
this.a.Insert(name);
}
public void InsertToB(string name)
{
this.b.Insert(name);
}
this is the main program:
public static void Main(string[] args)
{
SuperMarket a = new SuperMarket();
Console.WriteLine("Enter Names For first");
string name = Console.ReadLine();
while (name.CompareTo("aaa") != 0)
{
a.InsertToA(name);
name = Console.ReadLine();
}
Console.WriteLine("Enter Names For second");
name = Console.ReadLine();
while (name.CompareTo("aaa") != 0)
{
a.InsertToB(name);
name = Console.ReadLine();
}
}
What might cause this effect?
Thank you very much!
After a few years later, I can easily answer my own question:
public void InsertToA(string name)
{
this.a.Enqueue(name);
}
public void InsertToB(string name)
{
this.b.Enqueue(name);
}
--> One should use Enqueue instead of Insert when working with generics in c#.
I've got a working test state machine in a console app - 3 states and 5 events.
Problem: How to run in Windows Forms ie do I have a main loop which is running all the time looking at state..and if so where...if am using events ie btnPress.
The goal is that the app can be in a number of different states/screens and it needs to be solid, so using a state machine to enforce where we are, and that there are no edge cases unhandled.
Working console app code:
namespace StateMachineTest {
class Program {
static void Main(string[] args) {
var fsm = new FiniteStateMachine();
while (true) {
if (fsm.State == FiniteStateMachine.States.EnterVoucherCode) {
Console.WriteLine("State: " + fsm.State);
Console.WriteLine("Enter Voucher Code:");
string voucherCode = Console.ReadLine();
Console.WriteLine("voucher is " + voucherCode);
Console.WriteLine();
fsm.ProcessEvent(FiniteStateMachine.Events.PressNext);
}
if (fsm.State == FiniteStateMachine.States.EnterTotalSale) {
Console.WriteLine("State: " + fsm.State);
Console.WriteLine("Enter Total Sale or x to simulate back");
string voucherSaleAmount = Console.ReadLine();
if (voucherSaleAmount == "x")
fsm.ProcessEvent(FiniteStateMachine.Events.PressBackToVoucherCode);
else {
Console.WriteLine("total sale is " + voucherSaleAmount);
Console.WriteLine();
fsm.ProcessEvent(FiniteStateMachine.Events.PressRedeem);
}
}
if (fsm.State == FiniteStateMachine.States.ProcessVoucher) {
Console.WriteLine("State: " + fsm.State);
Console.WriteLine("Press 1 to fake a successful redeem:");
Console.WriteLine("Press 2 to fake a fail redeem:");
Console.WriteLine("Press 3 to do something stupid - press the Next Button which isn't allowed from this screen");
Console.WriteLine();
string result = Console.ReadLine();
//EnterVoucherCode state
if (result == "1")
fsm.ProcessEvent(FiniteStateMachine.Events.ProcessSuccess);
if (result == "2")
fsm.ProcessEvent(FiniteStateMachine.Events.ProcessFail);
if (result == "3")
fsm.ProcessEvent(FiniteStateMachine.Events.PressNext);
}
//how to handle async calls?
//how to handle many many states.. matrix could get unwieldy
}
}
}
class FiniteStateMachine {
//first state is the default for the system
public enum States { EnterVoucherCode, EnterTotalSale, ProcessVoucher };
public enum Events { PressNext, PressRedeem, ProcessSuccess, ProcessFail, PressBackToVoucherCode };
public delegate void ActionThing();
public States State { get; set; }
private ActionThing[,] fsm;
public FiniteStateMachine() {
//array of action delegates
fsm = new ActionThing[3, 5] {
//PressNext, PressRedeem, ProcessSuccess, ProcessFail, PressBackToVoucherCode
{PressNext, null, null, null, null}, //EnterVoucherCode.... can pressnext
{null, PressRedeem, null, null, PressBackToVoucherCode}, //EnterTotalSale... can pressRedeem or pressBackToVoucherCode
{null, null, ProcessSuccess, ProcessFail, null} }; //moving from ProcessVoucher... can be a processSuccess or ProcessFail.. can't go back to redeem
}
public void ProcessEvent(Events theEvent) {
try {
var row = (int)State;
var column = (int)theEvent;
//call appropriate method via matrix. So only way to change state is via matrix which defines what can and can't happen.
fsm[row, column].Invoke();
}
catch (Exception ex) {
Console.WriteLine(ex.Message); //possibly catch here to go to an error state? or if do nothing like here, then it will continue on in same state
}
}
private void PressNext() { State = States.EnterTotalSale; }
private void PressRedeem() { State = States.ProcessVoucher; }
private void ProcessSuccess() { State = States.EnterVoucherCode; }
private void ProcessFail() { State = States.EnterVoucherCode; }
private void PressBackToVoucherCode() { State = States.EnterVoucherCode; }
}
}
Not working WinForms code:
//goal is to get a fsm demo working with 3 states and 5 events.
//need number buttons, redeem and back to work.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e) {
SystemSettings.ScreenOrientation = ScreenOrientation.Angle90;
var fsm = new FiniteStateMachine();
while (true)
{
if (fsm.State == FiniteStateMachine.States.EnterVoucherCode)
{
//Console.WriteLine("State: " + fsm.State);
//if next/redeem button is pressed
//fsm.ProcessEvent(FiniteStateMachine.Events.PressNext);
}
if (fsm.State == FiniteStateMachine.States.EnterTotalSale)
{
Console.WriteLine("State: " + fsm.State);
Console.WriteLine("Enter Total Sale or x to simulate back");
string voucherSaleAmount = Console.ReadLine();
if (voucherSaleAmount == "x")
fsm.ProcessEvent(FiniteStateMachine.Events.PressBackToVoucherCode);
else
{
Console.WriteLine("total sale is " + voucherSaleAmount);
Console.WriteLine();
fsm.ProcessEvent(FiniteStateMachine.Events.PressRedeem);
}
}
if (fsm.State == FiniteStateMachine.States.ProcessVoucher)
{
Console.WriteLine("State: " + fsm.State);
Console.WriteLine("Press 1 to fake a successful redeem:");
Console.WriteLine("Press 2 to fake a fail redeem:");
Console.WriteLine("Press 3 to do something stupid - press the Next Button which isn't allowed from this screen");
Console.WriteLine();
string result = Console.ReadLine();
//EnterVoucherCode state
if (result == "1")
fsm.ProcessEvent(FiniteStateMachine.Events.ProcessSuccess);
if (result == "2")
fsm.ProcessEvent(FiniteStateMachine.Events.ProcessFail);
if (result == "3")
fsm.ProcessEvent(FiniteStateMachine.Events.PressNext);
}
}
}
private void btn_0_MouseUp(object sender, MouseEventArgs e)
{
txtCode.Text += '0';
}
private void btn_1_MouseUp(object sender, MouseEventArgs e)
{
txtCode.Text += '1';
}
private void btn_2_MouseUp(object sender, MouseEventArgs e)
{
txtCode.Text += '2';
}
private void btn_del_MouseUp(object sender, MouseEventArgs e)
{
txtCode.Text = txtCode.Text.Substring(0, txtCode.Text.Length - 1);
}
private void btn_redeem_MouseUp(object sender, MouseEventArgs e)
{
txtCode.Visible = false;
txtStatus.Visible = true;
txtStatus.Text = "PROCESSING PLEASE WAIT";
}
Code from:
Simple state machine example in C#?
There is no need to have a polling event loop that's constantly checking the state, that's what a WinForm does automatically. You should have your UI elements wire up event handlers, and those event handlers should be responsible for checking/toggling state.
This is a very dirty implementation. If you apply the State Pattern (Chapter 9 of Head First Design Patterns has a really clean example), you should be able to use your Form as the Client that holds another object corresponding to the Context that is called by the event handlers of your UI elements.
Your code smells in many ways. First of all, winforms works using a single thread, therefore with your loop, you block the thread and hence the form. Secondly, you work with Console logic within your winforms app... did you even test this? Thirdly, you never set the statemachine to a different state. Are you intending to make the buttons set the next state?
A state machine should loop something like this.
public class StateManager
{
public void Transition(IState state)
{
state.Transition(CurrentState, StateManager);
}
public IState CurrentState { get; private set; }
public event EventHandler StateSwitched;
}
public class FirstState : IState
{
private Form _form;
public FirstState(Form form)
{
_form = form;
}
public void Transition(IState oldState, StateManager stateManager)
{
_form.Closing += (sender, e) =>
{
stateManager.Transition(new SecondState(_form));
};
}
}
public class SecondState : IState
{
...
}