#include <stdint.h>

#define SET_BIT(var, pos)      ((var) |= (1U << (pos)))
#define CLEAR_BIT(var, pos)    ((var) &= ~(1U << (pos)))
#define IS_BIT_SET(var, pos)   (((var) >> (pos)) & 1U)

#define PALO_ESPADA 0
#define PALO_BASTO  1
#define PALO_ORO    2
#define PALO_COPA   3

#define TOTAL_CARTAS 40
#define MAX_JUGADORES 6
#define MAX_CARTAS_MANO 3
#define MAX_BAZAS_RONDA 3

#define CARTA_INVALIDA (TOTAL_CARTAS + 1)
#define JUGADOR_INVALIDO (MAX_JUGADORES +1)
#define EQUIPO_INVALIDO 2

typedef struct{
    uint8_t palo;
    uint8_t valor_carta;
} Carta;

const Carta cartas[TOTAL_CARTAS] = {
    {PALO_ESPADA, 1},
    {PALO_BASTO,  1},
    {PALO_ESPADA, 7},
    {PALO_ORO,    7},

    {PALO_ESPADA, 3},
    {PALO_BASTO,  3},
    {PALO_ORO,    3},
    {PALO_COPA,   3},

    {PALO_ESPADA, 2},
    {PALO_BASTO,  2},
    {PALO_ORO,    2},
    {PALO_COPA,   2},

    {PALO_ORO,    1},
    {PALO_COPA,   1},

    {PALO_ESPADA, 12},
    {PALO_BASTO,  12},
    {PALO_ORO,    12},
    {PALO_COPA,   12},

    {PALO_ESPADA, 11},
    {PALO_BASTO,  11},
    {PALO_ORO,    11},
    {PALO_COPA,   11},

    {PALO_ESPADA, 10},
    {PALO_BASTO,  10},
    {PALO_ORO,    10},
    {PALO_COPA,   10},

    {PALO_BASTO,  7},
    {PALO_COPA,   7},

    {PALO_BASTO,  6},
    {PALO_ORO,    6},
    {PALO_COPA,   6},
    {PALO_ESPADA, 6},

    {PALO_BASTO,  5},
    {PALO_ORO,    5},
    {PALO_COPA,   5},
    {PALO_ESPADA, 5},

    {PALO_BASTO,  4},
    {PALO_ORO,    4},
    {PALO_COPA,   4},
    {PALO_ESPADA, 4},
};

/*Posibles estados de ronda:
    -------------------------------
    Envido
    Envido Envido
    Real Envido
    Falta Envido

    Truco
    Retruco
    Vale 4

    Flor
    Contraflor
    Contraflor al resto

    Parda
    Cantante <- EL del último canto hecho
    -------------------------------

    Con esto en mente puedo tener todo el estado en un int de 16 bits.
    El caso de la parda es obvio, 
    pero el del envido y flor es más útil, porque como estan separados y en secuencia,
    puedo ver cómo están encadenados por ejemplo 
    los envidos con solo iterar desde el bit 0 hasta el 3, lo cual me deja sumar puntos más facil, de hecho
    "no quiero" con encadenados es más elegante de lo que parece, porque es sumar el valor numérico de los bits 0-3 lol.
    -------------------------------

    hola soy yo de vuelta 1 semana después. creo que flashie cualquiera con lo de arriba.
*/

#define FLAG_ENVIDO 0
#define FLAG_ENVIDO_ENVIDO 1
#define FLAG_REAL_ENVIDO 2
#define FLAG_FALTA_ENVIDO 3

#define FLAG_TRUCO 4
#define FLAG_RETRUCO 5
#define FLAG_VALE_CUATRO 6

#define FLAG_FLOR 7
#define FLAG_CONTRAFLOR 8
#define FLAG_CONTRAFLOR_AL_RESTO 9

#define FLAG_PARDA 10
#define FLAG_TIENE_RETRUCO 11 /**<Tener retruco y lo que siga es tener retruco. */
#define FLAG_TIENE_MAYOR_TANTO 12
#define FLAG_ENVIDO_CANTADO 13
#define FLAG_TRUCO_INTERRUMPIDO 14


