Sugarcube2: How to get lowest variable from list of 36 variables

Twine Version: 2.7.1
Sugarcube2

Situation: Writing a ghost story where the ghost is going to manipulate the shittiest relationship available between 9 people, in order to make chaos. I need to figure out how to determine which relationship is lowest in order to do this.

I have 36 variables, one for each relationship between the 9 people. I need to figure out which variable has the lowest number, so that I can then have the system use that variable to pick a version of the conversation that applies tension for those two characters.

Code so far:


check spirit type
if good send [[Good]] 
if neutral, send [[Neutral]] 

Otherwise - 
<<math.min ($R1_2, $R1_3, $R1_4, $R1_5, $R1_6, $R1_7, $R1_8, $R1_9, $R2_3, $R2_4, $R2_5, $R2_6, $R2_7, $R2_8, $R2_9, $R3_4, $R3_5, $R3_6, $R3_7, $R3_8, $R3_9, $R4_5, $R4_6, $R4_7, $R4_8, $R4_9, $R5_6, $R5_7, $R5_8, $R5_9, $R6_7, $R6_8, $R6_9, $R7_8, $R7_9, $R8_9)>>
/*$PC tells the system which character the player chose to play as. 
<<set Medium = 
/* Relationship values (Digits equal person) (0 = hatred, 50= friendship, 100 equals love)
<<set R1_2 = 65>>
<<set R1_3 = 45>>
<<set R1_4 = 55>>
<<set R1_5 = 65>>
<<set R1_6 = 35>>
<<set R1_7 = 65>>
<<set R1_8 = 80>>
<<set R1_9 = 75>>
<<set R2_3 = 45>>
<<set R2_4 = 60>>
<<set R2_5 = 35>>
<<set R2_6 = 60>>
<<set R2_7 = 40>>
<<set R2_8 = 45>>
<<set R2_9 = 65>>
<<set R3_4 = 55>>
<<set R3_5 = 70>>
<<set R3_6 = 50>>
<<set R3_7 = 40>>
<<set R3_8 = 50>>
<<set R3_9 = 45>>
<<set R4_5 = 65>>
<<set R4_6 = 60>>
<<set R4_7 = 75>>
<<set R4_8 = 75>>
<<set R4_9 = 75>>
<<set R5_6 = 45>>
<<set R5_7 = 40>>
<<set R5_8 = 50>>
<<set R5_9 = 40>>
<<set R6_7 = 65>>
<<set R6_8 = 65>>
<<set R6_9 = 75>>
<<set R7_8 = 80>>
<<set R7_9 = 60>>
<<set R8_9 = 55>>


<<set Blessings = "Neutral">>
<<set Divine_contacted = false>>
<<set WhichDivine = "">>
/*options are: Divine_good, Divine_bad, Divine_neutral

/*Spirit options:
<<set Spirit_contacted = 0>>
/*Options are: 0 - none, 1,2,3 - Good, 4,5,6 (died of natural causes) - Neutral, 7,8,9 - Evil (demon or vengeful spirit), 10 - Haunted (Murdered brutally)
<<set SpiritName = "">>
<<set SpiritDeath = 0>>
<<set SpiritFamily = 0>>
<<set SpiritAfterlife = 0>>
/*Make arrays for Names, Deaths (different arrays for types of spirits), Family and Afterlives.

Death types: Murder by stranger, Murder by family, Accidental Death, Drowning, Hanging (punishment), Illness, Starvation, Old Age,

<<set Guidance = 0>>

Any help or guidance that can be offered would be appreciated. All the options I’ve found so far have been about pulling the lowest number and not the lowest variable… and I can’t find anything indicating what I could alter in those codes to figure out what I need.

2 Likes

Some cursory web searching gave me this: javascript - Return index of greatest value in an array - Stack Overflow, which I reversed so you get the index of the minimum value instead of the max.

I prefer plain JS syntax over Sugarcube, so you might want to try this:

<<set _listOfVariables to ($R1_2, $R1_3, $R1_4, $R1_5, $R1_6, $R1_7, $R1_8, $R1_9, $R2_3, $R2_4, $R2_5, $R2_6, $R2_7, $R2_8, $R2_9, $R3_4, $R3_5, $R3_6, $R3_7, $R3_8, $R3_9, $R4_5, $R4_6, $R4_7, $R4_8, $R4_9, $R5_6, $R5_7, $R5_8, $R5_9, $R6_7, $R6_8, $R6_9, $R7_8, $R7_9, $R8_9)>>

<<script>>
function indexOfMin(arr) {
    if (arr.length === 0) {
        return -1;
    }

    var min = arr[0];
    var minIndex = 0;

    for (var i = 1; i < arr.length; i++) {
        if (arr[i] < min) {
            minIndex = i;
            min = arr[i];
        }
    }

    return minIndex;
}

State.temporary.index = indexOfMin(State.temporary.listOfVariables);

<</script>>

The index of the lowest value is _index.

The lowest value is _listOfVariables[_index].

For example, if the variable with the lowest value was $R1_2, then _index would be 0 (because arrays are numbered from 0). If it was $R1_3, _index would be 1. If it was $R1_4, index would be 2, and so on. Then you could access the value of that variable with _listOfVariables[_index].

It’s not exactly the same as getting the name of the variable–that’d be a little more complicated, would involve objects/dictionaries and stuff. Still wouldn’t be that hard, though you wouldn’t need it if this works for your situation?

1 Like

See the problem is, I need the name of the variable, specifically, because I’m using it to reference to which two people are gonna start killing each other. The value of the variable doesn’t matter, it’s the variable itself that matters… Because the name of the variable will tell me if character 2 is about to shoot character 7 or if character 1 is going to choke out character 3, you see?

If there’s an easier way of doing this, I would love to know, I just need to find it.

1 Like

In this situation I’d probably use arrays for the values…

<<set $R = [
    [00, 65, 45, 55, 67, 35, 65, 80, 75],
    [00, 00, 45, 60, 35, 60, 40, 45, 65],
    [00, 00, 00, 55, 70, 50, 40, 50, 45],
    [00, 00, 00, 00, 65, 60, 75, 75, 75],
    [00, 00, 00, 00, 00, 45, 40, 50, 40],
    [00, 00, 00, 00, 00, 00, 65, 65, 75],
    [00, 00, 00, 00, 00, 00, 65, 65, 75],
    [00, 00, 00, 00, 00, 00, 00, 80, 60],
    [00, 00, 00, 00, 00, 00, 00, 00, 55]
]>>

This gives you people numbered starting at 0 instead of 1. So $R[0][1] would give you the relationship between the first two people.

And then in your StoryInit passage you can define a function to get the minimum:

function window.worstRelationship(R) {
    let r0, a0, b0;
    for(let a=0; a<R.length; ++a) {
        for(let b=a+1; b<R[a].length; ++b) {
            if(r0 != null && R[a][b] < r0) { r0=R[a][b]; a0=a; b0=b }
        }
    }
    return {r: r0, a: b0, b: b0}
}

And finally you should be able to do <<set _worst to worstRelationship($R)>> and then get your people numbers with _worst.a and _worst.b (and, if you need it, the relationship value _worst.r.

I should call it a night, and I haven’t tested this, but maybe that points you in a possible direction…

2 Likes

Oh yeah, this should work much better than my solution. If you’re doing stuff involving a network of relationships between a bunch of characters, 2d array is the way to go. That’s what I get for not reading the initial post closely.

You’d just have to be careful to always use R[0][1] and not R[1][0], and so on (lower value 1st, higher 2nd), to only get the upper diagonal of the array. Alternatively, always make sure that R[0][1] and R[1][0] etc. always have the same values, unless relationships are directional – where one person can feel positively towards another person but the other person absolutely hates them. Which would be interesting but I’m not sure if you’d want that.

Also, murder! Fun!

1 Like

There’s two ideas I’d like to volunteer.

First, relations might not be one number but two. Somehow, A might think B is nice, while B despises A. So it would make sense relations between two people being tracked from each perspective.

Second, the lowest relationship value may not be unique. How do you intend to decide which relation between relations at the same lowest level will trigger?

I would go with two arrays, both with 36 members. One to track the relationship values, the other to track the couples of people.

Here’s a limited example of what I think (Lines with no codes are juste my way to ensure my coding is doing what I intend it to do).

<<set $R to ["Alan and Bill","Alan and Charlie","Alan and Dave","Bill and Charlie","Bill and Dave","Charlie and Dave","Alan and Ernie"," Charlie and Fred"]>>
<<set $a to [1,2,3,4,5,1,1,2]>>
$a

<<set _b to Math.min.apply(Math, $a)>>
_b

<<set _c to $a.count(_b)>>
_c

<<set _d to random(1,_c)>>
_d

<<set _occurrence to 0>>
<<for _i to 0 ; _i < $a.length ; _i ++>>
<<if $a[_i] == _b>><<set _occurrence ++>><<switch _occurrence>><<case _d>><<break>><</switch>><</if>>
<</for>>
_i

Some nasty things happen between $R[_i]

You can use the indexOfMin() code with a slight variation, passing a list of variable names instead of the variables themselves:

setup.minVar = function minVar(arr) {
    if (arr.length === 0) {
        return '';
    }

    var min    = Infinity;
    var minVar = '';

    for (const i in arr) {
        if (State.getVar(arr[i]) < min) {
            minVar = arr[i];
            min    = State.getVar(arr[i]);
        }
    }

    return minVar;
}

And then call it like:

<<set _minVar = setup.minVar([ "$R1_2", "$R1_3", "$R1_4" .... ])>>

Note that this passes an array of variable names (including sigils) not the variables themselves, and what you get back is the variable name (also including the sigil) of the variable with the lowest value.