public class DameSpiel extends MiniJava {

    private int nrRows, nrColumns; // Board dimensions
    private boolean[][] board = null;     // true = queen, false = empty
    private boolean whiteToMove;   // Whose turn it is
    private String white, black;   // Players' names

    //TODO Mehr Attribute?

    //TODO Weitere Methoden?


    /**
     * Der Konstruktor registriert Spielernamen fuer Weiss und Schwarz.
     *
     * @param white Name des als 'Weiss' bezeichneten Spielers
     * @param black Name des als 'Schwarz' bezeichneten Spielers
     */
    public DameSpiel(String white, String black){
        // TODO 04 
        this.white = white;
        this.black = black;
    }


    /**
     * Gibt das Spielbrett aus.
     */
    private void printBoard(){
        for (int j = board[0].length - 1; j >= 0; j--) {
            System.out.print("\n " + (1 + j));
            for (int i = 0; i < board.length; i++) {
                System.out.print(board[i][j] ? " X" : " -");
            }
        }
        System.out.print("\n  ");
        for (int i = 1; i <= board.length; i++) {
            System.out.print(" " + i);
        }
        System.out.println("\n" + (whiteToMove ? white : black) + " ist am Zug.");
    }


    /**
     * Initialisiert das Spielbrett ueberall mit false.
     * Dazu wird (ggf. neuer) Speicher allokiert.
     */
    private void initBoard() {
        /* TODO 01
         * Diesmal vertauscht:
         * Erste Dimension entspricht der Breite (Spalten bzw. x),
         * zweite Dimension entspricht der Länge/Höhe (Zeilen bzw. y).
         */
        
        board = new boolean[nrColumns][nrRows];  
        
        for (int x = 0; x < board.length; x++)
            for (int y = 0; y < board[x].length; y++)
                board[x][y] = false;
    }


    /**
     * Ermittelt die Groesse des Spielbretts gemaess den Spielregeln.
     * Das Ergebnis der Abfrage wird in den Attributen nrRows und nrColumns abgelegt.
     */
    private void determineBoardSize() {        
        /* TODO 02 */
        
        write("Willkommen zum Spiel \"Kampf der Damen\". Zunächst bestimmt ihr "
                + "die Spielfeldmaße.");
        
        nrColumns = readInt(white + " bestimmt die Breite des Spielfeldes. Wie groß soll "
                  + "das Spielfeld sein (5-8)?");
        while (nrColumns < 5 || nrColumns > 8) // -> ungültige Eingabe
            nrColumns = readInt("Diese Eingabe ist ungültig. Gib für die Breite des "
                      + "Spielfelds eine Zahl zwischen 5 und 8 (jeweils inklusive) "
                      + "ein.");
        
        nrRows = readInt(black + " bestimmt die Länge/Höhe des Spielfeldes. Wie groß "
                       + "soll das Spielfeld sein (" + (nrColumns-1) + "-"
                       + (nrColumns+1) + ")?");
        while (nrRows < nrColumns-1 || nrRows > nrColumns+1) // -> ungültige Eingabe
            nrRows = readInt("Diese Eingabe ist ungültig. Gib für die Breite des "
                      + "Spielfelds eine Zahl zwischen " + (nrColumns-1) + " und " 
                      + (nrColumns+1) + " (jeweils inklusive) ein.");
    }


    /**
     * Ermittelt, wer anfaengt zu ziehen.
     * Das Ergebnis der Abfrage wird im Attribut whiteToMove abgelegt.
     */
    private void determineFirstPlayer() {
        /* TODO 03
         * Diesmal eine andere Möglichkeit, ohne Code-Duplikate einzulesen :)
         */
        
        while (true) { // -> wird mit return verlassen
            String eingabe = readString(white + " bestimmt, wer das Spiel beginnt "
                                      + "(w = weiß, s = schwarz):");
            if (eingabe.length() == 1) {
                char wahl = eingabe.charAt(0); // eingegebenes Zeichen zwischenspeichern
                if (wahl == 'w' || wahl == 's') {
                    whiteToMove = (wahl == 'w'); // mal drüber nachdenken!
                    return;
                }
            } // else:
            write("Bitte gib einen gültigen Wert ('w' oder 's') ein. Das Spiel "
                    + "kann im später abgebrochen werden (falls erwünscht).");
        }
    }


