/**
  *
  * Programmieraufgabe P-14 (Spiel Nim, nim.jar)
  *
  * @author Leonhard Fellermayr
  * @version 1.0
  *
  */

/** Implementierung der Klasse Spieler. */

public class Spieler {

	/** ***** VARIABLEN ***** */

	/**
	  *  @param MODE_DUMM   Konstanten-Codierung fuer den Spielmodus "dumm"
	  *  @param MODE_CLEVER Konstanten-Codierung fuer den Spielmodus "clever"
	  *
	  *  @param modus Hier wird der von waehleModus() festgelegte Spielmodus gespeichert
	  *
	  */

	private final static int MODE_DUMM   = 0;
	private final static int MODE_CLEVER = 1;

	private static int modus;


	/** ***** PUBLIC METHODEN ***** */

	/**
	  *  modusToString () - Gibt einen kurzen String zurueck, der den derzeitigen
	  *  Spielmodus beschreibt.
	  *
	  *  @return Derzeitiger Spielmodus als String
	  *
	  */

	public static String modusToString () {

		if (modus == MODE_DUMM)
			return ("dumm");
		else
			return ("clever");

	} // modusToString ()

	/**
	  *
	  *  macheZug () - Art "Methoden-Multiplexer"
	  *
	  *  Falls der Modus auf "Clever" steht sowie die Gewinnstrategie aus der
	  *  Aufgabenstellung ueberhaupt moeglich ist, so wird clevererZug () aufgerufen.
	  *
	  *  In allen anderen Faellen wird lediglich ein herkoemmlicher legalerZug ()
	  *  durchgefuehrt.
	  *
	  *  @return Anzahl weggenommener Steine
	  *
	  */

	public static int macheZug () {

		if ((modus == MODE_CLEVER) && (gewinnstrategieMoeglich()))

			return (clevererZug ());

		else

			return (legalerZug ());

	} // macheZug ()


	/** ***** PRIVATE METHODEN ***** */

	/**
	  *  waehleModus () - Lege den Spielmodus des Computers fest (clever/dumm).
	  *
	  *  @return void
	  *
	  */

	private static void waehleModus () {

		modus = Spiel.gen.nextInt (2);	/* dumm oder clever */

	} // waehleModus ()

	/**
	  *  isInt () - Stellt fest, ob es sich beim Argument um eine Integer-Zahl handelt.
	  *
	  *  Vorgehensweise: Vergleiche Wert nach double->int Typecasting mit dem Wert
	  *  vor dem Typecasting.
	  *
	  *  @param myNum Zu pruefende Zahl vom Typ double
	  *
	  *  @return Ergebnis der Ueberpruefung als boolescher Wert
	  *
	  */

	private static boolean isInt (double myNum) {

		return ((int)(myNum) == myNum);

	} // isInt ()

	/**
	  *  gewinnstrategieMoeglich () - Stellt fest, ob die Gewinnstrategie
	  *  "Lass stets eine Anzahl Steine uebrig, die von der Form (2^d) - 1 (mit d > 0) ist"
	  *  auf Basis der aktuellen Steinanzahl ueberhaupt moeglich ist.
	  *
	  *  Dies ist NICHT der Fall, wenn die vorliegende Anzahl schon von dieser Gestalt ist.
	  *
	  *  Dazu loesen wir die Gleichung
	  *
	  *  (2^d) - 1 = getSpielstand ()
	  *
	  *  nach d auf. Ist d (Logarithmenquotient) ganzzahlig, so laesst sich die Gewinnstrategie
	  *  NICHT anwenden.
	  *
	  *  @return Ergebnis der Ueberpruefung als boolescher Wert
	  *
	  */

	private static boolean gewinnstrategieMoeglich () {

		return (!isInt (Math.log (Haufen.getSpielstand () + 1) / Math.log (2)));

	} // gewinnstrategieMoeglich ()

	/**
	  *  legalerZug () - Fuehre einen zufaelligen legalen ("dummen") Zug durch.
	  *
	  *  @return Anzahl entfernter Steine
	  *
	  */

	private static int legalerZug () {

		/** Entferne mindestens einen, jedoch hoechstens die Haelfte der
		  * vorhandenen Steine.
		  */

		int nimmWeg;

		if (Haufen.getSpielstand () > 1)
			nimmWeg = Spiel.gen.nextInt (Haufen.getSpielstand () / 2) + 1;
		else
			nimmWeg = 1;

		/** Jetzt Steine tatsaechlich entfernen */

		Haufen.entferneSteine (nimmWeg);

		/** Gib die entfernte Anzahl Steine zurueck. */

		return (nimmWeg);

	} // legalerZug ()

	/**
	  *  clevererZug () - Fuehre einen cleveren Zug mit Gewinnstrategie durch.
	  *
	  *  Die Anwendbarkeit der Gewinnstrategie ist vor Aufruf dieser Methode
	  *  bereits sichergestellt, wird hier also nicht nochmals ŸberprŸft.
	  *
	  *  @return Anzahl entfernter Steine
	  *
	  */

	private static int clevererZug () {

		/**  Mindestens die (aufgerundete) Haelfte aller Steine muessen auf dem
		  *  Haufen zurueckbleiben, also z. B.
		  *
		  *  Menge aller Steine - abgerundete Menge der Haelfte
		  *
		  *  Dieser Wert wird in minUebriglassen abgelegt.
		  *
		  */

		int minUebriglassen = Haufen.getSpielstand () - Haufen.getSpielstand () / 2;

		/**  Berechne den groessten (ganzzahligen) Wert fuer d in der Formel
		  *
		  *  (2^d) - 1 = Anzahl zurueckbleibender Steine >= minUebriglassen
		  *
		  *  und lege diesen in maxZweierpotenz ab.
		  *
		  */

		int maxZweierpotenz = (int)(Math.ceil (Math.log (minUebriglassen + 1) / Math.log (2)));

		/**  Berechne nun einfach die Anzahl der zu entfernenden Steine.
		  *  Hierzu ziehe von der Anzahl aller Steine die Zahl der zurueckbleibenden
		  *  Steine ab.
		  */

		int nimmWeg = Haufen.getSpielstand () - (int)(Math.pow (2, maxZweierpotenz)) + 1;

		/** Jetzt Steine tatsaechlich entfernen */

		Haufen.entferneSteine (nimmWeg);

		/** Gib die entfernte Anzahl Steine zurueck. */

		return (nimmWeg);

	} // clevererZug ()


	/** ***** KONSTRUKTOR der Klasse "Spieler" ***** */

	public Spieler () {

		/** Waehle zufŠllig den Spielmodus (clever/dumm) */

		waehleModus ();

	} // Konstruktor Spieler

} // Klasse Spieler
