public GameEffect ChooseBestAction(GameChoice choice, Game game) { game = game.Clone(); // Inform the game and its descendents that it is a hypothetical game, so all players use the strategy of the player doing the imagining Readable_GamePlayer chooser = game.Get_ReadableSnapshot(choice.ControllerID); game.Strategy = game.GetStrategy(chooser); IEnumerable <GameEffect> effects = choice.Options; // create the base game Analyzed_GameState rootState = new Analyzed_GameState(game, null, null, this.GameEvaluator.EstimateWinProbabilities(game)); rootState.ChoosingPlayerID = choice.ControllerID; // Put the first set of choices onto the starting gameState this.PutGameOptions(rootState, choice); // loop until we run out of time while (rootState.NumDescendents < this.NumTimeSteps) { // Find the current best path and explore it for one more time unit this.ProcessOnce(rootState); double winProbability = rootState.WinProbabilities[choice.ControllerID]; if (winProbability == 0 || winProbability == 1) { break; } } if (this.ShouldPrint) { Console.WriteLine("Plan for " + chooser.ToString()); rootState.printBestPath(); } return(rootState.FavoriteChild.SourceEffect); }
public override string ToString(Game game) { Readable_GamePlayer player = this.playerProvider.GetValue(this.Cause, game, (Writable_GamePlayer)null); Resource bonusResources = this.resourcesToGain_provider.GetValue(this.Cause, game, (Resource)null); return(player.ToString() + " gains " + bonusResources.ToString()); }
public virtual bool IsPlayable(Game game) { // check that we have enough resources to play this card Readable_GamePlayer controller = game.Get_ReadableSnapshot(this.ControllerID); return(controller.Get_CurrentResources().Minus(this.cost).IsValid); }
public double EstimateWinProbability(Game game, ID <Readable_GamePlayer> playerId) { Readable_GamePlayer player = game.Get_ReadableSnapshot(playerId); // first check for obvious things like whether one player has already lost if (game.GetWinner() != null) { if (game.GetWinner() == player) { return(1); } else { return(0); } } if (game.GetLosers().Count == game.NumPlayers) { return(0); //return 1.0 / game.NumPlayers; } // Now compute a heuristic based on how much stuff each player has double totalScore = 0; double playerScore = 0; double activePlayer = 1; foreach (Readable_GamePlayer candidate in game.Players) { // Exchange rates: // n power and n toughness is worth n mana // losing 25% of your life is worth 2 mana // 1 crystal per turn is worth 2 mana now. // n cards in hand is worth n mana (the cards often generate n mana worth of value (plus the mana spent to play them) even though they often cost 2*n to draw them) // Being the active player is worth 0.5 cards and 0.5 mana/turn, but the active player has received 1 additional card and 1 additional mana, so these must each be subtracted instead // This results in this score (equivalent amount of mana this turn): // mana+manaPerTurn*2++((power+toughness)/2)+(log(life)/log(4/3))+(activePlayer?)*-1.5+handSize /* * double score = candidate.Get_CurrentResources().ToNumber() * activePlayer / 2 + candidate.Get_ResourcesPerTurn().ToNumber() - activePlayer * 2 + * (candidate.Get_Total_MonsterDamage(game) + candidate.Get_Total_MonsterHealth(game)) / 2 + Math.Log(candidate.GetHealth()) / Math.Log(4.0 / 3.0); */ double score = candidate.Get_CurrentResources().ToNumber() * activePlayer + candidate.Get_ResourcesPerTurn().ToNumber() * 2 + (candidate.Get_Total_MonsterDamage(game) + candidate.Get_Total_MonsterHealth(game)) / 2 + Math.Log(candidate.GetHealth()) / Math.Log(4.0 / 3.0) + activePlayer * -1.5 + candidate.Get_ReadableHand().Count; if (score < 1) // can't have 0 or less probability of winning unless you've actually lost already { score = 1; } totalScore += score; if (candidate == player) { playerScore = score; } activePlayer *= 0; } return(playerScore / totalScore); }
public void AddPlayer(Readable_GamePlayer player) { WriteControlled_Item <Readable_GamePlayer, Writable_GamePlayer> playerPair = new WriteControlled_Item <Readable_GamePlayer, Writable_GamePlayer>(new PlayerConverter()); playerPair.PutReadonly(player); this.players.PutReadonly(player); this.TurnOrder.AddLast(player.GetID((Readable_GamePlayer)null)); }
public override string ToString(Game game) { int numCards = this.numCards_provider.GetValue(this, game, default(int)); string cardSource = this.cardProvider.ToString(); Readable_GamePlayer player = this.playerProvider.GetValue(this, game, (Writable_GamePlayer)null); return("Draw " + numCards + " from " + cardSource + " for " + player); }
public GameStrategy GetStrategy(Readable_GamePlayer player) { if (this.Strategy != null) { // This is a hypothetical game, so all players use the same strategy as the player doing the imagining return(this.Strategy); } // This is a real game, so each player can use their own strategy return(player.Strategy); }
public bool IsPlayable(Readable_MonsterCard card, Game game) { Readable_GamePlayer controller = game.Get_ReadableSnapshot(card.Get_ControllerID()); IEnumerable <ID <Readable_MonsterCard> > cardsInPlay = controller.Get_MonsterIDsInPlay(); if (cardsInPlay.Count() < 7) { return(true); } return(false); }
public GameChoice Get_NextChoice() { if (this.pendingEffects.Count > 0) { // a previous effect still requires a player to make a choice, so that must be done before moving on to normal options (like playing more cards, attacking, or ending the turn) GameChoice choice = this.pendingEffects.First(); this.pendingEffects.RemoveFirst(); return(choice); } // There aren't any effects still requiring user input, so now the active player can choose on of the usual choices (playing a card, attacking, ending the turn or whatever) Readable_GamePlayer activePlayer = this.ActivePlayer; return(new GameChoice(this.Referee.Get_AvailableGameActions(this, activePlayer), activePlayer.GetID((Readable_GamePlayer)null))); }
// returns all monsters in play public IList <Readable_MonsterCard> Get_MonstersInPlay() { List <Readable_MonsterCard> targets = new List <Readable_MonsterCard>(); foreach (ID <Readable_GamePlayer> playerId in this.players.GetKeys()) { Readable_GamePlayer player = this.players.GetReadable(playerId); foreach (ID <Readable_MonsterCard> monsterId in player.Get_MonsterIDsInPlay()) { Readable_MonsterCard monster = this.Get_ReadableSnapshot(monsterId); targets.Add(monster); } } return(targets); }
// returns all valid LifeTargets in play (monsters and players) public IList <Readable_LifeTarget> Get_LifeTargets() { List <Readable_LifeTarget> targets = new List <Readable_LifeTarget>(); foreach (ID <Readable_GamePlayer> playerId in this.players.GetKeys()) { Readable_GamePlayer player = this.players.GetReadable(playerId); targets.Add(player); } foreach (Readable_MonsterCard monster in this.Get_MonstersInPlay()) { targets.Add(monster); } return(targets); }
public void CopyFrom(Readable_GamePlayer original) { // the hand should be small enough that we can just clone it for the moment this.hand.PutReadonly(original.Get_ReadableHand()); this.Deck = original.GetDeck().Clone(); this.Health = original.GetHealth(); this.MaxHealth = original.GetMaxHealth(); this.NumDrawsSkipped = original.Get_NumDrawsSkipped(); this.sourcePlayer = original.SourcePlayer; this.ID = original.GetID((Readable_GamePlayer)null).ToInt(); this.MonsterIDsInPlay = new WriteControlled_Item <IReadOnlyList <ID <Readable_MonsterCard> >, List <ID <Readable_MonsterCard> > >(new ListConverter <ID <Readable_MonsterCard> >()); this.MonsterIDsInPlay.PutReadonly(original.Get_MonsterIDsInPlay()); this.CurrentResources = original.Get_CurrentResources(); this.ResourcesPerTurn = original.Get_ResourcesPerTurn(); }
public override string ToString(Game game) { int amountToGain = this.amountToGain_provider.GetValue(this, game, default(int)); Readable_GamePlayer controller = this.chooserProvider.GetValue(this, game, (Readable_GamePlayer)null); string result = controller.ToString(); if (amountToGain > 0) { result += " heals a target for " + amountToGain; } else { result += " damages a target for " + (amountToGain * -1); } return(result); }
} // in charge of which type of game (Hearthstone, Magic, Hearts, or whatever) is being played public List <GameEffect> Get_AvailableGameActions(Readable_GamePlayer player) { return(this.Referee.Get_AvailableGameActions(this, player)); }
public List <GameEffect> Get_AvailableGameActions(Game game, Readable_GamePlayer player) { // This function only gets called when there are no effects in progress (like choosing the target of a triggered effect). List <GameEffect> options = new List <GameEffect>(); // So, a player has these types of options: 1. Play a card. 2. Attack with a monster. 3. Activate their special ability. 4. End their turn // Let the player play any card foreach (ID <ReadableCard> cardId in player.Get_ReadableHand()) { ReadableCard card = game.Get_ReadableSnapshot(cardId); if (card.IsPlayable(game)) { options.Add(new PlayCard_Effect(card.GetID((ReadableCard)null))); } } // Let the player attack with any monster IEnumerable <ID <Readable_MonsterCard> > availableAttacker_IDs = player.Get_MonsterIDsInPlay(); // first figure out which monsters can be attacked (if any monsters have Taunt, they are the only ones that may be attacked) foreach (ID <Readable_GamePlayer> playerId in game.TurnOrder) { // make sure this is a different player if (!playerId.Equals(player.GetID((Readable_GamePlayer)null))) { LinkedList <ID <Readable_LifeTarget> > requiredTarget_IDs = new LinkedList <ID <Readable_LifeTarget> >(); LinkedList <ID <Readable_LifeTarget> > allTarget_Ids = new LinkedList <ID <Readable_LifeTarget> >(); Readable_GamePlayer controller = game.Get_ReadableSnapshot(playerId); foreach (ID <Readable_MonsterCard> monsterId in controller.Get_MonsterIDsInPlay()) { Readable_MonsterCard monster = game.Get_ReadableSnapshot(monsterId); ID <Readable_LifeTarget> convertedID = monster.GetID((Readable_LifeTarget)null); allTarget_Ids.AddLast(convertedID); if (monster.Get_MustBeAttacked()) { requiredTarget_IDs.AddLast(convertedID); } } if (requiredTarget_IDs.Count != 0) { // There is a monster with taunt, so the only valid targets are the monsters with taunt allTarget_Ids = requiredTarget_IDs; } else { // There are no monsters with taunt, so the valid targets are all monsters and the opponent too allTarget_Ids.AddLast(controller.GetID((Readable_LifeTarget)null)); } // Now allow each monster to attack each available target foreach (ID <Readable_MonsterCard> attackerId in availableAttacker_IDs) { if (game.Get_ReadableSnapshot(attackerId).Get_CanAttack()) { foreach (ID <Readable_LifeTarget> targetId in allTarget_Ids) { options.Add(new AttackEffect(attackerId.AsType((Readable_LifeTarget)null), targetId)); } } } } } // Let the player end their turn options.Add(new EndTurn_Effect(player.GetID((Readable_GamePlayer)null))); return(options); }
public ID <Readable_GamePlayer> GetID(Readable_GamePlayer outputType) { return(new ID <Readable_GamePlayer>(this.ID)); }