/*

  TicTacToeDe.ino
  
*/

#include <Arduino.h>
#include <U8g2lib.h>


/*==============================================*/
/* Konstante Werte */

#define GITTER_X_OFFSET 35
#define GITTER_Y_OFFSET 2
#define ZELLE_BREITE 17
#define ZELLE_HOEHE 17
#define ZELLE_RAHMENBREITE 3

#define FELD_NICHT_BELEGT 0
#define FELD_MARKIERT_DURCH_SPIELER 1
#define FELD_MARKIERT_DURCH_COMPUTER 2

/*==============================================*/
/* Variablen */

/* U8g2 Graphics API, setup for ARDUBOY Production, Kickstarter Edition */
U8G2_SSD1306_128X64_NONAME_1_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 12, /* dc=*/ 4, /* reset=*/ 6);	

uint8_t cursor_position; 	/* Aktuelle Cursorposition die vom User ausgewählt wurde */
u8g2_uint_t zelle_x, zelle_y;	/* Berechnete Position einer Zelle (berechne_zell_position) */

int spielfeld[3][3];		/* Enthält die aktuelle Belegung mit x und o */
int gewinner;			/* Zeigt den gewinner an (wenn es einen gibt) */

/*==============================================*/
/* vorbelegung und spiel initalisierenalisierung */
void spiel_initalisieren(void)
{
  int x, y;
    
  gewinner = FELD_NICHT_BELEGT;
  for( y = 0; y < 3; y = y + 1 )
  {
    for( x = 0; x < 3; x = x + 1 )
    {
      spielfeld[y][x] = FELD_NICHT_BELEGT;
    }
  }
}


/*==============================================*/
/* Hilfsroutine */

/* berechne die Zell-Position, Ergebnis in zelle_x und zelle_y */
void berechne_zell_position(uint8_t x, uint8_t y )
{
  zelle_x = x;
  zelle_x*=ZELLE_BREITE+ZELLE_RAHMENBREITE;
  zelle_x+= GITTER_X_OFFSET;
  zelle_y = y;
  zelle_y *=ZELLE_HOEHE+ZELLE_RAHMENBREITE;
  zelle_y +=GITTER_Y_OFFSET;
}

/*==============================================*/
/* "game engine" */

void zeichne_gitter(void)
{
  u8g2_uint_t x, y;
  x = GITTER_X_OFFSET+ZELLE_BREITE;  
  u8g2.drawVLine(x+1, GITTER_Y_OFFSET, ZELLE_HOEHE*3+ZELLE_RAHMENBREITE*2);
  x += ZELLE_BREITE+ZELLE_RAHMENBREITE;
  u8g2.drawVLine(x+1, GITTER_Y_OFFSET, ZELLE_HOEHE*3+ZELLE_RAHMENBREITE*2);

  y = GITTER_Y_OFFSET+ZELLE_HOEHE;  
  u8g2.drawHLine(GITTER_X_OFFSET, y+1, ZELLE_BREITE*3+ZELLE_RAHMENBREITE*2);
  y += ZELLE_HOEHE+ZELLE_RAHMENBREITE;
  u8g2.drawHLine(GITTER_X_OFFSET, y+1, ZELLE_BREITE*3+ZELLE_RAHMENBREITE*2);
}

void zeichne_spielfeld(void)
{
  int x, y;
  for( y = 0; y < 3; y = y + 1 )
  {
    for( x = 0; x < 3; x = x + 1 )
    {
      if ( spielfeld[y][x] == FELD_NICHT_BELEGT )
      {
	  /* nichts anzeigen */
      }
      if ( spielfeld[y][x] == FELD_MARKIERT_DURCH_SPIELER )
      {
	berechne_zell_position(x, y);
	u8g2.drawLine(zelle_x, zelle_y, zelle_x+ZELLE_BREITE-1, zelle_y+ZELLE_HOEHE-1);
	u8g2.drawLine(zelle_x+ZELLE_BREITE-1, zelle_y, zelle_x, zelle_y+ZELLE_HOEHE-1);
      }
      if ( spielfeld[y][x] == FELD_MARKIERT_DURCH_COMPUTER )
      {
	berechne_zell_position(x, y);
	u8g2.drawCircle(zelle_x+ZELLE_BREITE/2, zelle_y+ZELLE_HOEHE/2, ZELLE_HOEHE/2);
      }
    }	/* for x */
  } /* for y */
}

