Author Topic: Guard Bots  (Read 6463 times)

BGB

  • Global Moderator
  • Full Member
  • *****
  • Posts: 226
    • View Profile
Guard Bots
« on: September 21, 2014, 05:22:33 PM »
Myself and another botter have been using some guard bots to help defend some areas on the board. The bots have been working well, but its a matter of having enough of them to keep up with the raids. Also there are a couple more things I would like to do to make them better yet.

With the introduction of more gathering bots via Mithral's client... more guarding may be needed, and it has been asked for many times on the forum. However, one issue I can see happening is if several players are running guarding bots, they may become less effective if they start targeting the same opponents and possibly destructing at the same time causing more loss than gain. Although having multiple ones running is necessary in case of misfiires, bad wallets, crashes, or other failures.

I was wondering, especially with the introduction of Mithral's heart seeking if players would be interested in submitting their players for guarding and getting back HUC in return for the favor.

For example if you have a general that has 10 characters, you could transfer the character to a HUC address that is running a guarding algo. Making sure you set the reward address to yourself. You would get a few HUC per character submitted to the reward address (there would have to me some limitations such as not transferring right before a disaster). The general could be used to gather coins in a safe position and still send coins to the reward address while the hearted characters are used in the guarding system. Once the characters are used up guarding the general could be sent back to you via the reward address.

Looking for any thoughts?

Mithril Man

  • Hero Member
  • *****
  • Posts: 589
    • View Profile
    • Mithril Man Web!
Re: Guard Bots
« Reply #1 on: September 22, 2014, 01:14:58 PM »
as you pointed out, using a player with several hunters could cause problems when applying bot behaviours because you can't synchronize well movements, anyway it could be interesting trying it, how do you mean to handle player transfer?

If you plan to run some server that's always on, and intercept players transfered there and applying to them your behaviours, i could implement a gui interface in my client to transfer it to that address

but what will your service do in detail?
i mean, a player value is at least 10 huc, how much coin will you return to transfered players?
Alternative GUI client for Huntercoin http://www.mithrilman.com
HUC donation: HMSCYGYJ5wo9FiniVU4pXWGUu8E8PSmoHE
BTC donation: 1DKLf1QKAZ5njucq37pZhMRG67qXDP3vPC

BGB

  • Global Moderator
  • Full Member
  • *****
  • Posts: 226
    • View Profile
Re: Guard Bots
« Reply #2 on: September 22, 2014, 03:01:51 PM »
as you pointed out, using a player with several hunters could cause problems when applying bot behaviours because you can't synchronize well movements, anyway it could be interesting trying it, how do you mean to handle player transfer?

If you plan to run some server that's always on, and intercept players transfered there and applying to them your behaviours, i could implement a gui interface in my client to transfer it to that address

but what will your service do in detail?
i mean, a player value is at least 10 huc, how much coin will you return to transfered players?

Lets say that a group decides on a color to gather from. So there are a bunch of Blue Mithral and other bots gathering coins, and heart seekers. If your lucky general gets a bunch of hearts, it might be good to use those extra characters to protect the blue coin zones from small attacks which we are seeing all the time.

While setting your reward address to your own wallet, you could transfer the player to a centralized guard address. The guarding bots protect coin areas with their characters while still using the general to harvest coins. Any coins harvested by the general of course would be sent back to the owner via the reward address. Also when the characters are used up from attacking, the general could be sent back to the owner via the reward address also.

Obviously, this is a service that a team might pay HUC for, but with the current gamestate I would be willing to send a few HUC per character to encourage some use and build some teams. Of course who knows what the future gamestate will be, and this might not be needed or some alterations would be needed.  Once you have those kind of bots in MME, than anybody could host a certain type of bot and share their functionality so the game characters can work together to fulfill a common objective.


Mithril Man

  • Hero Member
  • *****
  • Posts: 589
    • View Profile
    • Mithril Man Web!
Re: Guard Bots
« Reply #3 on: September 22, 2014, 04:55:24 PM »
ok I understand what you mean and this is what I aim to achieve with the Behaviours market i've in mind

I didn't though about creating a "public service" on a single client instance, rather than allow coders to implement their behaviours and sell them on market, so everyone could buy/rent for a fee, but your solution could be complementary to my solution because you could code that behaviour, sell it on market and let buyers run their business for people who doesn't have my client or don't want to buy and configure the behaviour or... whatever

