Here is my refactored code. I tried to make it as readable/English as possible. Hope it is easy to follow.
Please let me know if it can be improved.
One area of improvement is the design of the code. To add a new level, I would need to go to many places in the code and add adjustments. Without classes, it is harder to do this. I have also thought about mimicking classes using parallel arrays.
public class Hacker : MonoBehaviour
{
enum Access
{
None = -1,
Library = 0,
Bank = 1,
PoliceStation = 2,
NASA = 3
}
Access access;
const int PASSWORD_INDEX = 0;
const int PASSWORD_HINT_INDEX = 1;
// passwords is a table of Access passwords. Access passwords are password/hint pairs
// passwords[0]-->{ {password0, hint0}, ... , {passwordN, hintN} }
// :
// passwords[M]-->{ {password0, hint0}, ... , {passwordN, hintN} }
//string[][,] passwords = new string[System.Enum.GetValues(typeof(Access)).Length - 1][,];
string[][,] passwords;
string accessPassword;
string accessPasswordHint;
bool accessPasswordHintDisplayed;
bool accessPasswordAnagramDisplayed;
readonly string[] artMap = {
// Library art: index 0 = Access.Library
@"
______
/ //
/ //
(_____(/
"
,
// Bank art: index 1 = Access.Bank
@"
____________
) )
/ $$$$$$$ /
/ $$$$$$$$ /
(___________(
"
,
// Police Station art: index 2 = Access.PoliceStation
@"
____
/ \_________
| (O ==w_w=w_/
\____/
"
,
// NASA art: index 3 = Access.NASA
@"
^
/ \
/| |\
| |
| |
/| | |\
/ | | | \
| | | | |
------------
"
};
// Game state
string terminalInput;
enum State
{
Initialized,
MainMenu,
Password,
Win
};
State state;
enum Event
{
Start,
TerminalInput,
Menu
}
// Start is called before the first frame update
void Start()
{
InitializePasswordTable();
RestartGame();
}
private void PickPasswordFromTable()
{
// Because passwords[x] is an array of 2 dimensional arrays,
// the number of entries/pairs for passwords[x] is actually passwords[x].Length / 2
int maxIndex = passwords[(int) access].Length / 2;
int passwordChoice = Random.Range(0, maxIndex);
accessPassword = passwords[(int) access][passwordChoice, PASSWORD_INDEX];
accessPasswordHint = passwords[(int) access][passwordChoice, PASSWORD_HINT_INDEX];
}
private void InitializePasswordTable()
{
passwords = new string[System.Enum.GetValues(typeof(Access)).Length - 1][,];
passwords[(int) Access.Library] = new string[,] {
{ "dog", "Has a wet nose"}
, { "cat", "Chases mice"}
, { "bat", "Has a partner, Robin" }
};
passwords[(int) Access.Bank] = new string[,] {
{ "planes", "Wright brothers made these." }
, { "trains", "Amtrack has these" }
, { "automobiles", "Henry Ford made these" }
};
passwords[(int) Access.PoliceStation] = new string[,] {
{ "detective", "Sherlock Holmes" }
, { "assault", "? and battery" }
, { "misdemeanor", "A trifle offense" }
};
passwords[(int) Access.NASA] = new string[,] {
{ "mercury", "thermometers" }
, { "venus", "Bananarama" }
, { "jupiter", "Roman God" }
};
}
void OnUserInput(string input)
{
Event ev;
if (input == "menu")
{
ev = Event.Menu;
}
else
{
ev = Event.TerminalInput;
terminalInput = input;
}
RunGame(ev);
}
void RunGame(Event ev)
{
switch (state)
{
case State.Initialized:
if (ev != Event.Start)
{
// Unexpected event
// Report the error
ReportError("Current state: " + state + ". Received unexpected event: " + ev);
}
ShowMainMenu();
break;
case State.MainMenu:
if (ev == Event.Start)
{
// Unexpected event
// Report the error
ReportError("Current state: " + state + ". Received unexpected event: " + ev);
}
else if (ev == Event.TerminalInput)
{
if (isValidMenuItem(this.terminalInput))
{
PickPasswordFromTable();
GetPassword();
}
else
{
// Prompt the user again
// Stay in the same state: State.MainMenu
InvalidMenuInput();
}
}
else if (ev == Event.Menu)
{
RestartGame();
}
else
{
// Unexpected event
// Report the error
ReportError("Current state: " + state + ". Received unexpected event: " + ev);
}
break;
case State.Password:
if (ev == Event.Start)
{
// Unexpected event
// Report the error
ReportError("Current state: " + state + ". Received unexpected event: " + ev);
}
else if (ev == Event.TerminalInput)
{
// validate the password that was entered
if (isValidPassword(this.terminalInput))
{
Win();
}
else
{
InvalidPassword();
GetPassword();
}
}
else if (ev == Event.Menu)
{
RestartGame();
}
else
{
// Unexpected event
// Report the error
ReportError("Current state: " + state + ". Received unexpected event: " + ev);
}
break;
case State.Win:
if (ev == Event.Start)
{
// Unexpected event
// Report the error
ReportError("Current state: " + state + ". Received unexpected event: " + ev);
}
else if (ev == Event.TerminalInput || ev == Event.Menu)
{
RestartGame();
}
else
{
// Unexpected event
// Report the error
ReportError("Current state: " + state + ". Received unexpected event: " + ev);
}
break;
default:
// Unexpected state
// Report the error
ReportError("Current state: " + state + ". Received unexpected event: " + ev);
break;
}
}
private void RestartGame()
{
// Initialize game state
ResetGame();
// Start the game
RunGame(Event.Start);
}
void ShowMainMenu()
{
state = State.MainMenu;
Terminal.ClearScreen();
Terminal.WriteLine("Welcome, Hacker.");
Terminal.WriteLine("Which system would you like to access?");
Terminal.WriteLine("");
Terminal.WriteLine("1 ) Library");
Terminal.WriteLine("2 ) Bank");
Terminal.WriteLine("3 ) Police Station");
Terminal.WriteLine("4 ) NASA");
Terminal.WriteLine("");
Terminal.WriteLine("Enter 'menu' any time to return to main menu.");
PromptChoices();
}
private void PromptChoices()
{
Terminal.WriteLine("Enter selection [1, 2, 3, 4, or menu]:");
}
private void GetPassword()
{
state = State.Password;
Terminal.WriteLine("Enter password:");
}
private void Win()
{
state = State.Win;
Terminal.ClearScreen();
// Get the art to display
string art = GetArt();
// Display the art
Terminal.WriteLine("Successfully entered!");
Terminal.WriteLine(art);
Terminal.WriteLine("Press enter to access a different area");
}
private string GetArt()
{
string art = artMap[(int) access];
return art;
}
private void InvalidMenuInput()
{
Terminal.WriteLine("Invalid menu option. Try again.");
PromptChoices();
}
private void InvalidPassword()
{
Terminal.WriteLine("Invalid password. Please try again.");
if (!accessPasswordHintDisplayed)
{
Terminal.WriteLine("Hint: " + accessPasswordHint);
accessPasswordHintDisplayed = true;
}
else if (!accessPasswordAnagramDisplayed)
{
Terminal.WriteLine("Hint: " + accessPassword.Anagram());
accessPasswordAnagramDisplayed = true;
}
}
private bool isValidPassword(string password)
{
return accessPassword == password;
}
private bool isValidMenuItem(string input)
{
bool isValid = true;
switch (input)
{
case "1":
access = Access.Library;
break;
case "2":
access = Access.Bank;
break;
case "3":
access = Access.PoliceStation;
break;
case "4":
access = Access.NASA;
break;
default:
isValid = false;
break;
}
return isValid;
}
void ResetGame()
{
Terminal.ClearScreen();
access = Access.None;
state = State.Initialized;
accessPassword = null;
accessPasswordHint = null;
accessPasswordHintDisplayed = false;
accessPasswordAnagramDisplayed = false;
}
private void ReportError(string error)
{
Debug.LogError(error);
}
}