Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Topics - Toupie

Pages: [1] 2
1
Programming with Blackvoxel / A* pathfinding routine
« on: October 31, 2013, 10:41:38 pm »
Now that we have the 3D functions I thought that I could write me a path finding routine. So here it is. It's a basic A* implementation that is configurable between 2D (XZ) and 3D (XYZ).

A little warning. 3D path finding over large distances or in complex scenarios can be very slow.

I've implemented it as 2 classes.

AStar is the real code of the path finding.
Node is just a simple container for a node in the path. It is used by AStar

Node(x,y,z) creates an instance of Node for that location.

AStar() creates an instance of AStar.

Methods of AStar
result = GetPath( start, goal )
start and goal are instances of Node(x,y,z) representing the start position and the goal of the path.
result will be an array containing the necessary move directions you need to take to get from start to goal. The moves are stored in reverse order so you can just .pop() them from back of the array. GetPath will limit it's processing time to 200ms for every call to it, and will return null until it is done. So you will have to call it several times before getting your path. In case it fails to find a path to goal it will return an empty array.

Clear()
Resets the instance of AStar for a new search. Needs to be called after you have done a search before you start a new one. Can be used instead of creating a new instance of AStar.

Set2d()
Sets the search mode to 2D (XZ), ignoring Y. This is the default.

Set3D()
Sets the search mode to full 3D. Warning, can be slow.

SetMax(value)
Sets the limits of the search to value in all directions (xyz) from the center point between start and goal. Default values are 64, which gives it an area of 128*128 or 128*128*128 in 3D that will be used when searching for a path. This is necessary, or GetPath would never give up in case there is no path between start and goal.

Members of AStar

xmax, ymax, zmax
The limits on the 3 coordinate for the search. Default value 64.

debug
How much will be printed out to stderr.txt. 0 = nothing. 1 = summary.  2 = detail information (not implemented since it slows things down to much)

AStar.nut
Code: [Select]
// A* pathfinding

class Node {
  constructor(x,y,z)
  {
    this.x = x;
    this.y = y;
    this.z = z;
  }

  // This one does not seem to work.
  function _cmp(other)
  {
    error("_cmp This.x="+x+"This.z="+z+" other.x="+other.x+" other.z="+other.z+"\n");
    if (x < other.x) return -1;
    if (y < other.y) return -1;
    if (z < other.z) return -1;
    if (x > other.x) return 1;
    if (y > other.y) return 1;
    if (z > other.z) return 1;
    return 0;
  }

  function index()
  {
    return format("%d:%d:%d",x,y,z);
  }

  x = null;  // X position
  y = null;  // Y position
  z = null;  // Z position
  d = null; // Direction to get here;
  g = 0; // Cost from start to here
  h = 0; // Estimated cost from here to goal
  f = 0; // Total estimated cost to goal
  p = null; // Came from (parent). Used to walk back from goal to get the shortest path.
}

class AStar {
  constructor()
  {
    open = {};
    closed = {};
  }

  // Private Members
  open = null; // Our open set of nodes
  closed = null; // Our close set of nodes
  checked = 0; // The number of nodes checked
  time = 0; // Total time processed
  directions = 4; // 4 = X & Z, 6 = X, Y & Z
  moveCoord = [ [0,0,1], [1,0,0], [0,0,-1], [-1,0,0], [0,1,0], [0,-1,0] ];

  // Public Members
  // A full 3D scan of an area of the size [64*2,64*2,64*2] will take up to 10 minutes
  xmax = 64;
  ymax = 64;
  zmax = 64;

  debug = 1; // 0=nothing, 1=summary, 2=full debug info

  // Private Methods
  function CostEstimate(start,goal)
  {
    return (abs(goal.x-start.x)+abs(goal.y-start.y)+abs(goal.z-start.z)) * 10;
  }

  function GetCheapest()
  {
    local m=null;
    local i,v;
    foreach (i,v in open) {
      if (m==null || v.f<m.f) {
        m = v;
      }
    }
    delete open[m.index()];
    return m;
  }

  function ReconstructPath(n)
  {
    local path = [];
    while( n.p!=null ) {
      path.append(n.d);
      n = n.p;
    }
    if (debug>0) error("PathLen="+path.len()+"\n");
    return path;
  }

  // Public Methods
  function Clear()
  {
    closed = {};
    open = {};
    checked = 0;
    time = 0;
  }

  function GetPath(start,goal)
  {
    local i,x,y,z,v;
    local time = GetGameTime();

    local midx = (goal.x-start.x)/2+start.x;
    local midy = (goal.y-start.y)/2+start.y;
    local midz = (goal.z-start.z)/2+start.z;

    if (open.len()==0) {
      start.g = 0;
      start.h = CostEstimate(start,goal);
      start.f = start.h;
      open[start.index()] <- start; // open.push(start);
    }

    while (open.len() > 0) {
      local current = GetCheapest();
      if (current.x == goal.x && current.z == goal.z && (current.y == goal.y || directions == 4)) {
        this.time += GetGameTime()-time;
        if (debug>0) error("Found path to goal after "+checked+" passes and "+(this.time)+"ms!\n");
        return ReconstructPath(current);
      }

      closed[current.index()] <- current; // closed.push(current);
      //error(format("Check %d,%d,%d g=%d h=%d",current.x,current.y,current.z,current.g,current.h));
      for (i=0; i<this.directions; i++) {
        x = current.x+moveCoord[i][0];
        y = current.y+moveCoord[i][1];
        z = current.z+moveCoord[i][2];
        if (abs(x-midx) <= xmax) {
          if (abs(y-midy) <= ymax) {
            if (abs(z-midz) <= zmax) {
              v = Look3D(x-GetX(),y-GetY(),z-GetZ());
              if (GetVoxelProp(v,0)) {
                local neighbor = Node(x, y, z);
                if (!(neighbor.index() in closed)) {
                  if (!(neighbor.index() in open)) {
                    neighbor.d = i;
                    neighbor.g = current.g + 10;
                    neighbor.h = CostEstimate(neighbor,goal);
                    neighbor.f = neighbor.g + neighbor.h;
                    neighbor.p = current;
                    //error(format(" | added %d,%d,%d g=%d h=%d",neighbor.x,neighbor.y,neighbor.z,neighbor.g,neighbor.h));
                    open[neighbor.index()] <- neighbor; // open.push(neighbor);
                  }
                }
              }
            }
          }
        }
      }
      //error("\n");
      checked++;
      if (GetGameTime()-time>=200) { // Allow max 200ms in every call.
        this.time += GetGameTime()-time;
        if (debug>0) error("Yielded after "+checked+" passes and "+(this.time)+"ms!\n");
        // Return to prevent Voxel_Step from taking to long.
        // We will continue where we left of on the next call
        return null; // Not finished yet.
      }
    }
    if (debug>0) error("Did not find a path to goal! "+checked+" passes and "+(this.time)+"ms\n");
    return []; // Not able to find a path to goal.
  }

