From 789e05e5f62722532c27964a8512f9d88f25da05 Mon Sep 17 00:00:00 2001 From: raeleus Date: Sun, 10 Nov 2024 22:54:18 -0800 Subject: [PATCH] The stragedy AI is smarter than it ever was before. --- Library.js | 195 ++++++++++++++++++++++++++++++++--------------------- Output.js | 4 +- 2 files changed, 122 insertions(+), 77 deletions(-) diff --git a/Library.js b/Library.js index 1d98ec6..e3ff0f9 100644 --- a/Library.js +++ b/Library.js @@ -3025,46 +3025,39 @@ function stragedyEnemyTurn() { var has3 = hand.filter(x => /^.*3.*$/gi.test(x)).length > 0 var has2 = hand.filter(x => /^.*2.*$/gi.test(x)).length > 0 + var faceCardHandCount = hand.filter(x => /.*\D.*/gi.test(x)).length + var kingNumberedCardsInHand = [] for (var card of hand) { if (kingCards.has(card)) kingNumberedCardsInHand.push(card) } kingNumberedCardsInHand.sort((a, b) => parseInt(a) - parseInt(b)) - var aceNumberCards = [] - var aceNumberSet = new Set() - for (var myCard of hand) { - for (var card of playerHand) { - if (myCard == card) { - aceNumberCards.push(card) - aceNumberSet.add(card) - } + var bestAceCard = null + var bestAceCardTotal = 0 + for (var card of battlefield) { + var number = card.replaceAll(/\D/gi, "") + var playerTotal = 0 + for (var playerCard of playerBattlefield) { + var playerNumber = card.replaceAll(/\D/gi, "") + if (playerNumber == number) playerTotal += parseInt(playerNumber) } + if (playerTotal > bestAceCardTotal) bestAceCard = card } - var bestAceNumber = aceNumberSet.length > 0 ? aceNumberSet[0] : null - var bestAceTotal - for (var aceCard of aceNumberCards) { - var total = 0 - if (aceCard == bestAceNumber) total += parseInt(aceCard) - bestAceTotal = total - } - for (var card of aceNumberSet) { - for (var aceCard of aceNumberCards) { - var total = 0 - if (aceCard == card) total += parseInt(aceCard) - if (total > bestAceTotal) { - bestAceTotal = total - bestAceNumber = card - } + + var bestKingCardToBustPlayer = null + for (var playerCard of playerBattlefield) { + for (var card of battlefield) { + var playerNumber = playerCard.replaceAll(/\D/gi, "") + var number = card.replaceAll(/\D/gi, "") + if (number == playerNumber && (bestKingCardToBustPlayer == null || parseInt(number) > parseInt(bestKingCardToBustPlayer))) bestKingCardToBustPlayer = card } } - var bestKingCard = null - for (var playerCard of playerBattlefield) { - for (var card of hand) { - var playerNumber = playerCard.replaceAll(/\D/gi, "") - if (card == playerNumber && kingCards.has(playerNumber) && (bestKingCard == null || parseInt(card) > parseInt(bestKingCard))) bestKingCard = card - } + var bestKingCardToSave = null + for (var card of battlefield) { + var number = card.replaceAll(/\D/gi, "") + if ((bestKingCardToSave == null || parseInt(number) > parseInt(bestKingCardToSave)) && parseInt(number) + score < 30) bestKingCardToSave = card } if (hand.length == 0) { @@ -3075,8 +3068,9 @@ function stragedyEnemyTurn() { log(2) if (hasJoker) state.stragedyEnemyTurnText = stragedyPlayCard(false, "?" + lowestNumberedBattlefieldCard) else if (hasQueen && highestNumberedBattlefieldCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "q" + highestNumberedBattlefieldCard) - else if (hasAce && bestAceNumber != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "a" + bestAceNumber) - else if (hasKing && bestKingCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "k" + bestKingCard) + else if (hasAce && bestAceCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "a" + bestAceCard) + else if (hasKing && bestKingCardToBustPlayer != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "k" + bestKingCardToBustPlayer) + else if (hasKing && bestKingCardToSave != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "k" + bestKingCardToSave) else if (highestNumberedHandCardAddUpTo30 != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, highestNumberedHandCardAddUpTo30) else state.stragedyEnemyTurnText = stragedyEnemyRetire() } else if (playerRetired && score > playerScore) { @@ -3093,31 +3087,38 @@ function stragedyEnemyTurn() { log(6) if (score < 20 && highestNumberedHandCardAddUpTo20 != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, highestNumberedHandCardAddUpTo20) else if (highestNumberedHandCardAddUpTo30 != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, highestNumberedHandCardAddUpTo30) + else if (faceCardHandCount > 1 && hasAce && bestAceCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "a" + bestAceCard) + else if (faceCardHandCount > 1 && hasKing && bestKingCardToBustPlayer != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "k" + bestKingCardToBustPlayer) + else if (faceCardHandCount > 1 && hasQueen && highestNumberedBattlefieldCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "q" + highestNumberedBattlefieldCard) else if (deck.length > 0) state.stragedyEnemyTurnText = stragedyEnemyDiscardCard() else if (hasQueen && highestNumberedBattlefieldCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "q" + highestNumberedBattlefieldCard) - else if (hasAce && bestAceNumber != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "a" + bestAceNumber) - else if (hasKing && bestKingCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "k" + bestKingCard) + else if (hasAce && bestAceCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "a" + bestAceCard) + else if (hasKing && bestKingCardToBustPlayer != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "k" + bestKingCardToBustPlayer) else stragedyEnemyRandom() } else if (score > 30 && battlefield.length > 0) { log(7) - if (hasQueen && highestNumberedBattlefieldCard != null) { - state.stragedyEnemyTurnText = stragedyPlayCard(false, "q" + highestNumberedBattlefieldCard) - } else if (hasPriest && highestNumberedBattlefieldCard != null) { - state.stragedyEnemyTurnText = stragedyPlayCard(false, "p" + highestNumberedBattlefieldCard) - } else if (hasJack && highestNumberedBattlefieldCardSubtractDownTo30 != null) { - state.stragedyEnemyTurnText = stragedyPlayCard(false, "j" + highestNumberedBattlefieldCardSubtractDownTo30) - } else if (hasAce && bestAceNumber != null) { - state.stragedyEnemyTurnText = stragedyPlayCard(false, "a" + bestAceNumber) - } else if (kingCards.length > 0 && kingNumberedCardsInHand.length > 0) { - state.stragedyEnemyTurnText = stragedyPlayCard(false, kingNumberedCardsInHand[kingNumberedCardsInHand.length - 1]) - } else { - state.stragedyEnemyTurnText = stragedyEnemyRetire() - } - } else if (highestNumberedHandCardAddUpTo20 == null && hand.length > 0) { + if (hasQueen && highestNumberedBattlefieldCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "q" + highestNumberedBattlefieldCard) + else if (hasPriest && highestNumberedBattlefieldCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "p" + highestNumberedBattlefieldCard) + else if (hasJack && highestNumberedBattlefieldCardSubtractDownTo30 != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "j" + highestNumberedBattlefieldCardSubtractDownTo30) + else if (hasAce && bestAceCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "a" + bestAceCard) + else if (kingCards.length > 0 && kingNumberedCardsInHand.length > 0) state.stragedyEnemyTurnText = stragedyPlayCard(false, kingNumberedCardsInHand[kingNumberedCardsInHand.length - 1]) + else state.stragedyEnemyTurnText = stragedyEnemyRetire() + } else if (score >= playerScore && hasWitch) { log(8) - state.stragedyEnemyTurnText = stragedyEnemyDiscardCard() + state.stragedyEnemyTurnText = stragedyPlayCard(false, "w") + } else if (score >= playerScore && hasBrigand) { + log(10) + state.stragedyEnemyTurnText = stragedyPlayCard(false, "b") + } else if (highestNumberedHandCardAddUpTo20 == null && hand.length > 0) { + log(11) + if (score >= 20 && score < playerScore && faceCardHandCount > 1 && hasAce && bestAceCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "a" + bestAceCard) + else if (score >= 20 && score < playerScore && faceCardHandCount > 1 && hasKing && bestKingCardToBustPlayer != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "k" + bestKingCardToBustPlayer) + else if (score >= 20 && score < playerScore && faceCardHandCount > 1 && hasQueen && highestNumberedBattlefieldCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "q" + highestNumberedBattlefieldCard) + else if (deck.length > 0) state.stragedyEnemyTurnText = stragedyEnemyDiscardCard() + else if (hand.length > 0) state.stragedyEnemyTurnText = stragedyEnemyRandom() + else state.stragedyEnemyTurnText = stragedyEnemyRetire() } else { - log(9) + log(12) state.stragedyEnemyTurnText = stragedyEnemyRandom() } @@ -3137,7 +3138,31 @@ function stragedyEnemyDrawCard() { function stragedyEnemyDiscardCard() { log(`Enemy discard a card`) - var card = state.stragedyEnemyHand.splice(getRandomInteger(0, state.stragedyEnemyHand.length - 1), 1) + var hand = [...state.stragedyEnemyHand] + var score = state.stragedyEnemyScore + + var hasAce = hand.filter(x => /^.*a.*$/gi.test(x)).length > 0 + var hasJack = hand.filter(x => /^.*j.*$/gi.test(x)).length > 0 + var hasQueen = hand.filter(x => /^.*q.*$/gi.test(x)).length > 0 + var hasKing = hand.filter(x => /^.*k.*$/gi.test(x)).length > 0 + var hasJoker = hand.filter(x => /^.*\?.*$/gi.test(x)).length > 0 + var hasWitch = hand.filter(x => /^.*w.*$/gi.test(x)).length > 0 + var hasPriest = hand.filter(x => /^.*p.*$/gi.test(x)).length > 0 + var sortedNumberedHandCardsToAddUpTo30 = hand.filter(x => /^\d+$/gi.test(x) && parseInt(x) <= 30 - score).sort((a, b) => parseInt(a) - parseInt(b)) + var highestNumberedHandCardAddUpTo30 = sortedNumberedHandCardsToAddUpTo30.length > 0 ? sortedNumberedHandCardsToAddUpTo30[sortedNumberedHandCardsToAddUpTo30.length - 1] : null + + if (hand.length > 1) { + if (hasQueen) hand.splice(hand.indexOf("q")) + else if (hasPriest) hand.splice(hand.indexOf("p")) + else if (hasKing) hand.splice(hand.indexOf("k")) + else if (hasWitch) hand.splice(hand.indexOf("w")) + else if (hasJoker) hand.splice(hand.indexOf("?")) + else if (hasJack) hand.splice(hand.indexOf("j")) + else if (hasAce) hand.splice(hand.indexOf("a")) + else if (highestNumberedHandCardAddUpTo30 != null) hand.splice(hand.indexOf(highestNumberedHandCardAddUpTo30)) + } + + var card = state.stragedyEnemyHand.splice(state.stragedyEnemyHand.indexOf(getRandomInteger(0, hand.length - 1)), 1) state.stragedyEnemyDiscard.push(card) var newCards = state.stragedyEnemyDeck.splice(state.stragedyEnemyDeck.length - 2, 2) state.stragedyEnemyHand.push(...newCards) @@ -3223,11 +3248,13 @@ function stragedyPlayerTurn(text) { return `You draw a ${drawCard}` } else if (text == "r") { state.stragedyPlayerRetired = true + stragedyCalculateScores() + var text = `You retire at ${state.stragedyPlayerScore}.` stragedyEnemyTurn() stragedyCalculateScores() stragedyCheckForWin() state.stragedyTurn = "gameOver" - return `You retire at ${state.stragedyPlayerScore}.` + return text } else { var text = stragedyPlayCard(true, text) if (state.stragedyEnemyRetired) { @@ -3273,42 +3300,47 @@ function stragedyPlayCard(player, text) { var targetCard = isNumberedCard ? null : text.substring(1).toLowerCase() var handIndex = hand.findIndex(x => x.toLowerCase() == handCard) - if (handIndex == -1) return "\nYou can only play cards that are in your hand\n" + if (handIndex == -1) { + if (player) state.stragedyEnemySkipTurn = true + return "\nYou can only play cards that are in your hand\n" + } var targetIndex = targetCard == "" ? -1 : battlefield.findIndex(x => x.toLowerCase() == targetCard) - if (!isNumberedCard && targetCard != "" && targetIndex == -1) return "\nYou must specify a target that is placed on your side of the battlefield.\n" + if (!isNumberedCard && targetCard != "" && targetIndex == -1) { + if (player) state.stragedyEnemySkipTurn = true + return "\nYou must specify a target that is placed on your side of the battlefield.\n" + } switch (handCard) { case "a": - if (targetCard == "") return "\nYou must specify a target to use the Ace (ie. a2)\n" + if (targetCard == "") { + if (player) state.stragedyEnemySkipTurn = true + return "\nYou must specify a target to use the Ace (ie. a2)\n" + } hand.splice(handIndex, 1) - while (targetIndex != -1) { - var discardGroups = battlefield.splice(targetIndex, 1) - var discardCards = [] - for (var group of discardGroups) { - discardCards.push(...group) + for (var i = battlefield.length - 1; i >= 0; i--) { + if (battlefield[i].endsWith(targetCard)) { + discard.push(...battlefield[i]) + battlefield.splice(i, 1) } - discard.push(...discardCards) - targetIndex = battlefield.findIndex(x => x.toLowerCase().endsWith(targetCard.substring(targetCard.length - 1, targetCard.length))) } - targetIndex = enemyBattlefield.findIndex(x => x.toLowerCase().endsWith(targetCard.substring(targetCard.length - 1, targetCard.length))) - while (targetIndex != -1) { - var discardGroups = battlefield.splice(targetIndex, 1) - var discardCards = [] - for (var group of discardGroups) { - discardCards.push(...group) + for (var i = enemyBattlefield.length - 1; i >= 0; i--) { + if (enemyBattlefield[i].endsWith(targetCard)) { + enemyDiscard.push(...enemyBattlefield[i]) + enemyBattlefield.splice(i, 1) } - discard.push(...discardCards) - targetIndex = enemyBattlefield.findIndex(x => x.toLowerCase().endsWith(targetCard.substring(targetCard.length - 1, targetCard.length))) } stragedyCalculateScores() return `\n${characterName} ${playedWord} an ace on ${targetCard}. All ${targetCard}s are removed.\n` case "j": - if (targetCard == "") return "\nYou must specify a target to use the Jack (ie. j2)\n" + if (targetCard == "") { + if (player) state.stragedyEnemySkipTurn = true + return "\nYou must specify a target to use the Jack (ie. j2)\n" + } battlefield.splice(targetIndex, 1) var discardCards = [...targetCard] @@ -3323,9 +3355,12 @@ function stragedyPlayCard(player, text) { hand.push(addCard) stragedyCalculateScores() - return `\n${characterName} ${playedWord} a jack on the ${targetCard}. The ${targetCard} is removed. ${characterName} drew a ${addCard} from the discard pile\n` + return `\n${characterName} ${playedWord} a jack on the ${targetCard}. The ${targetCard} is removed. ${player ? `${characterName} drew a ${addCard} from the discard pile.` : ""}\n` case "q": - if (targetCard == "") return "\nYou must specify a target to use the Queen (ie. q2)\n" + if (targetCard == "") { + if (player) state.stragedyEnemySkipTurn = true + return "\nYou must specify a target to use the Queen (ie. q2)\n" + } hand.splice(handIndex, 1) battlefield.splice(targetIndex, 1) @@ -3335,7 +3370,10 @@ function stragedyPlayCard(player, text) { stragedyCalculateScores() return `\n${characterName} ${playedWord} a queen on the ${targetCard}. The value is added to the opponent.\n` case "k": - if (targetCard == "") return "\nYou must specify a target to use the King (ie. k2)\n" + if (targetCard == "") { + if (player) state.stragedyEnemySkipTurn = true + return "\nYou must specify a target to use the King (ie. k2)\n" + } hand.splice(handIndex, 1) battlefield.splice(targetIndex, 1) @@ -3345,7 +3383,10 @@ function stragedyPlayCard(player, text) { stragedyCalculateScores() return `\n${characterName} ${playedWord} a king on the ${targetCard}. All ${targetCard.match(/\d+/g)} values are doubled.\n` case "?": - if (targetCard == "") return "\nYou must specify a target to use the Joker (ie. ?2)\n" + if (targetCard == "") { + if (player) state.stragedyEnemySkipTurn = true + return "\nYou must specify a target to use the Joker (ie. ?2)\n" + } hand.splice(handIndex, 1) battlefield.splice(targetIndex, 1) @@ -3363,7 +3404,10 @@ function stragedyPlayCard(player, text) { stragedyCalculateScores() return `\n${characterName} ${playedWord} a witch on the opponent.\n` case "p": - if (targetCard == "") return "\nYou must specify a target to use the Priest (ie. p2)\n" + if (targetCard == "") { + if (player) state.stragedyEnemySkipTurn = true + return "\nYou must specify a target to use the Priest (ie. p2)\n" + } hand.splice(handIndex, 1) battlefield.splice(targetIndex, 1) @@ -3397,6 +3441,7 @@ function stragedyPlayCard(player, text) { stragedyCalculateScores() return `\n${characterName} ${playedWord} a ${handCard}.\n` default: + if (player) state.stragedyEnemySkipTurn = true return "\nUnrecognized card specified. Stop playing with counterfit cards!\n" } } diff --git a/Output.js b/Output.js index d8e3532..9fb00b8 100644 --- a/Output.js +++ b/Output.js @@ -608,7 +608,7 @@ The opponent has ${enemyDeckCount} cards in the deck, ${enemyDiscardCount} in th Opponent's cards on the battlefield: ${enemyBattlefield} = ${state.stragedyEnemyScore} points ${possessiveName} cards on the battlefield: ${playerBattlefield} = ${state.stragedyPlayerScore} points ------${possessiveName} cards----- +-----${possessiveName} Cards----- ${possessiveName} hand: ${playerHand} ${toTitleCase(character.name)} ${haveWord} ${playerDeckCount} cards in the deck and ${playerDiscardCount} in the discard pile. @@ -631,7 +631,7 @@ Type f to forfeit. This quits the game immediately. case "gameOver": text = "" - if (state.stragedyWinner != "forfeit" && state.stragedyEnemyTurnText != null) text += "\n" + state.stragedyEnemyTurnText + if (state.stragedyWinner != "forfeit" && state.stragedyEnemyTurnText != null) text += state.stragedyEnemyTurnText text += ` The battle has concluded.${state.stragedyWinner != "forfeit" ? `\nFinal scores:\n${character.name}: ${state.stragedyPlayerScore}\nOpponent: ${state.stragedyEnemyScore}`: ""}