Posts: 18
Threads: 7
Joined: May 2016
Reputation:
0
Is there any ready to cook code to implement following:
several bots simultaneously grab titanium ore, and they don't use the same block.
And i completely don't understand how the interconnection between bots works. Is there any examples (in game don't count, i can't understand these.)
Posts: 825
Threads: 26
Joined: Oct 2012
Reputation:
90
I don't know if it's most efficient way to do this (didn't coded with public classes so much), but I think about public class ResourceManager with array storing coordinates of every thing. It would be shared for all bots (that's why it's public class) and every bot should read it before touching anything. It would work like this:
Bot #1 search for ore. Found one, but after searching in array it turns out it's present in there, because another bot marked it as "his", so other bots won't take it before him. So first bot search further and found another ore, that's not present in array, so bot write ore coordinates to array, then simply go for it, grab and deliver to destination point. When ore won't be needed anymore for bot, then its coordinates are removed from array.
Posts: 7
Threads: 2
Joined: Jan 2017
Reputation:
1
Sorry for the thread necromancy, but I actually wrote one of these for the original Colobot (sadly I no longer have the code).
It wasn't just mining, but a general economic support program that could run on multiple bots. I ended up using a public class for communication, with a few functions:
Code: synchronized int registerBot() // Returns an un-used unique ID number to the caller.
void alive(int id) // Updates the last-active timestamp for the given id
The public class includes an array that uses the Bot-id as an index and stores the timestamp of the bot's last activity as the value, this lets it identify bots that were destroyed or re-assigned by the player so it can clear any locks they might have held.
Code: synchronized int lock(Point p, int id, int lockno) // Tries to lock the given point for the given bot ID
synchronized int isLocked(Point p) // Returns 1 if-and-only-if a lock exists for the given point
synchronized void clearLock(int id, int lockno) // Release a lock
The public class also maintains a list of locked (reserved) locations in use by bots. This allows them to establish mutually-exclusive use of resources and buildings. At any given time a bot may have up to three locks, and no point may ever be locked by more than one bot.
It worked by breaking tasks down into small atomic units of work, so a typical task might be "provide energy for defense towers", which would boil down to:
1. Find a defense tower that needs a recharge
2. Lock the tower's location (or continue looking for a tower if the lock failed) with lockno=0
3. Find an energy cell and lock its location with lockno=1 (abort the task and release lock 0 if find or lock failed)
4. Go pick up the energy cell and release lock 0
5. If the energy cell isn't fully charged, find a power station
6. Move to a random empty spot near the power station
7. Attempt to lock the power station location with lockno=0, if the lock fails, wait randomly from 1 to 5 seconds and then repeat this step
8. Move onto the power station & recharge the energy cell
9. Release lock 0
10. Move to the defense tower and swap cells
11. Release lock 1
12. Move to a random empty position near a point 20m away from the nearest powers station in the direction of the defense tower
13. Drop the energy cell
This worked pretty well, though AFAIR I ended up having a few specialized versions (e.g. one for the last few missions for running a subber that just collected ore).
The main point of failure in this, like any similar program, was that if the bot that had the public class loaded was destroyed, all the bots running the program would error out. To avoid this, I generally loaded the public class on a bot that I parked behind one of my buildings and never moved.
If you decide to re-implement something like this, I'll also warn you that while timing out locks (or otherwise responding to destroyed bots) is essential, you do need to be careful of using abstime() because it resets (or at least the old version did) when loading a saved game. I had a workaround in my class that basically scanned the whole registered bots array on a regular basis and set any timestamp in the future to the current time. This wasn't seamless, but it let them recover fairly quickly from a game load.
Posts: 2
Threads: 0
Joined: Oct 2017
Reputation:
0
10-09-2017, 12:57 AM
(This post was last modified: 10-09-2017, 01:08 AM by Caveman.
Edit Reason: Improved version of code.
)
I would like to know if there is option for hiding walltext(spoiler or smth).
I had the same idea about bots working simultaneously :]
I had troubles with arrays, I went somewhere outside of array, so I decided to rewrite code using lists. It looks simplier.
The idea of this list is to:
store point of position taken,
store next item (null for last element),
and share between bots first item of list (which leads to store whole list) via private static variable (named first);
Then we take position of item, reserve it, do job on it, and free it.
It's extremely error unsafe. One bot can't go to destination, program stops and he blocks item in a list forever, it's need to be restarted. Also somatimes bots want to "steal" eachother material (who will catch titanium produced by converter first - bot that converts it to power cell, or bot that produced it), or somatimes even cell from back(yeah!).
Functions for jobs need to be rewrited, I'll do that when I'll find inspiration and free time.
But when started almost at same time, it works for few "jobs" like it should.
That's my old code:
Code: public class pointListElement{
//we use point non-achieveable in game for first element
point nanpoint=new point(nan,nan,nan);
//here we store all our public variables:
public point place;
public pointListElement next;
//constructor, function used when new object is created
public void pointListElement(){
this.place=nanpoint;
this.next=null;
}
//another constructor, with starting value
public void pointListElement(point dest){
this.place=dest;
this.next=null;
}
//we add items to end of list, so we "jump" to last element(by searching
//for null value recursively and then create new item of list attached to
//last item's "next".
synchronized public void add(point pointDest){
if(next!=null){
next.add(pointDest);
}else{
next=new pointListElement(pointDest);
}
}
//another recursively function, it returns false if point searched is different than value
//of last element, or true if trough jumping one element is equal.
public bool contains(point searched){
if(this.next==null&&searched!=this.place){
return false;
}
if(this.place==searched){
return true;
}
return this.next.contains(searched);
}
//yet another recursive function, it remove elements by skipping them in a list
//(didn't find option to remove from memory, i hope it's automatic :D)
synchronized public void remove(point removed,pointListElement previous){
if(!contains(removed)){ //just for lulz, didn't see it happen
message("not removed");
return;// false;
}
if(this.place==removed){
previous.next=this.next;
return;// true;
}else{
if(this.next!=null)/*return*/ this.next.remove(removed,this);
}
return;// false;
}
}
public class WorkingClass{
//function added for orders
void findEmptySpace(point here){
goto(space(here));
}
/*first element of list is static->whole list is accessible in different instances
of class*/
static private pointListElement first=null;
int dist;
bool taken;
point findObjectPosition(object robot,int ObjectType){
//it's a smart finding
//it finds only non-taken positions
object item;
if(first==null){
first=new pointListElement(); //it must be in first function that use it
}
dist=0; //minimum starting radar
while(true){
/*
we search for elements by increasing minimum radar range
*/
item=radar(ObjectType,0,360,dist,1000);
if(item==null){
dist=0; //if item not found decrease minimal range to 0 and start again
wait(1); //here it should wait for it's own point
continue;
}else{
//here we check if position is already taken
taken=first.contains(item.position);
//if it's not, then reserve it in last position
if(!(taken)){
first.add(item.position);
return item.position;
}else{
//if taken, increase minimum detection range
dist+=2;
}
}
}
//must be for no error
return robot.position;
}
//object types(categories) are just ints, and they can be threated like that
//for example, mathematically, PowerStation=7(not sure about exact value, but it shows
//the idea
public void workAt(object robot,int work,int ObjectType){
//we get our destination and reserve point at same time
point destination = findObjectPosition(robot,ObjectType);
//turn(direction(radar(ObjectType).position));
switch(work){
case 1:
goto(destination);
grab();
break;
case 2:
goto(destination);
drop();
move(-2.5);
break;
case 3:
goto(destination);
sniff();
break;
case 4:
goto(destination);
findEmptySpace(robot.position);
drop();
break;
default:message("WRONG WORK TYPE");
}
//after job is done, remove point from list
first.remove(destination,first);
}
//this is overloaded version of previous function
//it has additional parameter, waitingFor, which is used for example when want to wait
//for titanium at Converter
public void workAt(
object robot,
int work,
int ObjectType,
int waitingFor)
{
point destination = findObjectPosition(robot,ObjectType);
//turn(direction(radar(ObjectType).position));
switch(work){
case 1:
goto(destination);
if(waitingFor==PowerCell){
wait(20);
}else{
while(radar(waitingFor,0,360,0,5)==null){
wait(1);
}
goto(radar(waitingFor,0,360,0,5).position);
}
grab();
break;
case 2:
goto(destination);
drop();
move(-2.5);
break;
case 3:
goto(destination);
sniff();
break;
case 4:
findEmptySpace(robot.position);
drop();
break;
default:message("WRONG WORK TYPE");
}
first.remove(destination,first);
}
//both functions are really crap, they have to be rewritten in more specific tasks
//and here we use the same idea for refueling
public void refuel(object robot,float treshold){
//int refuelers[]={PowerStation,PowerCaptor};
if(robot.energyCell.energyLevel<treshold){
point destination=findObjectPosition(robot,PowerStation);
goto(destination);
while(robot.energyCell.energyLevel<1){
wait(1);
}
first.remove(destination,first);
}
}
}
extern void object::Work()
{
WorkingClass workfield();
while(true){
workfield.workAt(this,1,TitaniumOre); //go to nearest free TitaniumOre, grab it
workfield.workAt(this,2,Converter); //go to nearest free Converter, drop item
workfield.workAt(this,1,Converter,Titanium); //at converter wait for titanium,
//and grab it
workfield.workAt(this,4,BotFactory);//leave at free space near free BotFactory
workfield.refuel(this,0.4); //:>
}
}
[edit] I made it work better, with some error handling:
Code: public class pointListElement{
//we use point non-achieveable in game for first element
point nanpoint=new point(nan,nan,nan);
//here we store all our public variables:
public point place;
public pointListElement next;
//constructor, function used when new object is created
public void pointListElement(){
this.place=nanpoint;
this.next=null;
}
//another constructor, with starting value
public void pointListElement(point dest){
this.place=dest;
this.next=null;
}
//we add items to end of list, so we "jump" to last element(by searching
//for null value recursively) and then create new item of list attached to
//last item's "next".
synchronized public void add(point pointDest){
if(next!=null){
next.add(pointDest);
}else{
next=new pointListElement(pointDest);
}
}
//another recursively function, it returns false if point searched is different than value
//of last element, or true if trough jumping one element is equal.
public bool contains(point searched){
if(this.next==null&&distance(searched,this.place)>2){
return false;
}
if(distance(this.place,searched)<=2){
return true;
}
return this.next.contains(searched);
}
//yet another recursive function, it remove elements by skipping them in a list
//(didn't find option to remove from memory, i hope it's automatic :D)
synchronized public void remove(point removed,pointListElement previous){
if(!contains(removed)){ //just for lulz, didn't see it happen
message("not removed");
return;// false;
}
if(this.place==removed){
previous.next=this.next;
return;// true;
}else{
if(this.next!=null)/*return*/ this.next.remove(removed,this);
}
return;// false;
}
}
public class WorkingClass{
/*first element of list is static->whole list is accessible in different instances
of class*/
static private pointListElement first=null;
bool taken;
point findObjectPosition(object robot,int ObjectType){
//it's a smart finding
//it finds only non-taken positions
object item[];
if(first==null){
first=new pointListElement(); //it must be in first function that use it
}
while(true){
item=radarall(ObjectType,0,360,0,1000);
if(item==null){
wait(1); //here it should wait for it's own point
continue;
}else{
for(int i=0;i<sizeof(item);++i){
//here we check if position is already taken
taken=first.contains(item[i].position);
//if it's not, then reserve it in last position
if(!(taken)){
first.add(item[i].position);
return item[i].position;
}else{
}
}
wait(1);
}
}
//must be for no error
return robot.position;
}
point findObjectPosition(object robot,int[] ObjectType){
//it's a smart finding
//it finds only non-taken positions
object item[];
if(first==null){
first=new pointListElement(); //it must be in first function that use it
}
while(true){
item=radarall(ObjectType,0,360,0,1000);
if(item==null){
wait(1); //here it should wait for it's own point
continue;
}else{
for(int i=0;i<sizeof(item);++i){
//here we check if position is already taken
taken=first.contains(item[i].position);
//if it's not, then reserve it in last position
if(!(taken)){
first.add(item[i].position);
return item[i].position;
}else{
}
}
wait(1);
}
}
//must be for no error
return robot.position;
}
point findEmptySpaceNearObjectPosition(object robot,int ObjectType){
//it's a smart finding
//it finds only non-taken positions
object item[];
if(first==null){
first=new pointListElement(); //it must be in first function that use it
}
point emptySpace;
while(true){
item=radarall(ObjectType,0,360,0,1000);
if(item==null){
wait(1); //here it should wait for it's own point
continue;
}else{
for(int i=0;i<sizeof(item);++i){
int j=0;
taken=true;
while(taken&&j<100){
//here we check if position is already taken
emptySpace=space(
item[i].position,16+j,100+j*2,4
);
taken=first.contains(emptySpace);
++j;
}
//if it's not, then reserve it in last position
if(!(taken)){
first.add(emptySpace);
return emptySpace;
}else{
}
}
wait(1);
}
}
//must be for no error
return robot.position;
}
//object types(categories) are just ints, and they can be threated like that
//for example, mathematically, PowerStation=7(not sure about exact value, but it //shows the idea)
public void findAndTake(object robot,int ObjectType){
errmode(0);
while(true){
point destination = findObjectPosition(robot,ObjectType);
//point errdestination;
while(goto(destination)!=0){
//we want another destination in case of error
//errdestination=destination;
//destination = findObjectPosition(robot,ObjectType);
//first.remove(errdestination,first);
}
first.remove(destination,first);
if(grab()!=0){
continue;//in case of this error we repeat whole process
}
errmode(1);
return;
}
errmode(1);
}
public void process(object robot,int processor,int ObjectType){
point destination = findObjectPosition(robot,processor);
//point errdestination;
errmode(0);
while(goto(destination)!=0){
//errdestination=destination;
//destination=findObjectPosition(robot,processor);
//first.remove(errdestination,first);
}
while(drop()!=0){
point start=robot.position;
if(radar(ObjectType,0,45,0,5)!=null){
drop(Behind);
grab();
dropAtEmptySpace(robot,processor);
goto(start);
grab(Behind);
continue;
}else{
move(-6);
wait(5);
goto(start);
}
}
move(-2.5);
switch(ObjectType){
case PowerCell:
wait(17);
move(1);
break;
case NuclearCell:
wait(35);
move(1);
break;
default:
while(radar(ObjectType,0,90,0,5)==null){
wait(1);
}
break;
}
grab();
first.remove(destination,first);
errmode(1);
}
//we want to drop at empty space near object
public void dropAtEmptySpace(object robot,int ObjectType){
point destination = findEmptySpaceNearObjectPosition(robot,ObjectType);
errmode(0);
/*
while(goto(space(destination,0,100,4))!=0){
wait(1);
}*/
while(goto(destination)!=0){
wait(1);
}
while(drop()!=0){
message("Place Taken");
wait(10);
}
first.remove(destination,first);
errmode(1);
}
//and here we use the same idea for refueling
public void refuel(object robot,float treshold){
int refuelers[]={PowerStation,PowerCaptor};
if(robot.energyCell.category==PowerCell){
if(robot.energyCell.energyLevel<treshold){
errmode(0);
point destination=findObjectPosition(robot,refuelers);
//point errdestination;
while(goto(destination)!=0){
//errdestination=destination;
//destination=findObjectPosition(robot,refuelers);
//first.remove(errdestination,first);
}
while(robot.energyCell.energyLevel<1){
wait(1);
}
move(-3);
first.remove(destination,first);
errmode(1);
}
}else{
if(robot.energyCell.energyLevel<treshold){
goto(space(radar(NuclearPlant).position,20,100));
while(robot.energyCell.energyLevel<1){
wait(1);
}
}
}
}
}
public void object::workTitanium(){
//this is a public function, that can be used on produced bots
errmode(0);
move(-5);
errmode(1);
WorkingClass workfield();
while(true){
workfield.findAndTake(this,TitaniumOre);
workfield.process(this,Converter,Titanium);
workfield.dropAtEmptySpace(this,Converter);
if(energyCell.category==PowerCell){
workfield.refuel(this,0.4);
}else{
workfield.refuel(this,0.1);
}
}
}
extern void object::Work()
{
workTitanium();
}
|