Skip to main content

News

Topic: Link.js v0.4.2 (Read 95887 times) previous topic - next topic

0 Members and 6 Guests are viewing this topic.
  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Link.js v0.3.0
Reply #255
I think if we were going to standardize on a save file format (as part of Sphere 2.0 perhaps), JSON would be a no-brainer.  It loads directly into a POJO and all modern JS engines include a parser built-in.

The clone-based approach is nice.  I kind of do something similar with Specs, where battlers and such are constructed by cloning a descriptor from the gamedef, so modifications don't touch the original definition.  Having that in a more general capacity would be useful, I think.  Although in my case, I need a deep clone (see cycle-preserving clone function above), since the descriptors have a lot of nested stuff.  Moves have a list of turns, which have a list of actions, etc.  That kind of thing.
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Link.js v0.3.0
Reply #256
So a funny thing occurred to me while fiddling with LINQ in Sphere Studio: This (Link.js) is actually more powerful than its namesake. :P  For example there's no easy way in LINQ to iterate over two or more collections as if they were one, you have to concatenate them first.  Whereas in Link, you just do this:

Code: (javascript) [Select]

Link(arr1, arr2, arr3)
    .where(...)
    .etc()


I use the above in the Specs battle engine a lot.  It's handy for iterating over both PCs and enemies in one query (for turn resolution, e.g.), which keeping them neatly in separate arrays for other operations.

There are also a lot of constructs that Link has which are technically possible with LINQ, but are more clunky (requiring the ugly extension method syntax) or just not as obvious.  So, long story short: Awesome work on this. :)
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Link.js v0.3.0
Reply #257
I'm glad to hear that. I looked at LINQ as inspiration but added things I thought would make it easier to use. :)

I'm still looking into ways to expand this. I mean it's still at 0.3.0 which means that I think it's still in beta. For instance I really want to get started on a backwards traversal mechanism which is basically running the logic backwards. Then I was thinking of branching out to trees (pun intended) and then other data structures.

Thanks for the feedback! You've no doubt been a great help by using it. :)
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Link.js v0.3.0
Reply #258
Interesting discovery: Using Link in TypeScript is nearly indistinguishable from LINQ method syntax in C# (capitalization notwithstanding):

Code: (javascript) [Select]

var neoHippos = [
{ name: "maggie", fatness: 812 },
{ name: "Beverly", fatness: 999 },
{ name: "Gail", fatness: 1208 },
{ name: "Burke", fatness: 500 },
{ name: "Machel", fatness: 100 },
];
link(neoHippos)
.where(nh => nh.name != "maggie")
.execute(nh => DebugPrint("neo-Hippo member:", nh.name, nh.fatness))
.each(nh => Print(nh.name + " is really hungry!");


Incidentally, the above code shows a really great use case for .execute() as well. :)
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Link.js v0.3.0
Reply #259
Nice one. I updated the first page description. It now mentions minisphere and an example for TypeScript. I even removed the legacy code gist that I had embedded.
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Link.js v0.3.0
Reply #260
One area of game development where I've found Link to be extremely useful (and the reason it's included with minisphere) is in implementing things like status conditions, where statuses often have complex clauses determining how they affect various things.  For example, the special Final Stand status used by the final boss of Spectacles: Bruce's Story contains the following Link query:

Code: (javascript) [Select]

link(eventData.action.effects)
.where(it => it.targetHint == 'selected')
.where(it => it.type == 'damage')
.each(effect =>
{
var oldPower = effect.power;
effect.power = Math.round(effect.power / this.fatigue);
if (effect.power != oldPower) {
console.log("Outgoing POW modified by Final Stand to " + effect.power);
console.append("was: " + oldPower);
}
});


Enemy AI benefits greatly from the functionality as well:
Code: (javascript) [Select]

var isPhysical = link(action.effects).filterBy('type', 'damage').pluck('damageType').contains('physical')
                 || link(action.effects).filterBy('type', 'damage').pluck('element').contains('earth');


Or even this masterpiece:
Code: (javascript) [Select]

