///CS 7A Sam Lloyd's 14-15 Puzzle. - Page 2 of 2 Due 3/26/15
/// Geoff Hagopian

#include "std_lib_facilities.h"
#include <iomanip>

/// Helper functions are not part of the class,
/// but the class can use them nonetheless.
unsigned getBlock(vector<unsigned> board, unsigned block) {
    unsigned i = 0;
    while(board[i]!=block) ++i;
    return i;
}

class SliderPuzzle {
public:
    unsigned edge;
    vector<unsigned> board;
    SliderPuzzle(unsigned e) : edge(e) {
        for(unsigned i=0; i < edge*edge; ++i)
            board.push_back(i);
    }
    void display();
    void shuffle(); ///takes a board and mixes it up  with the
                /// Fisher Yates shuffle algorithm
                ///After shuffling the board , a call to the 15
                ///function display() will produce a shuffled
                ///board like this
                /**
                14  5  4 11
                17 12  9  6
                7  13  8 10
                   15 13  2
                */
    bool won() {
        for(unsigned i = 0;i < edge*edge-1; ++i)
            if(board[i]!=i+1) return false;
        return true;
    }
    ///returns true if the board is as below, otherwise false
    /**
     1  2  3  4
     5  6  7  8
     9 10 11 12
    13 14 15
    */
    void getMove() {
    ///prompts the user to enter a, d, s, w
    /// moves the pieces on the board accordingly
        char move;
        cin>>move;
        switch(move) {
        case 's': {
            unsigned blank = getBlock(board,0);
            if(blank >= edge*(edge-1)) { //bottom row
                cout << "\nInvalid move, try again" << endl;
                return;
            }
            board[blank]=board[blank+edge];
            board[blank+edge]=0;
            break;
        }
        case 'a': {
            unsigned blank = getBlock(board,0);
            if(blank%edge==0) { //left side
                cout << "\nInvalid move, try again" << endl;
                return;
            }
            board[blank]=board[blank-1];
            board[blank-1]=0;
            break;
        }
         case 'w': {
            unsigned blank = getBlock(board,0);
            if(blank < edge) { //top row
                cout << "\nInvalid, try again" << endl;
                return;
            }
            board[blank]=board[blank-edge];
            board[blank-edge]=0;
            break;
        }
        case 'd': {
            unsigned blank = getBlock(board,0);
            if(blank%edge==edge-1) { //right side
                cout << "\nInvalid move, try again" << endl;
                return;
            }
            board[blank]=board[blank+1];
            board[blank+1]=0;
            break;
        }

        default :
            cout << "\nI don't know that command, try again." << endl;
            return;
        }



    }
    void doMove(char move) {
    ///prompts the user to enter u, d, l, r
    /// moves the pieces on the board accordingly
        //char move;
        //cin>>move;
        switch(move) {
        case 'w': {
            unsigned blank = getBlock(board,0);
            if(blank >= edge*(edge-1)) { //bottom row
                cout << "\nInvalid move, try again" << endl;
                return;
            }
            board[blank]=board[blank+edge];
            board[blank+edge]=0;
            break;
        }
        case 'a': {
            unsigned blank = getBlock(board,0);
            if(blank%edge==0) { //left side
                cout << "\nInvalid move, try again" << endl;
                return;
            }
            board[blank]=board[blank-1];
            board[blank-1]=0;
            break;
        }
         case 's': {
            unsigned blank = getBlock(board,0);
            if(blank < edge) { //top row
                cout << "\nInvalid, try again" << endl;
                return;
            }
            board[blank]=board[blank-edge];
            board[blank-edge]=0;
            break;
        }
        case 'd': {
            unsigned blank = getBlock(board,0);
            if(blank%edge==edge-1) { //right side
                cout << "\nInvalid move, try again" << endl;
                return;
            }
            board[blank]=board[blank+1];
            board[blank+1]=0;
            break;
        }

        default :
            cout << "\nI don't know that command, try again." << endl;
            return;
        }
    }
    void putInPlace(unsigned);
};

void SliderPuzzle::display() {
    for(unsigned i=0; i<edge*edge;++i) {
        if(board[i]==0) cout << "    ";
        else cout << setw(3) << board[i] << ' ';
        if((i+1)%edge == 0)
            cout << endl;
    }
}

void SliderPuzzle::shuffle() {
    unsigned temp, next;
    for(int i = edge*edge-1; i > 0; i--) {
        next = rand()%i;
        temp = board[i];
        board[i]=board[next];
        board[next] = temp;
    }
}
//
void SliderPuzzle::putInPlace(unsigned block) {

    /// Find cell of block
    unsigned cellBlock = getBlock(board, block);
    /// Find cell of blank
    unsigned blank = getBlock(board, 0);
    ///move blank to column of block
    display(); cout << endl;
    cout << "\nblank is in column " << blank%edge
         << " and " << block << " in in column " << cellBlock%edge << endl;
    /// If the block is in the same row of the blank
    /// shift blank up
    if(blank/edge==cellBlock/edge) {
        ///cout << "\nSame row, so move:\n";
        ///display();
        if(blank/edge == 0) {
            doMove('s');
            blank += edge;
        }
        else {
            doMove('w');
            blank -= edge;
        }

        ///display();
    }
    ///While the blank is not in the same column as the block
    ///move the the blank in the right direction horizontally
    while(cellBlock%edge != blank%edge)  {
        if(cellBlock%edge > blank%edge) {
            doMove('d');
            ++blank;
        }
        else {
            doMove('a');
            --blank;
        }
        display();
        cout << endl;
        cin.clear();
        cin.get();
    }
    ///while the blank is not at the bottom, move it down
    while(blank/edge!=edge-1) {
        doMove('s');
        blank += edge;
    }
    ///now rotate
    int width = blank%edge;
    int height = edge-1;
    while(board[block-1]!=block) {
        for(int i = 0; i < width; ++i) {
            doMove('a');
            blank -= 1;
        }
        for(int i = 0; i < height; ++i) {
            doMove('w');
            blank -= edge;
        }
        for(int i = 0; i < width; ++i) {
            doMove('d');
            blank += 1;
        }
        for(int i = 0; i < height; ++i) {
            doMove('s');
            blank -= edge;
        }
        cout << "\nAfter rotating, " <<endl;
        display();
        cout << endl;
        cin.get();
    }
}