Just today i was looking for a source project that allow intellisense and syntax highlight to develop your own bot, and was thinking about how to let test it, it's something a bit complicated and need more thought...

anyway the code could be pretty straightforward and i could start anyway setting up a market and put there some of my future bot while i work on the public SDK.
Actually it's already possible for any C# coder create its own behaviour, but without guidelines it would be pretty hard and I've still to finish the behaviour system, anyway i want to share you all the SimpleHeartSeeker, that's working fine, to see how it's readable and easy to understand and improve (i commented heavily).

Let me start pasting current code:
Code: [Select]
using HuntercoinME.Behaviour.SDK.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace HuntercoinME.Behaviour.SDK.Behaviours {
   /// <summary>
   /// this bot just wander around, seeking for hearts
   /// when a new hunter is generated, he move that hunter one step further, outside spawning area
   /// </summary>
   [BehaviourDefinition("SimpleHeartseeker", "Collect heart that spawns in a specified range", "1")]
   public class SimpleHeartseeker : HunterBehaviour {
      /// <summary>
      /// maximum number of allowed hunters per player
      /// </summary>

      [BehaviourParameter("Step Out", "If true, whenever a new hunter is created, move that hunter one step away from spawn area", DefaultValue = "true")]
      public bool MoveAwayFromSpawnArea { get; set; }

      [BehaviourParameter("Sight Range", "The range given to the bot, in order to move him thoward a visible heart. Wider the range, slower the computation will be", 0, 501,
         Hint = "insert Y coordinate", DefaultValue = "250")]
      public int SightRange { get; set; }

      [BehaviourParameter("Max Hunters", "The number of max hunters this behaviour have to try to create", 1, 20,
         Hint = "Maximum creable hunters", DefaultValue = "20")]
      public int MaxHunters { get; set; }


      /// <summary>
      /// hold a list of hunters to be moved from spawn area but that couldn't move earlier because of pending moves on hunter
      /// </summary>
      private List<string> _huntersToMove;

      /// <summary>
      /// Heart currently targeted
      /// </summary>
      private Heart _currentTargetedHeart;

      public SimpleHeartseeker(BehavioursManager manager)
         : base(manager, "HeartSeeker", registerToRequests: false) {
         this._huntersToMove = new List<string>();
         this._currentTargetedHeart = Heart.Empty;

         this.MoveAwayFromSpawnArea = true;
         this.SightRange = 250;
         this.MaxHunters = 20;
      }


      public override void OnBeforeFirstPerformUpdate() {
         ///check for each waypoint, if there is an heart and set it as the target
         if (this.AutomatedHunter.Hunter.CurrentWaypoints != null) {
            foreach (var waypoint in this.AutomatedHunter.Hunter.CurrentWaypoints) {
               if (HasHeart(waypoint)) {
                  this._currentTargetedHeart = this.CurrentGameState.Hearts.First(h => h.Coordinate == waypoint);
                  break;
               }
            }
         }
      }


      /// <summary>
      /// look around the map to see if an Heart is visible and try to reach it
      /// </summary>
      /// <returns></returns>
      public override void PerformUpdate() {
         ///if this isn't the first update and MoveAwayFromSpawnArea = true,
         ///check if I've just created a new hunter (so previous block i've picked an heart) and move it one step away from spawn area
         if (this.MoveAwayFromSpawnArea && !this.IsFirstUpdate) {
            var newHunters = this.AutomatedHunter.Player.Hunters.Where(h => h.IsNew);
            //if i've picked up an heart, move the new hunter away
            foreach (var hunter in newHunters) {
               if (this.CanMove && IsSpawnArea(hunter.Hunter.CurrentPosition)) {
                  this.GameBotService.MoveTo(
                     hunter.Name,
                     new Coordinate(
                        (short)(hunter.Hunter.CurrentPosition.X == 0 ? 1 : hunter.Hunter.CurrentPosition.X - 1),
                        (short)(hunter.Hunter.CurrentPosition.Y == 0 ? 1 : hunter.Hunter.CurrentPosition.Y - 1)
                     )
                  );
                  this.GameBotService.LogDebug("New hunter created, move him 1 step away from spawn area");
               }
               //if hunter is in pending move, save it in a temporary list in order to move it as soon as hunter can move
               else {
                  _huntersToMove.Add(hunter.Name);
                  this.GameBotService.LogDebug("New hunter created, but this hunter can't move so store him and move later");
               }
            }
         }


         //if I'm in pending or have reached maximum allowed hunters to be created
         if (!this.CanMove || this.AutomatedHunter.Player.Hunters.Count >= this.MaxHunters) return;

         //ensure I'm moving thoward an heart, or clear _currentTargetedHeart
         if (
            _currentTargetedHeart != Heart.Empty
            && (
               HasHeart(_currentTargetedHeart.Coordinate) == false
               ||
               this.AutomatedHunter.Hunter.CurrentWaypoints == null
               ||
               (this.AutomatedHunter.Hunter.CurrentWaypoints != null && this.AutomatedHunter.Hunter.CurrentWaypoints.Exists(wp => wp == _currentTargetedHeart.Coordinate) == false)
               )
            ) {
            _currentTargetedHeart = Heart.Empty;
            this.GameBotService.LogDebug("Not going thoward heart, clear current target");
         }

         ///if i've found an heart and some other behaviour has set a queued moves, i remove that
         ///todo: handle better the priority
         if (this._currentTargetedHeart != Heart.Empty && this.GameBotService.HasQueuedMove(this.AutomatedHunter.Name)) {
            this.GameBotService.ClearQueuedMove(this.AutomatedHunter.Name);
            this.GameBotService.LogDebug("Following hearth, clear previous queued moves");
         }

         ///get a list of all hearts in range
         var hearts = this.CurrentGameState.Hearts
            .Where(l => l.Coordinate.Distance(CurrentHunterPosition) <= this.SightRange)
            .OrderBy(l => l.Coordinate.Distance(CurrentHunterPosition))
            .ToList();

         //find the nearest hearth, ensuring it stays into sightrange boundaries
         Path bestPath = null;
         Heart foundHeart = new Heart();
         foreach (var heart in hearts) {
            var path = this.GameBotService.ComputePath(new List<Coordinate> { this.CurrentHunterPosition, heart.Coordinate });
            if (bestPath == null || path.Length < bestPath.Length) {
               bestPath = path;
               foundHeart = heart;
            }
            else {
               //if bestPath < of the straight distance from hunter to this heart, exit from loop
               if (bestPath.Length <= this.CurrentHunterPosition.Distance(heart.Coordinate)) {
                  break;
               }
            }
         }

         ///if heart found, go to take it if it's different from current target and is nearer
         if (bestPath != null && bestPath.Length < this.SightRange && foundHeart != _currentTargetedHeart) {
            this._currentTargetedHeart = foundHeart;
            this.GameBotService.MoveTo(this.AutomatedHunter.Name, foundHeart.Coordinate);
            this.GameBotService.LogDebug("Heart found at coordinate ({0};{1})", foundHeart.Coordinate.X, foundHeart.Coordinate.Y);
         }
         //if heart not found, set current target to null
         else if (foundHeart == null) {
            this._currentTargetedHeart = Heart.Empty;
            this.GameBotService.LogDebug("no heart in sight range");
         }



         //if hunter can move, move eventually others hunter that are in spawn area
         for (int i = _huntersToMove.Count - 1; i >= 0; i--) {
            MyHunter hunter = this.AutomatedHunter.Player.Hunters.FirstOrDefault(h => h.Name == _huntersToMove[i]);
            if (hunter != null) {
               this.GameBotService.LogDebug("Move hunter {0} that was waiting hunter to exit from pending", hunter.Name);
               //check if hunter is in spawn area
               if (IsSpawnArea(hunter.Hunter.CurrentPosition)) {
                  this.GameBotService.MoveTo(
                     hunter.Name,
                     new Coordinate(
                        (short)(hunter.Hunter.CurrentPosition.X == 0 ? 1 : hunter.Hunter.CurrentPosition.X - 1),
                        (short)(hunter.Hunter.CurrentPosition.Y == 0 ? 1 : hunter.Hunter.CurrentPosition.Y - 1)
                     )
                  );
               }

               //remove hunters from the list, since now i've moved him
               _huntersToMove.RemoveAt(i);
               this.GameBotService.LogDebug("remove hunter {0} from _huntersToMove (pending list)", hunter.Name);
            }
         }
         return;
      }


      /// <summary>
      /// Return true if current coordinate is a spawn area
      /// </summary>
      /// <param name="coordinate"></param>
      /// <returns>true if current coordinate is a spawn area</returns>
      public bool IsSpawnArea(Coordinate coordinate) {
         return this.CurrentGameState.GetAt(coordinate.X, coordinate.Y).IsSpawnArea;
      }

      /// <summary>
      /// Giving a coordinate, return if an heart is there
      /// </summary>
      /// <param name="coordinate"></param>
      /// <returns></returns>
      public bool HasHeart(Coordinate coordinate) {
         return this.CurrentGameState.GetAt(coordinate.X, coordinate.Y).HasHeart;
      }
   }
}



