Author Topic: Digging and sorting robot  (Read 5706 times)

Yves

  • Newbie
  • *
  • Posts: 9
    • View Profile
Digging and sorting robot
« on: February 26, 2019, 11:43:38 pm »
Hello,
Here I share the program I wrote to automatically dig, and place all that was taken in the appropriate storages.

This is still a bit rough around the edges, because I did not mind too much having to interfere with the robots once in a while instead of just fixing the program :-D
For example:
— Two robots can block themselves when they are front-to-front. Just alter the stock sequence of the robot that is going towards a storage, and it will go away.
— The robots expect a free path towards the storages, which actually depends on the position of the digging zones, relative to the factory zone.

Enough “talk”, here’s the code:
Code: [Select]
// Tunnel 16x16 downward
// *******************************************************************

const SOFT_LIMIT = 50;
const HARD_LIMIT = 1323;
const QUARTER_SIZE = 8;
const KEY_LEARN = 108;    // L (https://wiki.libsdl.org/SDLKeycodeLookup)
const KEY_VALIDATE = 111; // O (https://wiki.libsdl.org/SDLKeycodeLookup)
const KEY_CANCEL = 113;   // Q (https://wiki.libsdl.org/SDLKeycodeLookup)

const ST_END = 0;
const ST_INIT = 10;
const ST_CENTER = 20;
const ST_QUARTER = 30;
const ST_ASIDE = 40;
const ST_CLIMB = 50;
const ST_RETURN = 60;
const ST_DOCK = 70;
const ST_TRANSPORT = 80;
const ST_LEARN = 90;
const ST_UNLOAD = 100;
const ST_NEXTVOXEL = 110;

const WAIT = 2000;
Time <- 0;

InitDir <- 0;
State <- 0;
StartX <- 0;
StartY <- 0;
StartZ <- 0;
CenterX <- 0;
CenterZ <- 0;
StepX <- 0;
StepZ <- 0;
StepXBeforeZ <- true;
FlatSteps <- 0;
Dir <- 0;
Depth <- 0;
Quarter <- 0;
CellCount <- 0;
RowCount <- 0;
StartStock <- 0;
UnloadType <- 0;
UnloadXYZ <- {};
HardLimitReached <- false;

// This function is called at each robot cycle.

