ribrdb / desynced-tools

Tools for working with behaviors and blueprints from Desynced.
MIT License
4 stars 3 forks source link

Type system #55

Closed ttm02 closed 5 months ago

ttm02 commented 5 months ago

Do you have a documentation to explain how the type system works? (I am not particularly familiar with typescript.) Consider the following behavior:

export function request_ingredients(prod_item:Value) {

    let num_ingredients: number;
    num_ingredients=1;//or 2 to leave more space for the produced item?
    for (const ingredient of recipieIngredients(prod_item))
    {num_ingredients=num_ingredients+1;}// count number of ingredients

    let num_slots : number
    num_slots= getSelf().countStorageSlots();

    let slots_per_ingredient :number = num_slots / num_ingredients;
    if (slots_per_ingredient ==0){
    notify("ERROR: not enough slots for ingredients");
    return;
    }

    for (const ingredient of recipieIngredients(prod_item)){
    let ingredientCount:ItemNum = ingredient
    ingredientCount = ingredient + getMaxStack(ingredient) *slots_per_ingredient;
    requestItem(ingredientCount)
    }
 }

gives me the following errors:

test.ts (9,5): Type 'Value' is not assignable to type 'number'.
test.ts (18,9): Type 'Value' is not assignable to type 'ItemNum'.
  Property 'id' is missing in type 'Value' but required in type '{ id: Item; num: number; }'.
test.ts (19,23): Operator '+' cannot be applied to types 'Value' and 'number'.
test.ts (19,36): The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
test.ts (20,17): Argument of type 'string | number | { id: Item; num: number; }' is not assignable to parameter of type 'Value | ItemNum'.
  Type 'string' is not assignable to type 'Value | ItemNum'.
Error: test.ts:12: unknown method check_number
    at Compiler._Compiler_error (/usr/local/lib/node_modules/desynced-tools/dist/compile.js:907:11)
    at Compiler.compileResolvedCall (/usr/local/lib/node_modules/desynced-tools/dist/compile.js:476:85)
    at Compiler._Compiler_compileEquality (/usr/local/lib/node_modules/desynced-tools/dist/compile.js:847:25)
    at Compiler.compileCondition (/usr/local/lib/node_modules/desynced-tools/dist/compile.js:642:110)
    at Compiler.compileIf (/usr/local/lib/node_modules/desynced-tools/dist/compile.js:581:40)
    at Compiler.compileStatement (/usr/local/lib/node_modules/desynced-tools/dist/compile.js:131:18)
    at Array.forEach (<anonymous>)
    at Compiler.compileBehavior (/usr/local/lib/node_modules/desynced-tools/dist/compile.js:117:28)
    at /usr/local/lib/node_modules/desynced-tools/dist/compile.js:77:19
    at Array.forEach (<anonymous>)
/usr/local/lib/node_modules/desynced-tools/dist/compile.js:994
    throw new Error("No source file found");
    ^

Error: No source file found
    at compileProgram (/usr/local/lib/node_modules/desynced-tools/dist/compile.js:994:11)
    at Object.<anonymous> (/usr/local/lib/node_modules/desynced-tools/dist/js2ds.js:33:42)
    at Module._compile (node:internal/modules/cjs/loader:1376:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1435:10)
    at Module.load (node:internal/modules/cjs/loader:1207:32)
    at Module._load (node:internal/modules/cjs/loader:1023:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:135:12)
    at node:internal/main/run_main_module:28:49

Node.js v20.11.1

First error messages are related to my type-system based question. How can i calculate the number of item slots present per ingredient necessary? The other error probably indicates that the if condition with compare number instruction is not working as intended.

Btw: having these error messages in the web-app version will also be very helpful :-)

swazrgb commented 5 months ago

I am not able to reproduce this issue locally on the main branch.

Have you ran npm run generate if you are running locally? This is a recent requirement since https://github.com/ribrdb/desynced-tools/pull/52

I see the web based playground produces a different error than what you provided:

Uncaught (in promise) Error: Expected string: {"reg":null,"type":"regRef"} at line 3

When I run the web based playground locally your behavior compiles successfully.

@ribrdb Any idea? Maybe needs an update?