the "magic" of the dynamic behaviour system is that you have to derive the behaviour from HunterBehaviour class, decorate your class with the BehaviourDefinitionAttribute where you set name, description and version and decorate the public property you want to expose within interface, decorating with BehaviourParameterAttribute

my system take care of autogenerating the interface depending on those attributes, and the rest is up to you, you have just to override few methods:

OnBeforeFirstPerformUpdate is called the very first update after you start the game and it's used when the client warmup and load assigned behaviours from disk (in case client crashed or user closed it for any reason and restarted) and it's useful to infer current behaviour status because actually i don't store status (don't think it's much useful because if you lose some blocks that status is old, so i prefer to add some intelligence to behaviour to infer it's status from current game state)

PerformUpdate
this is the heart of the behaviour, every new block it get called and here is where the coder has to implement it's logic


let me post the base class behaviours hinerit from: HunterBehaviour
Code: [Select]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using HuntercoinME.Behaviour.SDK.Model;

namespace HuntercoinME.Behaviour.SDK {
   /// <summary>
   /// base class to implement Hunter Behaviours
   /// </summary>
   public abstract class HunterBehaviour : IBehaviour {
      /// <summary>
      /// Behaviour Name, Set
      /// </summary>
      public string Name { get; protected set; }

      /// <summary>
      /// Configuration Name used as a reference for current instance(null if instantiated by code and not by an existing configuration)
      /// </summary>
      public string ConfigurationReferenceName { get; set; }

