public int CompareTo(object obj) { if (obj == null) { return(1); } TichuCard other = obj as TichuCard; if (Suit != other.Suit) { return(Suit > other.Suit ? 1 : -1); } if (Rank != other.Rank) { return(Rank > other.Rank ? 1 : -1); } if (Special != other.Special) { return(Special > other.Special ? 1 : -1); } return(0); }
public override string ToString() { // What state do we want to show? // - Current cards in each hand // - Current player turn // - Cards played in current trick // - Trick counts by player StringBuilder sb = new StringBuilder(); for (int player = 0; player < 4; player++) { if (CurrentPlayerTurn == player) { sb.Append($"[{player}] "); } else { sb.Append($" {player} "); } sb.AppendLine(TichuCard.PrintCardsSortedBySuit(Players[player].Cards)); } sb.AppendLine(string.Join(" ", _currentTrick.Select(play => play.ToString()))); sb.AppendLine(string.Join(" ", Evaluate())); return(sb.ToString()); }
public static void Run(int numGames) { Random random = new Random(); int[] cumulativeTrickCounts = new int[4]; // [playerId] IPlayGenerator <Play>[] playGenerators = new IPlayGenerator <Play> [4]; //playGenerators[0] = new MultiThreadedMcts<Play>(100000, 50, random, 8); playGenerators[0] = new Mcts <Play>(10000, 50, random); playGenerators[1] = new RandomPlayGenerator <Play>(); playGenerators[2] = new HighestCardPlayGenerator(); playGenerators[3] = new HighestCardPlayGenerator(); Logger.Log.Enabled = true; for (int gameNumber = 0; gameNumber < numGames; gameNumber++) { var gameState = TichuGameRunHarness.SetupFourPlayerGame(random); if (true) { Logger.Log.WriteLine("Starting cards:"); for (int i = 0; i < 4; i++) { Logger.Log.WriteLine($"Player {i} {TichuCard.PrintCardsSortedBySuit(gameState.Players[i].Cards)}"); } } while (true) { gameState.SetPointOfViewPlayer(gameState.CurrentPlayerTurn); Play play = playGenerators[gameState.CurrentPlayerTurn].FindPlay(gameState); Logger.Log.WriteLine($"Player {play.Player} played {play.Cards[0].ToString()}"); gameState.CommitPlay(play); if (gameState.GameOver()) { break; } if (gameState.PlayedCards.Count % 4 == 0) { Logger.Log.WriteLine("----"); } } var scores = gameState.Evaluate(); for (int i = 0; i < 4; i++) { cumulativeTrickCounts[i] += (int)scores[i]; } Console.WriteLine($"Tricks: {scores[0]} {scores[1]} {scores[2]} {scores[3]} "); } string header = string.Format("{0, 6}{1, 10}{2, 10}", "Player", "Tricks", "Average"); Console.WriteLine(header); for (int i = 0; i < 4; i++) { string output = string.Format("{0, 6}{1, 10}{2, 10:N1}", i, cumulativeTrickCounts[i], (double)cumulativeTrickCounts[i] / numGames); Console.WriteLine(output); } }
public IList <Play> GetPlays() { // Any card that hasn't been played is a valid play List <Play> plays = new List <Play>(); if (CurrentPlayerTurn == _pointOfViewPlayer) { if (_currentTrick.Count > 0) { // Try and follow suit. If we have none of that suit, fall through into allowing all cards in our hand var suitCards = TichuCard.GetCardsWithSuit(Players[CurrentPlayerTurn].Cards, _currentTrick[0].Cards[0].Suit); if (suitCards != null) { foreach (var card in suitCards) { plays.Add(Play.PlayCards(CurrentPlayerTurn, new TichuCard[] { card })); } } } // If we didn't have to follow suit, any card is a valid play if (plays.Count == 0) { // Make correct plays from our own hand foreach (var card in Players[CurrentPlayerTurn].Cards) { plays.Add(Play.PlayCards(CurrentPlayerTurn, new TichuCard[] { card })); } } } else { // For simulated players we choose a random card from those unused. Also exclude cards // from the point-of-view player's hand since from the AI agent's pov those cards are // determined (ie, we're not cheating by looking at their hand here). // We could use a more sophisticated selection policy here to guide the search. //foreach (var card in RemainingCards.Except(Players[_pointOfViewPlayer].Cards)) foreach (var card in RemainingPlayoutCards) { plays.Add(Play.PlayCards(CurrentPlayerTurn, new TichuCard[] { card })); } } return(plays); }
/// <summary> /// Creates a random deck of cards excluding dog, dragon, phoenix, mahjong /// </summary> public static TichuDeck CreateWithoutSpecials() { List <TichuCard> drawPool = new List <TichuCard>(); for (int suit = 0; suit < 4; suit++) { for (int rank = 0; rank < 13; rank++) { TichuCard c = new TichuCard((CardSuit)suit, (CardRank)rank); drawPool.Add(c); } } TichuDeck deck = new TichuDeck(drawPool); Random random = new Random(); for (int i = 0; i < 52; i++) { int randomCard = random.Next(drawPool.Count); deck._deck.Push(drawPool[randomCard]); drawPool.RemoveAt(randomCard); } return(deck); }
private static ParsedHand ParseHand(StreamReader reader, ParsedGame game) { ParsedHand hand = new ParsedHand(); // Tichu cards string line = reader.ReadLine(); if (line == null) { return(null); } if (line != @"---------------Gr.Tichukarten------------------") { throw new ParseException(); } // Parse grand tichu cards (first 8 dealt) for (int i = 0; i < 4; i++) { line = reader.ReadLine(); int playerNum = int.Parse(line.Substring(1, 1)); string[] grandTichuCards = line.Substring(3).Split(" "); for (int cardIndex = 0; cardIndex < 8; cardIndex++) { hand.GrandTichuCards[i][cardIndex] = GetCard(grandTichuCards[cardIndex + 1]); } } line = reader.ReadLine(); if (line != @"---------------Startkarten------------------") { throw new ParseException(); } // Parse starting hand (14 cards dealt pre-pass) for (int i = 0; i < 4; i++) { line = reader.ReadLine(); int playerNum = int.Parse(line.Substring(1, 1)); string[] startingCards = line.Substring(3).Split(" "); for (int cardIndex = 0; cardIndex < 14; cardIndex++) { hand.StartingCards[i][cardIndex] = GetCard(startingCards[cardIndex + 1]); } } line = reader.ReadLine(); while (line.StartsWith("Grosses Tichu: ") || line.StartsWith("Tichu: ")) { if (line.StartsWith("Grosses Tichu: ")) { string[] grandPlayers = line.Substring(15).Split(" ", StringSplitOptions.RemoveEmptyEntries); int count = 0; foreach (string player in grandPlayers) { count++; if (count > 1) { throw new Exception("Multi grand in one line"); } hand.Plays.Add(Play.GrandTichu(int.Parse(player.Substring(1, 1)))); } } else { string[] tichuPlayers = line.Substring(7).Split(" ", StringSplitOptions.RemoveEmptyEntries); int count = 0; foreach (string player in tichuPlayers) { count++; if (count > 1) { throw new Exception("Multi tichu in one line"); } hand.Plays.Add(Play.GrandTichu(IndexFromPlayerTag(player))); } } line = reader.ReadLine(); } if (line != "Schupfen:") { throw new ParseException(); } // Parse card exchange for (int i = 0; i < 4; i++) { line = reader.ReadLine(); string[] passes = line.Substring(3).Split(" ", StringSplitOptions.RemoveEmptyEntries); string playerName = passes[0]; if (game.Players[i] == null) { // Seed the set of players names the first time we parse them game.Players[i] = playerName; } //Debug.Assert(game.Players[i] == playerName); // The names should be consistent between hands in a game hand.ExchangedCards[i][0] = GetCard(passes[3]); hand.ExchangedCards[i][1] = GetCard(passes[6]); hand.ExchangedCards[i][2] = GetCard(passes[9]); } line = reader.ReadLine(); // If there are bombs, the players with them are listed here if (line.StartsWith("BOMBE: ")) { string[] bombPlayers = line.Substring(7).Split(" "); foreach (string player in bombPlayers) { if (string.IsNullOrEmpty(player)) { continue; } hand.HasBomb[int.Parse(player.Substring(1, 1))] = true; } line = reader.ReadLine(); } if (line != @"---------------Rundenverlauf------------------") { throw new ParseException(); } // Remember who played the mahjong when we see the wish line int mahjongPlayer = 0; while (true) { // Parse plays until we see the scores indicating the end of the hand line = reader.ReadLine(); // Scores line; we're done playing if (line.StartsWith("Ergebnis: ")) { break; } // Wish if (line.StartsWith("Wunsch:")) { TichuCard wishCard = new TichuCard(default(CardSuit), RankFromParsedString(line.Substring(7))); hand.Plays.Add(Play.Wish(mahjongPlayer, wishCard)); } else if (line.StartsWith("Drache an: ")) { string dragonPlayer = line.Substring(11); hand.Plays.Add(Play.GiveDragon(IndexFromPlayerTag(dragonPlayer))); } else if (line.StartsWith("Tichu: ")) { string tichuPlayer = line.Substring(7); hand.Plays.Add(Play.Tichu(IndexFromPlayerTag(tichuPlayer))); } else if (line.StartsWith("(")) { // Options: // - Playing cards // - Passing string[] playLine = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); int playerIndex = IndexFromPlayerTag(playLine[0]); if (playLine[1] == "passt.") { // Pass hand.Plays.Add(Play.Pass(playerIndex)); } else { // Play some number of cards List <TichuCard> cards = new List <TichuCard>(); foreach (var x in playLine.AsSpan(1)) { cards.Add(GetCard(x)); } Play.PlayCards(playerIndex, cards.ToArray()); } } else { Debug.Assert(false, $"Unparsed line: {line}"); } } Debug.Assert(line.StartsWith("Ergebnis: ")); string[] scores = line.Substring(10).Split(" ", StringSplitOptions.RemoveEmptyEntries); hand.Scores[0] = int.Parse(scores[0]); hand.Scores[1] = int.Parse(scores[2]); Debug.Assert((hand.Scores[0] + hand.Scores[1]) % 100 == 0); return(hand); }