ikabot
ikabot copied to clipboard
Feature Request ( Improvement of existing feature)
hello is it possible that in the function distribute ressources the bot asks in which steps it should send ressources...
for example i have 2kk Wine on 5 citys and it should distribute to all cities who not producing wine ...
Actual the bot use all my 220 ships and send 110k Wine at once .. which is very slow because of the long loading time ...
so maybe it is possible to say the bot split up delivery to 20k or 40k each to minimize loading time of the ships...
and maybe also to make some multithreading so ---->Example: City1-5(Wine) City6-10(marble/sulfur/cristal) So City 1-5 loading each 20k ressources and send it to city 6 until target ressource limit is reached ... after that doing the same with city 7 ....
This feature will make ressource shipping with larger accounts much faster and efficient.
That's an interesting idea, by loading resources in multiple cities at the same time, you save time. I guess this could be automated, I will give it a try but I can't promise it will be quick, I have been really busy lately.
Yeah no problem thanks for trying.. and its not only loading in different citys .. you are also faster when splitting up ressources send from 1 city .. if you need for example transport like 200k goods and you can load like 20k in 5 min .. it will take 50 min to load and 20 min to travel... so you need about 70 min for 200k goods .. if sending them splittet .. every 5 min you can send 20k send ressources on the way so you can send away like 40k ress in 10 min and 80k after 20 min .. after that 80k traveled 20 min to the target .. other 80k has been loaded .. so splitting ressource transport in little brackets would increase the speed and effiency extrem :)
makes sense because you will perform 2 actions at the same time: load ships and transport resources, which you don't by sendint all at once not sure how much time you would actually save, I'm too lazy to do the math properly sounds like a nice time saver anyways
This needs to be implemented in the executeRoutes function. Instead of executing routes in series, ships should be split evenly across each distinct origin city. I don't really know how much time difference this will make.
yep, that is where it should be so everyone benefits from it
I'll try a little Example: given: 200 Ships 1kk Ressources which need to transported for example the palast Harbour level 20 which loads 1.5k goods/min an transport time of 20 min without flipper Example1: Loading 100k per transfer 100k loading takes 66,667 min
- transport time this are about 86 mins for one transport Example2: loading it in 15k stacks from the same city transport1: 10 min Loading Time .. 20 min travel after 30 min 15k arrived transport2: After 40 min (cause the 10 min loading time) 30k arrived transport3: After 50 min 45k arrived transport4: After 60 min 60k arrived transport5: After 70 min 75k arrived transport6: After 80 min 90k arrived so it would be nearly the same ammount if send all from one city .. but if we double the transport ammount to 200k the first example need 172 minutes for 200k ressources while letz us follow example 2 gives us following values: transport7: After 90 min 105k arrived transport8: After 100 min 120k arrived transport9: after 110 min 135k arrived transport10: after 120 min 150k arrived transport11: after 130 min 165k arrived transport12: after 140 min 180k arrived transport13: after 150 min 195k arrived transport14: after 160 min 210k arrived transport15: after 170 min 225k arrived Between transport 13. and 14. this methods beats the single transport method .. and this speed will stack up more and more depending on how high ressources amount rises...
Example3: Maybe Let us get an Example Layout for the citys .. in my example i would take my 12town layout which consits of 5 x marble 5 x wine 1x sulfur 1xcrystal .. All cities have lvl 20 harbour and marble and wine are the most transferred good as you need the one mainly for building and the other for your citizens Transport Time for Example the same as above so i want 100k Wine transfered to an other non-wine city:
now all 5 cities load simoultaniously 15k goods in 10min batch transport1: 10 min loading 20 min travel --> 30 min --->75k ressources batch transport2: after 40 min ----> 150k ressources batch transport3: after 50min -----> 225k ressources batch transport4: after 60min -----> 300k ressources batch transport5: after 70min -----> 375k ressources batch transport6: after 80min -----> 450k ressources batch transport7: after 90min -----> 525k ressources batch transport8: after 100min -----> 600k ressources batch transport9: after 110min -----> 675k ressources batch transport10: after 120min -----> 750k ressources batch transport11: after 130min -----> 825k ressources batch transport12: after 140min -----> 900k ressources batch transport13: after 150min -----> 975k ressources batch transport14: after 160min -----> 1.050k ressources batch transport15: after 170min -----> 1.125k ressources
Conclusion: Example 1 and 2 after 1 hour are nearly the same ... but Example 2 gets better and better if it runs with more ressources so the time it runs gets more.... So maybe include a function for 1 city to 1 city transfer where the bot user can say .. for example split the ships with a certain value .. so if the bot user types 10 in .. the bot will transport the goods using 10 ships to load .. after that loads 10 ships .. so like Example2 ..
Example3 ist the best solution but maybe more complicated to build in .. because he need serveral tasks .. First he must lookup the ressources in the Citys which are the sender citys ( maybe you can manually select the sender and reciever) and then split up the amount by the number of senders .. and after that for perfect efficenys split up the total ships evenly...
And maybe in the future you can set more than one receiver city ... but i think in the beginning this makes the feature too complicated to build in..
Hope that can helps you ... maybe in the future some KI or can help making this process better .. if the KI calculates the perfect split of ships according to ressource volume .. harbor level ... amount of ships .. and amount of senders
Well. as you point out, one method might be better than the other on a specific situation, it depends on the amount of resources to send. Is really a mathematical problem: given the charging speed of S, the travel time of T, find the optimal amount of ships to send on each round to deliver N resources from one city to another. Once we can answer that, we would need to generalize that to many cities (which would be even more complicated, given that many cities load at the same time so the rules kinda change there) Is really a great problem to solve, I will give it a try but unless we are able to calculate that number, I won't add this functionality, given that it would greatly increase the complexity without really adding much value (unless the user can perfectly choose the amount of ships to send on each round, which of course is not the case)
Edit: And all of this assuming we are not sending resources to another player, there you have return time which changes everything
Ok, I think I have solved it for one city to one city (simplest case):
a few constants:
total ships: S
load speed: L
(1/minutes)
travel time: T
(minutes)
resources to transport: C
number of ships to send on each round (what we want to know): R
the loading time for each round will be: (R*500) / L
the traveling time will of course be: T
Lets study for a bit the current approach, in which R = S
.
round1: starts at: 0,
ends at (R*500) / L + T
round1: starts at: (R*500) / L + T
, ends at: 2((R*500) / L + T)
...
roundn: starts at: (N-1)((R*500) / L + T)
, ends at: N((R*500) / L + T)
sending S
ships per round, there will be: C/(500*S)
rounds
so it will all end at: (C/(500*S)) * ((R*500) / L + T)
which is: C/L + T*(C/500*S)
now lets assume we have a number R
which allows us to always have ships traveling and ships loading (so we save time by doing two tasks at the same time)
round1: starts at: 0
, ends at (R*500) / L + T
round2: starts at: (R*500) / L
, ends at: 2((R*500) / L) + T
...
roundn: starts at: (N-1)((R*500) / L
, ends at: N((R*500) / L) + T
sending R
ships per round, there will be: C/(500*R)
rounds
so it will al end at: (C/(500*R))((R*500) / L) + T
which is: C/L + T
So we have C/L + T*(C/500*S)
vs C/L + T
We assume we are sending more resources that we can carry in one shipment, so C/(500*S) > 1
meaning that the second approach is always faster AND the time it takes doesn't depend on R
!!
this is all very find, but how do we find a number R
which guarantees us that there always be ships traveling and loading?
By the time the first shipment returns, we want to still have ships loading, so we can keep the loop going:
(T*L) / 500 <= S - R
or
R <= S - (T*L)/500
this, non the less, is not enough because lets assume:
L = 1500
T = 20
S = 200
and R = 140
(max possible value given that 140 = S - (T*L)/500
)
We simulate a shipment: min 0: 140 ships loading min 46.6: 60 ships loading, 140 traveling min 66.6: 140 ships loading, 60 traveling min 86.6: 140 ships still loading while the other 60 have arrived!! At the end, we waste time
So this doesn't work
we need the loading time to divide the traveling time evenly, so:
T % (R*500) / L = 0
this gives us several possible values for R: 3
, 6
, 12
, 15
, 30
and 60
as we demonstrated before, which R
we choose doesn't matter.
as an example, using R = 60
min 0: 60 ships loading min 20: 60 ships loading, 60 ships traveling min 40: 60 ships loading, 60 ships traveling, 60 ships arrived min 60: 60 ships loading, 60 ships traveling, 120 ships arrived ...
using R = 15
min 0: 15 ships loading
min 5: 15 ships loading, 15 ships traveling
min 10: 15 ships loading, 30 ships traveling
min 15: 15 ships loading, 45 ships traveling
min 20: 15 ships loading, 60 ships traveling
min 25: 15 ships loading, 60 ships traveling, 15 ships arrived
min 25: 15 ships loading, 60 ships traveling, 30 ships arrived
...
we can see that this will work for any amount of resources
note that if the target city is from another player, just add the return time to the travel time and the math is the same.
now, making this work for N cities sending resources to M cities, that is another story...
So at the end:
R <= S - (T*L)/500
and T % (R*500) / L = 0
are the two equations that we need to find R
.
Is interesting that the only factors that we care about are S
, L
and T
, the amount of resources C
only "needs" to be greater than S*500
for all of this to make sense.
I'm not sure that this will work every time for all possible values of L
, T
and S
.
if no R
satisfies T % (R*500) / L = 0
then I assume the R
that comes closest should be used.
Any comments?
I think you guys are both making this optimization problem WAAAAAY more complicated than it needs to be. For one, you need to understand that the number of ships you send on each journey doesn't affect the total time you will be waiting for ALL the resources to arrive at the destination city. If you have 100k wood to transport from City1 to City2, the travel time is 20 mins and you have 200 ships, it doesn't matter if you do it all in 1 batch or if you do it in batches of 50 ships or 20 ships or whatever, you will just be wasting action points (which is something important I have not yet seen anyone mention in this issue). It is true that if you send the resources in batches, you will have pieces of your cargo arrive faster in City2, but at the end, you will wait the same amount of time for ALL the resources to get to City2.
Also, it is important to know that it does make sense to split your transfer into batches if the number of resources you want to send exceeds the number of your ships * 500. The absolute solution to this problem needs to be calculated on a per transfer basis, other transfers that are queued do not need to be taken into account. The best solution is to calculate the travel time (in our case it is 20 mins) and to make the loading bay work for that exact amount of time, so that no action points are wasted, and the cargo will be transferred in batches. This means that the time the loading bay is working needs to be equal to the travel time or (R * 500) / L = T. The number of ships in that case is equal to R = ( T * L ) / 500. This is the best and an equivalent solution even in the case that we have enough ships to transfer all the cargo in one batch, because the waiting time is the same and no action points are wasted. Increasing R from this number will just make the loading bay do nothing sometimes, which wastes time, and making it smaller will just waste action points. It is also important to realize that if you have available to you less than ( T * L ) / 500 ships, then you are wasting time by waiting for the right number to arrive, you should just send what ships you have available at the time, up until you have sent a total of ( T * L ) / 500 ships and the first ships you have sent have not yet returned.
If you're sending resources to a foreign city, simply multiply the travel time by two, and it's absolutely the same thing.
Sending cargo from multiple cities to one city in parallel is a very good idea, but it is a completely separate problem from the one I solved above. The previous problem only takes into account one big transfer (for example City1 ---> City2 500k wood), but this problem has to take into account all cities. Basically given a list of transactions ( [ {C1, C2, 500K wood}, {C3, C1, 100k wood), {C4, C2, 200k wood} ]) you want to simply sort the list by the destination city (the transfer to the same destination city will be close together) and that's it. Using this solution, assume we have 200 ships, the first transfer will take it's own R = ( T * L ) / 500 ships, and then, the second transfer will take it's own R = ( T * L ) / 500 ships, and then the third transfer may or may not have enough ships to fill it's own ( T * L ) / 500, but it will either way send whatever ships it has available.
The first solution needs to be implemented in the sendGoods function, the second is a simple sort by destination that needs to be done in the executeRoutes function. From what I can see, this is the absolutely best possible solution. It will however be a bit complicated to edit the existing code, because the way it works now is that ikabot simply just sends resources if ships are available, and if not it just waits for ships to come back. The first solution would need to basically somehow split each transfer into it's own world where it sends ( T * L ) / 500 ships, and waits internally for them to come back without looking at what other transfers are doing. This could be implemented using threads, but then much of the logic from executeRoutes would need to be moved to sendGoods (the waiting part mainly). executeRoutes should pretty much just sort the list of routes by destination city, and then call sendGoods in a separate thread with the arguments of each route for each particulat route. Then sendGoods will send ( T * L ) / 500 ships, or whatever is available or it will wait if nothing is available. If something less than ( T * L ) / 500 is available, then it should send whatever that amount is and it will also keep sending ships as soon as they come available until it has sent ( T * L ) / 500 ships, and the first ships that it has sent have not yet returned. Basically it needs to ensure that ( T * L ) / 500 ships are always on the move to the destination.
What do you guys think?
In your first paragraph. the scenario you describe is irrelevant because you can transfer all the resources in one shipment, it doesn't satisfy the necessary condition of C/(500*S) > 1
.
When you have more resources than ships available, doing what I described above is 100% of the time faster, because you are always loading resources and sending them at the same time.
Also when you say
The best solution is to calculate the travel time (in our case it is 20 mins) and to make the loading bay work for that exact amount of time
it is not necessary to be equal, it just needs to be divisible by that, as shown above. In fact, it may not even be possible to make it equal if the travel distance is very long.
When you find all possible values of R
with T % (R*500) / L = 0
you should take the biggest R
(to save action points) which may or may not be the R = ( T * L ) / 500
(in the example I gave, its actually possible with R = 60)
As of the multiple cities problem, I believe I have found an elegant solution: If you look at the examples I presented before, I show 2 of the possible values for R: 60 and 15. With R = 60 only 120 ships are used at any given time, leaving 80 ships unused. With R = 15, interestingly, only 75 ships are used, leaving 125 ships unused. While both values of R are equally fast, the bigger the R the less action points are used BUT the more ships are needed.
How can we express the relation between R and the number of ships are are actually used?
this is simply:
Sn (ships needed) = R * (T / ((R*500) / L)) + R
All you need to do to extend the solution to multiple cities, is choose the Rs of each source city in a way that it uses a portion of the available ships (Sn) and leaves enough ships for the rest of the origin cities. If there are too many source cities, simply choose a few and when one finishes, assign its Sn portion to a new origin city. Interestingly, origin cities with a given R do not care about extra ships being left unused, so we don't need to care about re-defining Rs after a shipment finishes, just check if there is any origin city waiting for an R to be assigned.
I do not understand how increasing R will save action points. It will not save any action points, it will simply make it so that the one action point you will be using will be used only part of the time, unlike with using R = ( T * L ) / 500, where that action point will be in use all the time. By increasing R you will be just be using more ships, ships that could be used by other transfers if there are any to send resources in parallel.
it is not necessary to be equal, it just needs to be divisible by that
Well obviously ( T * L ) / 500 is the smallest number that is divisible by ( T * L ) / 500. And we should use the lowest number to save on ships, as I have said above. I think we should send resources in batches of ( T * L ) / 500 even when the condition C/(500*S) > 1 is not satisfied. This condition in fact plays no part in anything, it just tells you when you will have a performance increase, but it shouldn't be considered at all, because by sending resources in batches of ( T * L ) / 500 when this condition is NOT satisfied DOES NOT lead to any performance decrease.
While both values of R are equally fast, the bigger the R the less action points are used BUT the more ships are needed.
No less action points are used for any R that is greater or equal to ( T * L ) / 500. If you lower R below ( T * L ) / 500, then more action points are used, if you increase it above ( T * L ) / 500, you will still be using 1 action point, just less frequently, this is absolutely meaningless, we should not conserver action points like this, because it's not conserving anything and is instead just using that action point less frequently. Lowering R to exactly ( T * L ) / 500 will conserve ships that other transfers could use.
All you need to do to extend the solution to multiple cities, is choose the Rs of each source city in a way that it uses a portion of the available ships (Sn) and leaves enough ships for the rest of the origin cities.
Yes, the way you choose the Rs of each source city is to use the lowest possible R value, such that ships are conserved, and the lowest possible R value is floor(( T * L ) / 500) if we want to use an additional action point or ceil(( T * L ) / 500) if we want to use an additional ship.
Interestingly, origin cities with a given R do not care about extra ships being left unused, so we don't need to care about re-defining Rs after a shipment finishes, just check if there is any origin city waiting for an R to be assigned.
Yes this is because the value of R doesn't depend on ships available, only on the travel time and the loading speed of the source town's port. There is no need to check if anything is waiting, just make it a race condition where whichever thread grabs the ships first gets to send them (we can use random waiting times as usual). As I have already said, if the R value is lets say 30 ships and only 12 are available currently, it's irrational and nonsensical to wait for the remaining 18 ships to arrive. Just send the 12 that are already available, doing anything else is just wasting time. A thread needs to ensure that ceil(( T * L ) / 500) number of ships are always on moving towards the destination city.
I do not understand how increasing R will save action points.
In the previous example, R = 15 you get this:
min 0: 15 ships loading min 5: 15 ships loading, 15 ships traveling min 10: 15 ships loading, 30 ships traveling min 15: 15 ships loading, 45 ships traveling min 20: 15 ships loading, 60 ships traveling min 25: 15 ships loading, 60 ships traveling, 15 ships arrived min 25: 15 ships loading, 60 ships traveling, 30 ships arrived
That means 5 rounds at the same time, meaning 5 action points used. Against R = 60
min 0: 60 ships loading min 20: 60 ships loading, 60 ships traveling min 40: 60 ships loading, 60 ships traveling, 60 ships arrived min 60: 60 ships loading, 60 ships traveling, 120 ships arrived
Where only 2 action points are used.
Well obviously ( T * L ) / 500 is the smallest number that is divisible by ( T * L ) / 500
I meant that R has to divide ( T * L ) / 500
. In the previous example, R can be as small as 3 and as large as 60.( T * L ) / 500
) is the largest possible value, not the smallest.
I think we should send resources in batches of ( T * L ) / 500 even when the condition C/(500*S) > 1 is not satisfied.
Well yes of course, we can totally use this approach with small shipments.
As of race conditions, that just won't happen, If you chose each R cleverly so that every origin cities can send their resources, all the shipments will finish timely and you won't need to wait for a certain ship to return. The exception of course being if the user or another process suddenly starts using ships, in that case I agree it makes no sense to wait for the remaining boats.
Take into account that ( T * L ) / 500
is a possible value for R. Other values are also valid and offer the same speed, different action points used and a different amount of ships needed.
Also, using a value of R that is greater than ( T * L ) / 500
was never suggested.
Yes, the way you choose the Rs of each source city is to use the lowest possible R value, such that ships are conserved, and the lowest possible R value is floor(( T * L ) / 500)
That is not the case, in the previous example the smallest value was 3, not floor(( T * L ) / 500)
All the possible values of R can be calculated with T % (R*500) / L = 0
and R <= S - (T*L)/500
You can get the amount of action points used with: Ap = T / ((R*500) / L) + 1
So if: L = 1500, T = 20 and S = 200
R can be: 3, 6, 12, 15, 30 and 60
With R = 3, Ap = 21, Sn = 63
With R = 6, Ap = 11, Sn = 66
With R = 12, Ap = 6, Sn = 72
With R = 15, Ap = 5, Sn = 75
With R = 30, Ap = 3, Sn = 90
With R = 60, Ap = 2, Sn = 120
If you have 2 origin cities, and 200 boats, R = 30 with both of them would mean that you only use 90 boats with each city. Leaving 20 boats free, and each city would be able to make its shipments perfectly fine. If you use R = 60 on one origin city, you have 80 boats left, meaning you could use R = 15 for the other one, have 5 unused ships and the results is the same, (Note that R = 3 uses very little boats but is impossible to use due to the action points needed) With this approach, every origin city will always have enough ships to use (unless the user or a new process comes in and start using ships that were being used by an origin city)
Hope that made it clearer.
I have went over the entire conversation again, and I have come to the conclusion that we're both talking about pretty much the same thing. ( T * L ) / 500 is a good value, increasing it will use more ships but less action points, and decreasing it will use more action points but less ships. ( T * L ) / 500 is not always the best option, because there might be other origin cities that could use the ships, and we have more than enough action points. So I guess we should just use the R value that uses as many action points as possible to leave ships for other cities?
Yes that is exactly what I have been thinking
There is one scenario I haven't been able to figure out yet, what If the destination city is very far away? the travel time T will be much much larger than the loading time of any amount of ships, so all the math breaks apart, we lose that nice feature of "Lets use 'foo' for R and we only use 'bar' ships"
In that case, is impossible to have ships both loading and traveling at all times, because the loading time is so small in comparison with the travel time. In that case there is no value of R that will define how many ships are used That is because for closer shipments, the first round returns pretty quickly (as seen in the examples before), this doesn't happen here. I guess we should just select a value of R like before and be careful while programming this to select how many ships to assign to that shipment. The truth is this kid of shipments will use all 200 ships if you let them, so if you want to have a close shipment and a large one, I would select an R for the close one, and and maybe an R for the large one that uses all the other ships, and make sure while programming that the large shipment does not use any ships of the close shipment When the close shipment finishes, we should probably assign all those ships to the longer one Programming that sound like a pain lol
I actually checked list of issues here to see if anyone had previously suggested transport optimisation and found this one. There is this script I was using for transport always until I found ikabot, and it used to transport stuff quite fast and it was also possible to queue transports. It is indeed based on using more action points. https://greasyfork.org/en/scripts/28845-ikariam-automation/code
A combination of using more action points + multiple threads would be good. But considering the complexity, I would suggest code implementation in the order below.
- To prevent race condition, tasks that require transport should be queued one after another.
- Use more action points per transport.
- Queued tasks can then be re-ordered depending on amount of estimated time required to complete the same.
I myself have 4 wine on 1 island + 5 marble on 1 island + 1 sulphur and 1 crystal. Optimisation implementing both multi-threading + action points would indeed be beneficial in my case.
Let me know if you need help testing the script.
Hey there, Thank you for your interest and comments. The main issue here is how to handle long shipments which doesn't allow to apply the solution described above. I'm honestly not interested in implementing and a shipment strategy that is not optimal. And while we have our desired solution for short shipments, that is only half the story. It more of a pen and paper problem than a programming one.
Any updates on this suggestion? I think it sounds really good, another addition to this could be a limit of ships to be used. Not to mention that there are now the new ships that can be used, although slow, but could be optionally used as well (not with regular ships though, as that would make them slow as well)