      /// <summary>
      /// Id of the configuration used as a reference for current instance(null if instantiated by code and not by an existing configuration)
      /// </summary>
      public Guid ConfigurationReferenceId { get; set; }


      public string Version { get; protected set; }

      public string Creator { get; protected set; }

      /// <summary>
      /// previous game state
      /// </summary>
      public GameState PreviousGameState { get; private set; }

      /// <summary>
      /// current game state
      /// </summary>
      public GameState CurrentGameState { get; private set; }

      /// <summary>
      /// reference the current hunter that use this behaviour instance
      /// </summary>
      public MyHunter AutomatedHunter { get; internal set; }

      /// <summary>
      /// current hunter position
      /// </summary>
      public Coordinate CurrentHunterPosition { get; protected set; }

      /// <summary>
      /// List of Actions that a bot can do
      /// </summary>
      public List<BehaviourAction> Actions { get; protected set; }


      /// <summary>
      /// Game service used to perform actions like move, update, destroy
      /// </summary>
      protected IGameBotService GameBotService { get; private set; }

      /// <summary>
      /// If true, this behaviour can listen to other behaviours requests
      /// </summary>
      public bool RegisterToRequests { get; private set; }

      /// <summary>
      /// return if the hunter can move (no pending moves)
      /// </summary>
      public bool CanMove { get; private set; }

      /// <summary>
      /// return if the hunter has queued moves
      /// </summary>
      public bool HasQueuedMoves { get; set; }

      /// <summary>
      /// Return if this is the first update (no previous moves done)
      /// </summary>
      public bool IsFirstUpdate { get; private set; }