function Voxel_Step()
{
  local ok = true;
  local slot;
  local x;
  local y;
  local z;

  switch ( State )
  {
    case ST_END:
      display("Robot " + GetRobotID() + " will not go deeper!");
      break;
    case ST_INIT:
      Init_This();
      State = ST_CENTER;
      break;
    case ST_CENTER:
      if ( GetX() < CenterX )      ok = Take_And_Move(1);
      else if ( GetX() > CenterX ) ok = Take_And_Move(3);
      else if ( GetZ() < CenterZ ) ok = Take_And_Move(0);
      else if ( GetZ() > CenterZ ) ok = Take_And_Move(2);
      else if ( Quarter < 1 && Count_Stock() == StartStock )
      {
        Quarter = -1;
        ok = Probe_And_Move(5);
      }
      else if ( Quarter < 3 )
      {
        Quarter += 1;
        Dir = (Dir + 1) % 4;
        CellCount = 0;
        RowCount = 0;
        State = ST_QUARTER;
        ok = Take_And_Move(Dir);
      }
      else
      {
        Depth += 1;
        HardLimitReached = ((StartY - GetY()) >= HARD_LIMIT);
        if ( Depth >= SOFT_LIMIT || HardLimitReached ) ok = false;
        else
        {
          Quarter = -1;
          ok = Take_And_Move(5);
        }
      }
      if ( ! ok )
      {
        display("Robot " + GetRobotID() + " going UP at (" + CenterX + "," + CenterZ + ")...");
        Init_Steps();
        State = ST_ASIDE;
      }
      break;
    case ST_QUARTER:
      if ( CellCount < (QUARTER_SIZE - 1) )
      {
        ok = Take_And_Move(Dir);
        CellCount += 1;
      }
      else if ( RowCount == (QUARTER_SIZE - 1) )
      {
        if ( (RowCount % 2) == 0 ) Turn_Left(); else Turn_Right();
        ok = Take_And_Move(Dir);
        if ( (RowCount % 2) == 0 ) Turn_Right(); else Turn_Left();
        State = ST_CENTER;
      }
      else
      {
        if ( (RowCount % 2) == 0 ) Turn_Right(); else Turn_Left();
        ok = Take_And_Move(Dir);
        if ( (RowCount % 2) == 0 ) Turn_Right(); else Turn_Left();
        CellCount = 0;
        RowCount += 1;
      }
      if ( ! ok )
      {
        display("Robot " + GetRobotID() + " going UP at (" + CenterX + "," + CenterZ + ")...");
        Init_Steps();
        State = ST_ASIDE;
      }
      break;
    case ST_ASIDE:
      switch ( CellCount )
      {
        case 0:
          CellCount = 1;
          ok = Take_And_Move(4);
          break;
        case 1:
          CellCount = 2;
          if ( StepXBeforeZ && GetX() < StepX )      ok = Take_And_Move(1);
          else if ( StepXBeforeZ && GetX() > StepX ) ok = Take_And_Move(3);
          else if ( GetZ() < StepZ )                 ok = Take_And_Move(0);
          else if ( GetZ() > StepZ )                 ok = Take_And_Move(2);
          else if ( GetX() < StepX )                 ok = Take_And_Move(1);
          else if ( GetX() > StepX )                 ok = Take_And_Move(3);
          break;
        case 2:
          CellCount = 0;
          ok = Take_And_Move(5);
          if ( GetX() == StepX && GetZ() == StepZ )  State = ST_CLIMB;
          break;
      }
      if ( ! ok )
      {
        State = ST_RETURN;
      }
      break;
    case ST_CLIMB:
      switch ( CellCount )
      {
        case 0:
        case 1:
          ok = Take_And_Move(4);
          if ( FlatSteps > 0 )
          {
            CellCount += 1;
            FlatSteps -= 1;
          }
          break;
        case 2:
          ok = Take_And_Move(Dir);
          break;
        case 3:
          ok = Take_And_Move(5);
          break;
      }
      CellCount += 1;
      if ( CellCount == 4 )
      {
        CellCount = 0;
        RowCount += 1;
        if ( GetY() >= StartY )
        {
          State = ST_RETURN;
        }
      }
      if ( RowCount == (QUARTER_SIZE * 2 + 2) )
      {
        Turn_Left();
        RowCount = 0;
      }
      if ( ! ok )
      {
        State = ST_RETURN;
      }
      break;
    case ST_RETURN:
      if ( GetY() == StartY )
      {
        if ( GetX() < StartX )       ok = Take_And_Move(1);
        else if ( GetX() > StartX )  ok = Take_And_Move(3);
        else if ( GetZ() < StartZ )  ok = Take_And_Move(0);
        else if ( GetZ() > StartZ )  ok = Take_And_Move(2);
        else
        {
          State = ST_DOCK;
          display("Robot " + GetRobotID() + " is docked.");
        }
      }
      else
      {
        if ( GetX() < CenterX )      ok = Take_And_Move(1);
        else if ( GetX() > CenterX ) ok = Take_And_Move(3);
        else if ( GetZ() < CenterZ ) ok = Take_And_Move(0);
        else if ( GetZ() > CenterZ ) ok = Take_And_Move(2);
        else if ( GetY() < StartY )  ok = Take_And_Move(4);
        else                         ok = Take_And_Move(5);
      }
      if ( ! ok )
      {
        display("Robot " + GetRobotID() + " is stuck at (" + GetX() + "," + GetY() + "," + GetZ() + ")!");
      }
      break;
    case ST_DOCK:
      slot = GetFirstUsedSlot();
      if ( slot < 0 )
      {
        if ( HardLimitReached ) State = ST_END;
        else
        {
          display("Robot " + GetRobotID() + " going down at (" + CenterX + "," + CenterZ + ")...");
          Reinit_This();
          State = ST_CENTER;
        }
      }
      else if ( GetSlot_Type(slot) in UnloadXYZ )
      {
        UnloadType = GetSlot_Type(slot);
        State = ST_TRANSPORT;
      }
      else if ( GetKey(KEY_LEARN) )
      {
        State = ST_LEARN;
      }
      else
      {
        display("Robot " + GetRobotID() + " needs to store " + GetVoxelName(GetSlot_Type(slot)));
      }
      break;
    case ST_LEARN:
      slot = GetFirstUsedSlot();
      if ( slot < 0 || GetKey(KEY_CANCEL) )
      {
        display("Robot " + GetRobotID() + " abandons.");
        State = ST_DOCK;
      }
      else if ( GetKey(KEY_VALIDATE) )
      {
        display("Robot " + GetRobotID() + " remembers.");
        UnloadXYZ[GetSlot_Type(slot)] <- [GetInfo(4), GetInfo(5), GetInfo(6)];
        State = ST_TRANSPORT;
      }
      else
      {
        display("Please stand where Robot " + GetRobotID() + " will store " + GetVoxelName(GetSlot_Type(slot)));
      }
      break;
    case ST_TRANSPORT:
      slot = GetFirstUsedSlot();
      if ( slot < 0 || GetSlot_Type(slot) != UnloadType ) State = ST_NEXTVOXEL;
      else
      {
        x = UnloadXYZ[UnloadType][0];
        y = UnloadXYZ[UnloadType][1];
        z = UnloadXYZ[UnloadType][2];
        if ( GetY() < y )                                 ok = Move(4);
        else if ( GetY() > y )                            ok = Move(5);
        else if ( GetX() < x )                            ok = Move(1);
        else if ( GetX() > x )                            ok = Move(3);
        else if ( GetZ() < z )                            ok = Move(0);
        else if ( GetZ() > z )                            ok = Move(2);
        else                                              State = ST_UNLOAD;
      }
      if ( ! ok ) display("Robot " + GetRobotID() + " needs help!");
      break;
    case ST_UNLOAD:
      slot = GetFirstUsedSlot();
      if ( slot < 0 || GetSlot_Type(slot) != UnloadType ) State = ST_NEXTVOXEL;
      else
      {
        for ( Dir = 0; Dir < 6; Dir += 1 )
        {
          if ( GetVoxelProp(Look(Dir), 1) )
          {
            ok = PushVoxelTo(Dir, GetSlot_Type(slot));
            if ( GetSlot_Quantity(slot) == 0 )            State = ST_NEXTVOXEL;
            break;
          }
        }
      }
      if ( ! ok ) display("Robot " + GetRobotID() + " cannot unload!");
      break;
    case ST_NEXTVOXEL:
      if ( GetZ() < StartZ )      ok = Move(0);
      else if ( GetZ() > StartZ ) ok = Move(2);
      else if ( GetX() < StartX ) ok = Move(1);
      else if ( GetX() > StartX ) ok = Move(3);
      else if ( GetY() < StartY ) ok = Move(4);
      else if ( GetY() > StartY ) ok = Move(5);
      else                        State = ST_DOCK;
      if ( ! ok ) display("Robot " + GetRobotID() + " needs help!");
      break;
  }
}

