#include "maedn.h"

using namespace std; // für iostream

///////////////////////////////////////////////////////////////////////////////
void Initialisierung(MAEDN & m) {                                            //
// Init. der Teil-Datenstruktur Spielfeld, einlesen der Spielernamen etc.    //
// statt Einlesen, bisher nur Default-Werte                                  //
///////////////////////////////////////////////////////////////////////////////
  // Werte richtig initialisieren
  int i,j;
  for(i=0;i<MAX_Spieler;i++) { // 4 Figuren je Startbereich
    m.feld.start[i]=4;
  }
  for(i=0;i<MAX_Felder;i++) { // alle Felder unbesetzt
    m.feld.besetzt[i]=-1;
  }
  for(i=0;i<MAX_Spieler;i++) { // alle Zielbereiche leer
    for(j=0;j<4;j++) {
      m.feld.ziel[i][j]=false;
    }
  }
  // per Default alles Computerspieler bis auf den ersten
  // ggf. ausprogrammieren und abfragen
  // oder "Farben" zufällig zuordnen
  // möglich auch, eine "Farbe" leer zu lassen (spieltmit=0)
  for(i=0;i<MAX_Spieler;i++) {
    m.spieler[i].spieltmit=2; // default Computerspieler
    strcpy(m.spieler[i].name,"Computer\0");
    m.spieler[i].aufFeld=0;
    m.spieler[i].imZiel=0;
    for(j=0;j<4;j++) {
      m.spieler[i].pos[j]=-1; // noch nicht draußen
    }
  }
  m.spieler[0].spieltmit=1; // der erste Spieler ist Mensch
  strcpy(m.spieler[0].name,"Spieler\0"); // und heißt "Spieler"
  //
  cout << "In der Initialisierungsroutine wurden Spieler 2 und 4 auf "
       << "'spielt nicht mit' gesetzt ..." << endl;
  m.spieler[1].spieltmit=0;
  m.spieler[3].spieltmit=0;
}

///////////////////////////////////////////////////////////////////////////////
bool EinzelzugMoeglich(const MAEDN & m, const int nr, const int figur, const int wert) {
// überprüft, ob Spieler <nr> die <figur> mit Wurf <wert> ziehen kann        //
///////////////////////////////////////////////////////////////////////////////
  // ausnahmsweise Abweichung von der Standardumsetzung!
  // das Ergebnis des Tests wird mit return zurückgegeben
  // sonst müsste man für das Ergebnis noch einen formalen Parameter haben
  // ebenso in der aufrufenden Funktion, die Abfrage in der
  // if-Anweisung sähe dann so aus:
  // if (EinzelzugMoeglich(m,nr,figur,wert,erg),erg)
  // also zunächst der Aufruf und durch Verwendung des Komma-Operators
  // ist der Wahrheitswert der Bedingung der Inhalt der Variablen erg
  // Dieses Vorgehen bietet sich an, wenn das Ergebnis ausschließlich direkt
  // verwendet wird und nie einer Variablen zugewiesen werden soll.
  // Man kann dies auch als "anonyme" Ausgangsvariable (ohne Namen) betrachten
  //
  // Einzelzug mit der bestimmten Figur möglich, wenn
  // - Figur draußen
  // - das Feld "wert" weiter existiert
  // - und nicht durch eine eigene Figur besetzt ist
  // Es fehlt noch (falls gewünscht) Überprüfung, dass im Zielbereich keine
  // eigenen Figuren übersprungen werden
  if (m.spieler[nr].pos[figur]>=0) {
    if (m.spieler[nr].pos[figur]+wert<MAX_Felder+4) {
      if (m.spieler[nr].pos[figur]+wert<MAX_Felder) {
	if (m.feld.besetzt[(10*nr+m.spieler[nr].pos[figur]+wert)%MAX_Felder]!=nr) {
	  return true; // im Feld und frei oder gegnerische Figur
	} else {
	  return false; // im Feld, aber eigene Figur
	}
      } else {
	return !(m.feld.ziel[nr][m.spieler[nr].pos[figur]+wert-MAX_Felder]);
	// es ist bereits ein bool-Wert,
	// Rückgabe true -> noch frei, false -> schon besetzt
      }
    } else {
      return false; // Zug schießt über das Ziel hinaus
    }
  } else {
    return false; // Figur ist noch im Startbereich
  }
}