  // Only search in X & Z. This is the default.
  function Set2D()
  {
    this.directions = 4;
  }

  // Full 3D pathfinding. CAN BE VERY SLOW!
  function Set3D()
  {
    this.directions = 6;
  }

  // Set all max limits to the same value.
  function SetMax(value)
  {
    xmax = value;
    ymax = value;
    zmax = value;
  }

} // class AStar

2
Updated the "include" file for saving variables to use the new features in v1.22b3.

All the variables that needs to be saved must be stored in a global table. The name of the table does not matter.

SaveVar.nut" now goes into the Universes\nr\Scripts\Squirrel\ directory.
Code: [Select]
// SaveVar.nut

SaveName <- null;

function MakeSaveName()
{
  if (GetPath(4)!=-1) {
    SaveName = format("%s%s%d_%d.save",GetPath(4),GetInfo(20),GetInfo(22),GetRobotID());
  } else {
    throw "Needs to be called from Voxel_Step()";
  }
}

function Dump(var)
{
    local i,v;
    local s="";
    if (typeof var=="null") {
      s = "null";
    } else if (typeof var=="string") {
      s = "\""+var+"\"";
    } else if (typeof var=="array") {
      s = "[";
      foreach (v in var) {
        s += Dump(v)+",";
      }
      s += "]";
    } else if (typeof var=="table") {
      s = "{";
      foreach (i,v in var) {
        if (typeof i=="integer") {
          s += "["+i+"]="+Dump(v)+",";
        } else {
          s += i+"="+Dump(v)+",";
        }
      }
      s += "}";
    } else { // integer  bool  ??
      s = var+"";
    }
    return s;
}

function SaveVar(varname)
{
  local c,s;
  if (SaveName == null)
    throw "Need to call LoadVar() or MakeSaveName() from Voxel_Step() before calling SaveVar()";

  s="";
  if (varname in getroottable() && typeof varname="string") {
    if (typeof (getroottable()[varname])=="table") {
      foreach (i,v in getroottable()[varname]) {
        if (typeof i=="integer") {
          s += "::"+varname+"["+i+"]<-"+Dump(v)+";\n";
        } else {
          s += "::"+varname+"."+i+"<-"+Dump(v)+";\n";
        }
      }
    } else {
      s = "::"+varname+"<-"+Dump(getroottable()[varname])+";\n";
    }

    error(s);

    c = compilestring(s);
    writeclosuretofile(SaveName,c);
  }
}

function LoadVar()
{
  if (SaveName==null)
    MakeSaveName();

  try {
    dofile(SaveName,false);
  } catch(e) {
    return false;
  }
/* Remove comment to have the save file removed on load
  try {
    remove(s);
  } catch(e) {}
*/
  return true;
}

result = LoadVar()
Loads the vars for this script. Must be called from Voxel_Step since the GetPath and GetInfo is not available in Voxel_Load.
Result is true if there where variables loaded, otherwise false.

SaveVar( tablename )
Save the variables in the global table named tablename. tablename is the name of the table, not the table it self.
You need to call LoadVar() or MakeSaveName() from Voxel_Step before a call to SaveVar() can be made in Voxel_Unload.
Ex: SaveVar("Persistent");

The variables are stored as ScriptNr_robotID.save in a compiled Squirrel script in Universes\nr\Scripts\UserData directory.

Example script that uses SaveVar.nut for saving and loading variables

Code: [Select]
NeedInit <- true;

//global table with the variables that the script needs to be saved and reloaded
Persistent <- {
  StartX = 0,            // The X pos we started at
  StartZ = 0,            // The Z pos we started at
  BlockType = 0,      // The type of Block we are on top of
}

function Voxel_Unload()
{
  // Save Variables
  SaveVar("Persistent");  // Important, use the name of the global table, not the table it self.
}

function Voxel_Step()
{
  // Load our variables if needed
  if (NeedInit)
        NeedInit = false;  // Need to take care of the Init our self now
        // Include SaveVar.nut
        dofile(GetPath(3)+GetInfo(20)+"SaveVar.nut",true);
        // Load our variables
        LoadVar();
  }

   // Do stuff
  Display("StartX="+Persistent.StartX+" StartZ="+Persistent.StartZ+" BlockType="+Persistent.BlockType, 2000, 4, 1000);

}

3
Programming with Blackvoxel / Include file for saving variables.
« on: October 26, 2013, 01:34:22 pm »
I made a "include" file for scripts that needs to have there variables saved and loaded when they go out of scope (game reload or outside of the loaded world).
All the variables that needs to be saved must be stored in a global table. The name of the table does not matter.