#define ENVIDO_PUNTOS 2
#define REAL_ENVIDO_PUNTOS 3

#define CANTO_QUIERO FLAG_CONTRAFLOR_AL_RESTO + 1 /**< Último canto en las flags +1 */
#define CANTO_NO_QUIERO CANTO_QUIERO + 1 

#define CANTO_SON_BUENAS CANTO_NO_QUIERO + 1

#define CANTO_INVALIDO CANTO_SON_BUENAS + 1 /**< Último canto +1 */

/*
    Envido:
    - Se pueden cantar todas las variantes excepto "envido envido",
    que solo se le responde al envido.
    - Sólo se puede cantar en la primera baza
    - No se puede cantar si se está en truco
    - Si se dice "no quiero", el envidador gana 1 punto por el envido no querido.
    Pero si hay envidos encadenados, el envidador se lleva los puntos apostados hasta ahora
    Ejemplo:
    Envido (+2) -> Envido (+2) -> No quiero = 2 puntos (se anula el último +2)
    Envido (+2) -> Real Envido (+3) -> No quiero = 2 puntos (se anula el último +3)
    Envido (+2) -> Real Envido (+3) -> Falta Envido (0) -> No quiero = 5 puntos 
    (no se puede cantar más de la falta así que digamos por ahora que es 0, aunque si se pudiese
    cantar algo como un super mega falta envido, entonces tal vez sería +puntaje que faltaba al
    jugador con más puntos para ganar? me inclino a creer que no porque cuando se canta la falta
    y uno gana, simplemente gana los puntos que le faltan al jugador con mas puntos, así que supongo
    que sería solo eso.)
    Envido (+2) -> Envido (+2) -> Real Envido (+3) -> Falta Envido (0) -> NQ = 7 puntos.
    - Si un jugador dice "truco" en primera baza, 
    se le puede reclamar "envido va primero" para cantar envido "fuera de turno" 
    A MENOS QUE el que cante sea el último jugador de la baza (pie).
    - Cuando se acepta el envido, empieza una ronda en la que se dicen los puntajes empezando 
    desde el jugador mano, quien tiene que anunciar su puntaje obligatoriamente (no puede decir son buenas
    ya que no hay comparación aún). 
    - Los jugadores siguientes pueden anunciar su puntaje si tienen más puntos de envido, 
    o deben decir son buenas si tienen menos (o si simplemente no quieren decir
    cuantos puntos de envido tienen realmente, por lo que el "son buenas" siempre es una opción
    para todos menos el mano).
    - Al sumar los puntos de envido (tantos), se suman a partir de tanto 
    las cartas jugadas como las que se tengan en mano.
    - Si se tienen dos o más cartas del mismo palo, el envido tiene 20 puntos de base. 
    Si la carta tiene un valor de entre 10 y 12, suma 0 puntos.
    - Si el jugador declara sus tantos, debe ser su mayor puntaje.
*/


uint8_t seed = 12;

#define ESTADO_PARTIDA_JUGANDO_BAZA 0
#define ESTADO_PARTIDA_EN_CANTO 1
#define ESTADO_PARTIDA_RONDA_ENVIDOS 2

typedef struct{
    uint8_t jugadores_cartas[MAX_JUGADORES][MAX_CARTAS_MANO];
    uint8_t cartas_bazas[MAX_BAZAS_RONDA][MAX_JUGADORES];

    uint8_t equipos_puntos[2];

    uint16_t flags_ronda;

    uint8_t max_puntos;
    uint8_t ronda_estado;

    uint8_t canto_ofrecido;
    uint8_t jugador_turno_canto;

    uint8_t mayor_envido;

    uint8_t numero_jugadores;

    uint8_t jugador_mano;
    uint8_t jugador_turno_actual;
    uint8_t jugador_inicial_baza;

    uint8_t baza_actual;
    uint8_t baza_puntos;
} EstadoPartida;


