/*

MasterMind by Jocke "Firetech" Andersson 2004
=============================================

- Call with "-d" to enable debug mode (make "right" answer yourself and print location specific correction)
- Note that everything is explained, just maybe later in the source...
  (The variable declarations isn't explained, but the variables are, later in the functions)
- Answer levels:
    * 0 = "incorrect"
    * 1 = "right color, wrong position"
    * 2 = "exact match"
- Feel free to use the code to whatever you want, but DON'T sell it!

//Firetech

*/


#include <conio.h>
#include <iostream>
#include <stdio.h>
#include <time.h>
#include <windows.h>
using namespace std;

//Declare global variables:
unsigned int g_right[4], g_guess[4], g_ans[4];
bool debug, corred[2] [4];
char version[] = "1.2.1.8";

//Declare functions:
int s_randcolor( void );
void s_setcolor( unsigned short color, unsigned short bkg );
void s_echop( short pcolor );
int s_getcolor( void );
void g_getguesses( void );
bool g_correct( void );
void g_printcorr( unsigned int rlvl1, unsigned int rlvl2 );
void g_printans( void );
bool g_round( unsigned int round );
void g_setup( void );
bool g_play( void );
void main( int argc, char * argv[] );

//Functions:
int s_randcolor( void )
{
  int r;
  if ( debug ) //If debug mode is on...
  {
    r = s_getcolor(); //...let the user select the number
  }
  else //Else... (duh!)
  {
    r = 1 + int( 6 * rand() / ( RAND_MAX + 1.0 ) ); //...generate a random number between 1 and 6
  }
  return r; //Return the number
}

void s_setcolor( unsigned short color = 7, unsigned short bkg = 0 )
{
  HANDLE hcon = GetStdHandle( STD_OUTPUT_HANDLE ); //Make a handler for the console
  SetConsoleTextAttribute( hcon, ( WORD )( ( bkg << 4 ) | color ) ); //Sets the color (Even I don't understand what it does...)
}

void s_echop( short pcolor = 1 )
{
  s_setcolor( pcolor + 8, pcolor + 8 ); //Set the color of the piece (The game uses colors 9-14)
  cout << ' '; //Print the piece
  s_setcolor(); //Reset the color
  cout << ' '; //Print a space, it looks ugly without it...
}

int s_getcolor( void )
{
  int inch; //A little ugly method... (see below)
  do
  {
    inch = getch() - 48; //The char value of numbers are n+48 (getch() returns a char)... Ugly method, I know...
  }
  while ( ( inch <= 0 ) || ( inch >= 7 ) ); //Repeat if value isn't 1-6 (error)
  s_echop( inch ); //Print the piece you selected...
  return inch; //...and return its value
}

void g_getguesses( void )
{
  unsigned int i;
  for ( i = 0; i <= 3; i++ ) //Repeat 4 times (One per color in guess)
  {
    corred[0] [i] = false; //Set that the position 'i' in guess isn't used in correction
    corred[1] [i] = false; //The same for the position 'i' in answer
    g_guess[i] = s_getcolor(); //Get the guessed color at position 'i' (of 4, user input)
    if ( g_guess[i] == g_right[i] ) //If color at pos. 'i' is right (exact match)...
    {
      g_ans[i] = 2; //...set that it is... (2 = "exact match")
      corred[0] [i] = true; //...set that the position 'i' in guess is used in correction...
      corred[1] [i] = true; //...and do the same for the position 'i' in answer
    }
    else //If color at pos. 'i' isn't an exact match...
    {
      g_ans[i] = 0; //...set that it is "incorrect" (temporarily)
    }
  }
}

bool g_correct( void )
{
  unsigned int i, j, rlvl1, rlvl2;
  bool correct = true; //Temporarily set that the combination is correct
  cout << "Corr.: "; //Start printing the correction
  rlvl1 = 0; //Set number of corrects at level 1 to zero
  rlvl2 = 0; //The same for corrects at level 2
  for ( i = 0; i <= 3; i++ ) //Repeat 4 times (One per color in answer)
  {
    if ( !corred[1] [i] ) //If the answer position 'i' isn't used in correction...
    {
      if ( g_ans[i] == 0 ) //Check if the answer level is 0
      {
        for ( j = 0; ( j <= 3 ) && ( g_ans[i] == 0 ); j++ ) //Repeat 4 times (One per color in guess), or until answer level is set
        {
          if ( g_guess[j] == g_right[i] ) //If the color at pos. 'j' in guess is equal to the color at pos. 'i' in answer...
          {
            if ( !corred[0] [j] ) //...and the position 'j' in guess isnt used...
            {
              g_ans[i] = 1; //...set the answer level to 1... (see above)
              corred[0] [j] = true; //...set the position 'j' in guess to used...
              corred[1] [i] = true; //...and do the same for the position 'i' in answer
            } //End "Corrected potition in guess" check
          } //End "Guess is equal to answer" check
        } //End Guess check loop
      } //End level 0 check
    } //End "Corrected position in answer" check

    if ( g_ans[i] == 2 ) //If answer level is 2...
    {
      rlvl2++; //...raise the number of level-2's with 1
    }
    else if ( g_ans[i] == 1 ) //Else, if answer level is 1...
    {
      rlvl1++; //...raise the number of level-1's with 1...
      correct = false; //...and set that the total combination is incorrect
    }
    else if ( g_ans[i] == 0 ) //Else, if answer level is 0...
    {
      correct = false; //...set that the total combination is incorrect
    }
  }
  g_printcorr( rlvl1, rlvl2 ); //Print the correction
  return correct; //Return if the total combination is correct or incorrect
}