"SaveVar.nut" must be saved in the games directory, not the script directory.
Code: [Select]
// SaveVar.nut

NeedLoad <- true;
Pos <- [0,0,0];

function Dump(var)
{
    local i,v;
    local s="";
    if (typeof var=="null") {
      s = "null";
    } else if (typeof var=="string") {
      s = "\""+var+"\"";
    } else if (typeof var=="array") {
      s = "[";
      foreach (v in var) {
        s += Dump(v)+",";
      }
      s += "]";
    } else if (typeof var=="table") {
      s = "{";
      foreach (i,v in var) {
        if (typeof i=="integer") {
          s += "["+i+"]="+Dump(v)+",";
        } else {
          s += i+"="+Dump(v)+",";
        }
      }
      s += "}";
    } else { // integer  bool  ??
      s = var+"";
    }
    return s;
}

function SaveVar(varname)
{
  local c,s,filename;
  s="";
  filename = format("%d_%d_%d.save",Pos[0],Pos[1],Pos[2]);
  if (varname in getroottable() && typeof varname="string") {
    if (typeof (getroottable()[varname])=="table") {
      foreach (i,v in getroottable()[varname]) {
        if (typeof i=="integer") {
          s += "::"+varname+"["+i+"]<-"+Dump(v)+";\n";
        } else {
          s += "::"+varname+"."+i+"<-"+Dump(v)+";\n";
        }
      }
    } else {
      s = "::"+varname+"<-"+Dump(getroottable()[varname])+";\n";
    }

    c = compilestring(s);
    writeclosuretofile(filename,c);
  }
}

function LoadVar()
{
  local s;
  s = format("%d_%d_%d.save",GetX(),GetY(),GetZ());
  NeedLoad = false;
  try {
    dofile(s);
  } catch(e) {
    return false;
  }
  try {
    remove(s);
  } catch(e) {}
  return true;
}

function SavePos()
{
  Pos[0]=GetX();
  Pos[1]=GetY();
  Pos[2]=GetZ();
}

result = LoadVar()
Loads the vars for this script.
Result is true if there where variables loaded, otherwise false.

SaveVar( tablename )
Save the variables in the global table named tablename. tablename is the name of the table, not the table it self.
Ex: SaveVar("Persistent");

SavePos()
Stores the robots position for use by SaveVar in Voxel_Unload() since we don't have access to the robots position in Voxel_Unload(). Needs to be called after every time we have moved.

The variables are stored as X_Y_Z.save in a compiled Squirrel script in the games directory. If you have the game installed in "/Program Files" you will need to give your self write access to the game directory, or it will not work.

WARNING! This script does not know what Universe you are in so problems may occur if you play several universes.

Example script that uses SaveVar.nut for saving and loading variables
Code: [Select]
//global table with the variables that the script needs to be saved and reloaded
Persistent <- {
  StartX = 0,            // The X pos we started at
  StartZ = 0,            // The Z pos we started at
  BlockType = 0,      // The type of Block we are on top of
}

function Voxel_Load()
{
  // "Include" SaveVar.nut
  dofile("SaveVar.nut");
}

function Voxel_Unload()
{
  // Save Variables
  SaveVar("Persistent");  // Important, use the name of the global table, not the table it self.
}

function Voxel_Step()
{
  // Load our variables if needed
  if (NeedLoad)
        LoadVar();


   // Do stuff
  Display("StartX="+Persistent.StartX+" StartZ="+Persistent.StartZ+" BlockType="+Persistent.BlockType, 2000, 4, 1000);


  // Last thing in the Voxel_Step
  SavePos();
}

Edit: Oops. Forgot to support "null", "true" and "false". Updated SaveVar.nut

4
Suggestions / Windows 64 bit or Large Address Aware
« on: October 25, 2013, 07:01:15 pm »
Could we please have a 64 bit windows exe, or at least a Large Address Aware Windows 32 bit exe. I would like to experiment with larger loaded world size.

That there is a 64 bit Ubuntu version doesn't help me since my machine running Ubuntu only has 2G of memory :(

5
General Discussion / Saving variables
« on: October 24, 2013, 01:46:09 pm »
Now it's possible to save variables between game save/load with the following code.

Code: [Select]
Len <- 1;
Dir <- 0;
Moves <- 0;

function Voxel_Load()
{
try {
dofile("save.nut");
remove("save.nut");
} catch(e) {}
}

function Voxel_Unload()
{
local s;
s = compilestring(format("::Moves<-%d;\n::Dir<-%d;\n::Len<-%d;\n",Moves,Dir,Len));
writeclosuretofile("save.nut",s);
}

But there is not enough information available to the script to distinguish different robots apart or different universes.

I was thinking I could save the variables for a script in it's Voxel_Unload function. I need to have a way to separated several programmable robots that all run the same script apart. So I thought their x,y,z position should do. But those are not available in the Voxel_Load and Voxel_Unload.
I also need to separate the saved variables from different universes, but there is no way of knowing what universe we are in.
There should probably also be a way for a script to read it's program number. (In case you have copied or renamed it)

I also have trouble identifying if Voxel_Load was called because the robot was placed in the world, loaded, or had it's program changed. I really only want to load stored variables when the robot is loaded into the world, not when it is first placed, or had it's program changed.

All in all, it would probably be better if there was a specific blackvoxel function that handled the saving and loading of variables that a script could use.

6
Suggestions / Extended voxel interface
« on: October 21, 2013, 03:41:58 pm »
How about an extended voxel interface between the programmable robot and the atomic compressor that lets the programmable robot query the compressors storage to see how many and what voxel types it has. And also the ability to get a specific voxel type.

This could be limited to higher tier robots and compressors.

