/* ===================================================================
 * by migf1
 *
 * Simple Guessing Game ( comments in Greek )
 * -	bilingual (English, Greek)
 * -	2 levels of difficulty (easy, hard)
 * -	player defined number of tries (MINTRIES to MAXTRIES)
 *
 * Απλό Παιχνίδι Εύρεσης Κρυφού Αριθμού
 * -	δίγλωσσο (Αγγλικά, Ελληνικά)
 * -	2 επίπεδα δυσκολίας (εύκολο, δύσκολο)
 * -	ο παίκτης επιλέγει πλήθος προσπαθειών (από MINTRIES έως MAXTRIES)
 *
 * 	Στο εύκολο επίπεδο ο κρυφός αριθμός επιλέγεται από το 1 έως το
 *	γινόμενο του πλήθους προσπαθειών επί LVL_EASY_MULTI, ενώ στο
 *	δύσκολο επίπεδο επί LVL_HARD_MULTI (ορισμένα ως 2 και 40, αντίστοιχα).
 *
 *	Επίσης, στο δύσκολο επίπεδο το πρόγραμμα δίνει 3 ειδών απαντήσεις
 *	για κάθε μαντεψιά του πάικτη: "πιο ψηλά", "πιο χαμηλά" και "το
 *	βρήκατε". Στο εύκολο επίπεδο δίνει 4 επιπλέον απαντήσεις:
 *	"πιο ψηλά (είστε πολύ κοντα)", "πιο ψηλά (είστε πολύ μακριά)",
 *	"πιο χαμηλά (είστε πολύ κοντα)" και "πιο χαμηλά (είστε πολύ μακριά)",
 *	ανάλογα αν η μαντεψιά του απέχει λιγότερο από DIFF_HOT από το κρυφό νούμερο
 *	ή αν απέχει περισσότερο από DIFF_COLD (ορισμένα ως 2 και 10, αντίστοιχα).
 * ===================================================================
 */

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

// -------- Macros ------------

#define ABS(x)			( (x) > 0 ? (x) : -(x) )

#define MAXINBUF		(255+1)			// μέγιστο μήκος γραμμής εισόδου
#define MINTRIES		3			// ελάχιστο πλήθος προσπαθειών
#define MAXTRIES		8			// μέγιστο πλήθος προσπαθειών

#define LVL_EASY_ID		1			// ταυτότητα εύκολου επιπέδου
#define LVL_EASY_MULTI		3			// πολλαπλ/στής εύκολου επιπέδου
#define LVL_HARD_ID		2			// ταυτότητα δύσκολου επιπέδου
#define LVL_HARD_MULTI		40			// πολλαπλ/στής δύσκολου επιπέδου

#define DIFF_HOT		2			// HOT απόσταση από κρυμμένο
#define DIFF_COLD		10			// COLD απόσταση από κρυμμένο

#define MAX_MESSAGES		15			// μέγιστο πλήθος μηνυμάτων

// ------- Πρόσθετοι Τύποι -----------

typedef enum {						// θέσεις στον πίνακα μηνυμάτων
	TXT_OUTOFBOUNDS = 0,
	TXT_HIGHER,
	TXT_HIGHERHOT,
	TXT_HIGHERCOLD,
	TXT_LOWER,
	TXT_LOWERHOT,
	TXT_LOWERCOLD,
	TXT_CONGRATS,
	TXT_FMT_TRIES,
	TXT_UPPERLIMIT,
	TXT_FMT_SETHIDDEN,
	TXT_FMT_PROMPT,
	TXT_FMT_MAXTRIES,
	TXT_LEVEL,
	TXT_FMT_LOST
} MsgPos;

typedef enum { LNG_EN=1, LNG_GR } LangCode;		// κωδικοί γλωσσσών

typedef struct level {					// δομή επιπέδου δυσκολίας
	int id, multi;					// ταυτότητα & πολλαπλασιαστής
} Level;

// -------- Πρότυπα Συναρτήσεων --------------

