Terpla adventures or blog-style guide for begginers

Thirdly, I do not want to run from rats - it’s a waste of time. In earlier versions, there was an algorithm for killing rats. We use it partially.

In the ship I have 2 drones and a ShieldBooster. We will configure the drones for the aggressive mode and we will release them on arrival to asteroid belt. Before the warp it is necessary to scope the drones to the drone bay.

image

Let’s write the functions for working with drones:

//  our window with drones
IWindowDroneView	WindowDrones	=>
	Measurement?.WindowDroneView?.FirstOrDefault();

//  drones in bay
DroneViewEntryGroup DronesInBayListEntry =>
	WindowDrones?.ListView?.Entry?.OfType<DroneViewEntryGroup>()?.FirstOrDefault(Entry => null != Entry?.Caption?.Text?.RegexMatchIfSuccess(@"Drones in bay", RegexOptions.IgnoreCase));

// drones in space
DroneViewEntryGroup DronesInSpaceListEntry =>
	WindowDrones?.ListView?.Entry?.OfType<DroneViewEntryGroup>()?.FirstOrDefault(Entry => null != Entry?.Caption?.Text?.RegexMatchIfSuccess(@"Drones in Local Space", RegexOptions.IgnoreCase));

int?	DronesInSpaceCount => DronesInSpaceListEntry?.Caption?.Text?.AsDroneLabel()?.Status?.TryParseInt();
int?	DronesInBayListEntryCount => DronesInBayListEntry?.Caption?.Text?.AsDroneLabel()?.Status?.TryParseInt();

// check our drones and scope them
void DroneEnsureInBay()
{
	if(0 == DronesInSpaceCount)
		return;

	DroneReturnToBay();
	
	Host.Delay(4444);
}

//  scope our drones 
void DroneReturnToBay()
{
	Host.Log("return drones to bay.");
	Sanderling.MouseClickRight(DronesInSpaceListEntry);
	Sanderling.MouseClickLeft(Menu?.FirstOrDefault()?.EntryFirstMatchingRegexPattern("return.*bay", RegexOptions.IgnoreCase));
}

// launch our drones
void DroneLaunch()
{
	Host.Log("launch drones.");
	Sanderling.MouseClickRight(DronesInBayListEntry);
	Sanderling.MouseClickLeft(Menu?.FirstOrDefault()?.EntryFirstMatchingRegexPattern("launch", RegexOptions.IgnoreCase));
}

Now we need to make sure that the drones are scoped before our ship warp to anywhere.

Func<object>	MainStep()
{
	// ...

	if(ReadyForManeuver)
	{	
		DroneEnsureInBay();
		
		//  ...
	}
}

In belt we should launch our drones and activate our shield booster if the rats attacked us.

Func<object> InBeltMineStep()
{if(ShouldSwitchMiningSite)
		return MainStep;

	EnsureWindowInventoryOreContainerIsOpen();

	EnsureOverviewTypeSelectionLoaded();

	if(OreContainerFilledForOffload)
		return null;
		
	if (!(ReadyForManeuver))
	{
		DelayWhileUpdatingMemory(6000);
		return InBeltMineStep;	
	}
		
	if (!(DronesInSpaceCount > 0) && (DronesInBayListEntryCount > 0))
		DroneLaunch();

	bool ShouldTurnOnShieldBooster = ShieldHpPercent <90;
	ToggleShieldBooster(ShouldTurnOnShieldBooster);

        //  the rest of the code is unchanged...

	return InBeltMineStep;
}

Now we’ll write a function to turn on / off the shield booster.

// get all shield boosters in one array
Sanderling.Accumulation.IShipUiModule[] SetModuleShieldBoosters =>
	Sanderling.MemoryMeasurementAccu?.Value?.ShipUiModule?.Where(module => module?.TooltipLast?.Value?.IsShieldBooster ?? false)?.ToArray();

// if true then we should turn on shield booster if false - turn off
void ToggleShieldBooster(bool turnOn = true)
{
	var shieldBooster = SetModuleShieldBoosters?.FirstOrDefault();
	if((shieldBooster?.RampActive ?? false) !=turnOn)
	{		
		Host.Log("toggle Shield Booster.");
		ModuleToggle(shieldBooster);
	}
}