7
General Discussion / Water to Sequencer?
« on: October 16, 2013, 09:50:00 pm »
How do I feed a sequencer with water?
Other then manually putting it in the Input area of the sequencer that is.


8
Programming with Blackvoxel / Complex Mining Robot Restarter
« on: October 16, 2013, 05:30:38 pm »
This is a script that is build on Qon's Simple Mining Robot Restarter.

This version can handle several compressors with 4 robots each.
There are a number of settings you can change at the top of the script to make it do what you want to.
If there is anything you want it to do that it can't, feel free to add that to the script your self  :P

Documentation is in the script.

Code: [Select]
// Complex Mining Robot Restarter
//
// Define the size of how many compressor/robots for it to manage.
// Place the Computer where you want it to start placing the first compressor/robots.
// Load it with the exact amount of Robots it needs (SizeX *  SizeZ * 4)
// Load it with at least the number of Atomic Compressors it need. If you are using "Master" compressors
//   you need to give it at least one more then the number compressor/robots to manage, and more if you want
//   it to place new "master" compressors along the way to speed things up and make it able to go deeper then -192 from the start.
// Start the script. (I recommend that you leave script 0.nut empty and give this script a higher number so you can load it with
//   with compressors and robots before starting it.)
// Stand somewhere inside the area being mined to follow it down to prevent the robots to move outside of the loaded area.
//
// Set BuildStarts <- 1 to have it automaticaly build a staircase right next to the "Master" compressors for easy access
//  to the "master" compressors and going up and down.
//
// For now the only time it can handle a load is when it is standing on top of the first compressor waiting for the robots to finish.
// This will be fixed when I will be able to save and restore variables.

Debug <- 1;           // Output debug info. 0=Only errors to stderr.txt 1=Errors to Display and to stderr.txt 2=Info and error to display and stderr.txt
SizeX <- 4;           // Number of compressor/robot sets to manage in X axis (8 is max practical value)
SizeZ <- 4;           // Number of compressor/robot sets to manage in Z axis (8 is max practical value)
UseMasterCompressor <- 1;  // Use one "master" Atomic Compressor to empty the computer to after every iteration. (Strongly recomended)
NewMasterAfter <- 128;// How often to place a new "Master" Atomic Compressor (Need to be <= 192. Make it an even 16 please, and anything less the 64 is a waste.)
BuildStairs <- 2;     // 0=Do not build stair, 1=Build Stair outside mining area, 2=Build stair inside mining area. (Only works if UseMasterCompressor is on)
MaxLevel <- -575;     // Don't place robots deeper then this. (Going below -1340 is dangerous)
Robot <- 153;         // The VoxelType of our robots. 153=XR-1(8*16*8) 154=XR-2(8*32*8) 155=XR-3(8*32*8) 156=XR-4(8*64*8) 157=XR-5(16*64*16) 158=XR-Z(16*64*16)
Compressor <- 49;     // The VoxelType of the Atomic Compressor (Should be 49 unless the game is changed)

// ============= DON'T CHANGE ANYTHING BELOW THIS ===========

Path <- {
  // Stair 2 Wide 4 High 16 Down. Size 7x7. Builds floor and railings if in empty space
  [0] = [ 9, 12, 12, 8, 13, 13, 13, 21, 16, 10, 21, 18,
          9, 12, 12, 8, 13, 13, 13, 21, 16, 10, 21, 18,
          9, 12, 12, 8, 13, 13, 13, 21, 16, 10, 21, 18,
          9, 12, 12, 8, 13, 13, 13, 21, 16, 10, 21,
          9, 12, 12, 12, 8, 13, 13, 13, 21, 16, 17, 10, 21, 17, 18,
          11 ],
  // Doorway 2 Wide 3 High. Ends in the same location it started
  [1] = [ 9, 9, 12, 12, 8, 13, 5, 10, 13, 11, 11 ],

  // Stair 2 Wide 4 High 16 Down. Size 7x7. Builds floor and railings if in empty space
  [2] = [ 21, 8, 21, 11, 21, 10, 21,
          11, 13, 21, 18, 8, 21, 16,
          11, 13, 21, 16, 10, 21, 18,
          11, 13, 21, 18, 8, 21, 16,
          11, 13, 21, 10, 21, 18,
          11, 21, 18, 19, 8, 21, 19,
          10 ]
};
PathNo <- 0;

// StartX             // The X pos we started at
// StartZ             // The Z pos we started at
// CurrentC           // What compressor are we working with
// State              // What are we currently doing
// MiningSize         // How wide the Robot mines.
// MiningLevel        // The level where the latest compressors where placed from
// MasterLevel        // The Y pos of the latest master
// StairsLevel        // Stairs are built to this level
// Step <- 0;         // Current step in the path
// Rotation <- 0;     // Current rotation adjustment

Time <- 0;
LastState <- 0;
UnknownReturn <- 256000;
NextC <- 0;
NumC <- 0;
PullC <- 0;

/*
Logic:
Place all compressor/robots
Place Master Compressor if not already placed.
Empty Computer into Master Compressor
Empty all compressors
Dig staircase down to mining level.
Go to the compressors and wait for there robots to finish.
Pick up all the mined material, robots and the compressors.
Go down to the bottom of the mine shaft.
Restart
*/