// This function is called when robot is loaded from save file or when created

function Voxel_Load()
{
  local name = Get_Save_Filename();
  print("read state file = " + name + "\r\n");
  try
  {
    local save = dofile(name);
    InitDir      = save.InitDir;
    State        = save.State;
    StartX       = save.StartX;
    StartY       = save.StartY;
    StartZ       = save.StartZ;
    CenterX      = save.CenterX;
    CenterZ      = save.CenterZ;
    StepX        = save.StepX;
    StepZ        = save.StepZ;
    StepXBeforeZ = save.StepXBeforeZ;
    FlatSteps    = save.FlatSteps;
    Dir          = save.Dir;
    Depth        = save.Depth;
    Quarter      = save.Quarter;
    CellCount    = save.CellCount;
    RowCount     = save.RowCount;
    StartStock   = save.StartStock;
    UnloadType   = save.UnloadType;
    UnloadXYZ    = save.UnloadXYZ;
  }
  catch (e)
  {
    print("Could not read state file for robot " + GetRobotID() + "\r\n  " + e + "\r\n");
  }
  if ( GetInfo(23) == 0 || GetInfo(23) == 4 )
  {
    print("Robot " + GetRobotID() + " set to initial state.\r\n");
    State = ST_INIT;
  }
  else
  {
    print("Robot " + GetRobotID() + " restored.\r\n");
  }
}