#define EVENTO_CARTA_JUGADA 0
#define EVENTO_NUEVA_BAZA 1
#define EVENTO_RONDA_TERMINADA 2
#define EVENTO_VICTORIA 3
#define EVENTO_CANTO 4
#define EVENTO_TANTOS_CANTADOS 5

typedef struct{
    uint8_t eventos; /**<Flag de eventos. De los de arriba */
    // uint8_t jugador;/**<Índice del jugador causador del evento*/

    uint8_t carta; /**<Carta jugada*/

    uint8_t canto; /**<Canto hecho */

    uint8_t ganador_baza; /**<Ganador de la baza */

    uint8_t equipo_ganador; /**<Ganador de lo que sea. */
    uint8_t puntos_ganados; /**<Cantidad de puntos que ganó equipo_ganador */

    uint8_t tantos_cantados; /**< Tantos de la ronda de envidos*/

    // uint8_t siguiente_jugador;
} ResultadoJugada;

uint8_t rand8() {
    seed ^= (uint8_t)(seed << 7);
    seed ^= (uint8_t)(seed >> 5);
    seed ^= (uint8_t)(seed << 3);
    return seed;
}

void pasar_turno_actual(EstadoPartida *partida){
    partida->jugador_turno_actual += 1;
    if(partida->jugador_turno_actual >= partida->numero_jugadores) partida->jugador_turno_actual = 0;
}

void repartir(EstadoPartida *partida){
    uint64_t usadas = 0;

    for(uint8_t jugador_i = 0; jugador_i < partida->numero_jugadores; jugador_i++){
        for(uint8_t carta_i = 0; carta_i < MAX_CARTAS_MANO; carta_i++){
            uint8_t indice;

            do {indice = rand8() % TOTAL_CARTAS;} while(usadas & (1ULL << indice));

            usadas |= (1ULL << indice);

            partida->jugadores_cartas[jugador_i][carta_i] = indice;
        }
    }
}

void limpiar_ronda(EstadoPartida *partida){
    partida->baza_actual = 0;
    partida->baza_puntos = 0;
    //resetear todas las flags
    partida->flags_ronda = 0;

    partida->canto_ofrecido = CANTO_INVALIDO;
    partida->jugador_turno_canto = JUGADOR_INVALIDO;
    partida->ronda_estado = ESTADO_PARTIDA_JUGANDO_BAZA;
    partida->mayor_envido = 0;

    for(uint8_t b = 0; b < MAX_BAZAS_RONDA; b++){
        for(uint8_t j = 0; j < MAX_JUGADORES; j++){
            partida->cartas_bazas[b][j] = CARTA_INVALIDA;
        }
    }

    for(uint8_t j = 0; j < MAX_JUGADORES; j++){
        for(uint8_t c = 0; c < MAX_CARTAS_MANO; c++){
            partida->jugadores_cartas[j][c] = CARTA_INVALIDA;
        }
    }
}

void iniciar_ronda(EstadoPartida *partida){
    limpiar_ronda(partida);

    partida->jugador_turno_actual = partida->jugador_mano;
    partida->jugador_inicial_baza = partida->jugador_mano;

    repartir(partida);
}

uint8_t iniciar_partida(EstadoPartida *partida, uint8_t jugadores, uint8_t max_puntos){
    //rechazar jugadores impar, 0, 1 (redundante pero porque por ahi despues meto gallo) o mayores
    //a MAX_JUGADORES.
    if(jugadores <= 1 || jugadores > MAX_JUGADORES || jugadores % 2 != 0) return 0;

    partida->numero_jugadores = jugadores;
    partida->max_puntos = max_puntos;
    for(uint8_t i = 0; i < 2; i++) partida->equipos_puntos[i] = 0;

    partida->jugador_mano = 0;
    partida->jugador_turno_actual = 0;
    partida->jugador_inicial_baza = 0;

    limpiar_ronda(partida);
    iniciar_ronda(partida);
    return 1;
}