function Voxel_Load()
{
  Time = GetGameTime() - 2000;
  if ("State" in getroottable()) {
    log_file("Starting at State "+State);
  } else {
    ::State <- 0;
  }
  if ("CurrentC" in getroottable()) {
    log_file("Working on Compressor "+CurrentC);
  } else {
    ::CurrentC <- 0;
  }
  if ("MasterLevel" in getroottable()) {
    log_file("Using old MasterLevel of "+MasterLevel);
  } else {
    ::MasterLevel <- 1;
  }
  if ("StartX" in getroottable()) {
    log_file("Using old StartX of "+StartX);
  } else {
    ::StartX <- GetX();
  }
  if ("StartZ" in getroottable()) {
    log_file("Using old StartZ of "+StartZ);
  } else {
    ::StartZ <- GetZ();
  }
  if ("MiningLevel" in getroottable()) {
    log_file("Using old MiningLevel of "+MiningLevel);
  } else {
    ::MiningLevel <- 1;
  }
  if ("Step" in getroottable()) {
    log_file("Using old Step of "+Step);
  } else {
    ::Step <- 0;
  }
  if ("Rotation" in getroottable()) {
    log_file("Using old Rotation of "+Rotation);
  } else {
    ::Rotation <- 0;
  }
  if ("StairsLevel" in getroottable()) {
    log_file("Using old StairsLevel of "+StairsLevel);
  } else {
    ::StairsLevel <- 0;
  }
  if (Robot<153)
    Robot = 153;
  else if (Robot>158)
    Robot = 158;
  if (Robot>=157) {
    ::MiningSize <- 16;
  } else {
    ::MiningSize <- 8;
  }

  if ("NumR" in getroottable()) {
    log_file("Using old NumR of "+NumR);
  } else {
    ::NumR <- GetQuantity(Robot);
  }

  if (NewMasterAfter > 192)
    NewMasterAfter = 192;
  if (NewMasterAfter < 16)
    NewMasterAfter = 16;
  NewMasterAfter = (NewMasterAfter / 16) * 16
  if (BuildStairs == 2)
    PathNo = 2;
}

function Voxel_Unload()
{
}

