//write minimal holdem api

class Card {
    constructor(suit, rank) {
        this.suit = suit;
        this.rank = rank;
    }
}

class Deck {
    constructor() {
        this.cards = [];
        const suits = ["♥", "♦", "♣", "♠"];
        const ranks = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"];

        for (const suit of suits) {
            for (const rank of ranks) {
                this.cards.push(new Card(suit, rank));
            }
        }

        this.shuffle();
    }

    shuffle() {
        const { cards } = this;
        let m = cards.length,
            i;

        while (m) {
            m--;
            const i = Math.floor(Math.random() * (m + 1));
            [cards[m], cards[i]] = [cards[i], cards[m]];
        }

        return this;
    }

    draw(num = 1) {
        return this.cards.splice(0, num);
    }
}

function dealCards(deck, numPlayers) {
    const playerHands = Array.from({ length: numPlayers }, () => []);

    for (let i = 0; i < 2; i++) {
        playerHands.forEach((hand) => hand.push(...deck.draw()));
    }

    return playerHands;
}

function dealFlop(deck) {
    deck.draw(); // Burn card
    return deck.draw(3); // Flop
}

function dealTurn(deck) {
    deck.draw(); // Burn card
    return deck.draw(); // Turn
}

function dealRiver(deck) {
    deck.draw(); // Burn card
    return deck.draw(); // River
}


//1. Check: If there is no bet in front of them, a player can pass their action to the next player without betting any chips.

//2. Bet: Players can choose to wager chips by placing them in the middle of the table in front of them.

//3. Raise: If a player bets before them, a player can raise that bet by betting more than the previous player.

//4. Call: A player matches the same amount of chips as a previous bettor to stay in the hand.

//5. Fold: Players can forfeit their hand and cards by discarding them face-down on top of each other.

//6. All-in: If a player does not have enough chips to match the current bet but still wants to play, they can go all-in and bet all their remaining chips.

///////////////////////////////////////////////////////////////////////////////////////////////////
function evaluateHand(hand) {
    const suits = {};
    const ranks = {};

    // Count the number of each suit and rank in the hand
    for (const card of hand) {
        suits[card.suit] = (suits[card.suit] || 0) + 1;
        ranks[card.rank] = (ranks[card.rank] || 0) + 1;
    }

    // Check for royal flush
    if (Object.values(suits).some((count) => count >= 5) && ["10", "J", "Q", "K", "A"].every((rank) => ranks[rank])) {
        return { rank: 10, tiebreaker:[] };
    }

    // Check for straight flush
    const straightFlushSuit = Object.keys(suits).find((suit) => suits[suit] >= 5 && isStraight(getCardsWithSuit(hand, suit)));
    if (straightFlushSuit) {
        return { rank: 9, tiebreaker: getHighestRank(getCardsWithSuit(hand, straightFlushSuit)) };
    }

    // Check for four of a kind
    const fourOfAKindRank = Object.keys(ranks).find((rank) => ranks[rank] === 4);
    if (fourOfAKindRank) {
        return { rank: 8, tiebreaker: getTiebreakersForFourOfAKind(fourOfAKindRank, hand) };
    }

    // Check for full house
    const fullHouseRanks = getFullHouseRanks(ranks);
    if (fullHouseRanks.length > 0) {
        return { rank: 7, tiebreaker: fullHouseRanks };
    }

    // Check for flush
    const flushSuit = Object.keys(suits).find((suit) => suits[suit] >= 5);
    if (flushSuit) {
        return { rank: 6, tiebreaker: getTiebreakersForFlush(getCardsWithSuit(hand, flushSuit)) };
    }

    // Check for straight
    if (isStraight(hand)) {
        return { rank: 5, tiebreaker: getHighestRank(hand) };
    }

    // Check for three of a kind
    const threeOfAKindRank = Object.keys(ranks).find((rank) => ranks[rank] === 3);
    if (threeOfAKindRank) {
        return { rank: 4, tiebreaker: getTiebreakersForThreeOfAKind(threeOfAKindRank, hand) };
    }

    // Check for two pair
    const twoPairRanks = getTwoPairRanks(ranks);
    if (twoPairRanks.length > 0) {
        return { rank: 3, tiebreaker: twoPairRanks };
    }

    // Check for pair
    const pairRank = Object.keys(ranks).find((rank) => ranks[rank] === 2);
    if (pairRank) {
        return { rank: 2, tiebreaker: getTiebreakersForPair(pairRank, hand) };
    }

    // Highest card
    return { rank: 1, tiebreaker: getHighestRank(hand) };
}