Now we will collect all the changes in one script and test it in practice.
PS: I’m testing on the old version of Sanderling ((v17.11.19 - it’s free), so local chat checking is disabled.

3 Likes

reserved for future posts
reserved for future posts

reserved for future posts
reserved for future posts
reserved for future posts

reserved for future posts
reserved for future posts
reserved for future posts
reserved for future posts

reserved for future posts
reserved for future posts
reserved for future posts
reserved for future posts
reserved for future posts

can you put that adapted to abot, pls?
i try to travel with abot, but

yield return RouteElementMarkerNext.ClickMenuEntryByRegexPattern(bot, "Jump through stargate");

doesn’t make the left click on jump.
thank you :slight_smile:

Later:

Im dumbest, i found a method is not clean, but is working:

					{
						var MenuEntry =
	memoryMeasurement?.Menu?.FirstOrDefault()
	?.Entry?.FirstOrDefault(candidate => candidate.Text.RegexMatchSuccessIgnoreCase("dock|Jump through stargate"));
						if (null != MenuEntry)
							yield return new BotTask { Effects = new[] { MenuEntry.MouseClick(BotEngine.Motor.MouseButtonIdEnum.Left) } };

						yield return new BotTask { Effects = new[] { RouteElementMarkerNext.MouseClick(BotEngine.Motor.MouseButtonIdEnum.Right) } };

		}
1 Like

Hey Terpla, looks like you have discovered a new bot! Thank you for sharing this with us!
I added a link to this bot to the bot catalog: https://forum.botlab.org/t/bot-65b07837-mining-script-improved-by-terpla-version-tpminingscript-2018-06-21v2/1070

I try to make abot to take the asteroids from infopannel…

if I change retreatbookmark to "asteroid " then he take first menu.

yield return new MenuPathTask
{
RootUIElement = memoryMeasurement?.InfoPanelCurrentSystem?.ListSurroundingsButton,
Bot = Bot,
ListMenuListPriorityEntryRegexPattern = new[] { new[] { retreatBookmark }, new[] { @"dock", ParseStatic.MenuEntryWarpToAtLeafRegexPattern } },
};

but if I put twice;

			ListMenuListPriorityEntryRegexPattern = new[] { new[] { "asteroid" },  new[] { MiningFolderBookmark }, new[] { @"0", ParseStatic.MenuEntryWarpToAtLeafRegexPattern } },

he take the next level on submenu, but he didnt take the randomElement ( he take always first asteroid)

	string[] SetMiningFolderBookmark = { "asteroid" };
				Random rand = new Random();
				int IntDefault(int? entier) => entier ?? 0;
				T RandomElement<T>(IEnumerable<T> seq)
				{ var counted = seq?.ToArray(); if (!(0 < counted?.Length))
						return default(T);
					return counted[rand.Next(counted.Length)];
				}
				String MiningFolderBookmark = RandomElement(SetMiningFolderBookmark);

Also I tried like in beginers mining script ore, but honestly, is more than my skills in programming to do what i want.( to take the asteroid belts for ratting like a second source of rats in same system ). Can you help me?

I think this is normal behavior for this code.

string[] SetMiningFolderBookmark = { "asteroid" };

In this string you create an array of one element. Random of one element is always this element …

In my mining script i have many bookmarks in array, then the random makes sense

string[] SetMiningSiteBookmark = new[] {
    "mine1",
    "mine2",
    "mine3",
    "mine4",
    };

bool InitiateWarpToRandomMiningBookmark() =>
    InitiateDockToOrWarpToLocationInSolarSystemMenu(RandomElement(SetMiningSiteBookmark));

Random selects one of the bookmarks and sends it to the InitiateDockToOrWarpToLocationInSolarSystemMenu function.

If you look at how the code of the default script works, you can see another algorithm:

bool InitiateWarpToMiningSite() =>
    InitiateDockToOrWarpToLocationInSolarSystemMenu("asteroid belts", PickNextMiningSiteFromSystemMenu);

We send “asteroid belts” as submenuLabel and a function that selects the “random” asteroid belt.

bool InitiateDockToOrWarpToLocationInSolarSystemMenu(
    string submenuLabel,
    Func<IReadOnlyList<MemoryStruct.IMenuEntry>, MemoryStruct.IMenuEntry> pickPreferredDestination = null)
{      

    var listSurroundingsButton = Measurement?.InfoPanelCurrentSystem?.ListSurroundingsButton;   // looking our first button 
    Sanderling.MouseClickRight(listSurroundingsButton); // and click it

    var submenuEntry = Measurement?.Menu?.FirstOrDefault()?.EntryFirstMatchingRegexPattern("^" + submenuLabel + "$", RegexOptions.IgnoreCase); // looking our "asteroid belts" button
    Sanderling.MouseClickLeft(submenuEntry);      // and click it

    var submenu = Measurement?.Menu?.ElementAtOrDefault(1); // looking array of asteroids (see picture)
    var destinationMenuEntry = pickPreferredDestination?.Invoke(submenu?.Entry?.ToList()) ?? submenu?.Entry?.FirstOrDefault(); // we call the function and pass it an array of asteroids as a parameter

      // ...............
 }

image

In the default script, this function is called PickNextMiningSiteFromSystemMenu. You can see how it works (selects the asteroids visited), but with the same success we could call RandomElement function.

In the A-Bot program, the MenuPathTask task is used in several places and if we change it using random, then in this places there will also be random values.
Therefore, you must first get an array of asteroids (like Menu?.ElementAtOrDefault(1) in Sanderling), select a random one and use it in the MenuPathTask task.

This is what comes to mind without thinking. But it’s late night and I’ll go to bed.
Think, try different options, ask questions if you come to a standstill. If everything is bad, then we’ll figure something out.

1 Like

dont hurry and be care with your health :slight_smile: take your time.
so i already reverse engineering the code for picking the asteroids from asteroid belts folder ( Menu?.ElementAtOrDefault(1) in Sanderling) before crying for help :))
Its a pain in the ass with the abot, btw, for one with my lvl.
The result is always the same: “not best type found for implicitly-typed array” for random and for menu?element AtOrddefault.
I believe the problem come from the menupathtask, because he didnt read if its a folder and inside is a new array of “asteroids”. so he read first menu (0). In the second script ( the one with random) is the same.
Ofcourse, I can put all asteroids in the menu (0) but… I have 36 asteroid belts and 6 chars who play in the same system. sometime I do rats ( but i have 5 anomaly at max) but mining make the rats come to asteroids, so is a good source :slight_smile:
I will keep testing, maybe I’ll find a way