    /**
     * Fuehrt den Zug aus.
     *
     * @param move der auszufuehrende Zug!
     */
    private void applyMove(int move){
        /* TODO 05
         * Den Zug bekommen wir nach dem beschriebenen Schema übergeben, d. h.
         * hier erfolgt KEINE Zuweisung an move (sonst würden wir den Parameter
         * ja ÜBERSCHREIBEN! move haben wir gegeben (von der aufrufenden Methode)).
         * move=23 steht bspw. für den Wunsch, eine Dame (Spielfigur) auf das
         * Feld in Spalte 2 (x), Zeile 3 (y) zu stellen. Wir nehmen an, dass move
         * gültig und möglich ist, müssen das dann dafür in der Hauptmethode prüfen.
         */
        
        board[move/10-1][move%10-1] = true;
    }
    
    /**
     * Gibt true zurück, falls der angegebene Zug (in Form von x und y, wobei wir
     * bei 0 anfangen zu zählen!) gültig und möglich ist.
     */
    private boolean zugGueltig(int spalte, int zeile) {
        // x = spalte = eingabe/10
        
        // ungültige Eingabe
        if (spalte < 0 || zeile < 0 || spalte >= nrColumns || zeile >= nrRows)
            return false;
        
        
        int x, y;
        
        // Zeile prüfen:
        for (x = 0; x < nrColumns; x++)
            if (board[x][zeile]) return false;
        // Spalte prüfen:
        for (y = 0; y < nrRows; y++)
            if (board[spalte][y]) return false;
        
        // Diagonalen prüfen (am einfachsten in jede Richtung separat)
        // (alternativ mit Formeln; aus dieser Lösung der Einfachheit wegen entfernt)
                
        x = spalte; y = zeile;
        while (x < nrColumns && y < nrRows) // nach "unten" rechts
            if (board[x++][y++]) return false;
        
        x = spalte; y = zeile;
        while (x >= 0 && y < nrRows) // nach "unten" links
            if (board[x--][y++]) return false;
        
        x = spalte; y = zeile;
        while (x < nrColumns && y >= 0) // nach "oben" rechts
            if (board[x++][y--]) return false;
        
        x = spalte; y = zeile;
        while (x >= 0 && y >= 0) // nach "oben" links
            if (board[x--][y--]) return false;
        
        return true;
    }
    
    /**
     * Gibt true zurück, wenn kein Zug mehr möglich ist.
     */
    private boolean spielende() {
        for (int x = 0; x < nrColumns; x++)
            for (int y = 0; y < nrRows; y++)
                if (zugGueltig(x, y)) return false;
        return true;
    }


    /**
     * Startet die Hauptschleife des Spiels
     * mit der Abfrage nach Zuegen.
     * Die Schleife wird durch Eingabe von -1 beendet.
     */
    private void mainLoop() {
        /* TODO 06 */
        
        while (!spielende()) {
            int eingabe = readInt((whiteToMove ? white : black) +
                    " ist am Zug. Wohin möchtest du eine Dame setzen (xy)?");
            
            if (eingabe == -1) return; // -> Spiel beenden
            
            if (zugGueltig(eingabe/10-1, eingabe%10-1)) {
                applyMove(eingabe);
                whiteToMove = !whiteToMove; // Spielerwechsel (true -> false, false -> true)
                printBoard();
            } else {
                write("Dieser Zug ist ungültig bzw. nicht möglich.");
            }
        }
    }


    /**
     * Informiert die Benutzerin ueber den Ausgang des Spiels.
     * Speziell: Wer hat gewonnen (Weiss oder Schwarz)?
     */
    private void reportWinner(){
        /* TODO 07 */
        
        write("Spieler " + (whiteToMove ? black : white) + " gewinnt.");
    }


    /**
     * Startet das Spiel.
     */
    public void startGame(){
        determineBoardSize();
        initBoard();
        determineFirstPlayer();
        printBoard();
        mainLoop();
        reportWinner();
    }


    public static void main(String[] args) {
        DameSpiel ds = new DameSpiel("Weiß", "Schwarz");
        ds.startGame();
    }

}