← State machine lesson
Lab

State machine

Sign in to run your code

Read the brief and the starter here. With a free account you can edit it, run the sample, step through it and check your work, and your progress is kept.

type Phase = 'available' | 'plugged' | 'charging' | 'faulted';
type ConnectorEvent =
  | { type: 'plug' | 'unplug' | 'start' | 'stop' | 'fault' | 'reset' }
  | { type: 'meter'; wh: number };
type Connector = { plugged: boolean; charging: boolean; faulted: boolean; energy: number };
type Decision = { connector: Connector; reason: string; effect: string };

const newConnector = (): Connector =>
  ({ plugged: false, charging: false, faulted: false, energy: 0 });

// The phase the station's screen shows, worked out from the flags.
function phaseOf(c: Connector): Phase {
  if (c.faulted) return 'faulted';
  if (c.charging) return 'charging';
  return c.plugged ? 'plugged' : 'available';
}

// What the station's log writes after each event. The checker prints it.
const describe = (c: Connector) => `${phaseOf(c)} ${c.energy}Wh`;

const accept = (c: Connector, effect = 'none'): Decision =>
  ({ connector: c, reason: 'accepted', effect });
const refuse = (c: Connector): Decision =>
  ({ connector: c, reason: 'not-allowed', effect: 'none' });

// Each event sets the flags it knows about.
function transition(c: Connector, event: ConnectorEvent): Decision {
  switch (event.type) {
    case 'plug':
      return accept({ ...c, plugged: true });
    case 'unplug': {
      const effect = c.charging ? 'open-relay' : 'none';
      return accept({ ...c, plugged: false, charging: false }, effect);
    }
    case 'start':
      if (!c.plugged) return refuse(c);
      return accept({ ...c, charging: true, energy: 0 }, 'close-relay');
    case 'stop':
      if (!c.charging) return refuse(c);
      return accept({ ...c, charging: false }, 'open-relay');
    case 'meter':
      return accept({ ...c, energy: c.energy + event.wh });
    case 'fault': {
      const effect = c.charging ? 'open-relay' : 'none';
      return accept({ ...c, faulted: true, charging: false }, effect);
    }
    case 'reset':
      return accept({ ...c, faulted: false });
  }
}