// This function is called when robot is unloaded to save file or when destroyed

function Voxel_Unload()
{
  local name = Get_Save_Filename();
  local save = file(name, "w");
  local slot, xyz, data;
  local code = "return {\n";
  code += "  InitDir      = " + InitDir + ",\n";
  code += "  State        = " + State + ",\n";
  code += "  StartX       = " + StartX + ",\n";
  code += "  StartY       = " + StartY + ",\n";
  code += "  StartZ       = " + StartZ + ",\n";
  code += "  CenterX      = " + CenterX + ",\n";
  code += "  CenterZ      = " + CenterZ + ",\n";
  code += "  StepX        = " + StepX + ",\n";
  code += "  StepZ        = " + StepZ + ",\n";
  code += "  StepXBeforeZ = " + (StepXBeforeZ ? "true" : "false") + ",\n";
  code += "  FlatSteps    = " + FlatSteps + ",\n";
  code += "  Dir          = " + Dir + ",\n";
  code += "  Depth        = " + Depth + ",\n";
  code += "  Quarter      = " + Quarter + ",\n";
  code += "  CellCount    = " + CellCount + ",\n";
  code += "  RowCount     = " + RowCount + ",\n";
  code += "  StartStock   = " + StartStock + ",\n";
  code += "  UnloadType   = " + UnloadType + ",\n";
  code += "  UnloadXYZ    = {\n";
  foreach ( slot, xyz in UnloadXYZ )
  {
    code += ("    [" + slot + "] = [" + xyz[0] + ", " + xyz[1] + ", " + xyz[2] + "],\n");
  }
  code += "  }\n}\n";
  data = blob(code.len());
  foreach ( slot in code )
  {
    data.writen(slot, 'b');
  }
  save.writeblob(data);
  save.close();
  print("Robot " + GetRobotID() + " saved.\r\n");
}

function Get_Save_Filename()
{
  local saves = GetPath(4);
  local sep = GetInfo(20);
  local nut = GetInfo(22);
  local robot = GetRobotID();
  return saves + sep + nut + "_" + robot + ".state";
}

function Init_This()
{
  local yaw = (GetInfo(7) + (PI/4)) % (PI*2);
  StartX = GetX();
  StartY = GetY();
  StartZ = GetZ();

  if (yaw < (PI/2))
  {
    InitDir = 0;
    CenterX = StartX;
    CenterZ = StartZ + QUARTER_SIZE + 1;
  }
  else if (yaw < PI)
  {
    InitDir = 1;
    CenterX = StartX + QUARTER_SIZE + 1;
    CenterZ = StartZ;
  }
  else if (yaw < (3*PI/2))
  {
    InitDir = 2;
    CenterX = StartX;
    CenterZ = StartZ - QUARTER_SIZE - 1;
  }
  else
  {
    InitDir = 3;
    CenterX = StartX - QUARTER_SIZE - 1;
    CenterZ = StartZ;
  }
  Reinit_This();
}

function Reinit_This()
{
  Depth = 0;
  Quarter = 3;
  StartStock = Count_Stock();
  Dir = InitDir;
}

function Count_Stock()
{
  local c = 0;
  for (local s = 0; s < GetInfo(24); s += 1)
  {
    c += GetSlot_Quantity(s);
  }
  return c;
}