///////////////////////////////////////////////////////////////////////////////
bool SpielzugMoeglich(const MAEDN & m, const int nr, const int wert) {       //
// überprüft, ob Spieler <nr> mit Wurf <wert> eine Figur setzen kann         //
///////////////////////////////////////////////////////////////////////////////
  if (EinzelzugMoeglich(m,nr,0,wert)) {
    return true;
  } else if (EinzelzugMoeglich(m,nr,1,wert)) {
    return true;
  } else if (EinzelzugMoeglich(m,nr,2,wert)) {
    return true;
  } else if (EinzelzugMoeglich(m,nr,3,wert)) {
    return true;
  } else {
    return false;
  } // dies ist die ausführliche Variante von
  // return ( EinzelzugMoeglich(m,nr,0,wert) || EinzelzugMoeglich(m,nr,1,wert)
  //       || EinzelzugMoeglich(m,nr,2,wert) || EinzelzugMoeglich(m,nr,3,wert) );
}

///////////////////////////////////////////////////////////////////////////////
void SchlageFigur(MAEDN & m, const int index) {                              //
// Schlägt die Figur auf dem Feld <index>, das Feld darf nicht leer sein     //
///////////////////////////////////////////////////////////////////////////////
  int gegner,i;
  gegner=m.feld.besetzt[index];
  cout << "Die Figur von " << m.spieler[gegner].name
       << " fliegt raus!" << endl;
  m.feld.start[gegner]++;
  m.spieler[gegner].aufFeld--;
  for(i=0;i<4;i++){ // vielleicht nicht so schön, dass man suchen muss,
    // welche der gegenerischen Figuren rausflog.
    // Evtl. kann man das noch in die Figuren codieren,
    // ist aber etwas aufwendig und an einigen Stellen zu ändern
    // => wir lernen an diesem Beispiel: Konstruktion einer
    // geeigneten Datenstruktur ist schwierig. Man hat selten zu
    // Beginn gleich alles mögliche im Sinn.
    // Spätere Änderungen sind immer aufwendig,
	// eine detaillierte Planung zahlt sich aus
    if (((m.spieler[gegner].pos[i]+10*gegner)%MAX_Felder) == index) {
      m.spieler[gegner].pos[i]=-1;
      break; // es kann nur eine geben ...
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
void MacheZug(MAEDN & m, const int nr, const int figur, const int wert) {    //
// Führt den Zug für Spieler <nr> mit <figur> und Wurf <wert> aus            //
// darf nur aufgerufen werden, wenn der Spielzug möglich ist!                //
///////////////////////////////////////////////////////////////////////////////
  // Es sind mehrere Fälle zu unterscheiden:
  // Lag die alte Position auf der Rundstrecke oder im Ziel
  // Liegt die neue Position auf der Rundstrecke oder im Ziel
  if (m.spieler[nr].pos[figur]+wert<MAX_Felder) {
    // (alte und) neue Position ist noch auf der Rundstrecke,
    // evtl. wird gegnerische Figur geschlagen
    if (m.feld.besetzt[(10*nr+m.spieler[nr].pos[figur]+wert)%MAX_Felder]!=-1) {
      SchlageFigur(m,(10*nr+m.spieler[nr].pos[figur]+wert)%MAX_Felder);
    } // else { ... }
    // Der eigene Zug ist unabhängig davon auszuführen
    m.feld.besetzt[(10*nr+m.spieler[nr].pos[figur])%MAX_Felder]=-1;
    m.feld.besetzt[(10*nr+m.spieler[nr].pos[figur]+wert)%MAX_Felder]=nr;
    m.spieler[nr].pos[figur]+=wert; // den Wert kann man einfach erhöhen :)
  } else {
    // Figur ist danach im Zielbereich,
    // gegnerische Figur kann nicht geschlagen werden
    if (m.spieler[nr].pos[figur]<MAX_Felder) {
      // Figur war vorher draußen, jetzt aber im Zielbereich
	  m.feld.besetzt[(10*nr+m.spieler[nr].pos[figur])%MAX_Felder]=-1;
	  m.spieler[nr].pos[figur]+=wert; // den Wert kann man einfach erhöhen :)
	  m.feld.ziel[nr][m.spieler[nr].pos[figur]-MAX_Felder]=true;
	  m.spieler[nr].aufFeld--;
	  m.spieler[nr].imZiel++;
    } else {
	  // Figur war vorher und ist nachher im Zielbereich
	  m.feld.ziel[nr][m.spieler[nr].pos[figur]-MAX_Felder]=false;
	  m.spieler[nr].pos[figur]+=wert; // den Wert kann man einfach erhöhen :)
	  m.feld.ziel[nr][m.spieler[nr].pos[figur]-MAX_Felder]=true;
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
void Computerzug(MAEDN & m, const int nr, const int wert) {                  //
// darf nur aufgerufen werden, wenn <nr> überhaupt einen Zug machen kann     //
// Einfacher automatischer Spieler:                                          //
//   - wenn Figur auf Startposition, setze diese weg, wenn möglich           //
//   - sonst wähle erste Figur (nach interner Zählung), die möglich ist      //
///////////////////////////////////////////////////////////////////////////////
  int figur;
  figur=0;
  if (m.feld.besetzt[10*nr]==nr && m.feld.besetzt[10*nr+wert]!=nr) {
    // welche der 4 Figuren steht denn auf der Startposition?
    while (m.spieler[nr].pos[figur]!=0) {
      figur++;
	}
  } else { // suche erste mögliche Figur
	while (!(EinzelzugMoeglich(m,nr,figur,wert))) {
      figur++;
	}
  }
  // und nun noch den Zug ausführen ...
  cout << m.spieler[nr].name << " sagt: Ich setze Figur " << figur << endl;
  MacheZug(m,nr,figur,wert);
}

///////////////////////////////////////////////////////////////////////////////
void Spielzug1bis5(MAEDN & m, const int nr, const int wert) {                //
// Führt den Zug für Spieler <nr> aus, der eine <wert> gewürfelt hat         //
// (auch, wenn <nr> eine 6 gewürfelt hat und keine Figur raussetzen konnte)  //
///////////////////////////////////////////////////////////////////////////////
  int i, figur;
  if (SpielzugMoeglich(m,nr,wert)) {
    cout << "Du hast eine " << wert << " gewuerfelt." << endl;
	if (m.spieler[nr].spieltmit!=1) { // dann ist es ein Computerspieler
      Computerzug(m,nr,wert);
	} else {
      // Hier müss(t)en nun alle Sonderregeln abgefangen werden, wie z.B.
      // Figur auf Startfeld muss weggezogen werden,
      //   wenn noch weitere Figuren im Startbereich sind und der Zug möglich ist
      // ggf. Rauswerfpflicht?
      // kein Überspringen der eigenen Figuren im Zielbereich
      //   (wobei das eher in EinzelzugMoeglich gehört)
      do {
        cout << "Welche Figur willst Du ziehen?" << endl;
        for(i=0;i<4;i++) {
          if (EinzelzugMoeglich(m,nr,i,wert)) {
            cout << "Figur " << i << " steht von Deiner Startposition "
                 << "aus auf Feld " << m.spieler[nr].pos[i] << endl;
          }
        }
        cout << "Ich nehme Figur (eine der moeglichen Nummern auswaehlen) ";
        cin >> figur;
        // statt der Variablen figur könnte man auch i nehmen,
        // aber Lesbarkeit geht vor (vermeintlicher) Speichereffizienz
        // Aufgabe: was passiert, wenn der Spieler einen Wert <0 || >3 angibt?
        // Fangen Sie solche Eingaben noch ab!
      } while (!(EinzelzugMoeglich(m,nr,figur,wert))); // Wiederholung,
      // bis er endlich eine der möglichen Figuren gewählt hat
      //
      // und nun müssen wir den Zug noch ausführen ...
      MacheZug(m,nr,figur,wert);
	}
  } else { // gehört zu if (SpielzugMoeglich(m,nr,wert))
      cout << "Schade, kein Zug mit einer " << wert << " moeglich." << endl;
  }
}

///////////////////////////////////////////////////////////////////////////////
void Spielzug6(MAEDN & m, const int nr) {                                    //
// Führt den Zug für Spieler <nr> aus, der eine 6 gewürfelt hat              //
///////////////////////////////////////////////////////////////////////////////
  int i;
  // Zwangszug bei 6: Figur raussetzen, falls möglich
  if (m.feld.start[nr]>0 && m.feld.besetzt[10*nr]!=nr) {
    // noch Figuren am Start und Startfeld nicht durch eigene Figur besetzt
    cout << "Du hast eine 6 gewuerfelt! " 
	 << "Glueckwunsch! Neue Figur ist draussen!" << endl;
    if (m.feld.besetzt[10*nr]!=-1) {
      SchlageFigur(m,10*nr);
    } // else { ... }
    // Der eigene Zug ist unabhängig davon auszuführen
    m.feld.start[nr]--;
    m.spieler[nr].aufFeld++;
    // erste Figur finden, die noch draußen ist, auch das geht bestimmt schöner
    i=0;
    while (m.spieler[nr].pos[i]!=-1) { // mind. eine IST noch draußen
      i++;
    }
    m.feld.besetzt[10*nr]=nr;
    m.spieler[nr].pos[i]=0;
  } else { // alle anderen Sonderfälle wie bei 1 bis 5
    Spielzug1bis5(m,nr,6); // etwas gemogelt :) - ist aber korrekt und kurz
    // sonstige Sonderfälle werden in Spielzug1bis5 behandelt
  }
}

///////////////////////////////////////////////////////////////////////////////
void GesamtzugSpieler(MAEDN & m, const int nr) {                             //
// Gesamtzug für Spieler <nr> inkl. Wiederholungen nach 6 bzw. 3* am Anfang  //
///////////////////////////////////////////////////////////////////////////////
  int count, wert;
  cout << "Spieler " << m.spieler[nr].name
       << " (Figuren-Nummer " << nr+1 << ") ist dran" << endl;
  if (m.spieler[nr].aufFeld==0) { // die Abfrage ist nicht korrekt
    // wenn keine Figur auf dem Feld ist, kann trotzdem noch ein Zug mit
    // einer Figur innerhalb des Zielbereichs möglich sein
    count=0;
    do {
      count++;
      GetWurf(wert);
      cout << count << "-ter Wurf: eine " << wert << " ..." << endl;
    } while (!(count==3 || wert==6));
    if (wert==6) {
      do {
	Spielzug6(m,nr);
	SpielfeldAnzeigen(m.feld);  
	GetWurf(wert);
      } while (!(wert!=6)); // ok, hier würde man zu wert==6 vereinfachen :)
      Spielzug1bis5(m,nr,wert);
      SpielfeldAnzeigen(m.feld);  
    }
  } else {
    GetWurf(wert);
    while (wert==6) {
      Spielzug6(m,nr);
      SpielfeldAnzeigen(m.feld);  
      GetWurf(wert);
    }
    Spielzug1bis5(m,nr,wert);
    SpielfeldAnzeigen(m.feld);  
  }
}

///////////////////////////////////////////////////////////////////////////////
void MeinMAEDN(void) { // das "Hauptprogramm" für das MÄDN-Spiel             //
///////////////////////////////////////////////////////////////////////////////
  MAEDN m;
  int nr;
  Initialisierung(m);
  do {
    Auswuerfeln(MAX_Spieler,nr);
  } while (!(m.spieler[nr].spieltmit!=0)); // kleine Mogelpackung,
  // das Auswürfeln wird wiederholt, falls ein Spieler ermittelt wird,
  // der garnicht mitmacht. Die do-while-Schleife wieder entfernen,
  // nachdem die Auswuerfeln-Routine angepasst wurde!
  cout << "Der Spieler " << m.spieler[nr].name << " faengt an!" << endl; //
  SpielfeldAnzeigen(m.feld);  
  nr--; // kleiner Trick
  do { // damit hier einheitlich der nächste Spieler bestimmt werden kann
    nr=(nr+1)%MAX_Spieler; 
    if (m.spieler[nr].spieltmit>0) { // Mensch oder Computer
      GesamtzugSpieler(m,nr);
    }
  } while (!(m.spieler[nr].imZiel==4));
  cout << "Glueckwunsch! " << m.spieler[nr].name << " hat gewonnen!" << endl;
}
