/* ****
 * Juego de 21
 * ****/

#include <iostream>			// cout, cin
#include <vector>			// vector
#include <string>			// uso strings
#include <algorithm>		// random_shuffle
#include <ctime>			// Para usar time() - inicializar números aleatorios
#include <cstdlib>			// srand, rand

using namespace std;

/* ***
 * CLASE Carta 
 * *** */
class Carta {
	// Campos privados
	string palo,rango;
	bool visible;
	
	// Constructor
	public:
	    Carta(string,string);
	
	// Métodos
		void Voltear() {
			visible = !visible;
		}
		string Imprimir();
	    bool Visible() {
	    	return visible;
		}
		string Rango() {
			return rango;
		}
};

// Constructor
Carta::Carta(string palo,string rango) {
	this->palo = palo;
	this->rango = rango;
	visible = false;
}

// Imprimir
string Carta::Imprimir() {
	string x;
	x.append(rango).append(" de ").append(palo);
	return x;
}

/* ***
 * CLASE Baraja
 * ***/
class Baraja {
	// Campos
	private:
		vector <Carta> cartas;
	
	// Constructor
	public:
		Baraja();
		
	// Métodos
		Carta TomaCarta();
		void Barajear();
		void Imprimir();
		//void Partir(int);
		bool Vacia() {
			return cartas.empty();
		}
};

// Declaro arreglos útiles para inicializar
string rangos[] = {"As","2","3","4","5","6","7","8","9","10","Joto","Reina","Rey"};
string palos[] = {"Corazones","Diamantes","Treboles","Picas"};

// Constructor: llena la baraja con 52 cartas
Baraja::Baraja() {
	int i,j;
	
	for (i=0;i<4;i++) {
		for (j=0;j<13;j++) {
			Carta temp(palos[i],rangos[j]);
			cartas.push_back(temp);
		}
	}
}

// Imprimir: Imprime todas las cartas de la baraja
void Baraja::Imprimir() {
	int i;
	
	for (i=0;i<cartas.size();i++) {
		cout << cartas[i].Imprimir() << endl;
	}
}

// Genera números aleatorios (para uso con random_shuffle)
int myrandom (int i) { return rand()%i;}

// Barajear: barajea toda la baraja
void Baraja::Barajear() {
	random_shuffle(cartas.begin(),cartas.end(),myrandom);
}

// TomaCarta: Devuelve la última carta de la baraja
Carta Baraja::TomaCarta() {
	Carta x = cartas[cartas.size()-1];  		// x = la última carta
	cartas.pop_back();							// Elimino la última carta de la baraja
	return x;									// Devuelvo x
}

/* ***
 * CLASE: Jugador
 * ***/
class Jugador {
	// Campos
	private:
		vector<Carta> cartas;
		string nombre;
	
	// Métodos
	public:
		void AgregarCarta(Carta);
		// Devuelve el número de cartas que tengo en las manos
		int NumCartas() {
			return cartas.size();
		}
		int SumaCartas();
		string MuestraCartas();
		void AsignaNombre(string nombre) {
			this->nombre = nombre;
		}
		string MuestraNombre() {
			return nombre;
		}
};

// Agrega una carta a las que tengo en mis manos
void Jugador::AgregarCarta(Carta c) {
	cartas.push_back(c);
}

// Devuelve la suma de las cartas
int Jugador::SumaCartas() {
	int i,suma=0,temp;
	string r;
	
	for(i=0;i<cartas.size();i++) {
		r = cartas[i].Rango();
		if (r.compare("As")==0) {
			suma += 11;
		} else if (r.compare("Joto")==0 || r.compare("Reina")==0 || r.compare("Rey")==0) {
			suma += 10;
		} else {
			temp = stoi(r);		// Obtiene el entero que hay en la cadena del rango
			suma += temp;
		}
	}
	
	// Si me pasé de 21, veo si hubo As. Si hay, le resto 10
	if (suma > 21) {
		for(i=0;i<cartas.size();i++) {
			if (cartas[i].Rango().compare("As")==0) {
				suma -= 10;
			}
		}
	}
	
	return suma;
}