void nueva_baza(EstadoPartida *partida, uint8_t jugador_inicial){
    partida->jugador_inicial_baza = jugador_inicial;
    partida->jugador_turno_actual = partida->jugador_inicial_baza;
    partida->baza_actual++;
}

void alterar_resultado_jugada_si_puntuador_gano(EstadoPartida *partida, ResultadoJugada *resultado_jugada, uint8_t equipo_puntuador_i){
    if(partida->equipos_puntos[equipo_puntuador_i] < partida->max_puntos) return;
    SET_BIT(resultado_jugada->eventos, EVENTO_VICTORIA);
}

void sumar_puntos_y_manejar_victoria(EstadoPartida *partida, ResultadoJugada *resultado_jugada, uint8_t equipo_puntuador_i, uint8_t puntos){
    partida->equipos_puntos[equipo_puntuador_i] += puntos;
    alterar_resultado_jugada_si_puntuador_gano(partida, resultado_jugada, equipo_puntuador_i);
}

void terminar_ronda(EstadoPartida *partida, ResultadoJugada *resultado_jugada, uint8_t equipo_ganador_i){

    resultado_jugada->equipo_ganador = equipo_ganador_i;
    SET_BIT(resultado_jugada->eventos, EVENTO_RONDA_TERMINADA);

    //le damos un punto al ganador por ganar la ronda, y luego
    //iteramos sobre los bits que le corresponde a cada truco cantado
    //desde el mayor canto posible hasta el menor (vale cuatro a truco normal).
    //si encontramos un bit seteado, el equipo gana los puntos de:
    //i - FLAG_TRUCO + 1. 
    //supongamos que la ronda terminó en retruco, osea, tengo 011. 
    //este for se va a detener en el 0[1]1 y le va a sumar a el/los ganadores 2. (5 - 4 + 1 = 2) y romper.
    //Teniendo en cuenta que ya le dimos un punto por ganar, el jugador se llevó 3 puntos,
    //osea, el valor de un retruco

    partida->equipos_puntos[equipo_ganador_i] += 1;

    
    for(uint8_t i = FLAG_VALE_CUATRO; i >= FLAG_TRUCO; i--){
        if(!IS_BIT_SET(partida->flags_ronda, i)) continue;

        partida->equipos_puntos[equipo_ganador_i] += (i - FLAG_TRUCO + 1);
        break;
    }

    alterar_resultado_jugada_si_puntuador_gano(partida, resultado_jugada, equipo_ganador_i);

    if(IS_BIT_SET(resultado_jugada->eventos, EVENTO_VICTORIA)) return;

    partida->jugador_mano++;
    if(partida->jugador_mano == partida->numero_jugadores) partida->jugador_mano = 0;
    iniciar_ronda(partida);
}