function Voxel_Step()
{
    local i,j,v,q,b;

    if ( (GetGameTime() - Time) > 1 )  // To run the script in slow-motion set time to wait to 1000 or higher
    {
       Time = GetGameTime();
    } else {
       return;
    }

    if (LastState != State) { // Only log on State change
        log_file("State="+State+" CurrentC="+CurrentC);
        LastState = State;
    }

    switch (State)
    {
        case 0: // Start up State. Try to figure out what to do
            if (Look(5) == Compressor) {
                // If we start ontop of a Compressor we just assume this is compressor set 0. (Since this is where we spend most of our time.)
                // This is the best we can do until there is a way to save variables
                StartX = GetX();
                StartZ = GetZ();
                MasterLevel = UnknownReturn;
                MiningLevel = GetY();
                StairsLevel = GetY() -1;
                CurrentC = 0;
                State = 5;
                log_info("Starting at compressor 0 X:"+StartX+" Y:"+MasterLevel+" Z:"+StartZ+" CurrentC:"+CurrentC);
                break;
            }
            if (GetQuantity(Robot)>=(SizeX*SizeZ*4)) { // We have all our robots.
                if (GetQuantity(Compressor)>=(SizeX*SizeZ+UseMasterCompressor)) { // and our compressor.
                  // Start from scratch.
                  StartX = GetX();
                  StartZ = GetZ();
                  MiningLevel = GetY() + 1;
                  MasterLevel = GetY() + 1;
                  StairsLevel = GetY();
                  CurrentC = 0;
                  log_info("Starting from scratch X:"+StartX+" Y:"+MasterLevel+" Z:"+StartZ+" CurrentC:"+CurrentC);
                  State = 40;
                  break;
                } else {
                  log_err("Wrong amount of Atomic Compressors. Have "+GetQuantity(Compressor)+" but need "+(SizeX*SizeZ+UseMasterCompressor) );
                }
            } else {
              log_err("Wrong amount of Robots. Have "+GetQuantity(Robot)+" but need "+(SizeX*SizeZ*4) );
            }
            Time = Time + 5000;
            break;

        case 5: // Move down until we find a Compressor
          v = Look(5);
          if (v!=Compressor) {
              if (!Move(5)) {
                  log_err("Could not move down to my compressor "+CurrentC);
              }
          } else {
              log_info("Waiting for Robots for set "+CurrentC);
              NumR = GetQuantity(Robot);
              PullC = 0;
              State = 10;
          }
          break;

        case 10:  // Waiting on top of a compressor for all the robots to finish there work.
            if (Look(5)==Compressor) {
                j = 5000; // Limit the amount of voxels to pull every step (It will still only take us 4 seconds to empty the compressor)
                while(PullVoxelFrom(5) && j>0) { // Pull voxels from the atomic compressor
                    j--;
                }
                if (j>0) { // We didn't reach our pull limit so check if the robots done
                    if (GetQuantity(Robot)>=(NumR+4)) { // If all mining robots are picked up then we can move on as soon as the compressor is empty
                        State = 11;
                    } else {
                        // Check for storage full
                        if (UseMasterCompressor && IsStorageFull()) {
                            State = 15; // Handle storage full
                        } else {
                            // Robots not yet ready, so we can take a rest (This is to prevent the script from using unnessesary cpu time while just waiting)
                            Time = Time + 1000;
                        }
                    }
                }
            } else {
                // Something is wrong.
                log_err( "No Atomic compressor found a set "+CurrentC+". Moving on!" );
                CurrentC++;
                State = 20;
            }
            break;
        case 11: // Just because we have our robots doesn't mean the compressor is empty. But one last pull should be enough
            if (Look(5) == Compressor) {
                if(!PullVoxelFrom(5) && PullC>0) {
                    // Check that we didn't fail to Pull voxels because our storage is full.
                    if (IsStorageFull()) {
                      State = 15;
                    } else {
                      NumC = GetQuantity(Compressor);
                      State = 12;
                    }
                } else {
                    PullC++;
                    State = 10;
                }
            } else {
                State = 10;
            }
            break;
        case 12: // Pick up Atomic Compressor
            if (Look(5) == Compressor) {
                log_info("Picking up Compressor for set "+CurrentC);
                PickVoxel(5);
                if (GetQuantity(Compressor)>NumC) { // Make sure the atomic compressor is on board
                    State = 20;
                    CurrentC++;
                } else {
                    // Computer storage is probably full.
                    log_err("Can't pick up the Atomic Compressor for set "+CurrentC+". Is my storage full?");
                    if (IsStorageFull())
                        State == 15;
                }
            } else {
                State = 10;
            }
            break;

        case 15: // My storage is full
            if (UseMasterCompressor) {
                log_info("My storage is full. Moving to master to empty my self.");
                NextC = CurrentC; // Save this compressor to process it next
                CurrentC = SizeX * SizeZ;
                State = 50; // Go empty me
            } else {
                State = 16;
            }
            break;
        case 16: // Signal that I need to be manually emptied
            if (IsStorageFull()) {
                log_err("My storage is full! PLEASE EMPTY ME!");
                Time = Time + 15000;
            } else {
                State = 10;
            }
            break;

        case 20: // Move to the next Compressor to pick up
            if (MoveToNextCompressor(0)) {
                if (CurrentC==0) { // Are we back to the first Compressor?
                  State = 30;
                } else {
                  State = 5;
                }
            }
            break;

        case 30: // The compressors and all mining bots are in storage so its safe to dig down.
            if (GetY() < MaxLevel) {
              log_info("Hit max level. Just going to empty my storage then I stop my work.");
              State = 100;
              break;
            }
            // Check if we have hit bottom
            i = 0;
            for (j=0; j<4; j++) { // Count the number of solid voxels on our sides
                v = Look(j);
                b = GetVoxelProp(v,0)
                if (!b) {
                    i++;
                }
            }
            if (i>2) { // We have hit bottom
                State = 35;
            } else {
              SafeMove(5);
            }
            break;
        case 35: // Move up one so we are above the bottom
            if (SafeMove(4)) {
                State = 40;
            }
            break;

        case 40: // We are now in position to place the mining robots
            log_info("Placing Robots for set "+CurrentC);
            for (i=0; i<4; i++) {
                if (SafePickVoxel(i))
                    PlaceVoxel(i, Robot);
            }
            State = 41;
            break;
       case 41:
            if (SafeMove(4))
                State = 42;
            break;
        case 42: // Place the atomic compressor between the mining bots
            log_info("Placing Compressor for set "+CurrentC);
            PlaceVoxel(5, Compressor);
            if (CurrentC==0)
                MiningLevel = GetY();
            CurrentC++;
            State = 43;
            break;
        case 43: // Make sure the compressor is in place. Do we need to do this?
            if (Look(5)==Compressor) {
                State = 50;
            }
            break;

        case 50: // Move to place the next Compressor/Robot Set
            if (MoveToNextCompressor(UseMasterCompressor)) {
                State = 51;
            }
            break;
        case 51: // We are at the location of our next Compressor.
            if (CurrentC == 0) {
                State = 5;
            } else {
                if (CurrentC < SizeX*SizeZ) {
                    if (SafeMove(5))
                        State = 40; // Place next robot/compressor set
                } else {
                    log_info("Pushing Voxels to Master Compressor");
                    State = 60; // Place Master or Empty into Master
                }
            }
            break;

       case 60: // Empty my self into the master
            if (PushMaster()) {
                if (BuildStairs && NextC==0)
                    State = 70;
                else
                    State = 80;
            }
            break;

        case 70: // Make stairs down next to the Master Compressor
            if (MiningLevel < StairsLevel) { // Only build stairs if we are mining below the already built stairs level
                log_info("Building Stair");
                if (SafeMove(0)) // Move away from the Master Compressor
                    State = 71;
            } else {
                State = 80;
            }
            break;
        case 71: // Move to the start of the stair
            if (MoveToStair())
                State = 72;
            break;
        case 72: // Build stair down to MiningLevel
            if (BuildStair()) {
                State = 80;
            }
            break;

        case 80: // Move to the first compressor
            CurrentC = NextC;
            NextC = 0;
            if (MoveToNextCompressor(0)) {
                State = 5;
            }
            break;

        case 100: // Done mining. Just some stuff to finish up.
            CurrentC = SizeX * SizeZ;
            MiningLevel = GetY();
            if (UseMasterCompressor)
                State = 101;
            else
                State = 105;
            break;
        case 101: // Return to the nearest "master"
            if (MoveToNextCompressor(1)) {
                log_info("Pushing Voxels to Master Compressor");
                State = 102;
                break;
            }
            break;
        case 102: // Empty me
            if (PushMaster()) {
                if (BuildStairs) {
                    log_info("Building Stair");
                    if (SafeMove(0)) // Move away from the Master Compressor
                        State = 103;
                } else {
                    State = 105;
                }
            }
            break;
        case 103: // Move to the start of the stair
            if (MoveToStair())
                State = 104;
            break;
        case 104: // Build at least one full rotation of the stair
            if (BuildStair()) {
                State = 105;
            }
            break;
        case 105: // Inform that we are done
            log_info("ALL WORK DONE!");
            State = 999;
            break;
        case 999: // Do nothing
            Time = Time + 60000;
            break;
    }
}