if (this.tactics === null) {
var targets = link(this.aic.battle.enemiesOf(this.aic.unit)).shuffle();
var combos = link(link(this.combos)
.where(function(combo) { return phase >= combo.phase; }.bind(this))
.random(targets.length))
.sort(function(a, b) { return b.rating - a.rating; });
this.tactics = [];
for (var i = 0; i < targets.length; ++i) {
this.tactics.push({ moves: combos[i].moves, moveIndex: 0, unit: targets[i] });
}
}
this.tactics = link(this.tactics)
.where(function(tactic) { return tactic.unit.isAlive(); })
.where(function(tactic) { return tactic.moveIndex < tactic.moves.length; })
.toArray();
var tactic;
do {
tactic = RNG.sample(this.tactics);
} while (tactic === this.tactics[0] && tactic.moveIndex == tactic.moves.length - 1
&& this.tactics.length > 1);
this.aic.queueSkill(tactic.moves[tactic.moveIndex], tactic.unit.id);
++tactic.moveIndex;
if (this.tactics[0].moveIndex == this.tactics[0].moves.length) {
this.tactics = null;
}


Which basically translates in plain English to "pick a random combo for each party member, and use the finisher for the most powerful one last so that the other ones are decoys".
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Link.js v0.3.0
Reply #261
So my XML parser produces a DOM like in the attached screenshot and I'm wondering what the best way to query it is.  If it's not totally clear from the screenshot, the object model is basically this:

* The DOM has a list of nodes, stored as an array .nodes
* A node may either be a tag or text
* Text nodes are leaves and don't go any further down
* Tag nodes have their own .nodes array with the text and tags contained by them, and .attributes which is a dictionary of the tag's attributes
* And so forth

I can enumerate the top level easily enough with link(dom.nodes),what I'm wondering is how I can drill down through the heirarchy.  Basically if I have the following XML doc:
Code: (xml) [Select]

<neoHippos>
    <pig name="maggie" weight="812 tons">
        <food>Lizzie</food>
        <food>Chiroptera</food>
        <food>Gail</food>
        <food>non-kitty-eating cows</food>
    </pig>
    <cow name="Kittycow">
        <food>Prance</food>
        <food>Shrek</food>
        <food>kitties</food>
    </cow>
</neoHippos>


How could I use Link to enumerate only the <food> elements under <pig>, given the DOM described above?
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Link.js v0.3.0
Reply #262
This should work, .recurse will recursively go through sub-arrays if all items are set up the same. I made it to go through filesystem structures a bit easier. If it encounters a node without a 'nodes' it knows to skip it. The where clause knows to check just nodes named 'pig'. The rest is history. :)

Code: (javascript) [Select]

Link(dom).recurse('nodes').where(function(item) { return item.name == 'pig'; }).each(function(item) { console.log(item); });
  • Last Edit: April 03, 2016, 02:59:40 pm by Radnen
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Link.js v0.3.0
Reply #263
Does .recurse() unroll the whole structure, or only one level at a time? i.e. Could I do this:

Code: (javascript) [Select]

Link(dom)
    .recurse('nodes')
    .where(x => x.type == 'tag' && x.name == 'neoHippos')
    .recurse('nodes')
    .where(x => x.type == 'tag' && x.name == 'pig')
    .recurse('nodes')
    .where(x => x.type == 'tag' && x.name == 'food')
    .recurse('nodes')
    .where(x => x.type == 'text')
    .each(element =>
{
    console.log("the pig eats: " + element.text);
});
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Link.js v0.3.0
Reply #264

Does .recurse() unroll the whole structure, or only one level at a time? i.e. Could I do this:

Code: (javascript) [Select]

Link(dom)
    .recurse('nodes')
    .where(x => x.type == 'tag' && x.name == 'neoHippos')
    .recurse('nodes')
    .where(x => x.type == 'tag' && x.name == 'pig')
    .recurse('nodes')
    .where(x => x.type == 'tag' && x.name == 'food')
    .recurse('nodes')
    .where(x => x.type == 'text')
    .each(element =>
{
    console.log("the pig eats: " + element.text);
});



It unrolls the structure. It checks if the current element has nodes and then enters it and checks if that element has nodes. So, you can't do that. To unroll one level at a time I guess you could use pluck and where:

Code: (javascript) [Select]

Link(dom)
    .pluck('nodes')
    .where(x => x.type == 'tag' && x.name == 'neoHippos')
    .pluck('nodes')
    .where(x => x.type == 'tag' && x.name == 'pig')
    .pluck('nodes')
    .where(x => x.type == 'tag' && x.name == 'food')
    .pluck('nodes')
    .where(x => x.type == 'text')
    .each(element =>
{
    console.log("the pig eats: " + element.text);
});


