//SnakeC2 by Jocke "Firetech" Andersson
//Licence: Open Source something...

#include <iostream>
#include <conio.h>
#include <time.h>
#include <windows.h>
using namespace std;

const char vers[] = "2.4.3.2";

bool dualplayer = false;
char screen[76] [21], dir[2];
const char wormchr = 'o', wormhead = '\x94', applechr = '\x0F', flin[] =
     "                                                                              \n";
int applecnt, sleeps, x[2], y[2], score[2], lscore[2];

int random( int lowest_number, int highest_number )
{
  int range = highest_number - lowest_number + 1;
  return lowest_number + int( range * rand() / ( RAND_MAX + 1.0 ) );
}

void setcolor( unsigned short color, unsigned short bkg = 0 )
{
  HANDLE hcon = GetStdHandle( STD_OUTPUT_HANDLE );
  SetConsoleTextAttribute( hcon, ( WORD )( ( bkg << 4 ) | color ) );
}

void rand_apple( bool randpos = false )
{
  if ( !randpos )
  {
    if ( dualplayer )
    {
      applecnt = random( 50, 100 );
    }
    else
    {
      applecnt = random( 100, 200 );
    }
  }
  else
  {
    int ax, ay;
    do
    {
      ax = random( 0, 74 );
      ay = random( 0, 19 );
    }
    while ( screen[ax] [ay] != ' ' );
    screen[ax] [ay] = applechr;
    gotoxy( ax + 2, ay + 2 );
    setcolor( 10 );
    cout << applechr;
  }
}

char pause( bool gamepause = false, int time = 0, bool pkat = true )
{
  char gchar;
  if ( gamepause )
  {
    setcolor( 2 );
    gotoxy( 33, 11 );
    cout << "SnakeC2 paused";
    gotoxy( 26, 13 );
  }
  else
  {
    setcolor( 7 );
  }
  if ( time > 0 )
  {
    int i, j, tx = wherex(), ty = wherey();
    for ( i = time; i >= 1; i-- )
    {
      cout << "Wait " << i << " second(s)...                    ";
      gotoxy( tx, ty );
      Sleep( 1000 );
    }
  }
  if ( pkat )
  {
    cout << "Press any key to continue...";
    gchar = getch();
  }
  sleeps = 0;
  return gchar;
}

void clean_draw( bool restart = true, bool apple = false, int p = 0 )
{
  char prc;
  if ( restart )
  {
    for ( int l = 0; l <= 20; l++ )
    {
      for ( int c = 0; c <= 75; c++ )
      {
        screen[c] [l] = ' ';
      }
    }
    if ( !apple )
    {
      x[0] = 40;
      y[0] = 10;
      score[0] = 0;
      lscore[0] = 25;
      dir[0] = 'r';
      if ( dualplayer )
      {
        x[1] = 35;
        y[1] = 10;
        score[1] = 0;
        lscore[1] = 25;
        dir[1] = 'l';
      }
    }
    else
    {
      score[p] += 10;
      setcolor( 14 );
      gotoxy( 1, 24 );
      rand_apple();
    }
    screen[x[0]] [y[0]] = 'A';
    if ( dualplayer )
    {
      screen[x[1]] [y[1]] = 'B';
    }
  }
  clrscr();
  setcolor( 9, 9 );
  cout << flin;
  for ( int l = 0; l <= 20; l++ )
  {
    cout << " ";
    for ( int c = 0; c <= 75; c++ )
    {
      prc = wormchr;
      switch ( screen[c] [l] )
      {
        case 'a':
          setcolor( 12 );
        break;
        case 'A':
          setcolor( 12 );
          prc = wormhead;
        break;
        case 'b':
          setcolor( 13 );
        break;
        case 'B':
          setcolor( 13 );
          prc = wormhead;
        break;
        case applechr:
          setcolor( 10 );
          prc = applechr;
        break;
        default:
          setcolor( 0 );
          prc = ' ';
      }
      cout << prc;
    }
    setcolor( 9, 9 );
    cout << " \n";
  }
  cout << flin;
  setcolor( 10 );
  if ( dualplayer )
  {
    cout << "Player 1: " << score[0] << "\nPlayer 2: " << score[1];
  }
  else
  {
    cout << "Score: " << score[0];
  }
}

