yangyuan / hearthrock

Hearthstone® Bot Engine
https://yangyuan.github.io/hearthrock/
MIT License
258 stars 68 forks source link

Example with logic #18

Closed SethKitchen closed 6 years ago

SethKitchen commented 7 years ago

Can you post an example in any language where you are actually parsing the card data and thinking about a good move? When I look at play actions and objects in the debugger they are just numbers. I don't know how to get card data for them

AiSatan commented 7 years ago

I have "renderer" class, as example


 public class Renderer
    {
        public List<int> PlayOption { get; private set; }
        public RockCard Card { get; private set; }
        public string CardName
        {
            get
            {
                if (Card != null)
                {
                    return Card.Name;
                }
                if (Minion != null)
                {
                    return Minion.Name;
                }
                return FriendlyHero != null ? FriendlyHero.Name : "";
            }
        }
        public RockMinion Minion { get; private set; }
        public RockMinion FriendlyMinion { get; private set; }
        public RockMinion EnemyMinion { get; private set; }
        public RockHero FriendlyHero { get; private set; }
        public RockHero EnemyHero { get; private set; }

        public bool IsTargetToEnemyMinion => EnemyMinion != null;
        public bool IsTargetToEnemyHero => EnemyHero != null;
        public bool IsTargetToEnemy => IsTargetToEnemyHero || IsTargetToEnemyMinion;
        public bool IsSpell => Card != null && Card.CardType == RockCardType.Spell;
        public bool IsWeapon => Card != null && Card.CardType == RockCardType.Weapon;
        public bool IsMinion => Card != null && Card.CardType == RockCardType.Minion;
        public bool IsTheCoin => Card != null && Card.Name == "The Coin";
        public bool WeCanPlayIt => Card != null;
        public bool IsMinionCanAttack => Minion != null && (EnemyHero != null || EnemyMinion != null);

        public Renderer(RockSceneContext context, List<int> action)
        {
            PlayOption = action;

            var id = action[0];
            switch (action.Count)
            {
                case 1:
                    if (context.IsFriendlyCard(id))
                    {
                        Card = context.GetRockCard(id);
                    }
                    else if (context.IsFriendlyHeroPower(id))
                    {
                        Card = context.GetRockCard(id);
                    }
                    break;
                case 2:
                    RenderUseCard(context, id);
                    RenderTarget(context, action[1]);
                    break;
                case 3:
                    //not supported
                    break;
            }
        }

        private void RenderUseCard(RockSceneContext context, int id)
        {
            if (context.IsFriendlyCard(id))
            {
                Card = context.GetRockCard(id);
            }
            else if (context.IsFriendlyHeroPower(id))
            {
                Card = context.GetRockCard(id);
            }
            else if (context.IsFriendlyMinion(id))
            {
                Minion = context.GetRockMinion(id);
            }
            else if (context.IsObjectType(id, RockObjectType.FriendlyHeroWeapon))
            {
                Card = context.GetRockCard(id);
            }
            else if (context.IsObjectType(id, RockObjectType.FriendlyHero))
            {
                FriendlyHero = context.GetRockHero(id);
            }
            else if (context.IsObjectType(id, RockObjectType.FriendlyHeroPower))
            {
                //FriendlyHero = context.GetRockHero(id);
            }
            else
            {
                //throw new Exception("action.Count == 2");
            }
        }

        private void RenderTarget(RockSceneContext context, int secondId)
        {
            if (context.IsObjectType(secondId, RockObjectType.EnemyHero))
            {
                EnemyHero = context.GetRockHero(secondId);
            }
            else if (context.IsEnemyMinion(secondId))
            {
                EnemyMinion = context.GetRockMinion(secondId);
            }
            else if (context.IsObjectType(secondId, RockObjectType.FriendlyHero))
            {
                FriendlyHero = context.GetRockHero(secondId);
            }
            else if (context.IsFriendlyMinion(secondId))
            {
                FriendlyMinion = context.GetRockMinion(secondId);
            }
            else
            {
                //throw new Exception("action.Count == 2");
            }
        }
    }
yangyuan commented 7 years ago

The code AiSatan posted is a good example to parse candidate play actions.

Parsing the card data Hearthrock does not provide card database, but it provides CardId of objects. https://github.com/yangyuan/hearthrock/blob/master/src/Hearthrock.Contracts/IRockObject.cs You can find card information via any card database on the internet with that CardId.

Thinking about a good move This is a big topic. Hearthstone is complex enough and I believe there is no existing method which can apply perfectly on Hearthstone. I tried to apply simple minimax algorithm, it sort of works if I can predict the result of actions. I am doing research on predicting the result of actions with historical data (use machine learning), still on going.

AiSatan commented 7 years ago

I will be glad to help you! Only problem that I see - new cards, it's crash all learning, because the "meta" will change