public ActionResult <ScoreResponse> ScoreCountedCard(string playedCardName, int currentCount, string countedCardsCSV) { (bool success, List <Card> countedCards, string badCard) = ParseCards(countedCardsCSV); if (!success) { return(NotFound($"Bad Card in counted cards: {badCard}")); } ActionResult <Card> result = GetCard(playedCardName); Card playedCard = result.Value; int tempCount = 0; for (int i = 0; i < countedCards.Count; i++) { tempCount += countedCards[i].Value; if (tempCount > 31) { tempCount = countedCards[i].Value; } } if (tempCount != currentCount) { return(BadRequest($"Count should be {tempCount} not {currentCount}")); } int score = CardScoring.ScoreCountingCardsPlayed(countedCards, playedCard, tempCount, out List <Score> scoreList); return(new ScoreResponse(score, scoreList)); }
public async Task <ActionResult <CountedCardResponse> > GetNextCountedCardAsync(string cardsLeftCSV, int currentCount, string cardsPlayedCSV) { (bool success, List <Card> cardsLeft, string badCard) = ParseCards(cardsLeftCSV); if (!success) { return(NotFound($"Bad Card in played cards: {badCard}")); } List <Card> cardsPlayed = null; if (cardsPlayedCSV != "") { (success, cardsPlayed, badCard) = ParseCards(cardsPlayedCSV); if (!success) { return(NotFound($"Bad Card in played cards: {badCard}")); } } CountingPlayer player = new CountingPlayer(true); Card toPlay = await player.GetCountCard(cardsPlayed, cardsLeft, currentCount); int score = CardScoring.ScoreCountingCardsPlayed(cardsPlayed, toPlay, currentCount, out List <Score> scoreList); return(new CountedCardResponse(toPlay, score, scoreList)); }
private async Task <int> ScoreCountedCard(Player currentTurn, List <Card> playedCards, Card card, int currentCount) { int score = CardScoring.ScoreCountingCardsPlayed(playedCards, card, currentCount); if (score != -1) { await LogLine(currentTurn.PlayerName, LogType.PlayCountCard, $"{card}"); await LogLine(currentTurn.PlayerName, LogType.Count, $"{currentCount + card.Value}"); await AddScore(currentTurn, score, ScoreType.Count); // mark the card as counted currentTurn.UncountedCards.Remove(card); playedCards.Add(card); } return(score); }
public override Card GetCountCard(List <Card> playedCards, List <Card> uncountedCards, int currentCount) { int maxScore = -1; Card maxCard = null; int score = 0; // // if we have only 1 card, play it if we legally can // NOTE: we assume that the Player correctly returns a legal card! // if (uncountedCards.Count == 1) { if (uncountedCards[0].Value + currentCount <= 31) { return(uncountedCards[0]); } else { return(null); } } // // see which card we can play that gives us the most points foreach (Card c in uncountedCards) { score = CardScoring.ScoreCountingCardsPlayed(playedCards, c, currentCount); if (score > maxScore) { maxScore = score; maxCard = c; } } if (maxScore == -1) { return(null); // we have no valid card to play } if (maxScore > 0) { // if we can play a card to score points, play it return(maxCard); } if (maxScore == 0) // there isn't a card for us to play that generates points { // // play a card that we have a pair so we can get 3 of a kind - as long as it isn't a 5 and the 3 of a kind makes > 31 // // this optimization changes the average count score from 2.59948 to 2.66909 over 100,000 games // for (int i = 0; i < uncountedCards.Count - 1; i++) { // dont' do it if it will force us over 31 if (uncountedCards[i].Rank * 3 + currentCount > 31) { continue; } if (uncountedCards[i].Rank == uncountedCards[i + 1].Rank) { if (uncountedCards[i].Rank != 5) { return(uncountedCards[i]); } } } // // make the right choice if assuming they'll play a 10 // // this optimization changes the average count score from 2.64235 to 2.67764 over 100,000 games // Combinations <Card> combinations = new Combinations <Card>(uncountedCards, 2); // at most 6 of these: 4 choose 2 foreach (List <Card> cards in combinations) { int sum = cards[0].Value + cards[1].Value; if (sum + currentCount == 5) // i'll 15 them if they play a 10 { return(cards[1]); } if (sum + currentCount == 21) // i'll 31 them if they play a 10 { return(cards[1]); } } // tried returning the smallest legal card -- no difference // tried returning the highest legal card -- no difference } // tried to generate a random card if currentCount == 0 -- no difference // // this one is important -- it basically says "if you can't score any points, induce a 3 of a kind, or try to create a run play whatever card we ened up with. // UNLESS IT IS A FIVE!...then pick a different one. over the course of 100,000 games, this is the difference between 2.61423 and 2.71498 ave counting points // turns out that if we don't do this, then both players get ~2.65 points / counting session - e.g. if one is not worried about dropping 5's and the other is, it // adds about .1/count and if both are being silly and dropping 5's then both get about .04 ave point boost. still a good optimization when playing humans. if (maxCard.Rank == 5) { // try to find a non 5 card to play foreach (Card c in uncountedCards) { if (c.Rank != 5 && c.Value + currentCount <= 31) { maxCard = c; break; } } } return(maxCard); }
public override Card GetCountCard(List <Card> playedCards, List <Card> uncountedCards, int currentCount) { int maxScore = -1; Card maxCard = null; int score = 0; if (uncountedCards.Count == 1) { if (uncountedCards[0].Value + currentCount <= 31) { return(uncountedCards[0]); } else { return(null); } } // // see which card we can play that gives us the most points foreach (Card c in uncountedCards) { score = CardScoring.ScoreCountingCardsPlayed(playedCards, c, currentCount); if (score > maxScore) { maxScore = score; maxCard = c; } } if (maxScore == -1) { return(null); // we have no valid card to play } if (maxScore == 0) // there isn't a card for us to play that generates points { // // play a card that we have a pair so we can get 3 of a kind - as long as it isn't a 5 and the 3 of a kind makes > 31 // for (int i = 0; i < uncountedCards.Count - 1; i++) { // dont' do it if it will force us over 31 if (uncountedCards[i].Rank * 3 + currentCount > 31) { continue; } if (uncountedCards[i].Rank == uncountedCards[i + 1].Rank) { if (uncountedCards[i].Rank != 5) { return(uncountedCards[i]); } } } // // try to play a card that will create a run Combinations <Card> combinations = new Combinations <Card>(uncountedCards, 2); // at most 6 of these: 4 choose 2 foreach (List <Card> cards in combinations) { int diff = Math.Abs(cards[0].Ordinal - cards[1].Ordinal); if (diff == 1) // they are consecutive { int val = cards[0].Value + cards[1].Value; if (val + currentCount > 31) { continue; } // // assume sorted if (cards[0].Ordinal > CardOrdinal.Ace) { // // this means we have somehing like 3 and 4 in our hand. if we play 7 and they play 6, we can play the 8 if (val + currentCount + cards[0].Value - 1 <= 31) { //Debug.WriteLine($"Trying to get a run! {cards[0].CardName} and {cards[1].CardName}"); if (cards[0].Rank != 5) { return(cards[0]); } else { return(cards[1]); } } } else { int highCardVal = cards[1].Value; if (highCardVal > 10) { highCardVal = 10; } if (val + currentCount + highCardVal <= 31) { //Debug.WriteLine($"Trying to get a run with a gap! {cards[0].Ordinal} and {cards[1].Ordinal}"); if (cards[0].Rank != 5) { return(cards[0]); } else { return(cards[1]); } } } } if (diff == 2) // there is a gap between the two cards -- eg. 4 and 6 { } } // // make the right choice if assuming they'll play a 10 // combinations = new Combinations <Card>(uncountedCards, 2); // at most 6 of these: 4 choose 2 foreach (List <Card> cds in combinations) { int sum = cds[0].Value + cds[1].Value; if (sum + currentCount == 5) // i'll 15 them if they play a 10 { return(cds[1]); } if (sum + currentCount == 21) // i'll 31 them if they play a 10 { return(cds[1]); } } } if (maxCard.Rank == 5) { // try to find a non 5 card to play foreach (Card c in uncountedCards) { if (c.Rank != 5 && c.Value + currentCount <= 31) { maxCard = c; break; } } } return(maxCard); }