BibliothecaDAO / DOJO

On-chain gaming toolkit
11 stars 1 forks source link

feat: Struct <> Array #5

Open ponderingdemocritus opened 1 year ago

ponderingdemocritus commented 1 year ago

To allow generalised parsing of data in the system, two util functions need to exist:

Can be found in Utils.cairo

struct_to_array converts any struct to an array and returns length and the array

array_to_struct converts any array into a struct and returns the struct

jobez commented 1 year ago

Howdy and merry merries--

I experimented with whether it was possible to pass the struct constructor as an argument to a helper and had no luck. I wrote out the ways to move from struct<>arr as a test, which I'll go ahead and share the contents here so I can get feedback if my understanding is on target:

%lang starknet
from starkware.cairo.common.alloc import alloc
from contracts.utils.Utils import Utils
from starkware.cairo.common.registers import get_label_location
from starkware.cairo.common.cairo_builtins import HashBuiltin

struct MyStruct {
    foo: felt,
    bar: felt,
}

@external
func test_struct_to_arr_using_new{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
    ) {
    // constructing a struct with 'new' gives test_struct the type 'MyStruct*'
    // which can be casted to felt*
    // note: cannot use let binding when using `new`
    tempvar test_struct_as_arr: felt* = new MyStruct(1, 2);

    // to use the MyStruct* as an array, we just need the size,
    // which we obtain by the SIZE field in the constructor

    assert [test_struct_as_arr + MyStruct.SIZE - 1] = 2;

    // I do not believe it is possible to parameterize the struct constructor
    // so it can be passed to a helper function

    return ();
}

@external
func test_struct_to_arr{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {
    alloc_locals;

    // in the case of not using 'new'
    local test_struct: MyStruct = MyStruct(3, 4);
    let test_struct_as_arr: felt* = &test_struct;

    assert [test_struct_as_arr + MyStruct.SIZE - 1] = 4;

    return ();
}

@external
func test_arr_to_struct{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {
    let (array_to_struct) = alloc();

    assert [array_to_struct] = 1;
    assert [array_to_struct + 1] = 2;

    let castedStruct = cast(array_to_struct, MyStruct*);

    assert castedStruct.foo = 1;

    return ();
}
ponderingdemocritus commented 1 year ago

nice!

also @milancermak suggested:

struct Army {
    troop1: felt,
    troop2: felt,
    troop3: felt,
}

@view
func army_to_array(a: Army) -> (a_len: felt, a: felt*) {
    let (__fp__, _) = get_fp_and_pc();
    return (Army.SIZE, &a);
}

@view
func array_to_army(a_len: felt, a: felt*) -> (a: Army) {
    let p: Army* = cast(a, Army*);
    return ([p]);
}

These two helpers could be at the bottom of every component and if we name the Struct that is imported the same then we get the correct result