#include <iostream>
#include <cstdlib>
#include <assert.h>
#include <ctime>

using namespace std;

unsigned GetRandom( unsigned min, unsigned max ) {
	assert( max - min + 1 < RAND_MAX );
	return rand() % (max - min + 1) + min;
}

/*
void TestGetRandom() {
	const unsigned width = 4;
	const unsigned attempts = width * 10000000;
	unsigned hits[ width + 1] = { 0 };
	for( unsigned i = 0 ; i < attempts ; i++ ) {
		unsigned randomNumber = GetRandom( 1, width );
		hits[ randomNumber - 1 ]++;
	}

	for( i = 0 ; i < width + 1; i++ ) {
		cout << hits[ i ] << " ";
	}
	cout << endl;
	
	double E = 0.0;
	for( i = 0 ; i < width ; i++ ) {
		E += (double) (i+1) * hits[ i ] / attempts;
	}
	cout << "E = " << E << endl;
	double Esquared = 0.0;
	for( i = 0 ; i < width ; i++ ) {
		Esquared += (double) (i+1) * (i+1) * hits[ i ] / attempts;
	}
	double Var = Esquared - E*E;
	cout << "Var = " << Var << endl;
}

void main() {
	TestGetRandom();
}
*/

class PaderBox {
public:
	enum Buttons {
		BUTTON_0,
		BUTTON_1,
		BUTTON_COUNT
	};
private:
	static const unsigned LAMP_COUNT = 3;
	static const unsigned STATE_COUNT = 3;
	static const unsigned LINK_COUNT = 2;
	unsigned stateLampLinks[ STATE_COUNT ][ LINK_COUNT ];
	// [button index][old state]
	unsigned stateTransitionMap[ BUTTON_COUNT ][ STATE_COUNT ];

	struct State {
		bool lampState[ LAMP_COUNT ];
		unsigned activeState;
	} initialState, currentState;

private:
	void SetupTransitions() {
		// setup the state transition map
		for( unsigned button = 0 ; button < BUTTON_COUNT ; button++ ) {
			for( unsigned state = 0 ; state < STATE_COUNT ; state++ ) {
				stateTransitionMap[ button ][ state ] = GetRandom( 0, STATE_COUNT - 1 );
			}
		}
		// possibilities: 3^(2*3)=3^6=729
	}

	void SetupLampLinks() {
		// the first state can choose the two lamps freely (thus 3*2/2*1 possibilities)
		// the second state can choose the first lamp freely but:
		// if an already linked lamp is chosen, then it must choose the unlinked lamp next
		// if the unlinked lamp is chosen, it can choose between two linked lamps next
		// the third state now has to choose the two links that are left
		// (possibilities: 3*2/2*1 * (2*1+1*2)/2 * 1 = 3 * 2 * 1 = 6)
		assert( LAMP_COUNT == 3 && STATE_COUNT == 3 );
		stateLampLinks[ 0 ][ 0 ] = GetRandom( 0, LAMP_COUNT - 1 );
		do {
			stateLampLinks[ 0 ][ 1 ] = GetRandom( 0, LAMP_COUNT - 1 );
		} while( stateLampLinks[ 0 ][ 0 ] == stateLampLinks[ 0 ][ 1 ] );
		
		if( GetRandom( 0, 1 ) ) {
			stateLampLinks[ 1 ][ 0 ] = stateLampLinks[ 0 ][ 0 ];
			stateLampLinks[ 2 ][ 0 ] = stateLampLinks[ 0 ][ 1 ];
		} else {
			stateLampLinks[ 1 ][ 0 ] = stateLampLinks[ 0 ][ 1 ];
			stateLampLinks[ 2 ][ 0 ] = stateLampLinks[ 0 ][ 0 ];
		}
		stateLampLinks[ 1 ][ 1 ] = stateLampLinks[ 2 ][ 1 ] = 0+1+2 - (stateLampLinks[ 0 ][ 0 ] + stateLampLinks[ 0 ][ 1 ]);
		// possibilities using this algorithm: 3*2/2*2 = 6
	}