function MoveToNextCompressor(includeMaster)
{
    local x,y,z,v,d,destX,destY,destZ;
    if (CurrentC >= (SizeX * SizeZ + includeMaster))
        CurrentC = 0;
    if (CurrentC < SizeX*SizeZ) {
        x = CurrentC%SizeX;
        z = CurrentC/SizeX;
        if (z%2 > 0)
            x = (SizeX-1)-x;
        destZ = StartZ + z * MiningSize * 2 + x; // Offset every row of compressors by one for efficency
        destX = StartX + x * MiningSize * 2 - z;
        destY = MiningLevel;
    } else {
        if (UseMasterCompressor == 1) {
            destZ = StartZ - MiningSize;
            if (MasterLevel == UnknownReturn) {  // Search for nearest Master
                destX = StartX;
                if (destX == GetX() && destZ == GetZ()) {
                    if (Look(1) == Compressor) {
                        MasterLevel = GetY()-1;
                        destY = MasterLevel;
                    } else {
                        destY = GetY()-1;
                        if (destY > 1)
                          return 1;  // Failed to find a master compressor
                    }
                } else
                    destY = GetY();
            } else {
                if (((MasterLevel-NewMasterAfter) >= MiningLevel) && GetQuantity(Compressor) > 0) {
                    // If it is more then the set number of blocks up to the master and we still have Compressors in inventory place a new master.
                    MasterLevel = MiningLevel;
                    log_info("Setting new MasterLevel to "+MasterLevel);
                }
                destX = StartX + 1;
                destY = MasterLevel;
            }
        } else {
            log_err("Unexpected CurrentC "+CurrentC);
            return 0;
        }
    }
    if (destY>GetY()) {
        d = 4;  // Always move up first
    } else if (destX>GetX()) {
        d = 1;
    } else if (destX<GetX()) {
        d = 3;
    } else if (destZ>GetZ()) {
        d = 0;
    } else if (destZ<GetZ()) {
        d = 2;
    } else if (destY<GetY()) {
        d = 5; // and always down last
    } else {
        return 1;
    }
    log_file("Moving to Compressor "+CurrentC+" X:"+destX+" Y:"+destY+" Z:"+destZ+" Dir:"+d);
    if (!SafeMove(d)) {
        log_err("Could not move in direction "+d+". Area is probably not loaded.");
        Time = Time + 10000;
    }
    return 0;
}

function PushMaster()
{
    local i,j,v,b;
    b = 0;
    if (PathNo==2)
      b = GetLastBlackRock(); // If we build stair inside mining area we need to save one BlackRock type
    if (Look(5)==Compressor) {
        i = 0;
        j = 5000; // Limit how many voxels to push every Step.
        while (i<20 && j>0) {
            if (GetSlot_Quantity(i) > 0) {
                v = GetSlot_Type(i);
                if (v!=Compressor && v!=Robot && v!=b) {
                    PushVoxelTo(5,v);
                    j--;
                } else {
                    i++;
                }
            } else {
                i++;
            }
        }
        if (i >= 20) { // All Done
          return 1;
        }
    } else {
        if (GetQuantity(Compressor)>0) {
          log_info("Placeing Master Compressor");
          SafePickVoxel(5);
          PlaceVoxel(5,Compressor);
        } else {
           log_err("No Master Compressor found and no Atomic Compressor in inventory");
           Time = Time + 10000;
        }
    }
    return 0;
}

function IsStorageFull()
{
    local i;
    i = 0;
    while (GetSlot_Quantity(i) > 0 && i<20) {
      i++;
    }
    if (i>=20)
        return 1;
    return 0;
}

function MoveToStair()
{
    local d,destX,destY,destZ;

    if (BuildStairs==1) {
        destX = StartX + 2;
        destZ = StartZ - MiningSize - 2;
    } else {
        destX = StartX - 1;
        destZ = StartZ - MiningSize;
    }
    destY = StairsLevel;

    if (destX>GetX()) {
        d = 1;
    } else if (destX<GetX()) {
        d = 3;
    } else if (destY<GetY()) {
        d = 5;
    } else if (destY>GetY()) {
        d = 4;
    } else if (destZ>GetZ()) {
        d = 0;
    } else if (destZ<GetZ()) {
        d = 2;
    } else {
        if (BuildStairs==1)
          PathNo = 0;
        else
          PathNo = 2;
        Rotation = 0; // Always start from the beginning since we are only building full revolutions
        Step = 0;
        return 1;
    }
    log_file("Moving to start of stairs "+CurrentC+" X:"+destX+" Y:"+destY+" Z:"+destZ+" Dir:"+d);
    if (!SafeMove(d)) {
        log_err("Could not move in direction "+d+". Area is probably not loaded.");
        Time = Time + 10000;
    }
    return 0;
}

function BuildStair()
{
  local i,j,v,done;
  done = 0;
  do {
    // Check for end of path
    if (Step >= Path[PathNo].len())
    {
      Step = 0;
      Rotation = (Rotation+1)%4; // Next execution of path will be rotated one step clockwise.
      if (Rotation==0) { // One rotation done
        if (PathNo==0) {
          PathNo = 1; // Make doorway
          Rotation=3; // Only one rotation
        } else {
          StairsLevel = GetY();
          if (PathNo==1)
              PathNo = 0; // Reset PathNo to Stair
          if (StairsLevel <=  MiningLevel) // Continue until we are below of equal to the mining level
              return 1; // We are done. Let the caller know
        }
      }
    }
    i = Path[PathNo][Step];

    // Get direction part of path
    j = (i%8);
    if (j<4) // Don't rotate up/down
    {
      // Modify move/dig value with rotation
      j = (j+Rotation)%4;
    }

    v = Look(j);
    if ((i & 16)==16) { // Bit 8 set = Place Voxel
      if (GetVoxelProp(v,0)) { // If it is not a solid, place one.
        v = GetFirstBlackRock();
        if (v>0)
          PlaceVoxel(j,v);
      }
    } else {
      if (!GetVoxelProp(v,0)) // If it is a solid, mine it.
        SafePickVoxel(j);
    }
    if ((i & 8)==8) { // Bit 3 set = Move also
      if (!Move(j)) {
        log_err("Could not move while building stair! "+j);
      }
      done = 1;
    }
    Step++;
  } while (done==0)
  return 0; // Need to return but not really done yet
}