void showabout( bool shutdown = false )
{
  clrscr();
  setcolor( 14 );
  if ( shutdown )
  {
    cout << "Thanks for playing SnakeC2\n==========================";
  }
  else
  {
    cout << "About SnakeC2\n=============";
  }
  setcolor( 7 );
  cout << "\n\nSnake Concept: Phase 2\n\tPowered by CrowD-S " << vers;
  cout << "\n\t\t(c) 2004 Jocke \"Firetech\" Andersson\n\n";
  pause();
}

bool showhelp( bool startup = false )
{
  char gchar;
  clrscr();
  setcolor( 14 );
  if ( startup )
  {
    cout << "Welcome to SnakeC2\n==================\n\n";
    setcolor( 7 );
    int i = 0;
    do
    {
      if ( i >= 1 )
      {
        cout << "Invalid input!";
      }
      else
      {
        cout << "1 or 2 players, or exit?";
      }
      cout << " (1/2/X) ";
      gchar = getche();
      cout << endl;
      i++;
    }
    while ( ( gchar != '1' ) && ( gchar != '2' ) && ( gchar != 'x' ) );
    switch ( gchar )
    {
      case '1':
        dualplayer = false;
      break;
      case '2':
        dualplayer = true;
      break;
      case 'x':
        showabout( true );
        return false;
    }
  }
  else
  {
    cout << "SnakeC2 Help\n============\n";
  }
  bool echodp = ( ( ( startup ) && ( gchar == '2' ) ) || ( ( !startup ) && ( dualplayer ) ) ? true : false );

  cout << endl;
  setcolor( 15 );
  cout << "Keys\n----\n";
  setcolor( 7 );
  cout << "Arrows\t:Steer snake ";
  if ( echodp )
  {
    setcolor( 12 );
    cout << "1";
    setcolor( 7 );
    cout << "\nW,A,S,D\t:Steer snake ";
    setcolor( 13 );
    cout << "2";
    setcolor( 7 );
  }
  cout << "\n   P\t:Pause\n   E\t:End game\n   R\t:Redraw screen\n";
  cout << "   I\t:About information\n   H\t:Show this help\n   X\t:Exit\n\n";
  setcolor( 15 );
  cout << "Scores\n------\n";
  setcolor( 7 );
  cout << "25 moves\t:+1 point\nTaking \"";
  setcolor( 10 );
  cout << applechr;
  setcolor( 7 );
  cout << "\"\t:+10 points";
  if ( echodp )
  {
    cout << "\nDying\t\t:-5 points";
  }
  cout << "\n\n";
  if ( startup )
  {
    switch ( pause() )
    {
      case 'i':
        showabout();
      break;
      case 'x':
        showabout( true );
        return false;
    }
  }
  else
  {
    pause();
  }
  if ( dualplayer )
  {
    cout << "\n\n";
    pause( false, 2, false );
  }
  return true;
}

bool endgame( bool dieend = false, int p = 0, bool headon = false )
{
  bool retval = true;
  char gchar;
  int op = ( p == 0 ? 1 : 0 ), wp, lp;
  clrscr();
  setcolor( 14 );
  cout << "Game Over\n=========\n\n";
  setcolor( 15 );
  if ( dualplayer )
  {
    if ( dieend )
    {
      score[p] -= 5;
      if ( headon )
      {
        score[op] -= 5;
        cout << "Headon collision! Both players died.";
      }
      else
      {
        cout << "Player " << p + 1 << " died.";
      }
      cout << "\n\n";
    }
    if ( score[p] != score[op] )
    {
      wp = ( score[p] > score[op] ? p : op );
      lp = ( wp == 0 ? 1 : 0 );
      cout << "Player " << wp + 1 << " won with " << score[wp] << " point(s).\n";
      cout << "Player " << lp + 1 << " got " << score[lp] << " point(s).\n\n";
    }
    else
    {
      cout << "Tie. Both players got " << score[p] << " points.\n\n";
    }
  }
  else
  {
    if ( dieend )
    {
      cout << "You died with " << score[0] << " points.\n\n";
    }
    else
    {
      cout << "You got " << score[0] << " points.\n\n";
    }
  }
  setcolor( 7 );
  if ( dieend )
  {
    pause( false, 2 );
    retval = showhelp( true );
    rand_apple();
    clean_draw( true );
  }
  else
  {
    pause();
  }
  return retval;
}