function isStraight(cards) {
    const values = cards.map((card) => (["J", "Q", "K", "A"].includes(card.rank) ? ["J", "Q", "K", "A"].indexOf(card.rank) + 11 : parseInt(card.rank)));
    const uniqueValues = new Set(values);

    if (uniqueValues.size < 5) {
        return false;
    }

    let sortedValues = [...uniqueValues].sort((a, b) => a - b);
    return sortedValues[sortedValues.length - 1] - sortedValues[0] === 4;
}

function getCardsWithSuit(cards, suit) {
    return cards.filter((card) => card.suit === suit);
}

function getHighestRank(cards) {
    const ranks = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"];

    for (const rank of ranks.reverse()) {
        if (cards.some((card) => card.rank === rank)) {
            return [rank];
        }
    }
}

function getTiebreakersForFourOfAKind(rank, hand) {
    const fourOfAKindCards = hand.filter((card) => card.rank === rank);
    const kickerCard = hand.find((card) => card.rank !== rank);
    return [rank, kickerCard.rank];
}

function getFullHouseRanks(ranks) {
    const threeOfAKindRank = Object.keys(ranks).find((rank) => ranks[rank] === 3);

    if (!threeOfAKindRank || Object.keys(ranks).length < 2) {
        return [];
    }

    delete ranks[threeOfAKindRank];

    const pairRank = Object.keys(ranks).find((rank) => ranks[rank] >= 2);

    if (!pairRank || Object.keys(ranks).length > 1 || (pairRank === threeOfAKindRank && ranks[pairRank] < 2)) {
        return [];
    }

    return [threeOfAKindRank, pairRank];
}

function getTiebreakersForFlush(cards) {
    return cards
        .map((card) => card.rank)
        .sort()
        .reverse();
}

function getTiebreakersForThreeOfAKind(rank, hand) {
    const threeOfAKindCards = hand.filter((card) => card.rank === rank);
    const kickerCards = hand.filter((card) => card.rank !== rank);
    return [rank, ...getHighestNRank(kickerCards, 2)];
}

function getTwoPairRanks(ranks) {
    const pairs = [];

    for (const rank of Object.keys(ranks)) {
        if (ranks[rank] >= 2) {
            pairs.push(rank);
        }
    }

    if (pairs.length < 2 || Object.keys(ranks).length > 2) {
        return [];
    }

    return [...new Set(pairs)].sort().reverse();
}

function getTiebreakersForPair(rank, hand) {
    const pairCards = hand.filter((card) => card.rank === rank);
    const kickerCards = hand.filter((card) => card.rank !== rank);
    return [rank, ...getHighestNRank(kickerCards, 3)];
}

function getHighestNRank(cards, n) {
    const ranks = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"];

    return ranks
        .reverse()
        .reduce((matches, rank) => (cards.some((card) => card.rank === rank && matches.indexOf(rank) < 0) ? [...matches, rank] : matches), [])
        .slice(0, n);
}


///////////////////////////////////////////////////////////////////////////////////////////////////

/*
function determineWinners(playerHands, sharedCards) {
	//playerHands = playerHands.filter(arr => arr.length > 0);
    const playerEvaluations = playerHands.map((hand) => evaluateHand(hand.concat(sharedCards)));

    let maxRank = null;
    const winners = [];
    let maxTiebreakers = [];

    for (const { rank, tiebreaker } of playerEvaluations) {
        if (maxRank === null || rank > maxRank) {
            maxRank = rank;
            maxTiebreakers = [tiebreaker];
        } else if (rank === maxRank) {
            maxTiebreakers.push(tiebreaker);
        }
    }

    for (let i = 0; i < playerEvaluations.length; i++) {
        const { rank, tiebreaker } = playerEvaluations[i];

        if (rank !== maxRank) {
            continue;
        }

        if (maxTiebreakers.every((maxTiebreaker) => compareTiebreakers(tiebreaker, maxTiebreaker))) {
            winners.push(i);
        }
    }

    return winners;
}
*/
function determineWinners(playerHands, sharedCards) {
    const activePlayers = playerHands.filter(hand => hand.length > 0);
    const playerEvaluations = activePlayers.map((hand) => evaluateHand(hand.concat(sharedCards)));

    let maxRank = null;
    const winners = [];
    let maxTiebreakers = [];

    for (const { rank, tiebreaker } of playerEvaluations) {
        if (maxRank === null || rank > maxRank) {
            maxRank = rank;
            maxTiebreakers = [tiebreaker];
        } else if (rank === maxRank) {
            maxTiebreakers.push(tiebreaker);
        }
    }

    for (let i = 0; i < activePlayers.length; i++) {
        const { rank, tiebreaker } = playerEvaluations[i];

        if (rank !== maxRank) {
            continue;
        }

        if (maxTiebreakers.every((maxTiebreaker) => compareTiebreakers(tiebreaker, maxTiebreaker))) {
            winners.push(playerHands.indexOf(activePlayers[i]));
        }
    }

    return winners;
}