const char **get_language( const char **msg_en, const char **msg_gr );
void get_level( Level *level, const char *fmttext );
void get_ntries( int *ntries, const char *fmttext );
MsgPos hint_getpos(const int guess, const int hidden, const int lim, const int levid);

// ----------------------------------------------------------------------------
int main( void )
{
	char inbuf[ MAXINBUF ] = "";			// η γραμμή εισόδου
	int ntries, limit, guess = -1, hidden, count = 0;
	MsgPos pos;					// θέση κατάλληλης απάντησης
	Level level = { LVL_EASY_ID, LVL_EASY_MULTI };	// επίπεδο δυσκολίας

	const char **messages = NULL;			// ενεργός πίνακας μηνυμάτων

	const char *msg_en[MAX_MESSAGES] = {		// αγγλικός πίνακας μηνυμάτων
		"\tout of bounds, try again\n",			// TXT_OUTOFBOUNDS
		"\thigher\n",					// TXT_HIGHER
		"\thigher (you're too close)\n",		// TXT_HIGHERHOT
		"\thigher (you're far away)\n",			// TXT_HIGHERCOLD
		"\tlower\n",					// TXT_LOWER
		"\tlower (you're too close)\n",			// TXT_LOWERHOT
		"\tlower (you're far away)\n",			// TXT_LOWERCOLD
		"\nCongratulations, you found it!",		// TXT_CONGRATS
		"\tit took you %d tries\n\n",			// TXT_FMT_TRIES
		"Give upper limit ( greater than 1 ): ",	// TXT_UPPERLIMIT
		"\tI set a number from 1 to %d\n\n",		// TXT_FMT_SETHIDDEN
		"Guess #%d/%d: ",				// TXT_FMT_PROMPT 
		"\nHow many tries (%d - %d)? ",			// TXT_FMT_MAXTRIES
		"\nSelect level (%d = easy, %d = hard): ",	// TXT_LEVEL
		"You loose, the number was: %d\n"		// TXT_FMT_LOST
	};

	const char *msg_gr[MAX_MESSAGES] = {		// ελληνικός πίνακας μηνυμάτων
		"\tεκτός ορίων, ξαναπροσπαθήστε\n",		// TXT_OUTOFBOUNDS
		"\tπιο ψηλά\n",					// TXT_HIGHER
		"\tπιο ψηλά (είστε πολύ κοντά)\n",		// TXT_HIGHERHOT
		"\tπιο ψηλά (είστε πολύ μακριά)\n",		// TXT_HIGHERCOLD
		"\tπιο χαμηλά\n",				// TXT_LOWER
		"\tπιο χαμηλά (είστε πολύ κοντά)\n",		// TXT_LOWERHOT
		"\tπιο χαμηλά (είστε πολύ μακριά)\n",		// TXT_LOWERCOLD
		"\nΣυγχαρητήρια, το βρήκατε!",			// TXT_CONGRATS
		"\tχρειαστήκατε %d προσπάθειες\n\n",		// TXT_FMT_TRIES
		"Ανώτατο όριο ( μεγαλύτερο του 1 ): ",		// TXT_UPPERLIMIT
		"\tέβαλα έναν αριθμό από το 1 έως το %d\n\n",	// TXT_FMT_SETHIDDEN
		"Μαντεψιά #%d/%d: ",				// TXT_FMT_PROMPT 
		"\nΠόσες προσπάθειες (%d - %d); ",		// TXT_FMT_MAXTRIES
		"\nΕπίπεδο δυσκολίας (%d = εύκολο, %d = δύσκολο); ",// TXT_LEVEL
		"Χάσατε, το νούμερο ήταν: %d\n"			// TXT_FMT_LOST
	};

	srand( time(NULL) );				// γεννήτρια ψευδοτυχαίων

	messages = get_language( msg_en, msg_gr );	// διάβασμα γλώσσας
	get_level( &level, messages[TXT_LEVEL] );	// διάβσαμα επιπέδου δυσκολίας
	get_ntries(&ntries,messages[TXT_FMT_MAXTRIES]);	// διάβασμα πλήθους προσπαθειών

	limit = ntries * level.multi;			// καθορισμός ανώτατου ορίου
	hidden = rand() % limit + 1;			// επιλογή τυχαίου αριθμού
	printf( messages[ TXT_FMT_SETHIDDEN ], limit);

	// το κεντρικό μας loop
	do {
		printf( messages[ TXT_FMT_PROMPT ], count+1, ntries);
		fgets(inbuf, MAXINBUF, stdin);		// διάβασμα μαντεψιάς
		guess = atoi( inbuf );			// μετατροπή της σε int

		pos = hint_getpos(guess,hidden,limit,level.id);// επιλογή απάντησης
		if ( pos != TXT_OUTOFBOUNDS )		// έγκυρη μαντεψιά
			count++;			// μετρητής προσπαθειών
		puts( messages[ pos ] );		// τύπωμα απάντησης

	} while ( count < ntries && guess != hidden);

	if ( guess == hidden )
		printf(messages[TXT_FMT_TRIES], count);	// τύπωμα προσπαθειών
	else
		printf(messages[TXT_FMT_LOST],hidden);	// αποκάλυψη αριθμού

	system("pause");\
	exit( EXIT_SUCCESS );
}