Pluck just doesn't detect if the 'nodes' property doesn't exist (the assumption being, you ought to know the item to pluck already), but since you filter to only 'tag' types, I think it might be fine.

Edit: but this will act almost no different than .recurse and where, recurse is just neater. Though this pluck-where method could be faster since it doesn't have to throw away as many "junk nodes".
  • Last Edit: April 03, 2016, 03:11:36 pm by Radnen
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Link.js v0.3.0
Reply #265
Yeah, tags always have a .nodes which is an array, even if they are self-closing (in which case it's empty).  I'll try it out with pluck and see what happens, thanks for the help. :)

If I use recurse, is there a way to figure out where I came from?  Since it unrolls everything each() only sees the element at the lowest level, with no idea where we came from.  That's why I was looking for a way to selectively drill down.
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Link.js v0.3.0
Reply #266

If I use recurse, is there a way to figure out where I came from?  Since it unrolls everything each() only sees the element at the lowest level, with no idea where we came from.  That's why I was looking for a way to selectively drill down.


Unfortunately no, but I do know what you mean. I think that's just the nature of recursive loops in general. It just kind of does it's own thing and you are are the mercy of it's automatic behavior.
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Link.js v0.3.0
Reply #267
Feature idea: the cross product!

Code: (javascript) [Select]

// old way:
Link(my_array).each(function(item) {
    var found = Link(other_array).first(function(i) { return i.ID == item; });
    if (found) { found.added = 1; }
});

// new way:
Link(my_array).cross(other_array, function(item1, item2) {
    if (item2.ID == item1) { item2.added = 1; }
});

// experimental non-blocking cross product, creates an interstitial cross-result object.
Link(my_array).crossObject(other_array).each(function(result) {
    if (result.B.ID == result.A) { result.B.added = 1; }
});

// or, using .where:
Link(my_array).crossObject(other_array).where(function(result) { return result.B.ID == result.A; }).each(function(result) { result.B.added = 1; });

// in typescript:
Link(my_array).crossObject(other_array).where(result => result.B.ID == result.A).each(result => result.B.added = 1);


The idea is I want to eliminate the need to have Link contexts unnecessarily inside of other link contexts. Item1 is from the parent Link context, Item2 is from the added link context. BTW, I'm going to modify Link so that every place where it may take an array, could take another link context.

Either I'm going to run the new context's array or I'm going to resolve the new context's chain before running the resulting array. Which do you guys think is appropriate? I'm thinking the resolve approach is better, but you'd have to know that it would resolve it before it is executed. Here is an example:

Code: (javascript) [Select]

var test = Link(array1).where(even);
Link(test).each(print);


In the above example, it will look at the array inputs, realize there is a link context, run it (find all even #'s), then use it's result to print the values (2, 4, 6, 8...). What do you think?

Edit:
Added .where example for the non-blocking code
  • Last Edit: May 23, 2016, 03:18:40 pm by Radnen
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Link.js v0.3.0
Reply #268
Link contexts are conceptually arrays, so it makes sense to make them interchangeable, I like it. :)

Not sure I understand the cross-product thing.  I know what a cross product is in math, just not sure how it pertains here.  For the resolving thing, I think it would be nice to maintain the lazy evaluation behavior of possible.  Not sure how difficult that'd be to implement though.
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Link.js v0.3.0
Reply #269

Not sure I understand the cross-product thing.  I know what a cross product is in math, just not sure how it pertains here.


Basically it does a 2D search of all value permutations. If you look at the code example you can see how it simplified the code a lot, perhaps even giving it a performance boost.


For the resolving thing, I think it would be nice to maintain the lazy evaluation behavior of possible.  Not sure how difficult that'd be to implement though.


Hmm, yeah, but the final results would need iteration on so it can only be so lazy until you run it. I wonder if I can expose a private API so I can "manually" step through a link execution chain and only execute what I need on-the-fly:

Code: (javascript) [Select]

var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var ctxt = Link(array).where(even).yield('toArray');

var v1 = ctxt.step().step().step().step().step().end(); // [0, 2, 4]
var v2 = ctxt.step(5).end(); // [0, 2];
var v3 = ctxt.run(); // [0, 2, 4, 6, 8, 10]


Kind of experimental, but it will ensure that only 'n' steps()'s get executed. That's as lazy as it gets.
  • Last Edit: May 23, 2016, 04:53:32 pm by Radnen
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here