ResultadoJugada jugar_baza(EstadoPartida *partida, uint8_t jugador_i, uint8_t carta_mano_i){

    ResultadoJugada resultado = {
        .carta = CARTA_INVALIDA,
        .ganador_baza = JUGADOR_INVALIDO,
        .equipo_ganador = EQUIPO_INVALIDO,
    };


    //si el jugador o la carta son invalidos o si no se puede jugar, salir
    if(jugador_i >= partida->numero_jugadores || carta_mano_i >= MAX_CARTAS_MANO ||
    partida->ronda_estado != ESTADO_PARTIDA_JUGANDO_BAZA) return resultado;

    uint8_t carta_i = partida->jugadores_cartas[jugador_i][carta_mano_i];

    if(partida->jugador_turno_actual != jugador_i || 
    jugador_i >= partida->numero_jugadores || 
    carta_i == CARTA_INVALIDA) return resultado;

    resultado.carta = carta_i;

    //ya pasó la verificación. todo ok.
    SET_BIT(resultado.eventos, EVENTO_CARTA_JUGADA);
    //poner la carta en la mesa en la posición del jugador y sacarsela.
    partida->cartas_bazas[partida->baza_actual][jugador_i] = carta_i;
    partida->jugadores_cartas[jugador_i][carta_mano_i] = CARTA_INVALIDA;

    /*
        ahora es turno del siguiente jugador. si llegamos al máximo de jugadores de la partida,
        empezamos por el primer jugador de la mesa, pero si es el del primer jugador que jugó,
        eso significa que terminó la baza.
    */
    pasar_turno_actual(partida);

    if(partida->jugador_turno_actual == partida->jugador_inicial_baza){
        //evaluar y terminar baza/ronda/partida
        uint8_t mayor_carta_i = CARTA_INVALIDA;
        uint8_t ganador_baza = JUGADOR_INVALIDO;
        
        for(uint8_t i = 0; i < partida->numero_jugadores; i++){
            uint8_t carta_actual = partida->cartas_bazas[partida->baza_actual][i];
            if(carta_actual < mayor_carta_i){
                mayor_carta_i = carta_actual;
                ganador_baza = i;
            } 
        }

        // revisamos que no sea parda. 
        // si la carta es una de las cuatro primeras, obvio que no es ya que tienen valores de "fuerza" únicos.
        // de lo contrario, me fijo si hay una carta del mismo valor
        // numérico que la "ganadora". si la hay, (y es de alguien de otro equipo), es parda.
        if(mayor_carta_i > 3){
            for(uint8_t i = 0; i < partida->numero_jugadores; i++){
                uint8_t carta_actual = partida->cartas_bazas[partida->baza_actual][i];

                if(carta_actual != CARTA_INVALIDA && 
                    cartas[carta_actual].valor_carta == cartas[mayor_carta_i].valor_carta &&
                   (i % 2 != ganador_baza % 2)){
                    mayor_carta_i = CARTA_INVALIDA;
                    break;
                }
            }
        }


        uint8_t baza_no_es_parda = mayor_carta_i != CARTA_INVALIDA;
        //pasar de baza o terminar ronda
        if(partida->baza_actual < MAX_BAZAS_RONDA - 1){

            uint8_t es_primera_baza = partida->baza_actual == 0;
            uint8_t jugador_siguiente_baza = ganador_baza;
            uint8_t equipo_ganador = ganador_baza % 2;

            if(baza_no_es_parda){
                //caso normal. hay carta ganadora.

                //solo si el ganador es el equipo 1 seteamos el bit de la baza actual.
                if(equipo_ganador == 1) partida->baza_puntos |= (1 << partida->baza_actual);
                resultado.ganador_baza = ganador_baza;
                resultado.equipo_ganador = equipo_ganador;

                //si es la primera baza o
                //si las últimas 2 bazas están intercaladas (osea no gano nadie la ronda) y
                //si se esta en parda y la ronda actual se pardó,
                //jugar nueva baza. de lo contrario, terminar la ronda.
                if((es_primera_baza ||
                    IS_BIT_SET(partida->baza_puntos, partida->baza_actual-1) != IS_BIT_SET(partida->baza_puntos, partida->baza_actual)) &&
                    !IS_BIT_SET(partida->flags_ronda, FLAG_PARDA)){
                        nueva_baza(partida, jugador_siguiente_baza);
                    } 
                else terminar_ronda(partida, &resultado, equipo_ganador);
            }
            else{
                //si se parda en primera o si ya hay parda, mano empieza la siguiente baza
                //de lo contrario, quien tenga primera ganó la ronda.
                if(es_primera_baza || IS_BIT_SET(partida->flags_ronda, FLAG_PARDA)){
                    SET_BIT(partida->flags_ronda, FLAG_PARDA);
                    nueva_baza(partida, partida->jugador_mano);
                } 
                else terminar_ronda(partida, &resultado, IS_BIT_SET(partida->baza_puntos, 0));
            }

        }else{
            uint8_t equipo_ganador = ganador_baza % 2;

            //como es la última, si se viene pardando hasta la tercera y pie no tiene primera, equipo mano ganó la ronda
            if(!baza_no_es_parda){
                //si el equipo del jugador mano no es igual al bit de la primera basa,
                //el equipo contrario al mano ganó.
                equipo_ganador = (partida->jugador_mano % 2) != IS_BIT_SET(partida->baza_puntos, 0) 
                ? partida->jugador_mano % 2
                : !(partida->jugador_mano % 2);
            }

            terminar_ronda(partida, &resultado, equipo_ganador);
        }
    }
    
    return resultado;
}