      /// <summary>
      ///
      /// </summary>
      /// <param name="manager"></param>
      /// <param name="registerToRequests">if true, this behaviour can respond to other behaviours requests</param>
      public HunterBehaviour(BehavioursManager manager, string behaviourName, bool registerToRequests) {
         this.GameBotService = manager.GameBotService;
         this.RegisterToRequests = registerToRequests;
         this.Name = behaviourName;
         this.IsFirstUpdate = true;
      }

      /// <summary>
      /// Update behaviours
      /// </summary>
      /// <param name="gameState"></param>
      public void Update(GameState currentGameState, GameState previousGameState) {
         this.PreviousGameState = previousGameState;
         this.CurrentGameState = currentGameState;
         this.CurrentHunterPosition = this.AutomatedHunter.Hunter.CurrentPosition;

         this.CanMove = this.AutomatedHunter.PendingMove == null;
         this.HasQueuedMoves = this.GameBotService.HasQueuedMove(this.AutomatedHunter.Name);

         GameBotService.LogDebug("Performing {1} update to {0}", this.Name, AutomatedHunter.Name);

         if (this.IsFirstUpdate) {
            OnBeforeFirstPerformUpdate();
         }

         try {
            PerformUpdate();
         }
         catch (Exception ex) {
            GameBotService.LogError("Error Performing {1} update to {0}", ex, this.Name, AutomatedHunter.Name);
         }

         if (this.IsFirstUpdate) {
            this.IsFirstUpdate = false;
         }
      }

      /// <summary>
      /// called before the first PerformUpdate
      /// </summary>
      public abstract void OnBeforeFirstPerformUpdate();

      /// <summary>
      /// Update behaviours
      /// </summary>
      public abstract void PerformUpdate();


      /// <summary>
      /// A request has been raised from a behaviour, return true if the current behaviour can handle it
      /// this method will be invoked sorting the hunter position of each behaviour, so nearest can help faster then others
      /// </summary>
      /// <param name="hunter">MyHunter doing the request</param>
      /// <param name="request">request</param>
      /// <param name="arguments">arguments of the request (key,value)</param>
      /// <returns></returns>
      public virtual bool OnRequestIncome(MyHunter hunter, string request, Dictionary<string, object> arguments) {
         return false;
      }



      /// <summary>
      /// write in the log the current waypoints of current hunter
      /// </summary>
      /// <param name="hunterName"></param>
      public void Debug_DumpQueuedPath(string hunterName) {
         var path = GameBotService.GetHunterQueuedPath(hunterName);
         string pathString = String.Empty;
         foreach (var p in path) {
            pathString += String.Format("{0};{1}-", p.X, p.Y);
         }
         if (pathString != String.Empty) {
            GameBotService.LogDebug("QueuedPath: {0}", pathString);
         }
      }
   }
}


here you can see some internals, behaviours code shouldn't know all about this but could be useful, here you can see the property it expose and the LifeCycle (Update call PerformUpdate after setting CurrentGameState, PreviousGameState and other shortcut properties

as you see you have previousgamestate and currentgamestate that you can use to compare things, etc...

for the moment forget about *Request* things because this is what i'm working on: a system which allow behaviours to exchange messages (ask for help, setting target in order to not pick the same, etc...)
It's a work in progress so things can change (advice are welcome)


I imagine a bright future for chained behaviours and generic behaviour i still need to implement (useful for disaster run and other stuffs)

P.S.
3rd party source code behaviours should be validated before being sold on market (or i need a good way to sandbox them) in order to prevent possible harm because a malicious coder could do anything with C#(in some extents you actually have the same critical issue with the pyton bot sdk)
Alternative GUI client for Huntercoin http://www.mithrilman.com
HUC donation: HMSCYGYJ5wo9FiniVU4pXWGUu8E8PSmoHE
BTC donation: 1DKLf1QKAZ5njucq37pZhMRG67qXDP3vPC

Mithril Man

  • Hero Member
  • *****
  • Posts: 589
    • View Profile
    • Mithril Man Web!
Re: Guard Bots
« Reply #4 on: September 22, 2014, 05:06:37 PM »
i forget to post the IGameBotService interface, so you can see which methods are available


Code: [Select]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HuntercoinME.Behaviour.SDK.Model;

namespace HuntercoinME.Behaviour.SDK {
   /// <summary>
   /// interface that expose commands usable to create, update and destroy hunters
   /// </summary>
   public interface IGameBotService {
      /// <summary>
      /// Log a bot debug message
      /// </summary>
      /// <param name="message"></param>
      void LogDebug(string message, params object[] args);