bool incplayer( int p )
{
  int op = ( p == 0 ? 1 : 0 ), px[2], py[2];
  bool retval = true;
  char pc = ( p == 0 ? 'a' : 'b' );
  if ( --lscore[p] <= 0 )
  {
    score[p] ++;
    lscore[p] = 25;
    setcolor( 10 );
    if ( dualplayer )
    {
      switch ( p )
      {
        case 0:
          gotoxy( 1, 24 );
          cout << "Player 1: " << score[0];
        break;
        case 1:
          gotoxy( 1, 25 );
          cout << "Player 2: " << score[1];
        break;
      }
    }
    else
    {
      gotoxy( 1, 24 );
      cout << "Score: " << score[0];
    }
  }
  py[p] = y[p];
  px[p] = x[p];
  switch ( dir[p] )
  {
    case 'u':
      y[p] --;
    break;
    case 'd':
      y[p] ++;
    break;
    case 'l':
      x[p] --;
    break;
    case 'r':
      x[p] ++;
    break;
  }
  if ( ( screen[x[p]] [y[p]] == 'A' ) || ( screen[x[p]] [y[p]] == 'B' ) )
  {
    if ( ( ( dir[p] == 'u' ) && ( dir[op] == 'd' ) ) || ( ( dir[p] == 'd' ) && ( dir[op] == 'u' ) )
         || ( ( dir[p] == 'r' ) && ( dir[op] == 'l' ) ) || ( ( dir[p] == 'l' ) && ( dir[op] == 'r' ) ) )
         {
           retval = endgame( true, p, true );
    }
    else {
      retval = endgame( true, p );
    }
  }
  else if ( ( y[p] <= -1 ) || ( y[p] >= 21 ) || ( x[p] <= -1 ) || ( x[p] >= 76 ) || ( screen[x[p]] [y[p]] == 'a' )
       || ( screen[x[p]] [y[p]] == 'b' ) )
       {
         retval = endgame( true, p );
  }
  else if ( screen[x[p]] [y[p]] == applechr )
  {
    clean_draw( true, true, p );
  }
  else
  {
    screen[px[p]] [py[p]] = pc;
    screen[x[p]] [y[p]] = toupper( pc );
    setcolor( 12 + p );
    gotoxy( px[p] + 2, py[p] + 2 );
    cout << wormchr;
    gotoxy( x[p] + 2, y[p] + 2 );
    cout << wormhead;
  }
  return retval;
}

int main( void )
{
  bool cont[2];
  srand( time( NULL ) );
  do
  {
    cont[0] = false;
    cont[1] = showhelp( true );
    if ( cont[1] )
    {
      clean_draw();
      rand_apple();
    }
    while ( cont[1] )
    {
      Sleep( 50 );
      if ( kbhit() )
      {
        switch ( tolower( getch() ) )
        {
          case 0:
            switch ( getch() )
            {
              case 72:
                dir[0] = ( dir[0] != 'd' ? 'u' : 'd' );
              break;
              case 80:
                dir[0] = ( dir[0] != 'u' ? 'd' : 'u' );
              break;
              case 75:
                dir[0] = ( dir[0] != 'r' ? 'l' : 'r' );
              break;
              case 77:
                dir[0] = ( dir[0] != 'l' ? 'r' : 'l' );
              break;
              default:
              break;
            }
          break;
          case 'w':
            dir[1] = ( dir[1] != 'd' ? 'u' : 'd' );
          break;
          case 's':
            dir[1] = ( dir[1] != 'u' ? 'd' : 'u' );
          break;
          case 'a':
            dir[1] = ( dir[1] != 'r' ? 'l' : 'r' );
          break;
          case 'd':
            dir[1] = ( dir[1] != 'l' ? 'r' : 'l' );
          break;

          case 'p':
            pause( true );
            clean_draw( false );
          break;
          case 'e':
            endgame();
            cont[0] = true;
            cont[1] = false;
          break;
          case 'r':
            clean_draw( false );
          break;
          case 'i':
            showabout();
            clean_draw( false );
          break;
          case 'h':
            showhelp();
            clean_draw( false );
          break;
          case 'x':
            endgame();
            showabout( true );
            cont[0] = false;
            cont[1] = false;
          break;
          default:
          break;
        }
      }
      if ( ( cont[1] ) && ( ++sleeps >= 5 ) )
      {
        sleeps = 0;
        cont[1] = incplayer( 0 );
        if ( ( cont[1] ) && ( dualplayer ) )
        {
          cont[1] = incplayer( 1 );
        }
        if ( --applecnt == 0 )
        {
          rand_apple( true );
        }
      }
    }
  }
  while ( cont[0] );
  return 0;
}