void g_printcorr( unsigned int rlvl1, unsigned int rlvl2 )
{
  unsigned int i;
  if ( debug ) //If debug mode is on...
  {
    for ( i = 0; i <= 3; i++ ) //...repeat one time per color in guess/answer...
    {
      cout << g_ans[i] << ' '; //...print the answer for position 'i' right below it
    }
  }
  else //Else, if debug mode is off...
  {
    for ( i = 1; i <= rlvl2; i++ ) //...repeat once per each level 2 piece...
    {
      s_echop( -6 ); //...print the level 2 piece (dark green)...
    }
    for ( i = 1; i <= rlvl1; i++ ) //...then repeat once per each level 1 piece...
    {
      s_echop( -4 ); //...print the level 1 piece (dark red)
    }
  }
}

void g_printans( void )
{
  unsigned int i;
  cout << "The answer was: ";
  for ( i = 0; i <= 3; i++ ) //Repeat once per color in answer...
  {
    s_echop( g_right[i] ); //...print the piece at pos. 'i' in answer
  }
}

bool g_round( unsigned int round )
{
  bool correct;
  cout << endl << "Round " << round << ":" << endl << "--------------" << endl << "Guess: "; //Print round number amd prompt to guess
  g_getguesses(); //Get the user's guessed combination
  cout << endl; //New line, start to print the correction
  correct = g_correct(); //Correct the guess and set the total combination of this round to correct or incorrect
  cout << endl; //Print a new line and a separator...
  return correct; //...and return if the combination of this round was correct or incorrect
}

void g_setup( void )
{
  unsigned int i;
  system( "CLS" ); //Clear screen
  cout << "MasterMind " << version << " by Jocke \"Firetech\" Andersson 2004" << endl; //Print version info...
  cout << "=====================================================" << endl << endl; //...and a separator
  cout << "During input:" << endl << "-------------" << endl;
  for ( i = 1; i <= 6; i++ ) //Number to color-help START
  {
    cout << i << " = "; //Print number 'i' and '='
    s_echop( i ); //Print the color for 'i'
    cout << endl; //Print a new line
  } // Number to color-help END
  if ( debug ) //If debug mode is on...
  {
    cout << endl << "Right: "; //...prompt the user to enter the "right" answer
  }
  for ( i = 0; i <= 3; i++ ) //Repeat 4 times (Once per color in answer)
  {
    g_right[i] = s_randcolor(); //Set the right colors (Manually selected id debug mode is on, otherwise random)
  }
  if ( debug ) //If debug mode is on...
  {
    cout << endl; //...print a new line to separate the "right answer input" from round 1
  }
}

bool g_play( void )
{
  unsigned int round = 0;
  bool correct, retbool;
  char inch;
  g_setup(); //Setup a game (Clear screen, set the right answer and print the Number to color-help)
  do
  {
    correct = g_round( ++round ); //Play one round of the game
    if ( ( round >= 20 ) && ( !correct ) ) //If maximum number of rounds is reached and the answer isn't correct...
    {
      cout << endl << "Maximum number of rounds reached!" << endl << endl; //...print so...
      g_printans(); //...print the right answer...
      cout << endl; //...and a new line
    }
  }
  while ( ( !correct ) && ( round <= 19 ) ); //While the answer is incorrect and maximum rounds isn't reached
  if ( correct ) //If answer was correct...
  {
    cout << endl << "Congratulations! You got the combination in " << round << " round(s)." << endl; //...print so
  }
  do
  {
    cout << endl << "New game? (Y/N)"; //Ask if user wants a new game
    inch = tolower( getch() ); //Get users answer
    if ( inch == 'y' )
    {
      retbool = true; //User wants a new game
    }
    else if ( inch == 'n' )
    {
      retbool = false; //User wants to exit
    }
    cout << endl; //New line in case of invalid answer or Visual Studio's stupid auto-pause...
  }
  while ( ( inch != 'y' ) && ( inch != 'n' ) ); //Repeat until valid answer
  return retbool; //Return if user wants a new game or wants to exit
}


void main( int argc, char * argv[] )
{
  bool contgame;
  srand( time( NULL ) ); //Seed random numbers by the number of seconds since 00:00 1/1 1970...
  if ( ( argc >= 2 ) && ( argv[1] [0] == '-' ) && ( argv[1] [1] == 'd' ) && ( argv[1] [2] == '\0' ) ) //If the only parameter is "-d"...
  {
    debug = true; //...set debug mode to ON
  }
  else //Else...
  {
    debug = false; //...set it to OFF
  }
  do
  {
    contgame = g_play(); //Play one game (<= 20 rounds)
  }
  while ( contgame ); //New game if user want to
}

