static void SaveLeaf(FightNode leaf, int mcCount) { System.IO.File.AppendAllText(Output, $"\n==============Fight {mcCount} {leaf.Fight.Status} {leaf.Value}\n"); var hh = leaf.AALeafHistory(); System.IO.File.AppendAllText(Output, SJ('\n', hh)); }
public void Setup() { SetRandom(164); var relics = GetRandomRelics(3); var potions = GetRandomPotions(2); var enemyHp = 100; _Player = new Player(hp: 80, relics: relics, potions: potions); _Enemy = new Cultist(enemyHp); //var hand = gsl("Strike", "Strike", "Strike", "Strike", "Strike", "Defend", "Defend", "Defend", "Defend", "Bash","WildStrike","PommelStrike+"); var cis = GetRandomCards(10); cis.Add(Helpers.GetCi("Armaments")); cis.Add(Helpers.GetCi("Armaments")); cis.Add(Helpers.GetCi("Armaments+")); cis.Add(Helpers.GetCi("TrueGrit")); Console.WriteLine("Deck: " + SJ(separator: ' ', cis.OrderBy(el => el.Card.Name))); var deck = new Deck(cis); deck.InteractiveContext = true; _Fight = new Fight(deck, _Player, _Enemy); _Root = new FightNode(_Fight); _Current = _Root; _Fight.FightNode = _Root; _Fight.StartFight(); }
public MonteCarlo(Deck deck, Enemy enemy, Player player, int turnNumber = 0, List <string> firstHand = null) { _Deck = deck ?? throw new ArgumentNullException(nameof(deck)); _Enemy = enemy ?? throw new ArgumentNullException(); _Player = player ?? throw new ArgumentNullException(); _TurnNumber = turnNumber; if (firstHand == null) { //by default shuffle the deck at fight start. _Deck.Reshuffle(new EffectSet(), new List <string>()); } else { var cards = _Deck.FindSetOfCards(_Deck.GetDrawPile, firstHand); var firstAction = new FightAction(FightActionEnum.StartTurn, cardTargets: cards); _FirstAction = firstAction; } var fight = new Fight(_Deck, _Player, _Enemy); fight.TurnNumber = _TurnNumber; var root = new FightNode(fight); fight.FightNode = root; root.StartFight(); Root = root; if (_FirstAction != null) { root.ApplyAction(_FirstAction); } }
/// <summary> /// Returns the leaf node of a single mc run. /// </summary> public FightNode MC(FightNode fn) { if (fn.Depth == 1) { MCCount++; } var actions = fn.Fight.GetAllActions(); var ii = Rnd.Next(actions.Count()); //ii = 0; var action = actions[ii]; var childNode = fn.ApplyAction(action); switch (childNode.Fight.Status) { case FightStatus.Ongoing: return(MC(childNode)); case FightStatus.Won: return(childNode); case FightStatus.Lost: return(childNode); default: throw new Exception("Other status"); } }
public static void ShowInitialValues(FightNode drawNode) { System.IO.File.AppendAllText(Output, $"\n---------Best FirstRound Choices: {drawNode.Choices.Count}"); foreach (var c in drawNode.Choices.OrderByDescending(el => el.Value)) { ShowRound(c); } }
public NodeValue(double value, int cards, FightNode bestChoice) { Value = value; //How many cards played this round. inherited from non-round ending child bestnode. Cards = cards; BestChoice = bestChoice; }
public static void SaveAllLeaves(FightNode root) { var leaves = GetAllLeaves(root); var ii = 0; foreach (var l in leaves) { ii++; SaveLeaf(l, ii); if (ii > 10000) { break; } } }
public static void ShowRound(FightNode c) { var target = c; var res = new List <string>(); res.Add($"{c.Value.ToString()} W:{c.Weight}"); while (true) { res.Add(target.ToString()); if (target.Value.BestChoice == null) { break; } target = target.Value.BestChoice; } System.IO.File.AppendAllText(Output, "\n"); System.IO.File.AppendAllLines(Output, res); }
private void DisplayStatus(FightNode current) { var fa = _Current.Fight.FightAction?.GetList(); if (fa != null) { Console.WriteLine("===== Last Action:"); foreach (var part in fa) { Console.WriteLine($"\t{part}"); } } var status = _Current.Fight.Status; Console.WriteLine($"\nTurn:{ _Current.Fight.TurnNumber,2} - {status}"); Console.WriteLine(_Current.Fight._Player.Details()); Console.WriteLine(_Current.Fight._Enemies[0].Details()); Console.WriteLine($"\tEnergy: {_Current.Fight._Player.Energy}/{_Current.Fight._Player.MaxEnergy()}"); }
public static List <FightNode> GetLeaves(FightNode node) { var res = new List <FightNode>(); foreach (var set in new List <IList <FightNode> > { node.Choices, node.Randoms }) { foreach (var c in set) { if (c.Fight.Status != FightStatus.Ongoing) { res.Add(c); } else { res.AddRange(GetLeaves(c)); } } } return(res); }
public static List <FightNode> GetAllLeaves(FightNode root) { var res = new List <FightNode>(); var leaf = true; foreach (var c in root.Choices) { res.AddRange(GetAllLeaves(c)); leaf = false; } foreach (var r in root.Randoms) { res.AddRange(GetAllLeaves(r)); leaf = false; } if (leaf) { res.Add(root); } return(res); }
/// <summary> ///// What should we actually do if there are multiple randoms? ///// </summary> public static FightNode GetBestLeaf(FightNode root) { return(root.Choices.OrderBy(el => el.GetValue().Value).First()); }
/// <summary> /// Add node to the proper place and return it for further MCing /// </summary> public FightNode AddChild(FightNode child) { //interesting, although enemymoves are also randoms, there is no point in having an intermediate copy node. //i.e. Cendturn => RenemyMove => [Rvarious enemy moves] is kind of of pointless //unlike CstartTurn => Rwildstrike => [Rvarious wildstrikes] // => Rother random card => [ROther random card random outputs] child.FightAction = child.Fight.FightAction; if (child.FightAction.FightActionType == FightActionEnum.EnemyMove) { child.Parent = this; child.FightAction = child.Fight.FightAction; Randoms.Add(child); } else if (child.FightAction.Random) { //two cases: //the intermediate node is already created: FightNode intermediateNode = null; foreach (var c in Choices) { if (c.FightAction.IsEqual(child.FightAction)) { intermediateNode = c; break; } } if (intermediateNode == null) { //if we have a random, then consider it a c with this as a random child. intermediateNode = new FightNode(child.Fight.Copy()); intermediateNode.Parent = this; var src = child.Fight.FightAction; //we convert the specced out action into a generic one again. var nonspecifiecAction = new FightAction(fightActionType: src.FightActionType, card: src.CardInstance, cardTargets: src.CardTargets, potion: src.Potion, target: src.Target, hadRandomEffects: src.Random); intermediateNode.FightAction = nonspecifiecAction; //zero these out since at this stage they represent the action pre-randomization. if (intermediateNode.Fight.FightNode != null) { intermediateNode.Fight.FightNode.FightAction.Key = null; } intermediateNode.FightAction.Key = null; Choices.Add(intermediateNode); //there is not a random with this key already since this is the first time we played this card with random children. } //we also have to check for identity with the child nodes. foreach (var other in intermediateNode.Randoms) { if (other.FightAction.IsEqual(child.Fight.FightAction) && other.FightAction.Key == child.Fight.FightAction.Key) { //I should just compare the order of the draw pile actually. other.Weight++; return(other); } else { var ae = 4; } } child.Parent = intermediateNode; child.FightAction = child.Fight.FightAction; //child.fightaction still has its key intermediateNode.Randoms.Add(child); } else { child.Parent = this; child.FightAction = child.Fight.FightAction; Choices.Add(child); } //Fight is over. Calc values. if (child.Fight.Status != FightStatus.Ongoing) { child.CalcValue(); } return(child); }
public FightNode GetNode() { var child = new FightNode(Fight.Copy(), Depth + 1); return(child); }
private void CalcValue() { ValueCalculated = true; switch (GetChoiceType()) { case NodeType.TooLong: var tooLongVal = Fight._Player.HP - Fight._Enemies[0].HP; SetValue(new NodeValue(tooLongVal, 0, null)); break; case NodeType.Leaf: var val = 0; var tt = this; switch (Fight.Status) { case FightStatus.Ongoing: var hh = AALeafHistory(); throw new Exception("Leaves can't have ongoing fights."); //this happens when we get toolong //A //BC // B is toolong, C ended. // we backgrack to A but can't calc b. B is a leaf. //TODO this is obviously wrong. //no point in calculating them as we go. //val = Fight._Player.HP - Fight._Enemies[0].HP; return; case FightStatus.Won: val = Fight._Player.HP; break; case FightStatus.Lost: val = -1 * Fight._Enemies[0].HP; break; } var cards = 0; if (Fight.FightAction.FightActionType == FightActionEnum.PlayCard) { cards++; } SetValue(new NodeValue(val, cards, null)); break; case NodeType.Choice: if (FightAction == null) { throw new Exception("This should not happen"); } NodeValue value = null; FightNode bc = null; foreach (var c in Choices) { var cval = c.GetValue(); if (value == null || cval > value) { value = cval; bc = c; } } int cards2 = 0; //Todo prefer playing cards to playing potions! if (FightAction.FightActionType == FightActionEnum.PlayCard || FightAction.FightActionType == FightActionEnum.Potion) { cards2++; } var myVal = new NodeValue(value.Value, value.Cards + cards2, bc); SetValue(myVal); break; case NodeType.Random: var rsum = 0.0d; var rc = 0; foreach (var r in Randoms) { rsum += r.GetValue().Value *r.Weight; rc += r.Weight; } SetValue(new NodeValue(rsum * 1.0 / rc, 0, null)); //TODO okay to not assign bestchild? it doesn't make sense over random. break; } }
public void Start() { while (true) { DisplayStatus(_Current); var ii = 0; var actionMap = new Dictionary <int, FightAction>() { }; var actions = _Current.Fight.GetAllActions(includeUnplayable: true); FightAction action; if (actions.Count == 1) { action = actions[0]; } else { var orderedActions = actions .OrderBy(el => el.CardInstance == null) .ThenByDescending(el => el.Playable) .ThenBy(el => el.CardInstance?.Card?.Name) .ThenBy(el => el.CardInstance?.EnergyCost()); foreach (var a in orderedActions) { var cost = ""; if (a.FightActionType == FightActionEnum.PlayCard) { ///no indexer but still show the energy cost. if (a.Playable) { cost = $" E: {a.CardInstance.EnergyCost()}"; } else { cost = $" E: -"; } } if (a.Playable) { Console.WriteLine($"\t{ii}: {a}{cost}"); actionMap[ii] = a; ii++; } else { Console.WriteLine($"\t-: {a}{cost}"); } } var parsed = Int32.TryParse(Console.ReadLine(), out int num); if (!parsed) { continue; } if (!actionMap.ContainsKey(num)) { continue; } action = actionMap[num]; } _Current = _Current.ApplyAction(action); if (_Current.Fight.Status != FightStatus.Ongoing) { DisplayStatus(_Current); break; } } }