void zeichne_gitter_und_spielfeld(void)
{
  zeichne_gitter();
  zeichne_spielfeld();
}

void zeige_spielfeld_an(void)
{
    u8g2.firstPage();
    do 
    {
      yield();	// Arduino
      zeichne_gitter_und_spielfeld();
    } while ( u8g2.nextPage() );
}

/*==============================================*/
/* Benitzerschnittstelle (User Interface) */

/* Zeichne den Cursor für die Benutzereingabe */
void zeichne_cursor(void)
{
  u8g2_uint_t x, y;
  x = cursor_position % 3;
  y = cursor_position / 3;
  berechne_zell_position(x, y );
  u8g2.drawFrame(zelle_x, zelle_y, ZELLE_BREITE, ZELLE_HOEHE);
  u8g2.drawFrame(zelle_x+1, zelle_y+1, ZELLE_BREITE-2, ZELLE_HOEHE-2);
}

void zug_spieler(void)
{
  static uint8_t keycode = 0;
  int x;
  int y;
  int zaehler = 0;
  
  for(;;)   /* check valid entry */
  {
    for(;;)	/* cursor movement loop */
    {
      keycode = 0;
      u8g2.firstPage();
      do 
      {
	zeichne_gitter_und_spielfeld();
	zeichne_cursor();
	if ( keycode == 0 )
	  keycode = u8g2.getMenuEvent();
      } while ( u8g2.nextPage() );

      while ( keycode == 0 )
      {
	yield();	// Arduino
	keycode = u8g2.getMenuEvent();
      }

      if ( keycode == U8X8_MSG_GPIO_MENU_DOWN )
      {
	cursor_position += 3;
	if ( cursor_position > 9 )
	  cursor_position -= 9;
      }

      if ( keycode == U8X8_MSG_GPIO_MENU_NEXT )
      {
	if ( cursor_position % 3 == 2 )
	  cursor_position-=2;
	else
	  cursor_position += 1;
      }

      if ( keycode == U8X8_MSG_GPIO_MENU_UP )
      {
	cursor_position -= 3;
	if ( cursor_position > 9 )
	  cursor_position += 9;
      }

      if ( keycode == U8X8_MSG_GPIO_MENU_PREV )
      {
	if ( cursor_position % 3 == 0 )
	  cursor_position+=2;
	else
	  cursor_position -= 1;
      }
      cursor_position = cursor_position % 9;

      if ( keycode == U8X8_MSG_GPIO_MENU_SELECT )
	break;
      if ( keycode == U8X8_MSG_GPIO_MENU_HOME )
	break;
    }

    y = cursor_position / 3;
    x = cursor_position % 3;
    
    if ( spielfeld[y][x] == FELD_NICHT_BELEGT )
    {
      spielfeld[y][x] = FELD_MARKIERT_DURCH_SPIELER ;
      return;
    }

    /*
    if ( zaehler > 3 )
    {
      printf("Du doedl, jetzt langts aber...\n");
      exit(1);
    }
    printf("Ahh, das feld ist schon belegt, nochmal...\n");
    zaehler = zaehler + 1;
    */
  }
}

/*==============================================*/
/* Künstliche Computer Intelligenz */

void zug_computer(void)
{
  int x, y;
  
  /* Sehr dumme AI: Nimm den nächsten freien Platz */
  
  for( y = 0; y < 3; y = y + 1 )
  {
    for( x = 0; x < 3; x = x + 1 )
    {
      if ( spielfeld[y][x] == FELD_NICHT_BELEGT )
      {
	spielfeld[y][x] = FELD_MARKIERT_DURCH_COMPUTER;
	return;
      }
    }
  }
}

/*==============================================*/
/* Spiel Zuende Prüfung */

/* gibt 1 zurück, wenn in der zeile überall markierung gesetzt ist */
int zeile_gewonnen(int y, int markierung)
{
  int x;	
  for( x = 0; x < 3; x++ )
  {
    if ( spielfeld[y][x] !=  markierung )
      return 0;
  }
  return 1;
}