function compareTiebreakers(a1, a2) {
    for (let i = 0; i < a1.length; i++) {
        if (a1[i] !== a2[i]) {
            return false;
        }
    }
    return true;
}


///////////////////////////////////////////////////////////////////////////////////////////////////function
function randomFold(playerHands)
{
	//return;
	for(var i=0;i<playerHands.length;i++)
		if(Math.random()<0.5)playerHands[i]=[];
		//if(Math.random()<0.5)playerHands[Math.floor(Math.random()*playerHands.length)]=[];

}
///////////////////////////////////////////////////////////////////////////////////////////////////

var stats={}
for(var i=0;i<10000;i++)
{
var deck1 = new Deck();
var numPlayers = 6;
var playerHands = dealCards(deck1, numPlayers);
//console.log(playerHands);
//betting
randomFold(playerHands)
var flop = dealFlop(deck1);
//console.log(flop);
//betting
randomFold(playerHands)
var turn = dealTurn(deck1);
//console.log(turn);
//betting
randomFold(playerHands)
var river = dealRiver(deck1);
//console.log(river);
//betting
randomFold(playerHands)
var winners = determineWinners(playerHands, flop.concat(turn, river));
//console.log(winners);
	winners.forEach((winner) => {
		 var playerId = "PLAYER"+winner;
		 if (stats[playerId]==null)stats[playerId]=0;
		 stats[playerId]++;
		 if (stats.TOTALWINS==null)stats.TOTALWINS=0;
		 stats.TOTALWINS++;
	});

}
stats.ROUNDS=i;
console.log(JSON.stringify(stats));document.getElementById("result").innerHTML+="\n\n"+outputStatsTable(stats);

///////////////////////////////////////////////////////////////////////////////////////////////////
var stats={}
for(var i=0;i<10000;i++)
{
var deck1 = new Deck();
var numPlayers = 2;
var playerHands = dealCards(deck1, numPlayers);
//console.log(playerHands);
//betting
randomFold(playerHands)
var flop = dealFlop(deck1);
//console.log(flop);
//betting
randomFold(playerHands)
var turn = dealTurn(deck1);
//console.log(turn);
//betting
randomFold(playerHands)
var river = dealRiver(deck1);
//console.log(river);
//betting
randomFold(playerHands)
var winners = determineWinners(playerHands, flop.concat(turn, river));
//console.log(winners);
	winners.forEach((winner) => {
		 var playerId = "PLAYER"+winner;
		 if (stats[playerId]==null)stats[playerId]=0;
		 stats[playerId]++;
		 if (stats.TOTALWINS==null)stats.TOTALWINS=0;
		 stats.TOTALWINS++;
	});

}
stats.ROUNDS=i;
console.log(JSON.stringify(stats));document.getElementById("result").innerHTML+="\n\n"+outputStatsTable(stats);
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
function outputStatsTable(stats) {
  const players = Object.keys(stats).filter(key => key.includes("PLAYER")).sort();

  let html = "<table border=1 style='font-size:inherit'>";

  // Output player rows
  for (let i = 0; i < players.length; i++) {
    html += "<tr>";
    html += `<td>${players[i]}</td>`;
    html += `<td>${stats[players[i]]}</td>`;
    html += "</tr>";
  }

  // Output other values column
  //html += "<tr><td colspan=2>Other Values:</td></tr>";

  const otherKeys = Object.keys(stats).filter(key => !key.includes("PLAYER"));

  for (let i = 0; i < otherKeys.length; i++) {
    html += "<tr>";
    html += `<td>${otherKeys[i]}</td>`;
    html += `<td>${stats[otherKeys[i]]}</td>`;
    html+="</tr>"
    
  }
  
  



html+= "</table>"
return(html)


}
//LET'S ADD PLAYER CLASS WITH CHIPS AND POT CLASS WHICH AWARDS TO WINNER AND /*BETTING*/ FUNCTION THAT ASKS POSSIBLE AVAILABLE WORD FROM PLAYER INSTEAD OF RANDOMFOLD
//OUTPUT NEW OR REPLACED CODE ONLY