About the type errors, looks like I made a mistake in a recent PR (https://github.com/ribrdb/desynced-tools/pull/44) of mine. Once https://github.com/ribrdb/desynced-tools/pull/56 is merged, you can write the following with no type errors:

export function request_ingredients(prod_item: Value) {

    let num_ingredients: Value = 1; //or 2 to leave more space for the produced item?
    for (const ingredient of recipieIngredients(prod_item)) {
        num_ingredients = num_ingredients + 1;
    }// count number of ingredients

    let num_slots: Value = getSelf().countStorageSlots();

    let slots_per_ingredient: Value = num_slots / num_ingredients;
    if (slots_per_ingredient == 0) {
        notify("ERROR: not enough slots for ingredients");
        return;
    }

    for (const ingredient of recipieIngredients(prod_item)) {
        let ingredientCount: Value = ingredient
        ingredientCount = ingredient + getMaxStack(ingredient) * slots_per_ingredient;
        requestItem(ingredientCount)
    }
}

The intention is that you always use the Value type for all variables, which tries to match the behavior of an in-game variable/parameter.

swazrgb commented 5 months ago

By the way, this is the behavior I use to request items to a chest. Simply set recipe to the item you want to craft, and the chest will start requesting the resources with the correct proportions.

// noinspection JSAssignmentUsedAsCondition,JSUnusedAssignment,InfiniteLoopJS

export function crafterRequester(
    recipe: Value,
    ingredient1: Value,
    ingredient2: Value,
    ingredient3: Value,
    ingredient4: Value,
) {
    // Wait until recipe is set
    if (compareItem(recipe, null)) {
        return;
    }

    // Get number of slots in storage
    let slots: Value = self.countStorageSlots();

    // Set visual to indicate the active recipe
    visual = setNumber(recipe, 0)

    // Calculate the amount of items the recipe consists of
    let totalIngredients: Value = 0;
    for (const ingredient of recipieIngredients(recipe)) {
        totalIngredients = totalIngredients + ingredient;
    }

    // Clear the request variables. These are parameters for manual inspection but could be variables as well.
    ingredient1 = null;
    ingredient2 = null;
    ingredient3 = null;
    ingredient4 = null;

    // Calculate the amount of slots to dedicate for each ingredient
    loop: for (const ingredient of recipieIngredients(recipe)) {
        // Calculate the number of slots to dedicate to this ingredient
        // Multiply/divide by 100000 to avoid integer truncation
        let amount = ((slots * ((ingredient * 100000) / totalIngredients)) / 100000);
        if (amount == 0) {
            amount = 1;
        }

        amount = amount * getMaxStack(ingredient)

        // Fill ingredient1..4 with the required items in order
        if (compareItem(ingredient1, null)) {
            ingredient1 = combineRegister(amount, ingredient);
            continue loop;
        }

        if (compareItem(ingredient2, null)) {
            ingredient2 = combineRegister(amount, ingredient);
            continue loop;
        }

        if (compareItem(ingredient3, null)) {
            ingredient3 = combineRegister(amount, ingredient);
            continue loop;
        }

        if (compareItem(ingredient4, null)) {
            ingredient4 = combineRegister(amount, ingredient);
            continue loop;
        }

        // Recipe has more than 4 ingredients, which is not supported. No such recipes exist in the game currently. If it happens, add an ingredient5 variable.
        visual = value("v_color_red", 1);
        return;
    }

    // As long as the amount of slots doesn't change, request the ingredients in a loop
    while (slots == self.countStorageSlots()) {
        requestItem(ingredient1);
        requestItem(ingredient2);
        requestItem(ingredient3);
        requestItem(ingredient4);
    }
}

I've been meaning to (suggest to) add some more example scripts, but I'd like to fix some more issues first. For example in the script above I'd prefer to use if(x) { ... } else if (y) { .... } else { ... }, but currently else-if is not yet fully supported.

ttm02 commented 5 months ago

I see. For the web version, I used the linked one in the readme. I can confirm that my example does compile with a local one from the main branch. I tried the versions obtained via npm install -g desynced-tools with the local ones it does work. Thank You.

ttm02 commented 5 months ago

Sorry, I have another question:

function get_producing_component(produced_item: Value) {

    let component: Value= "c_robotics_factory";
    if (canProduce(produced_item, component)) {
        return component;
    } 
   /*try other components*/
    notify("ERROR: Dont know where to produce item");
    return;
}// so that the production facility can request and equip the needed component

gives me this error:

factroy_block_worker.ts (69,9): Type 'string' is not assignable to type 'Value'. // the let component line
  Type 'string' is not assignable to type 'number'.
/home/tim/desynced-tools/dist/assembler.js:387
    throw new Error(`Expected string: ${JSON.stringify(a)} at line ${inst.lineno}`);
    ^

Error: Expected string: undefined at line 104
    at str (/home/tim/desynced-tools/dist/assembler.js:387:11)
    at pass (/home/tim/desynced-tools/dist/assembler.js:434:52)
    at Code._Code_applyReverse (/home/tim/desynced-tools/dist/ir/code.js:34:9)
    at Code.apply (/home/tim/desynced-tools/dist/ir/code.js:21:84)
    at Assembler.assembleSub (/home/tim/desynced-tools/dist/assembler.js:190:13)
    at Assembler.assembleBehavior (/home/tim/desynced-tools/dist/assembler.js:173:43)
    at assemble (/home/tim/desynced-tools/dist/assembler.js:85:22)
    at Object.<anonymous> (/home/tim/desynced-tools/dist/as.js:31:38)
    at Module._compile (node:internal/modules/cjs/loader:1376:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1435:10)

Node.js v20.11.1

Can you help me again with figuring out the correct Typing?

swazrgb commented 5 months ago
let component: Value = "c_robotics_factory";

Is unfortunatey not supported due to typing quirks. Instead you can write:

let component: Value = value("c_robotics_factory");

Or to set both the item & number register:

let component: Value = value("c_robotics_factory", 123);
ttm02 commented 5 months ago

thank you :-)