/* gibt 1 zurück, wenn in der zeile überall markierung gesetzt ist */
int spalte_gewonnen(int x, int markierung)
{
  int y;	
  for( y = 0; y < 3; y++ )
  {
    if ( spielfeld[y][x] !=  markierung )
      return 0;
  }
  return 1;
}

int diagonale_gewonnen(int markierung)
{
  int i;
  
  for( i = 0; i < 3; i ++ )
  {
    if ( spielfeld[i][i] != markierung )
      return 0;
  }
  return 1;
}

int inverse_diagonale_gewonnen(int markierung)
{
  int i;
  
  for( i = 0; i < 3; i ++ )
  {
    if ( spielfeld[2-i][i] != markierung )
      return 0;
  }
  return 1;
}

int ist_das_spiel_zuende(void)
{
  int i, x, y;
  int ist_alles_voll;
  
  zeige_spielfeld_an();

  for( i = 0; i < 3; i ++ )
  {
    if ( zeile_gewonnen(i, FELD_MARKIERT_DURCH_SPIELER) )
      gewinner = FELD_MARKIERT_DURCH_SPIELER;
    if ( zeile_gewonnen(i, FELD_MARKIERT_DURCH_COMPUTER) )
      gewinner = FELD_MARKIERT_DURCH_COMPUTER;
    if ( spalte_gewonnen(i, FELD_MARKIERT_DURCH_SPIELER) )
      gewinner = FELD_MARKIERT_DURCH_SPIELER;
   if ( spalte_gewonnen(i, FELD_MARKIERT_DURCH_COMPUTER) )
      gewinner = FELD_MARKIERT_DURCH_COMPUTER;
  }
  if ( diagonale_gewonnen(FELD_MARKIERT_DURCH_SPIELER) )
      gewinner = FELD_MARKIERT_DURCH_SPIELER;
  if ( diagonale_gewonnen(FELD_MARKIERT_DURCH_COMPUTER) )
      gewinner = FELD_MARKIERT_DURCH_COMPUTER;

  if ( inverse_diagonale_gewonnen(FELD_MARKIERT_DURCH_SPIELER) )
      gewinner = FELD_MARKIERT_DURCH_SPIELER;
  if ( inverse_diagonale_gewonnen(FELD_MARKIERT_DURCH_COMPUTER) )
      gewinner = FELD_MARKIERT_DURCH_COMPUTER;
  
  if ( gewinner != FELD_NICHT_BELEGT )
    return 1;

  ist_alles_voll = 1;
  for( y = 0; y < 3; y = y + 1 )
  {
    for( x = 0; x < 3; x = x + 1 )
    {
      if ( spielfeld[y][x] == FELD_NICHT_BELEGT )
      {
	ist_alles_voll = 0;
      }
    }
  }


  if ( ist_alles_voll )
    return 1;
    
  return 0;
}

/*==============================================*/

void zeige_ergebnis_an(void)
{
  u8g2.setFont(u8g2_font_helvB12_tr);
  if ( gewinner == FELD_MARKIERT_DURCH_SPIELER )
    u8g2.userInterfaceMessage("Spieler", "gewinnt", "", " ok ");
  else if ( gewinner == FELD_MARKIERT_DURCH_COMPUTER )
    u8g2.userInterfaceMessage("Computer", "gewinnt", "", " ok ");
  else
    u8g2.userInterfaceMessage("", "Unentschieden", "", " ok ");
}

/*==============================================*/

void setup(void) 	// Arduino setup
{
  // Intialisierung für Arduboy (Production)
  u8g2.begin(/*Select=*/ 7, /*Right/Next=*/ A1, /*Left/Prev=*/ A2, /*Up=*/ A0, /*Down=*/ A3, /*Home/Cancel=*/ 8); 
}

void loop(void) 		// Arduino loop 
{
  spiel_initalisieren();
  zeige_spielfeld_an();
  for(;;)
  {
    zug_spieler();
    zeige_spielfeld_an();
    if ( ist_das_spiel_zuende() )
    {
      break;
    } /* if */
    delay(300);
    zug_computer();
    zeige_spielfeld_an();
    if ( ist_das_spiel_zuende() )
    {
      break;
    } /* if */
    
  } /* for */
  zeige_ergebnis_an();
}