I did not investigate the structure of the Menu itself, it may be necessary to work with some other field (Entry or something else). It is necessary to watch the Measurement in debug mode.

tomorow i will try to put the regular one (warp from folder asteroid belts) in place, i will update my results

One step at time :))
from the last time, i tried and tried …many times, until I isolated the code into one single script. And all time it took only the first asteroid. Still, the original script is working ( tested many nights).

And look at my discovery:
Chronologically, After the first click to warp at the first asteroid, the script became busy with other things ( host logs lines 70-85, he make measurements for modules. So he has time to update in memory the visited locations because he enter in warp in almost same time.
But with an vni/capsule, and without any other things to do, he try again the warp at the same spot, ( first asteroid) at max 1 sec . Surprise, because he discover the menu isnt the same, ( no warp text at first asteroid),he declare "

Host.Log("no suitable menu entry found on '" + destinationMenuEntry?.Text + "'");
	return true;

and does not register/update the location in visited locations

So I put an delay of 8 sec at line 557 in original script

	if (null != maneuverMenuEntry)
	{
		Host.Log("initiating '" + maneuverMenuEntry.Text + "' on '" + destinationMenuEntry?.Text + "'");
		Sanderling.MouseClickLeft(maneuverMenuEntry);
		Host.Delay(8000);// this one is too big , of course2-3 sec is enough, i think
		return false;
	}

and … is working, he travel like a boss from one asteroid to another.

Now, he take the asteroids one after another in order ( first, the second etc etc until 38 in my case) but i believe you can use the random code and if the new asteroids is already in memory from visited locations, he can take another one, at random. When you filled all places , you wipe the memory and you start from scratch.
For now Im happy with sandy script of fighting

3 Likes

another ones :))