function Take_And_Move(Dir)
{
  PickVoxel(Dir);
  if ( ! Move(Dir) )
  {
    display("Robot " + GetRobotID() + " could not move at (" + GetX() + "," + GetY() + "," + GetZ() + "), Dir. " + Dir + ", BlockType " + Look(Dir));
    return false;
  }
  return true;
}

function Probe_And_Move(Dir)
{
  if ( ! GetVoxelProp(Look(Dir), 0) ) PickVoxel(Dir);
  if ( ! Move(Dir) )
  {
    display("Robot " + GetRobotID() + " could not move at (" + GetX() + "," + GetY() + "," + GetZ() + "), Dir. " + Dir + ", BlockType " + Look(Dir));
    return false;
  }
  return true;
}

function Turn_Right()
{
  Dir = (Dir + 1) % 4;
}

function Turn_Left()
{
  Dir = (Dir + 3) % 4;
}

function Init_Steps()
{
  local base0Depth = StartY - GetY() - 1;
  local edgeSize = QUARTER_SIZE * 2;
  local sideSize = edgeSize + 2;
  local sidesCount = floor(base0Depth / sideSize);
  local afterSides = base0Depth - (sideSize * sidesCount);
  FlatSteps = 0;
  if ( afterSides >= edgeSize )
  {
    sidesCount += 1;
    FlatSteps = sideSize - afterSides;
    afterSides = 0;
  }
  RowCount = sideSize - 1 - afterSides;
  CellCount = 0;
  switch ( (InitDir + sidesCount) % 4 )
  {
    case 0:
      StepX = CenterX - QUARTER_SIZE - 1;
      StepZ = CenterZ - QUARTER_SIZE + afterSides;
      Dir = 2;
      break;
    case 1:
      StepX = CenterX - QUARTER_SIZE + afterSides;
      StepZ = CenterZ + QUARTER_SIZE + 1;
      Dir = 3;
      break;
    case 2:
      StepX = CenterX + QUARTER_SIZE + 1;
      StepZ = CenterZ + QUARTER_SIZE - afterSides;
      Dir = 0;
      break;
    case 3:
      StepX = CenterX + QUARTER_SIZE - afterSides;
      StepZ = CenterZ - QUARTER_SIZE - 1;
      Dir = 1;
      break;
  }
  StepXBeforeZ = (StepX > GetX() && StepZ > GetZ()) || (StepX < GetX() && StepZ < GetZ());
}

function display(message)
{
  if ( (GetGameTime() - Time) >= WAIT )
  {
    print(message + "\r\n");
    Display(message, WAIT, 2, 0);
    Time = GetGameTime();
  }
}

Just put the robot on the ground and stand behind it. Open its interface and load the program; it will start immediately to dig a 16×16 “square”, the centre of which is 8 voxels-or-so in front of the robot. The shape is not exactly a square because it is that of what 4 of the game’s robots around a compressor would do.

So:
—1— The robot digs 50 layers of ground.
—2— The robot climbs around the hole, all the while carving a stairway, just for you :-)
—3— When “docked” at its starting point, it will store each voxel at the right place.
—4— It then starts again with 50 new layers of ground, and stops just before reaching the green acid (provided you started at the top of the central blue zone).

When the robot does not know where to put a voxel, it says so. In that case:
—1— Press “L” (learn), and the robot will ask you to stand near a compressor, at the exact place where the robot will be able to transfer the voxels into the compressor (at the level of your feet, not your head!).
—2— When you stand at the right place, press “O” (ok), and the robot will remember.
—3— If instead you prefer to cancel, and go retrieve yourself the voxels from inside the robot, press “Q” (quit), and the robot will quit the learning-mode.

Oh, a few tips, available if you quit the game (or at least the world):
— Each save-file is plain-text, so you can fix mistakes, in case you wrongly taught the robot.
— You can even copy-paste the knowledge of a know-it-all robot, to a newer robot ;-)

Cheers