void sumar_puntos_cantos_envido_y_manejar_victoria(EstadoPartida *partida, ResultadoJugada *resultado_jugada, uint8_t equipo_i){
    uint8_t puntos = 0;

    if(IS_BIT_SET(partida->flags_ronda, FLAG_ENVIDO)) puntos += 2;
    if(IS_BIT_SET(partida->flags_ronda, FLAG_ENVIDO_ENVIDO)) puntos += 2;
    if(IS_BIT_SET(partida->flags_ronda, FLAG_REAL_ENVIDO)) puntos += 3;

    sumar_puntos_y_manejar_victoria(partida, resultado_jugada, equipo_i, puntos);
}

uint8_t valor_tanto_carta(uint8_t carta_i){
    if(cartas[carta_i].valor_carta >= 10 && cartas[carta_i].valor_carta <= 12) return 0;
    return cartas[carta_i].valor_carta;
}

uint8_t calcular_envido(EstadoPartida *partida, uint8_t jugador_i){
    uint8_t max_envido = 0;

    for(uint8_t i = 0; i < MAX_CARTAS_MANO; i++){
        uint8_t carta_i = partida->jugadores_cartas[jugador_i][i];
        if(carta_i == CARTA_INVALIDA) carta_i = partida->cartas_bazas[0][jugador_i];
        
        for(uint8_t j = 0; j < MAX_CARTAS_MANO; j++){
            if(j == i) continue;
            uint8_t carta_j = partida->jugadores_cartas[jugador_i][j];
            if(carta_j == CARTA_INVALIDA) carta_j = partida->cartas_bazas[0][jugador_i];

            if(cartas[carta_i].palo == cartas[carta_j].palo){
                uint8_t valor_envido_pares = 20 + valor_tanto_carta(carta_i) + valor_tanto_carta(carta_j);
                if(valor_envido_pares > max_envido) max_envido = valor_envido_pares;
            }
        }

        if(valor_tanto_carta(carta_i) > max_envido) max_envido = valor_tanto_carta(carta_i);
    }

    return max_envido;
}

void avanzar_o_terminar_ronda_envido(EstadoPartida *partida, ResultadoJugada *resultado_jugada){
    partida->jugador_turno_canto += 1;
    if(partida->jugador_turno_canto >= partida->numero_jugadores) partida->jugador_turno_canto = 0;

    //caso de fin
    if(partida->jugador_turno_canto == partida->jugador_mano){
        uint8_t equipo_ganador_envido_i = IS_BIT_SET(partida->flags_ronda, FLAG_TIENE_MAYOR_TANTO);
        partida->ronda_estado = ESTADO_PARTIDA_JUGANDO_BAZA;
        partida->jugador_turno_canto = JUGADOR_INVALIDO;

        //en caso de que haya falta, sumamos al equipo de mayor envido lo que le falta al puntero para ganar.
        //de lo contrario, sumamos los puntos de los envidos de forma acumulativa.
        if(IS_BIT_SET(partida->flags_ronda, FLAG_FALTA_ENVIDO)){
            uint8_t equipo_puntero = partida->equipos_puntos[1] > partida->equipos_puntos[0];

            sumar_puntos_y_manejar_victoria(partida, resultado_jugada, equipo_ganador_envido_i, 
            partida->max_puntos - partida->equipos_puntos[equipo_puntero]);
        }else sumar_puntos_cantos_envido_y_manejar_victoria(partida, resultado_jugada, equipo_ganador_envido_i);
    }
}