// Devuelve una cadena con las cartas del jugador
string Jugador::MuestraCartas() {
	string temp;
	int i;
	
	for (i=0;i<cartas.size();i++) {
		temp.append(cartas[i].Imprimir()).append("\n");
	}
	return temp;
}

int main() {
	// Declaro variables y objetos
	Baraja baraja;
	int i,j,noJugadores = 0;
	vector <Jugador> jugadores;
	string siono;
	
	// Preparo la baraja
	srand(time(NULL));
	baraja.Barajear();
	
	// Preguntar número de jugadores
	while (noJugadores < 2 || noJugadores > 4) {
		cout << "Numero de jugadores: ";
		cin >> noJugadores;
	}
	getline(cin,siono);
	
	// Pedir cartas
	Jugador x;
	for (i=0;i<noJugadores;i++) {		// Creo un vector de jugadores del tamaño adecuado
		cout << "Nombre: ";
		getline(cin,siono);
		x.AsignaNombre(siono);
		jugadores.push_back(x);
	}
	for (i=0;i<2;i++) {					// Se reparten 2 cartas a cada jugador
		for (j=0;j<noJugadores;j++) {	// Se reparten a todos los jugadores
			jugadores[j].AgregarCarta(baraja.TomaCarta());
		}
	}
	
	// Si alguien tiene 21 (ya ganó desde el inicio)
	for (i=0;i<noJugadores;i++) {
		if (jugadores[i].SumaCartas() == 21) {
			cout << "¡Ganó el jugador " << jugadores[i].MuestraNombre() 
				<< "! Sus cartas fueron:\n" << jugadores[i].MuestraCartas();
			return 1;
		}
	}
	
	// Ciclo de pedir cartas
	for (i=0;i<noJugadores;i++) {
		do {
			// Muestro las cartas del jugador y le pregunto si quiere otra
			cout << jugadores[i].MuestraNombre() << ", tus cartas son:\n";
			cout << jugadores[i].MuestraCartas() << endl;
			cout << "Jugador " << jugadores[i].MuestraNombre() <<
				", ¿Otra carta (S/N)? ";
			getline(cin,siono);
			if (siono[0] == 'S' || siono[0] == 's') {
				jugadores[i].AgregarCarta(baraja.TomaCarta());
				// Si perdió, elimino al jugador y decremento noJugadores
				if (jugadores[i].SumaCartas() > 21) {
					cout << "Jugador " << jugadores[i].MuestraNombre()
						<< " perdió. Sus cartas suman " << 
						jugadores[i].SumaCartas() << "\nY sus cartas son\n"
						<< jugadores[i].MuestraCartas() << endl;
					jugadores.erase(jugadores.begin()+i);
					noJugadores --;
				} else if (jugadores[i].NumCartas() == 5) {	// Si llega a tener 5 cartas y no se pasa, gana
					cout << jugadores[i].MuestraNombre() 
						<< ", ¡ganaste! Tienes 5 cartas y no te pasaste de 21. "
						<< "Tus cartas fueron:\n" << jugadores[i].MuestraCartas() << endl;
					return 1;
				} else if (jugadores[i].SumaCartas() == 21) {	// Si sus cartas suman 21, gana
					cout << jugadores[i].MuestraNombre() 
						<< ", ¡ganaste! Tus cartas suman 21. "
						<< "Tus cartas fueron:\n" << jugadores[i].MuestraCartas() << endl;
					return 1;
				}
			}
		} while (siono[0] != 'N' && siono[0] != 'n');
	}
	
	// Ver quien ganó (de los que quedan)
	j = 0;				// En j guardo el índice de quien ganó
	if (!jugadores.empty()) {
		for (i=1;i<noJugadores;i++) {
			if (jugadores[i].SumaCartas() > jugadores[j].SumaCartas()) {
				j = i;
			}
		}
		cout << "El ganador es " << jugadores[j].MuestraNombre() << 
			" y sus cartas fueron:\n" << jugadores[j].MuestraCartas() << endl;
	} else {
		cout << "Todos perdieron\n";
	}
}