function GetFirstBlackRock()
{
  local a;
  for (a=1; a<=10; a++)
  {
      if(GetQuantity(a)>0)
      {
          return a;
      }
  }
  return 0;
}

function GetLastBlackRock()
{
  local a;
  for (a=10; a>=1; a--)
  {
      if(GetQuantity(a)>340)
      {
          return a;
      }
  }
  return 0;
}

function SafePickVoxel(dir)
{
    local v,b;
    v = Look(dir);
    if (v != Compressor && v != Robot) {
        b = GetVoxelProp(v,0)
        if (!b) { // If voxel is solid, pick it.
            return PickVoxel(dir);
        }
    } else {
      log_err("Oops. Almost picked a Compressor or Robot by mistake!");
      return 0;
    }
    return 1;
}

function SafeMove(dir)
{
    local v;
    if (SafePickVoxel(dir)) {
        v = Look(dir);
        if (v != 86 && v != 52) { // Avoid Green Acid and Lava
            return Move(dir);
        } else {
          log_err("Oops. Almost moved into Green Acid or Lava");
        }
    }
    return 0;
}

function log_file(msg)
{
    if (Debug>2) {
       error("Robot at " + GetX() + "," + GetY() + "," + GetZ() + " Info: " + msg+"\n");
    }
}

function log_info(msg)
{
    if (Debug>1) {
        Display( "Robot at " + GetX() + "," + GetY() + "," + GetZ() + " Info: " + msg, 1000, 4, 500);
        error("Robot at " + GetX() + "," + GetY() + "," + GetZ() + " Info: " + msg+"\n");
    }
}

function log_err(msg)
{
    if (Debug>0)
      Display( "ROBOT AT " + GetX() + "," + GetY() + "," + GetZ() + " Err: " + msg, 5000, 4, 2000);
    error("ROBOT AT " + GetX() + "," + GetY() + "," + GetZ() + " Err: " + msg+"\n");
    Time = Time + 2000;
}

9
Suggestions / Metalurgy
« on: October 14, 2013, 05:13:45 pm »
It's simple enough to automate the manufacturing of most metal bars. But Bronze Bars and Stainless Steel Bars are very difficult to automate since they need to mix correctly before being cooled.

Could it be possible to make the Melting Furnace not expel the melted metal before you tell it to with for example a BlackRock Blue or could we have some machine that can read the metallurgy mix and only let them pass when it is correct?

It is possible to store the melted metal in an Atomic compressor by having and Voxel Dematerialize store it there. But that seems to ruin the metallurgy mix. Those melted metals always make Defective Alloy when cooled.

10
Suggestions / More Vehicles
« on: October 14, 2013, 03:56:09 pm »
Suggestions for more Vehicles.

Elevator. Goes up / down. Needs at least one voxel on one of it's side all the time. So it cant travel into space.

Car. Can only travel on flat ground. Maybe 2-3 times faster then walking.

Boat. Can only travel on water.

Train. Who doesn't like trains. Needs rail to travel on.

11
Suggestions / Optical Transmission.
« on: October 14, 2013, 03:44:54 pm »
It's possible to directly dematerialize a voxel into a Optical Voxel Transmitter from a Pump, but it's not possible to directly materialize a voxel from an Optical Voxel Receiver with a Voxel Materialize without using a Atomic compressor between them.

Could that be made possible?

12
Suggestions / Power Generators
« on: October 14, 2013, 03:14:49 pm »
A lot of robots and machines uses a Wireless Energy Transmitter/Receiver to power them. But there is nothing in the game that actually generates this power that they are to receive.

So I suggest 4 types of power generators.

Solar panels. (I don't know if this works on this planet since it looks pretty dark.)
Coal Power Plant. (Not that environment friendly.)
Water Power Plant. (Needs a flow of water to drive a turbine to generate the power.)
Atomic Power Plant. (Since we already have Uranium.)

13
Suggestions / Machine Interface
« on: October 14, 2013, 03:08:03 pm »
Maybe make an interface panel for all the crafting machines (Base Machine, Multi-Purpose Robot...) that shows what items it has in it at the moment like the Sequencers "Input" field. Make that field so you can only pick up stuff from it and not put in stuff.

I would also be nice to be able to see what this machine can manufacture, and what is needed to manufacture it. Just as read only info so you don't need to switch to the manual all the time to check what items was needed to manufacture.

14
Suggestions / The Sequencer
« on: October 14, 2013, 03:02:07 pm »
The sequencer is probably the most powerful item in the game as it is now. Here are a couple of suggestions to make it a bit less power full to make the need for the other items (belt conveyer, voxel materializer/dematerialize, atomic compressors) higher.

Limit the "Input" field so you can only pick up stuff from it, not be able to put in stuff directly there. This way you will need to use Atomic Compressors, Voxel Materializers and conveyer belts to feed the Sequencer.

Make the "Sequence" field not store actual voxels. This is to prevent it from being used as a high capacity storage device. Explanation: If you drop 1 Coal in to one "sequence" slot you don't decrease the players inventory, and if you pull one Coal from a "sequence" slot you don't increase the players inventory. After all, it's just the blueprint for what to sequence out, not actual material.

15
Suggestions / Mineral amounts
« on: October 14, 2013, 02:52:18 pm »
The need for chrome, nickel and tin is very low, but they are found in plenty in the ground.
The amount of coal needed is huge but it's not found enough in the ground. The amount of coal is always my limiting factor for how much I can manufacture.

So my suggestion is to lower the amount of chrome, nickel and tin and increase the amount of coal.

Pages: [1] 2