ResultadoJugada cantar_tanto(EstadoPartida *partida, uint8_t jugador_i, uint8_t tantos){

    ResultadoJugada resultado = {
        .canto = CANTO_INVALIDO,
    };

    if(partida->ronda_estado != ESTADO_PARTIDA_RONDA_ENVIDOS || !partida->jugador_turno_canto == jugador_i) return resultado;

    uint8_t jugador_tantos = calcular_envido(partida, jugador_i);

    if(jugador_tantos != tantos || partida->mayor_envido > jugador_tantos) return resultado;

    if(jugador_tantos == partida->mayor_envido && ((jugador_i % 2) != (partida->jugador_mano % 2))) return resultado;

    partida->mayor_envido = jugador_tantos;

    SET_BIT(resultado.eventos, EVENTO_TANTOS_CANTADOS);
    resultado.tantos_cantados = jugador_tantos;

    if(jugador_i % 2) SET_BIT(partida->flags_ronda, FLAG_TIENE_MAYOR_TANTO);
    else CLEAR_BIT(partida->flags_ronda, FLAG_TIENE_MAYOR_TANTO);

    avanzar_o_terminar_ronda_envido(partida, &resultado);

    return resultado;
}


//Se puede abstraer en un if else gigante para no repetir unas 12 lineas?
ResultadoJugada cantar(EstadoPartida *partida, uint8_t jugador_i, uint8_t canto_i){

    ResultadoJugada resultado = {
        .canto = CANTO_INVALIDO,
    };

    if((partida->jugador_turno_canto != JUGADOR_INVALIDO && jugador_i != partida->jugador_turno_canto) ||
    partida->canto_ofrecido == canto_i || canto_i >= CANTO_INVALIDO) return resultado;

    //ESTO TAMBIEN PUEDE USARSE PARA LA FLOR?
    if(partida->ronda_estado == ESTADO_PARTIDA_RONDA_ENVIDOS){
        if(canto_i != CANTO_SON_BUENAS ||
        partida->mayor_envido == 0) return resultado;

        avanzar_o_terminar_ronda_envido(partida, &resultado);
        return resultado;
    }

    uint8_t canto_es_truco = canto_i >= FLAG_TRUCO && canto_i <= FLAG_VALE_CUATRO;
    uint8_t canto_es_envido = canto_i >= FLAG_ENVIDO && canto_i <= FLAG_FALTA_ENVIDO;
    uint8_t canto_es_flor = canto_i >= FLAG_FLOR && canto_i <= FLAG_CONTRAFLOR_AL_RESTO;

    if(canto_i == CANTO_QUIERO){
        if(partida->canto_ofrecido == CANTO_INVALIDO) return resultado;
        //se setea el bit del canto ofrecido
        SET_BIT(partida->flags_ronda, partida->canto_ofrecido);

        if(partida->canto_ofrecido >= FLAG_ENVIDO || partida->canto_ofrecido <= FLAG_FALTA_ENVIDO){
            partida->ronda_estado = ESTADO_PARTIDA_RONDA_ENVIDOS;
            partida->jugador_turno_canto = partida->jugador_mano;
            SET_BIT(partida->flags_ronda, FLAG_ENVIDO_CANTADO);
        }else{
            partida->jugador_turno_canto = JUGADOR_INVALIDO;
            partida->ronda_estado = ESTADO_PARTIDA_JUGANDO_BAZA;
        }
        //reseteo el canto ofrecido
        //estas
        partida->canto_ofrecido = CANTO_INVALIDO;
        resultado.canto = CANTO_QUIERO;
        SET_BIT(resultado.eventos, EVENTO_CANTO);

        return resultado;
    }

    if(canto_i == CANTO_NO_QUIERO){
        if(partida->canto_ofrecido == CANTO_INVALIDO) return resultado;
        uint8_t equipo_contrario_jugador_i = !(jugador_i % 2);
        //si se le dice no quiero al truco, gana la ronda el equipo contrario
        if(partida->canto_ofrecido >= FLAG_TRUCO && partida->canto_ofrecido <= FLAG_VALE_CUATRO) terminar_ronda(partida, &resultado, equipo_contrario_jugador_i);
        else if(partida->canto_ofrecido >= FLAG_ENVIDO && partida->canto_ofrecido <= FLAG_FALTA_ENVIDO) SET_BIT(partida->flags_ronda, FLAG_ENVIDO_CANTADO);
        else{
            partida->ronda_estado = ESTADO_PARTIDA_JUGANDO_BAZA;
            if(canto_es_envido) sumar_puntos_cantos_envido_y_manejar_victoria(partida, &resultado, equipo_contrario_jugador_i);
        }

        //estas
        partida->jugador_turno_canto = JUGADOR_INVALIDO;
        partida->canto_ofrecido = CANTO_INVALIDO;
        resultado.canto = CANTO_NO_QUIERO;
        SET_BIT(resultado.eventos, EVENTO_CANTO);
        return resultado;
    }

    //si el canto es flag y ya está seteado, escapar. ahora manejamos los cantos que son cantos y flags a la vez.
    if(IS_BIT_SET(partida->flags_ronda, canto_i)) return resultado;

    uint8_t canto_es_envido_o_flor = canto_es_envido || canto_es_flor;
    uint8_t canto_es_envido_o_flor_al_truco = (partida->canto_ofrecido == FLAG_TRUCO && canto_es_envido_o_flor);

    //si se canta envido o flor al truco, me fijo si el jugador que contestó no es el mano, o
    //si tengo envido o flor y mi canto es menor que el ofrecido, escapo.
    if((canto_es_envido_o_flor_al_truco && jugador_i == partida->jugador_mano) ||
    ((canto_i == CANTO_INVALIDO) && (canto_es_envido_o_flor && canto_i <= partida->canto_ofrecido))) return resultado;

    if(canto_es_truco){
        //si el canto no es el primer truco y no se está retrucando,
        //si el canto no es el siguiente en la cadena o 
        //el equipo que cantó no tiene el retruco, escapo.
        if(canto_i != FLAG_TRUCO){
            if((!IS_BIT_SET(partida->flags_ronda, canto_i-1) && partida->canto_ofrecido != canto_i-1) ||
            (IS_BIT_SET(partida->flags_ronda, FLAG_TIENE_RETRUCO) != (jugador_i % 2))) return resultado;
        }
        
        //le doy el retruco al equipo contrario
       if(!(jugador_i % 2)) SET_BIT(partida->flags_ronda, FLAG_TIENE_RETRUCO);
       else CLEAR_BIT(partida->flags_ronda, FLAG_TIENE_RETRUCO);
    }else if(canto_es_envido){
        if(IS_BIT_SET(partida->flags_ronda, FLAG_ENVIDO_CANTADO) || 
        (canto_i == FLAG_ENVIDO_ENVIDO && partida->canto_ofrecido != FLAG_ENVIDO)) return resultado;
    }else{
        //por si me mandan un canto no implementado xd
        return resultado;
    }

    if(partida->jugador_turno_canto == JUGADOR_INVALIDO || partida->jugador_turno_canto == partida->jugador_turno_actual){
        partida->jugador_turno_canto = partida->jugador_turno_actual + 1;
        if(partida->jugador_turno_canto >= partida->numero_jugadores) partida->jugador_turno_canto = 0;
    }else partida->jugador_turno_canto = partida->jugador_turno_actual;
    
    partida->ronda_estado = ESTADO_PARTIDA_EN_CANTO;

    //setear el canto anterior si se está respondiendo a un canto a menos que sea envido o flor al truco
    if(partida->canto_ofrecido != CANTO_INVALIDO) SET_BIT(partida->flags_ronda, partida->canto_ofrecido);
    else if(canto_es_envido_o_flor_al_truco) SET_BIT(partida->flags_ronda, FLAG_TRUCO_INTERRUMPIDO);
    
    //mierda estas
    partida->canto_ofrecido = canto_i;
    resultado.canto = canto_i;
    SET_BIT(resultado.eventos, EVENTO_CANTO);

    return resultado;
}