      /// <summary>
      /// Log a bot debug error and can have an exception
      /// </summary>
      /// <param name="message"></param>
      void LogError(string message, Exception ex, params object[] args);


      /// <summary>
      /// Move an hunter straight to a specific destination
      /// </summary>
      /// <param name="hunterFullName">full name of the hunter</param>
      /// <param name="destination">destination coordinate</param>
      /// <returns></returns>
      void MoveTo(string hunterFullName, Coordinate destination);


      /// <summary>
      /// Move an hunter straight to a specific destination (append = false), or append a destination (append = true)
      /// </summary>
      /// <param name="hunterFullName">full name of the hunter</param>
      /// <param name="destination">destination coordinate</param>
      /// <param name="append">true if destination has to be appended</param>
      /// <returns></returns>
      void MoveTo(string hunterFullName, Coordinate destination, bool append);

      /// <summary>
      /// Move an hunter to a multipoint destination
      /// </summary>
      /// <param name="hunterFullName">full name of the hunter</param>
      /// <param name="waypoints">waypoints coordinates</param>
      /// <returns></returns>
      void MoveTo(string hunterFullName, List<Coordinate> waypoints);

      /// <summary>
      /// command the hunter destruction
      /// </summary>
      /// <returns></returns>
      void Destroy(string hunterFullName);

      /// <summary>
      /// Create a player
      /// </summary>
      /// <param name="playerColor">Player Color</param>
      /// <param name="playerName">Player Name</param>
      /// <param name="rewardAddress">Reward Address</param>
      /// <param name="sentence">Sentence to write in blockchain (e.g. Eureka!)</param>
      void Create(PlayerColor playerColor, string playerName, string rewardAddress, string sentence);


      /// <summary>
      /// Accept the queued actions for current player and send them, generating a transaction
      /// </summary>
      /// <param name="playerName"></param>
      void SendPlayerUpdates(string playerName);


      /// <summary>
      /// given a player color, return a random spawn area
      /// </summary>
      /// <param name="color"></param>
      /// <returns></returns>
      Coordinate GetSpawnCoordinate(PlayerColor color);

      /// <summary>
      /// given a list of waypoints, return the shortest path that link waypoints in given order
      /// </summary>
      /// <param name="wayPoints">List of waypoints</param>
      /// <returns></returns>
      Path ComputePath(List<Coordinate> wayPoints);

      /// <summary>
      /// return true if the specified hunter has queued moves
      /// </summary>
      /// <param name="hunterFullName"></param>
      /// <returns></returns>
      bool HasQueuedMove(string hunterFullName);

      /// <summary>
      /// clear an hunters queued move
      /// </summary>
      /// <param name="hunterFullName"></param>
      /// <returns></returns>
      void ClearQueuedMove(string hunterFullName);

      /// <summary>
      /// return a list of waypoint, as coordinates, that compose the path
      /// </summary>
      /// <param name="hunterFullName"></param>
      /// <returns></returns>
      List<Coordinate> GetHunterQueuedPath(string hunterFullName);
   }
}
Alternative GUI client for Huntercoin http://www.mithrilman.com
HUC donation: HMSCYGYJ5wo9FiniVU4pXWGUu8E8PSmoHE
BTC donation: 1DKLf1QKAZ5njucq37pZhMRG67qXDP3vPC

domob

  • Developer
  • Sr. Member
  • *****
  • Posts: 284
    • View Profile
Re: Guard Bots
« Reply #5 on: September 23, 2014, 06:43:02 AM »
Since there was some discussion (with BAC) on the Bitcointalk thread, let me chime in here as well:  I'm willing to write a "guarding" logic for my own framework, too, so people could set up their own guarding bots.  Do you want to discuss particular logic to do the guarding?  You have probably more experience than I in these matters.  (Unless you think that publishing the logic helps the attackers.)
Use your Namecoin-ID as OpenID: https://nameid.org/
Donations: 1domobKsPZ5cWk2kXssD8p8ES1qffGUCm | HBkxA5QmYSATFoPN1wFk8eBkgwPpY97Mfu