Closed hsaturn closed 7 months ago
Edit: self-answer, the unreleased version fixes this. Thanks again
Hello
It seems that the website does not reflect this change. I don't know which version of the website (should be a little enhancement to display it somewhere ?)
I've used this code as the previous one was a little buggy (!!)
var factor = 1000;
var message = getBuilding("message1");
var last = Math.floor(Vars.time / factor);
while (Math.floor(Vars.time / factor) == last);
print("I want to arrive here but I cant");
printFlush(message);
Mlogjs is really great !
The github pages website is updated on each release. To view the latest changes, you can checkout out the unreleased version
About the unreleased version. You may ignore this post since it's about unreleased (!). => let me know if such comment is useless of if you're happy with it.
I've seen that generated code is longer and a few slower than the stable one (my js gives 719 lines vs 693) *** Which may become a issue as asm is limited to 1000 lines Loss of speed on my js is about 1%
=> My guess is that optimisation of expression evaluation seems to be ok but suffers of spurious jumps as saw in this code (as already commented there => My second guess is that jump optimization once implemented will reduce the oversize.
var a;
var b;
while (true) {
if (a == 0 && b == 1)
{
print("do some stuff");
}
}
print("end");
Generated asm should be
op equal &t0 a:1:4 0
jump 0 equal &t0 0
op equal &t0 b:2:4 1
jump 0 equal &t0 0
print "do some stuff"
jump 0 always
Which becomes same number of rows of old version but running faster ! Anyway, I guess that the short-circuit optimisation could lead to larger code that won't fit in 1000 lines. => Having an option (if both generators are available) could help in this situation (optimize for size / speed).
My js has a fps counter, loop is 1.06s vs 1.05s when the processor is connected to
Thanks again for this very cool project !
Here is my js script
const version: String = "hyper-controller-20240325-4";
var v = version; // First line of asm
const display_version = "[violet]" + version + "[white]";
var min_power = 0;
var message;
var keep_coal = 6500;
var keep_copper = 6500;
var keep_graphite = 6500;
var keep_lead = 8000;
var keep_sand = 6500;
var keep_tit = 6500;
var keep_pyr = 4080;
var keep_tho = 6500;
var keep_sil = 6500;
var keep_spore = 6500;
var keep_scrap = 6500;
var keep_phase = 5000;
var keep_surge = 6500;
var keep_blast = 6500;
var keep_glass = 6500;
var keep_plast = 6500;
var min_cryo = 25;
const need_color: String = "[cyan]";
const color_send: String = "[blue]";
const error = "[red]";
const warning = "[yellow]";
const white: String = "[white]";
var powSteam = 330;
var powCombustion = 60;
var powDifferential = 1080;
var n = "message";
var loop = 0; // Loops incremental counter
var core_max_rsrc = 4000; // Max resources of the core
var link_low_rsrc = 3990; // Hyper link enable build rsrc if below this value
var core : AnyBuilding = undefined;
var copper, coal, glass, scrap, lead, sand, pyr, tit, tho, sil, graphite, plast, phase, surge, blast, spore;
var enough_lead, enough_blast, enough_plast, enough_glass, enough_copper, enough_surge, enough_phase, enough_graphite, enough_scrap, enough_coal, enough_sand, enough_tit, enough_pyr, enough_tho, enough_sil, enough_spore;
var enabled : Boolean;
var mass_enabled = false;
var pow_node : AnyBuilding = undefined;
var pow_stored = 0, pow_capacity = 0;
var impact_next_change = Vars.second; // Next time to change state of impact reactor
// Automatic select item to send for launchPad
// will never go below keep_{rsrc}
var launch_item = undefined;
var sender: Boolean; // True if we find a unloader (normally connected to launchPad)
var standalone_disass_tho_cryo;
var need_disass;
var power = -999;
var power_max = 0; // max power ever seen
// memory cell link
var id = undefined;
var time = undefined; // Last time we received something
var last_tick = Vars.tick;
var avg_ticks = 60;
while (true) {
loop++;
var ticks = Vars.tick - last_tick;
last_tick = Vars.tick;
avg_ticks = 0.7 * avg_ticks + 0.3 * ticks;
var latency = avg_ticks / 60;
latency = Math.floor(latency * 100) / 100;
print(display_version, " #", id, "\nLoop ", loop, " / ", latency, "s\n");
sender = false;
if (core !== undefined) {
copper = core.copper;
coal = core.coal;
lead = core.lead;
sand = core.sand;
glass = core.metaglass;
pyr = core.pyratite;
tit = core.titanium;
tho = core.thorium;
sil = core.silicon;
scrap = core.scrap;
graphite = core.graphite;
plast = core.plastanium;
phase = sensor(Items.phaseFabric, core);
surge = sensor(Items.surgeAlloy, core);
blast = sensor(Items.blastCompound, core);
spore = sensor(Items.sporePod, core);
enough_copper = copper > keep_copper;
enough_lead = lead > keep_lead;
enough_coal = coal > keep_coal;
enough_sand = sand > keep_sand;
enough_tit = tit > keep_tit;
enough_pyr = pyr > keep_pyr;
enough_tho = tho > keep_tho;
enough_sil = sil > keep_sil;
enough_spore = spore > keep_spore;
enough_scrap = scrap > keep_scrap;
enough_graphite = graphite > keep_graphite;
enough_phase = phase > keep_phase;
enough_surge = surge > keep_surge;
enough_blast = blast > keep_blast;
enough_glass = glass > keep_glass;
enough_plast = plast > keep_plast;
}
else
{
enough_copper = true;
enough_lead = true;
enough_coal = true;
enough_sand = true;
enough_tit = true;
enough_pyr = true;
enough_tho = true;
enough_sil = true;
enough_spore = true;
enough_scrap = true;
enough_graphite = true;
enough_phase = true;
enough_surge = true;
enough_blast = true;
enough_glass = true;
enough_plast = true;
}
var old_std_disass = standalone_disass_tho_cryo;
standalone_disass_tho_cryo = 0;
var power_left = power;
for (let i = Vars.links - 1; i >= 0; i--) {
var unhandled: Boolean = false;
var link : AnyBuilding = getLink(i);
var ltype = link.type;
if (ltype == Blocks.thoriumReactor) {
var enough_cryo = sensor(Liquids.cryofluid, link) >= min_cryo;
enabled = (power_left < -15) && enough_cryo;
// print("Tho enabled ", enabled, ", pleft ", power_left, ", enough_cryo ", enough_cryo, "\n");
if (core === undefined) {
var t: number = sensor(Items.thorium, link);
var m = link.itemCapacity;
standalone_disass_tho_cryo += m - t;
if (!enough_cryo)
standalone_disass_tho_cryo = 1000;
if (enabled && link.enabled == false)
power_left = power_left + 30 * t;
}
asm`control enabled ${link} ${enabled} 0 0`;
}
else if (ltype == Blocks.powerNode || ltype == Blocks.powerNodeLarge) {
pow_node = link;
}
else if (ltype == Blocks.disassembler) {
if (core) {
asm`control enabled ${link} ${need_disass} 0 0`;
}
else {
enabled = old_std_disass > 1;
asm`control enabled ${link} ${enabled} 0 0`;
}
}
else if (ltype == Blocks.impactReactor) {
// print("impact cryo: ", link.cryofluid, "\n");
var cryo = link.cryofluid;
print("cryo:", cryo, "\n");
var capa = link.powerNetCapacity;
if (cryo > 10) {
enabled = (sensor(Items.blastCompound, link) > 5)
&& ((pow_stored > 30000) || (capa == 0))
&& (power_left < -200);
print("impact ", capa, " power_left:", power_left, " pow_stored:", pow_stored, ":", enabled, "\n");
if (enabled || (enabled != link.enabled) && (Vars.second > impact_next_change)) {
if (enabled)
impact_next_change = Vars.second + 60;
asm`control enabled ${link} ${enabled} 0 0`;
power_left = power_left - 6300;
}
}
else
asm`control enabled ${link} false 0 0`;
print("Impact Power_left, ", power_left, " next:", Math.max(0, Math.floor(impact_next_change - Vars.second)), "\n");
}
else if (ltype == Blocks.steamGenerator
|| ltype == Blocks.combustionGenerator // 60
|| ltype == Blocks.differentialGenerator) {
var plink = powSteam;
enabled = link.enabled;
if (ltype == Blocks.combustionGenerator)
plink = powCombustion;
else if (ltype == Blocks.differentialGenerator)
plink = powDifferential;
if (power_left < -50) {
enabled = true;
if (link.enabled == false)
power_left += plink;
}
else {
if (power >= plink && link.enabled) {
enabled = false;
power_left -= plink;
enabled = false;
}
}
asm`control enabled ${link} ${enabled} 0 0`;
// print("Power ", power_left, "/", power, ", link ", plink, " en=", enabled, "\n");
}
else if (ltype == Blocks.coalCentrifuge) {
enabled = coal != core_max_rsrc;
asm`control enabled ${link} ${enabled} 0 0`;
if (link.oil == 0)
print(warning + "Need oil in ", link, "\n" + white);
}
else if (ltype == Blocks.launchPad) {
enabled = link.totalItems / link.itemCapacity >= link.progress;
asm`control enabled ${link} ${enabled} 0 0`;
}
else if (ltype == Blocks.message) {
message = link;
}
else if (ltype == Blocks.coreShard) {
core = link;
}
else if (ltype == Blocks.coreFoundation) {
core = link;
}
else if (ltype == Blocks.coreNucleus) {
core = link;
}
else if (ltype == Blocks.graphitePress || ltype == Blocks.multiPress) {
if (graphite == core_max_rsrc)
enabled = false;
else
enabled = enough_coal;
asm`control enabled ${link} ${enabled} 0 0`;
}
else if (ltype == Blocks.siliconSmelter || ltype == Blocks.siliconCrucible) {
enabled = enough_coal && enough_sand;
if (sil == core_max_rsrc)
enabled = false;
else if (ltype == Blocks.siliconCrucible)
enabled = enabled && enough_pyr;
asm`control enabled ${link} ${enabled} 0 0`;
}
else if (ltype == Blocks.massDriver) {
enabled = (mass_enabled && link.totalItems > 50) || (link.totalItems == 0);
asm`control enabled ${link} ${enabled} 0 0`;
}
else if (ltype == Blocks.memoryCell) {
const memory = new Memory(link, 64);
if (core) {
print("Dumping to memory\n");
id = 0;
memory[0] = 65; // controller
memory[1] = 0; // main controller
memory[2] = core_max_rsrc;
memory[3] = link_low_rsrc;
memory[4] = Math.floor(Vars.time);
memory[5] = min_power;
memory[10] = copper;
memory[11] = coal;
memory[12] = lead;
memory[13] = sand;
memory[14] = glass;
memory[15] = pyr;
memory[16] = tit;
memory[17] = tho;
memory[18] = sil;
memory[19] = scrap;
memory[20] = graphite;
memory[21] = plast;
memory[22] = phase;
memory[23] = surge;
memory[24] = blast;
memory[25] = spore;
}
else if (memory[0] == 65) {
var cell_id = memory[1];
var next_id = cell_id + 1;
var last_update = Math.floor(Vars.time - memory[4]);
// print("Last update: ", last_update, ", id=", id, "\n");
if (id === undefined || id > next_id)
id = next_id;
if (last_update > 10000) {
print(warning + "Resync, id=", id, white + "\n");
memory[0] = 0; // data too old, trying to reconnect
id = undefined;
}
if (id != undefined && id > cell_id) {
print("Retrieving data from cell #", cell_id, ", id=", id, "\n");
core_max_rsrc = memory[2];
link_low_rsrc = memory[3];
time = Math.floor(Vars.time);
min_power = memory[5];
copper = memory[10];
coal = memory[11];
lead = memory[12];
sand = memory[13];
glass = memory[14];
pyr = memory[15];
tit = memory[16];
tho = memory[17];
sil = memory[18];
scrap = memory[19];
graphite = memory[20];
plast = memory[21];
phase = memory[22];
surge = memory[23];
blast = memory[24];
spore = memory[25];
}
if (id != undefined && id <= cell_id) {
print("Transmitting data to cell #", cell_id, ", id=", id, "\n");
memory[0] = 65;
memory[1] = id;
memory[2] = core_max_rsrc;
memory[3] = link_low_rsrc;
memory[4] = time;
memory[10] = copper;
memory[11] = coal;
memory[12] = lead;
memory[13] = sand;
memory[14] = glass;
memory[15] = pyr;
memory[16] = tit;
memory[17] = tho;
memory[18] = sil;
memory[19] = scrap;
memory[20] = graphite;
memory[21] = plast;
memory[22] = phase;
memory[23] = surge;
memory[24] = blast;
memory[25] = spore;
}
}
else if (id !== undefined) {
// Allocate this memory cell to this processor.
// print("Allocating memory #", id, "\n");
memory[0] = 65; // controller
memory[1] = id; //
memory[4] = Vars.time;
}
}
else if (ltype == Blocks.kiln) {
if (glass == core_max_rsrc)
enabled = false;
else
enabled = enough_lead & enough_sand;
asm`control enabled ${link} ${enabled} 0 0`;
}
else if (ltype == Blocks.sporePress) {
if (spore == core_max_rsrc)
enabled = false;
else {
enabled = true;
if (link.oil == 0)
print(warning + "Need oil in ", link, "\n" + white);
}
}
else if (ltype == Blocks.plastaniumCompressor) {
if (plast == core_max_rsrc)
enabled = false;
else {
enabled = enough_tit;
if (link.oil == 0)
print(warning + "Need oil in ", link, "\n" + white);
}
asm`control enabled ${link} ${enabled} 0 0`;
}
else if (ltype == Blocks.phaseWeaver) {
if (phase == core_max_rsrc)
enabled = false;
else
enabled = enough_sand & enough_tho;
asm`control enabled ${link} ${enabled} 0 0`;
}
else if (ltype == Blocks.surgeSmelter) {
if (surge == core_max_rsrc)
enabled = false;
else
enabled = enough_copper & enough_lead & enough_tit & enough_sil;
asm`control enabled ${link} ${enabled} 0 0`;
}
else if (ltype == Blocks.pyratiteMixer) {
if (pyr == core_max_rsrc)
enabled = false;
else
enabled = enough_coal & enough_lead & enough_sand;
asm`control enabled ${link} ${enabled} 0 0`;
}
else if (ltype == Blocks.blastMixer) {
if (blast == core_max_rsrc)
enabled = false;
else
enabled = enough_pyr & enough_spore;
asm`control enabled ${link} ${enabled} 0 0`;
}
else if (ltype == Blocks.cultivator) {
enabled = (spore < core_max_rsrc)
asm`control enabled ${link} ${enabled} 0 0`;
}
else if (ltype == Blocks.unloader) {
sender = true;
if (launch_item !== undefined) {
asm`control enabled ${link} true 0 0`;
asm`control config ${link} ${launch_item} 0 0`;
}
else
asm`control enabled ${link} false 0 0`;
}
else if (link.maxHealth > 2000) {
unhandled = true;
// Try to avoid power big failure by deactivating
// tower that eats a lot
var ammo = link.ammoCapacity;
enabled = true;
if (ammo == undefined)
unhandled = true;
else {
unhandled = false;
if (link.powerNetStored < 60000) {
print("[red]Avoiding loose of power for ", ltype, " pstored=", link.powerNetStored, "[white]\n");
enabled = false;
}
}
asm`control enabled ${link} ${enabled} 0 0`;
}
else
unhandled = true;
if (unhandled)
print(warning + "Not handled: ", link, "\n" + white);
}
if (sender) {
launch_item = undefined;
var count = 0;
if (count < blast && enough_blast) { launch_item = Items.blastCompound; count = blast; }
if (count < phase && enough_phase) { launch_item = Items.phaseFabric; count = phase; }
if (count < surge && enough_surge) { launch_item = Items.surgeAlloy; count = surge; }
if (count < tho && enough_tho) { launch_item = Items.thorium; count = tho; }
if (count < tit && enough_tit) { launch_item = Items.titanium; count = tit; }
if (count < spore && enough_spore) { launch_item = Items.sporePod; count = spore; }
if (count < plast && enough_plast) { launch_item = Items.plastanium; count = plast; }
if (count < pyr && enough_pyr) { launch_item = Items.pyratite; count = pyr; }
if (count < sil && enough_sil) { launch_item = Items.silicon; count = sil; }
if (count < scrap && enough_scrap) { launch_item = Items.scrap; count = scrap; }
if (count < glass && enough_glass) { launch_item = Items.metaglass; count = glass; }
if (count < lead && enough_lead) { launch_item = Items.lead; count = lead; }
if (count < graphite && enough_graphite) { launch_item = Items.graphite; count = graphite; }
if (count < coal && enough_coal) { launch_item = Items.coal; count = coal; }
if (count < sand && enough_sand) { launch_item = Items.sand; count = sand; }
if (count < copper && enough_copper) { launch_item = Items.copper; count = copper; }
if (count) {
print(color_send + "Sending ", launch_item, ":", count, "\n" + white);
}
else {
print(color_send + "Unloader stopped\n" + white);
}
}
if (pow_node) {
pow_capacity = sensor(LAccess.powerNetCapacity, pow_node);
pow_stored = sensor(LAccess.powerNetStored, pow_node);
power = Math.floor(pow_node.powerNetIn - pow_node.powerNetOut) - min_power;
if (pow_node.powerNetIn > power_max)
power_max = Math.ceil(pow_node.powerNetIn);
if (power < 0)
print(error + "Need power\n" + white);
// print("pnetin ", pow_node.powerNetIn, ", pnetout ", pow_node.powerNetOut, "\n");
var percent = Math.floor(100 * power / power_max + 0.5);
print("Power: ", Math.floor(power), "/", power_max , " (", percent, "%) :", Math.floor(pow_stored / 1000), "k/", Math.floor(pow_capacity / 1000), "k\n");
if (pow_stored < pow_capacity)
power = Math.floor((pow_stored - pow_capacity) / 100);
}
else {
power = 0;
}
if (core) {
core_max_rsrc = core.itemCapacity;
link_low_rsrc = Math.floor(core_max_rsrc * 0.97);
need_disass = enough_scrap &&
((tho != core_max_rsrc) || (tit != core_max_rsrc) || (graphite != core_max_rsrc) || (sand != core_max_rsrc));
mass_enabled = true;
if (!enough_coal) {
print(need_color + "Need coal\n" + white);
mass_enabled = false;
}
if (!enough_lead) {
print(need_color + "Need lead\n" + white);
mass_enabled = false;
}
if (!enough_copper) {
print(need_color + "Need copper\n" + white);
mass_enabled = false;
}
if (!enough_sand) {
print(need_color + "Need sand\n" + white);
mass_enabled = false;
}
if (!enough_pyr) {
print(need_color + "Need pyratite[white]\n" + white);
mass_enabled = false;
}
if (!enough_tit) {
print(need_color + "Need titanium\n" + white);
mass_enabled = false;
}
if (!enough_tho) {
print(need_color + "Need thorium\n" + white);
}
if (!enough_sil) {
print(need_color + "Need silicium\n" + white);
mass_enabled = false;
}
if (!enough_spore) {
print(need_color + "Need spore\n" + white);
mass_enabled = false;
}
if (!enough_scrap) {
print(need_color + "Need scrap\n" + white);
mass_enabled = false;
}
if (!enough_blast) {
print(need_color + "Need blast compound\n" + white);
mass_enabled = false;
}
if (!enough_graphite) {
print(need_color + "Need graphite\n" + white);
mass_enabled = false;
}
if (!enough_surge) {
print(need_color + "Need surge alloy\n" + white);
mass_enabled = false;
}
if (!enough_plast) {
print(need_color + "Need plastanium\n" + white);
mass_enabled = false;
}
} else
{
if (pow_node.powerNetCapacity) {
if (pow_node.powerNetStored > 600)
mass_enabled = true;
else if (pow_node.powerNetStored < 10)
mass_enabled = false;
}
else
mass_enabled = true;
}
pow_node = undefined;
printFlush(message);
}
The github pages website is updated on each release. To view the latest changes, you can checkout out the unreleased version
Yes I've edited the comment in the meanwhile
let me know if such comment is useless of if you're happy with it.
I'm happy with it, after all the unreleased version has to be released eventually.
My second guess is that jump optimization once implemented will reduce the oversize.
Indeed, it is one of the major problems with the current internal architecture of the compiler, as it cannot be implemented trivially without rewriting most of it from scratch (#222).
Having an option (if both generators are available) could help in this situation
A desirable improvement that may be implemented after the compiler rewrite.
May I suggest that a nth pass in order to polish the code is imho easy to code. I can write it in C++ in less than one hour. A first approach is to not remove the jumps, only to make them go to their final destination. A second one should be to also remove the jumps wich may seem easy also, even with renumbering, but one have to find all the set return_var @count in order to not break functions calls. And I understand now why you are not using labels, this would break function calls.
I do not prioritize the jump skipping optimization because it is already covered by #222. And I prefer to put more effort into finishing that pull request instead of duplicating the work just to bring a less flexible version of that feature to the main branch.
Ahem ... in fact I've finish to write the C++ code that resolves the jumps.
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <fstream>
#include <algorithm>
using namespace std;
bool is_number(const string& s)
{
return !s.empty() && all_of(s.begin(), s.end(), ::isdigit);
}
std::string getword(std::string& s, char sep=' ')
{
if (s.length()==0) return "";
std::string r;
auto pos=s.find(sep);
if (pos == std::string::npos) pos=s.length();
r=s.substr(0, pos);
s.erase(0,pos+1);
return r;
}
int getint(std::string& s)
{
string n = getword(s);
if (is_number(n))
return atol(n.c_str());
else
return -1;
}
int main(int argc, const char* argv[])
{
set<int> always; // list of always jumps
map<int, int> jumps; // line => destination
map<int, string> code; // rownum => original code
ifstream f("test.asm");
int row_nr = 0;
while(f.good())
{
string row;
getline(f, row);
code[row_nr] = row;
string op = getword(row);
if (op == "jump")
{
int dest = getint(row);
if (dest != -1)
{
jumps[row_nr] = dest;
code[row_nr] = row;
if (row == "always") always.insert(row_nr);
}
}
// else if (op == "end")
// jumps[row_nr] = 0;
row_nr++;
}
cerr << "detected jumps: " << jumps.size() << ":" << endl;
for(const auto& [n, d]: jumps)
cerr << " " << n << " goes to " << d << endl;
cerr << endl;
cerr << "result:" << endl;
for(const auto& [row_nr, origin] : code)
{
if (jumps.find(row_nr) != jumps.end())
{
int end = jumps[row_nr];
cerr << "resolving jump " << jumps[row_nr] << ", end=" << end;
while(always.find(end) != always.end() && end != row_nr)
{
end = jumps[end];
cerr << " -> " << end;
if (end == row_nr) break;
}
if (end == row_nr)
{
cerr << "jump at line row_nr is an infinite loop !" << endl;
cout << origin << endl;
}
else
{
cerr << " -> " << end << endl;
cout << "jump " << end << " " << origin << endl;
}
}
else
cout << origin << endl;
}
return 0;
}
compile: g++ jumps.cpp -o jumps
usage: ./jumps > result.asm
The source asm is test.asm. The result is written in result.asm
It works on the example of the bug report
test.asm:
jump 6 greaterThanEq c:1:4 10
jump 4 greaterThanEq c:1:4 5
set c:1:4 5
jump 5 always
set c:1:4 10
jump 7 always
set c:1:4 23
end
And result is
jump 6 greaterThanEq c:1:4 10
jump 4 greaterThanEq c:1:4 5
set c:1:4 5
jump 7 always
set c:1:4 10
jump 7 always
set c:1:4 23
end
for the curious, cerr output is
detected jumps: 4:
0 goes to 3
1 goes to 4
3 goes to 5
5 goes to 7
result:
resolving jump 3, end=3 -> 5 -> 7 -> 7
resolving jump 4, end=4 -> 4
resolving jump 5, end=5 -> 7 -> 7
resolving jump 7, end=7 -> 7
The github pages website is updated on each release. To view the latest changes, you can checkout out the unreleased version
Arf... of course, in the meantime, I've finish the c++ (very ugly) code. Anyway, this code will allow me to rejump actual ouput of mlogjs until #222 is out :-)
Here are some stats on a real case, (the big js I've posted here) 13 jumps are relocated all 13 jumps were doing only one indirection. Not so much !!!
I'm up for today (this is late here).
The optimisation there is not expected
See the infinite loop at line 3:
By the way, the optimiser should have discarded the print and printflush if the loop were ok.
Best regards