	void SetupInitialState() {
		initialState.activeState = GetRandom( 0, STATE_COUNT - 1 );
		for( unsigned i = 0 ; i < LAMP_COUNT ; i++ ) {
			initialState.lampState[ i ] = GetRandom( 0, 1 ) == 1;
		}
		// possibilities: 3 * 2^3 = 24
	}

public:
	PaderBox() {
		SetupTransitions();
		SetupLampLinks();
		SetupInitialState();
		Reset();
	}

	void ChangeState( unsigned newState ) {
		currentState.activeState = newState;
		for( unsigned i = 0 ; i < LINK_COUNT ; i++ ) {
			const unsigned lampIndex = stateLampLinks[ newState ][ i ];
			bool &lampState = currentState.lampState[ lampIndex ];
			lampState = !lampState;
		}
	}

public:
	void Reset() {
		currentState = initialState;
	}

	void PressButton( const Buttons button ) {
		ChangeState( stateTransitionMap[ button ][ currentState.activeState ] );
	}

	void DisplayLampState() const {
		for( unsigned i = 0 ; i < LAMP_COUNT ; i++ ) {
			cout << "Lampe " << (i+1) << ": " << (currentState.lampState[ i ] ? "AN" : "AUS") << " ";
		}
		cout << endl;
	}

	void DisplaySecretProgramming() const {
		assert( BUTTON_COUNT == 2 );
		cout << "Taste 0\t\tTaste 1 (Z steht für Zustand)" << endl;
		for( unsigned i = 0 ; i < STATE_COUNT ; i++ ) {
			cout << "Z" << (i + 1) << "->Z" << stateTransitionMap[ 0 ][ i ] + 1 << "\t\tZ" << (i + 1) << "->Z" << stateTransitionMap[ 1 ][ i ] + 1 << endl;
		}
		cout << endl;
		
		assert( LINK_COUNT == 2 );
		cout << "Zustand\t\tLampen" << endl;		
		for( i = 0 ; i < STATE_COUNT ; i++ ) {
			cout << "Z" << (i + 1) << "\t\tLampe " << stateLampLinks[ i ][ 0 ] + 1 << ", Lampe " << stateLampLinks[ i ][ 1 ] + 1 << endl;
		}
		cout << endl;

		cout << "Anfangszustand: Z" << initialState.activeState + 1 << endl;
	}
};

void RunGame() {
	PaderBox paderBox;
	cout << "Spiel startet.." << endl << endl;

	bool endGame = false;
	while( !endGame ) {
		paderBox.DisplayLampState();	

		while( 1 ) {
			cout << "0 Drücke Taste 0" << endl
				<< "1 Drücke Taste 1" << endl
				<< "R Setze die PaderBox zurück" << endl
				<< "? Verrate die Programmierung" << endl
				<< "Q Gehe zurück zum Hauptmenü" << endl
				<< endl;
			cout << "Menüwahl: ";
			unsigned char choice;
			cin >> choice;

			switch( choice ) {
				case '0':
					paderBox.PressButton( PaderBox::BUTTON_0 );
					break;
				case '1':
					paderBox.PressButton( PaderBox::BUTTON_1 );
					break;
				case 'R':
				case 'r':
					paderBox.Reset();
					break;
				case '?':
					paderBox.DisplaySecretProgramming();
					break;
				case 'Q':
				case 'q':
					return;
					break;
				default:
					continue;
			}
			break;
		}
	}
}
	
void ShowMainMenu() {
	bool quit = false;
	while( !quit ) {
		cout << endl
			<< "P Starte ein Spiel" << endl
			<< "Q Ende des Programms" << endl
			<< endl;
		cout << "Menüwahl: ";
		unsigned char choice;
		cin >> choice;
		switch( choice ) {
			case 'P':
			case 'p':
				RunGame();
				break;
			case 'Q':
			case 'q':
				quit = true;
				break;
			default:
				break;
		}
	}
}

void main() {
	srand( time( NULL ) );

	ShowMainMenu();	
}