// ----------------------------------------------------------------------------
const char **get_language( const char **msg_en, const char **msg_gr )
{
	char inbuf[ MAXINBUF ];
	LangCode dummy;

	puts("Choose language / Επιλέξτε γλώσσα:");
	printf("\n\t%d: English\n\t%d: Ελληνικά\n", LNG_EN, LNG_GR);
	do {
		printf("> ");
		fgets(inbuf, MAXINBUF, stdin);
		dummy = atoi( inbuf );
	} while (dummy < LNG_EN || dummy > LNG_GR );

	if ( dummy == LNG_GR )
		return msg_gr;

	return msg_en;
}

// ----------------------------------------------------------------------------
void get_level( Level *level, const char *fmttext )
{
	char inbuf[ MAXINBUF ];
	int dummy;

	do {
		printf( fmttext, LVL_EASY_ID, LVL_HARD_ID );
		fgets(inbuf, MAXINBUF, stdin);
		dummy = atoi( inbuf );
	} while (dummy < LVL_EASY_ID || dummy > LVL_HARD_ID);

	if ( dummy == LVL_EASY_ID ) {
		level->id = LVL_EASY_ID;
		level->multi = LVL_EASY_MULTI;
	}
	else {
		level->id = LVL_HARD_ID;
		level->multi = LVL_HARD_MULTI;
	}

	return;
}

// ----------------------------------------------------------------------------
void get_ntries( int *ntries, const char *fmttext )
{
	char inbuf[ MAXINBUF ];

	do {
		printf( fmttext, MINTRIES, MAXTRIES );
		fgets(inbuf, MAXINBUF, stdin);
		*ntries = atoi( inbuf );
	} while ( *ntries < MINTRIES || *ntries > MAXTRIES);

	return;
}

// ----------------------------------------------------------------------------
MsgPos hint_getpos(const int guess, const int hidden, const int lim, const int levid)
{
	int diff;

	if (guess > lim || guess < 1)
		return TXT_OUTOFBOUNDS;

	if (guess == hidden )
		return TXT_CONGRATS;

	if ( levid == LVL_HARD_ID)
		return (guess < hidden) ? TXT_HIGHER : TXT_LOWER; 

	diff = ABS(hidden-guess);

	if (guess < hidden)
	{
		if ( diff <= DIFF_HOT )
			return TXT_HIGHERHOT;
		if ( diff > DIFF_COLD )
			return TXT_HIGHERCOLD;
		return TXT_HIGHER;
	}

	if (guess > hidden)
	{
		if ( diff <= DIFF_HOT )
			return TXT_LOWERHOT;
		if ( diff > DIFF_COLD )
			return TXT_LOWERCOLD;
		return TXT_LOWER;
	}

	return TXT_CONGRATS;
}
