Recent Posts

Pages: [1] 2 3 ... 10
1
Announcements / Re: Blackvoxel is now on GitHub !
« Last post by Enigma on March 04, 2019, 05:43:39 pm »
Thanks for the release of the source code. Can I use some of the code snippets in my project?

Hello RobertThomas and welcome to the Blackvoxel Forum ;)

The answer is fixed by the GPL V3+ Free Software Licence.

To make it simple, it depends if your project is Free Software.

If you want to re-use some Blackvoxel code for a proprietary software, the answer is No. It's not permitted at all. It would be a licence infringement.

If you want to re-use Blackvoxel code in a free software with Free Software Licence, the answer is Yes. In this case, you'll have to fully comply with GPL V3+ licence (your licence must be compatible), and make citation of the source of the Blackvoxel project and it's copyright owners (authors and contributors).

Simple conclusion : Free Software projects are welcome, others are not.

In the hope we have answered to your questions.

The Blackvoxel Team
2
Announcements / Re: Blackvoxel is now on GitHub !
« Last post by RobertThomas on March 04, 2019, 03:50:30 pm »
We are happy to announce the release of Blackvoxel source code on GitHub.

You can find it here:
All contributors are welcome !

The Blackvoxel Team

Source

Thanks for the release of the source code. Can I use some of the code snippets in my project?
3
General Discussion / Re: Anyone went below the green acid?
« Last post by Enigma on March 01, 2019, 08:30:30 am »
Hi Yves,

Yes there is a way to get through the acid layer without bypassing it ;)

As you already guessed, the Falling Rock is the way to get safely on the surface of the acid layer.

There is very few blocks which are immune to green acid. But Falling Rock is one of them. That's not by coincidence.

Now, you have to find a way to make some voxels to get bellow the surface.

We'll give you one hint : when you put a  "non immune" block on the side of a block which is on the acid layer, it will react with acid and this will make a temporary hole in the acid layer.

The Blackvoxel Team
4
Suggestions / Re: Extended voxel interface
« Last post by Enigma on March 01, 2019, 08:28:13 am »
Hello Yves,

Thanks for your suggestion.
Yes, the transfer speed is rather slow.
At origin, we didn't made the programmable robot very efficient at mining in order to keep interest to the other mining robots. That said, the item transfer speed could be made better without harming this goal. This limitation isn't very interesting to keep and could harm some good use of the programmable robot in factories. We'll think about this and it's very likely we'll retain this wish and make some improvement.

The Blackvoxel Team
5
Gallery / Re: Learning with Blackvoxel
« Last post by Enigma on March 01, 2019, 08:26:21 am »
Hello Yves and welcome to the Blackvoxel Forum ;)

Thanks for your interesting thoughs about using Blackvoxel with Kanban.

Yes, Blackvoxel is a game about organisation. That's a good idea to experiment with professionnal organisation methods. Using such a simulation for testing and comparing approachs.

There is always some useful lessons to learn with video game which can be used in real life. And you are showing a good way on this kind of analysis.

It's well made, we'll take time to read it tourroughly.

Have a good fun with Blackvoxel...

The Blackvoxel Team
6
General Discussion / Anyone went below the green acid?
« Last post by Yves on February 27, 2019, 07:18:47 pm »
Using my automatic digging robot, I reached the layer just above the green acid.

Then I dug some more, and saw that I could not reach the acid layer, nor could I have access to any “hanging thing” to go down. Nevertheless, I was able to reach the acid itself by dropping loads of rolling voxels until I could reach them.

However, the rolling voxels seem to float on the acid instead of drowning, so I’m back to the initial problem: I’m standing of my raft of rolling voxels, but still unable to attach voxels to anything below, for the lack of anything reachable.

“Why?”, will you say… I don’t know. I just want to explore :-) And the challenge, too.

So, anyone managed to go below the green acid without bypassing it through another zone?
7
Programming with Blackvoxel / Programmatic handling of the player
« Last post by Yves on February 27, 2019, 07:08:48 pm »
I was pondering the creation of a program to move the player faster, more specifically an elevator, because going down/up stairs is so slow!

I had the feeling that the answer was in PlayerAddAccel or PlayerSetAccel. But playing with one only made the game more/less smooth, and playing with the other either 1º moved me of a few pixels, or 2º moved me a dozen voxels in the air, and then I died falling.

I was not able to understand either of these functions by trial-and-error… Is there a better explanation of these functions somewhere?
8
Programming with Blackvoxel / Digging and sorting robot
« Last post by Yves 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
9
Suggestions / Re: Metalurgy
« Last post by Yves on February 26, 2019, 11:04:40 pm »
This is not the only way.
I have an efficient and reliable production of bronze and stainless steel, taking a ground surface of roughly 10×5 voxels.

Oh, well, let me publish the related article now :-) Here it is:
http://yalis.fr/cms/index.php/post/2019/02/24/Fun-with-Kanban%2C-episode-2
10
Suggestions / Re: The Sequencer
« Last post by Yves on February 26, 2019, 10:57:06 pm »
My two cents:
All other properties non-withstanding, I see 3 main properties to the current sequencer:

— It is first of all a programmable sequencer. And it is important to keep the ability to have a sequence of way more than 5 items, thus allowing sequencer chains. For example, my programmable robot and XTR-1 factories are 100% automated based on this ability.

— It is also a filter: it never absorbs an item that is not part of its programmed sequence.

— And it is a batch regulator: it never absorbs an item while a run of the sequence is still ongoing, and then it only absorbs what is needed for the next run of the sequence.

All those three properties are really useful, and I wish that they are kept. I wrote 2 articles (only one published for now), and they heavily rely on those three properties:
http://yalis.fr/cms/index.php/post/2019/02/20/Fun-with-Kanban%2C-episode-1
Pages: [1] 2 3 ... 10