Finite state machine is a mathematical computation model.
Considering a machine which has finite number of possible states, and there will be only one state in which the machine is said to be in.
Ex:- Vending machine could be in sleep mode or requesting coins from user etc…
The machine may change its state, which could depend upon the triggers.
Ex:- User inserting coins into the machine, might change the machine state from sleep to accepting the coins.
Each state would have next possible states which are associated.
State Machine Diagram :
Below is the sample code, which works using state machine algorithm.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StateMachine_BeverageVendingMachine
{
class Program
{
static void Main(string[] args)
{
//creating vending machine instance and setting default product cost
BeverageVendingMachine machine = new BeverageVendingMachine() { Cost = 10 };
while (true)
{
try
{
Console.WriteLine("Current State : " + machine.ToString());
//if machine state is sleep, then asking user to enter coins
if (machine.CurrentState == MachineState.Sleep)
{
Console.WriteLine("Please enter coins for amount : " + (machine.Cost - machine.Amount));
Int32 coin = GetValidOption((i) =>i>0 && i <= (machine.Cost - machine.Amount), "Please enter coins for amount : " + (machine.Cost - machine.Amount));
machine.AddCoins(coin);
}
//if machine state is coins status, then asking for the remaining amount or either giving a cancel option
if (machine.CurrentState == MachineState.Coin_Status)
{
Console.WriteLine("Please enter coins for amount : " + (machine.Cost - machine.Amount) + " or hit -1 to cancel");
Int32 coin = GetValidOption((i) => i > 0 && i <= (machine.Cost - machine.Amount) || i == -1, "Please enter coins for amount : " + (machine.Cost - machine.Amount) + " or hit -1 to cancel");
if (coin == -1)
machine.Cancel();
else
machine.AddCoins(coin);
}
//if machine state is active, then asking user to select beverage option
if (machine.CurrentState == MachineState.Active)
{
Console.WriteLine("Please select menu \n 1. coffee\n 2. Tea");
Int32 coin = GetValidOption((i) => i == 1 || i == 2, "Please select menu \n 1. coffee\n 2. Tea");
if (coin == 1)
machine.Product = "Coffee";
if (coin == 2)
machine.Product = "Tea";
//supplyin the selected product
machine.Supply();
}
}
catch (Exception exp)
{
Console.WriteLine("Error : " + exp.Message);
}
}
}
public static Int32 GetValidOption(Func<int, bool> validate, string msg)
{
while (true)
{
Int32 option = 0;
if (int.TryParse(Console.ReadLine(), out option))
{
if (validate(option))
return option;
else
Console.WriteLine(msg);
}
else
Console.WriteLine("Please enter valid number");
}
}
}
public class BeverageVendingMachine
{
Dictionary<StateTransition, MachineState> machineTransitions = null;
public MachineState CurrentState { get; set; }
public int Amount { get; set; }
public int Cost { get; set; }
public string Product { get; set; }
public BeverageVendingMachine()
{
//adding each possible statetransition for a given state to dictionary.
machineTransitions = new Dictionary<StateTransition, MachineState>()
{
{ new StateTransition(MachineState.Sleep,MachineCommand.Sleep2CoinCheck), MachineState.Coin_Check},
{ new StateTransition(MachineState.Coin_Status,MachineCommand.CoinStatus2Cancel), MachineState.Cancel},
{ new StateTransition(MachineState.Coin_Check,MachineCommand.CoinCheck2CointStatus), MachineState.Coin_Status},
{ new StateTransition(MachineState.Coin_Status,MachineCommand.CoinStatus2Active), MachineState.Active},
{ new StateTransition(MachineState.Active,MachineCommand.Active2Supply), MachineState.Supply},
{ new StateTransition(MachineState.Supply,MachineCommand.Supply2Sleep), MachineState.Sleep},
{ new StateTransition(MachineState.Cancel,MachineCommand.Cancel2Sleep), MachineState.Sleep}
};
this.CurrentState = MachineState.Sleep;
}
//when user inserts coins
public void AddCoins(Int32 coin)
{
//In sleep state the amount is 0, hence changing the state to coin check state
if (Amount == 0)
GetNextState(MachineCommand.Sleep2CoinCheck);
//adding the total amount inserted so far
Amount += coin;
//if amount collected is same as cost
if (Amount == Cost)
{
//changing the coin check to coin status and then coin status to active
if (CurrentState == MachineState.Coin_Check)
GetNextState(MachineCommand.CoinCheck2CointStatus);
GetNextState(MachineCommand.CoinStatus2Active);
}
else
//changing coin check to coin status, when user enters coins, but the amount is less than the actual cost
if (CurrentState == MachineState.Coin_Check)
GetNextState(MachineCommand.CoinCheck2CointStatus);
}
//If user selects cancel operation, after entering few coins
public void Cancel()
{
//changing the state to cancel and sleep
GetNextState(MachineCommand.CoinStatus2Cancel);
GetNextState(MachineCommand.Cancel2Sleep);
//handovering the amout collected before cancel
if (Amount > 0)
Console.WriteLine("Please collect your amount : {0}", Amount);
Amount = 0;
}
//after user selects product from menu
public void Supply()
{
//supplying the product
GetNextState(MachineCommand.Active2Supply);
Console.WriteLine("Supplying {0} ...", Product);
//going to sleep mode
GetNextState(MachineCommand.Supply2Sleep);
Console.WriteLine("Thanks for choosing this vending machine");
Amount = 0;
}
//setting the next state based on current state and command issued
public void GetNextState(MachineCommandcommand)
{
StateTransition trans = new StateTransition(CurrentState, command);
MachineState nextState;
if (machineTransitions.TryGetValue(trans, out nextState))
{
CurrentState = nextState;
}
else
throw new Exception(string.Format("No valid state transition for state : {0}, command : {1}", CurrentState, command));
}
public override string ToString()
{
return CurrentState.ToString();
}
}
//each state transition object is the reference of StateTransition class
public class StateTransition
{
readonly MachineState CurrentState;
readonly MachineCommandCommand;
public StateTransition(MachineState state, MachineCommand command)
{
this.CurrentState = state;
this.Command = command;
}
public override bool Equals(object obj1)
{
StateTransition obj = obj1 as StateTransition;
return (obj.Command == this.Command && obj.CurrentState == this.CurrentState);
}
public override int GetHashCode()
{
return (10 * CurrentState.GetHashCode()) + (10 * Command.GetHashCode());
}
}
//Vending machine, finite possible states
public enum MachineState
{
Sleep = 1,
Coin_Check = 2,
Cancel = 3,
Coin_Status = 4,
Active = 5,
Supply = 6
}
//Vending machine, possible state transitions
public enum MachineCommand
{
Sleep2CoinCheck = 1,
CoinStatus2Cancel = 2,
Cancel2Sleep = 3,
CoinCheck2CointStatus = 4,
CoinStatus2Active = 5,
Active2Supply = 6,
Supply2Sleep = 7
}
}
Output :