in framework i cannot make him to orbit around targetselected ( current target) or any object. If i put a void to do that from overview around an object, it take too much time and the other tasks take place. Even so, its a timing thing, in time i can resolve that.
what it didnt work is the obrit using the keyboard.
I put the lines :slight_smile:

if (Broken?.Length > 0)
{
Sanderling.KeyDown(orbitKeyCode);
Sanderling.MouseClickLeft(Broken.FirstOrDefault());
Sanderling.KeyUp(orbitKeyCode);
ActivateAfterburnerExecute();
//Orbit(celestialOrbit);	
}
else
{
Sanderling.KeyDown(orbitKeyCode);
	Host.Log("press w");
Sanderling.MouseClickLeft(ListRatOverviewEntry?.FirstOrDefault(entry => !((entry?.MeTargeted ?? false) || (entry?.MeTargeting ?? false))));
				
	Host.Log("click target");
Sanderling.KeyUp(orbitKeyCode);
Host.Log("press w");
//ActivateAfterburnerExecute();
/*
Sanderling.KeyDown(VirtualKeyCode.VK_W);
Sanderling.MouseClickLeft(Broken.FirstOrDefault());
Sanderling.KeyUp(VirtualKeyCode.VK_W);
ActivateAfterburnerExecute();*/
}

I tried in both variants ( you see the code between /* */). and in fact he didnt make the click on object and sometime he didnt push the “w” ( i dont remember wich one is good or not)

I get to the next point: to do warp in A-bot to asteroids, like in framework.
So, i tried to combine the abot way of warp and to take the asteroids from infobutton.

				Queue<string> visitedLocations = new Queue<string>();
				MemoryStruct.IMenuEntry PickNextMiningSiteFromSystemMenu(IReadOnlyList<MemoryStruct.IMenuEntry> availableMenuEntries)
				{
					//Host.Log("I am seeing " + availableMenuEntries?.Count.ToString() + " mining sites to choose from.");

					var nextSite =
						availableMenuEntries
						?.OrderBy(menuEntry => visitedLocations.ToList().IndexOf(menuEntry?.Text))
						?.FirstOrDefault();

					//Host.Log("I pick '" + nextSite?.Text + "' as next mining site, based on the intent to rotate through the mining sites and recorded previous locations.");
					return nextSite;
				}

........ 
					yield return new MenuPathTask
					{
						RootUIElement = memoryMeasurement?.InfoPanelCurrentSystem?.ListSurroundingsButton,
						Bot = bot,
						ListMenuListPriorityEntryRegexPattern = new[] { new[] { "asteroid belts" }, new[] { PickNextMiningSiteFromSystemMenu/* ???? how ???? ToString*/ }, new[] { @"0", ParseStatic.MenuEntryWarpToAtLeafRegexPattern } },
					};

In fact I need PickNextMiningSiteFromSystemMenu which is a method to string. I tried to to that using an variable
var pickNextMiningSiteFromSystemMenu = ??
but with my knowledges i cannot do that. from what i found over the net, i have to transform him to object and this one to string. I believe its like that. Even if i try with random method it says is not no best type found for implicitly-typed array.
So Im stucked :d

What i wanna do in reality, well, on the one side to use framewok because is more fiable, the Abot is a pain in the ass with inventory, stoping the bot, you dont have an delay time, and more things. I good to do simple tasks,

is a complex structure. It is necessary to examine its fields and find the necessary ones.
If you need only string value from this structure, so maybe you should use some field like Text.

	yield return new MenuPathTask
					{
						RootUIElement = memoryMeasurement?.InfoPanelCurrentSystem?.ListSurroundingsButton,
						Bot = bot,
						ListMenuListPriorityEntryRegexPattern = new[] { new[] { "asteroid belts" }, new[] { PickNextMiningSiteFromSystemMenu(visitedLocations/*or whatever it is called?*/)?.Text ?? string.Empty }, new[] { @"0", ParseStatic.MenuEntryWarpToAtLeafRegexPattern } },
					};

In your example, var uses lazy asses when they do not want to write the variable type :slight_smile:

it says : no best type found for implicitly-typed array.

and VS guide me to implement:


		private string PickNextMiningSiteFromSystemMenu(Queue<string> visitedLocations)
		{
			throw new NotImplementedException();
		}

		private string PickNextMiningSiteFromSystemMenu(string v)
		{
			throw new NotImplementedException();
		}

		public IEnumerable<MotionParam> Effects => null;
	}

I tried with an string , is working and if i put var blabla = “skdjfh” is working

he do the same with randoms , so i supose in the end he take only string

[quote=“Terpla, post:19, topic:953”]
In your example, var uses lazy asses when they do not want to write the variable type :slight_smile:
[/quote] i saw, sanderling doesn’t love lazy peoples :d :))

Looks like i cant modify reserved post anymore…
After testing my script I found that the ModuleMeasureAllTooltip() function does not work well in a small amount of memory. So I changed it.
Now we specify in the configuration section how many Mining modules we have, how many ShieldBoosters and ArmorRepaiers.

//	Numbers of miner laser
const int MinerLasersCount = 2;
//	Numbers of ShieldBoosters
const int ShieldBoostersCount = 1;
// 	Number of ArmorRepeirs
const int ArmorRepairsCount = 0;

Our function goes through all the modules until it finds the number specified in the configuration.

void ModuleMeasureAllTooltip()
{
	Host.Log("measure tooltips of all modules.");
	
		var shieldBoostersCount = Sanderling.MemoryMeasurementAccu?.Value?.ShipUiModule.Count(m => m?.TooltipLast?.Value?.IsShieldBooster ?? false);
		var armorRapairCount = Sanderling.MemoryMeasurementAccu?.Value?.ShipUiModule.Count(m => m?.TooltipLast?.Value?.IsArmorRepairer ?? false);
		var miningLasersCount = Sanderling.MemoryMeasurementAccu?.Value?.ShipUiModule.Count(m => m?.TooltipLast?.Value?.IsMiner ?? false);

	while( (shieldBoostersCount <  ShieldBoostersCount) ||	(armorRapairCount < ArmorRepairsCount) || (miningLasersCount <  MinerLasersCount)	)
	{
		if(Sanderling.MemoryMeasurementParsed?.Value?.IsDocked ?? false)
			break;
		foreach(var NextModule in Sanderling.MemoryMeasurementAccu?.Value?.ShipUiModule)
		{
			if(null == NextModule)
				break;
	
			Host.Log("measure module.");
			//	take multiple measurements of module tooltip to reduce risk to keep bad read tooltip.
			Sanderling.MouseMove(NextModule);
			Sanderling.WaitForMeasurement();
			Sanderling.MouseMove(NextModule);
		}		
	
		shieldBoostersCount = Sanderling.MemoryMeasurementAccu?.Value?.ShipUiModule.Count(m => m?.TooltipLast?.Value?.IsShieldBooster ?? false);
		armorRapairCount = Sanderling.MemoryMeasurementAccu?.Value?.ShipUiModule.Count(m => m?.TooltipLast?.Value?.IsArmorRepairer ?? false);
		miningLasersCount = Sanderling.MemoryMeasurementAccu?.Value?.ShipUiModule.Count(m => m?.TooltipLast?.Value?.IsMiner ?? false);
	
		Host.Log("Shield Boosters count = " + shieldBoostersCount + ", Armor Repair count = " + armorRapairCount + ", Mining Lasers Count = " + miningLasersCount );

	}
}

All script can be found here (TPMiningScript-2018-08-02v3)..

3 Likes

Hey Terpla, looks like you have discovered a new bot! Thank you for sharing this with us!
I added a link to this bot to the bot catalog: https://forum.botlab.org/t/bot-4affc743-mining-script-improved-by-terpla-version-tpminingscript-2018-08-02v3/1231

is not simpler to use that?

if(Measurement?.InfoPanelRoute?.IsExpanded == false) Sanderling.MouseClickLeft(toogleButton);
else goto loop;

it happens to stay in space when you don’t travel to a station and he keeps clicking on the toggle button

@Terpla
It’s not caused by memory but by cpu
take a look onto this

it might help you as well :slight_smile:

1 Like