Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Weird variable problem [sugarcube]

Hey!

In order to teach myself sugarcube and more advanced scripting I wrote a pathfinding macro for maze/map games and such. Its purpose is to put out an array which contains the shortest path to a target as a list of coordinates. The widget is theoretically complete but when I try it out something weird happens. The basic idea is: the macro takes the map-array, which serves several purposes, as argument and converts it internally to something it can progress. It is not supposed to write on the original array, yet, when I run the macro, the original map-array is outputted as identical to the one the macro generates for its internal processing.

my test page looks like this:
<<set $m to [
[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
[-1,-1,+1,+1,+1,+1,+1,+1,-1,+1,+1,+1,+1,-1,-1,+1,-1,+1,+1,-1],
[-1,-1,+1,-1,-1,-1,-1,-1,-1,-1,-1,+1,-1,-1,-1,-1,-1,-1,-1,-1],
[-1,-1,+1,-1,-1,-1,-1,-1,+1,+1,+1,+1,-1,-1,-1,-1,-1,-1,-1,-1],
[-1,+1,+1,+1,-1,+1,+1,+1,+1,+1,+1,+1,+1,+1,-1,+1,+1,+1,+1,-1],
[-1,+1,+1,+1,+1,+1,-1,+1,+1,+1,+1,+1,+1,+1,+1,-1,+1,+1,-1,-1],
[-1,+1,-1,-1,-1,+1,-1,-1,+1,+1,+1,+1,+1,-1,+1,+1,-1,+1,-1,-1],
[-1,+1,+1,+1,+1,+1,+1,+1,+1,+1,+1,+1,+1,-1,-1,+1,-1,+1,+1,-1],
[-1,-1,-1,-1,-1,-1,-1,+1,+1,+1,+1,+1,+1,-1,-1,-1,-1,-1,-1,-1],
[-1,+1,+1,+1,-1,+1,+1,+1,+1,+1,+1,+1,+1,+1,-1,+1,+1,+1,+1,-1],
[-1,+1,+1,+1,-1,+1,-1,+1,+1,+1,+1,+1,+1,+1,+1,-1,+1,+1,-1,-1],
[-1,+1,-1,-1,-1,-1,-1,-1,-1,+1,+1,+1,+1,-1,+1,+1,-1,+1,-1,-1],
[-1,+1,+1,+1,+1,+1,+1,+1,+1,+1,+1,+1,+1,-1,-1,+1,-1,+1,+1,-1],
[-1,-1,-1,-1,-1,-1,-1,+1,-1,-1,-1,-1,-1,-1,-1,+1,-1,-1,-1,-1],
[-1,-1,-1,-1,-1,-1,-1,+1,-1,-1,-1,-1,-1,-1,-1,+1,-1,-1,-1,-1],
[-1,+1,+1,+1,-1,+1,+1,+1,-1,+1,+1,+1,+1,+1,-1,+1,+1,+1,+1,-1],
[-1,+1,+1,+1,-1,+1,-1,+1,-1,+1,+1,-1,+1,+1,+1,-1,+1,+1,-1,-1],
[-1,+1,-1,-1,+1,-1,-1,+1,-1,-1,-1,-1,+1,-1,+1,+1,+1,+1,-1,-1],
[-1,+1,+1,+1,+1,+1,+1,+1,-1,+1,+1,+1,+1,-1,-1,+1,-1,+1,+1,-1],
[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1]]>>


<<pathfind [11,1] [1,12] $m>>
The first two arguments of the macro are the start and the end points, after this there are only 2 for-loops on the page that print the contents of $m and of $pf_map, which is the internal map of the macro. Even though, beside the initial declaration there is no other operation which modifies $m, $m and $pf_map come out the same when the page is rendered. It seems as if at some point the script flows backwards.

The code of the widget is the following:
<<widget "pathfind">>

/%input of arguments. 1. start coordinates (x.y) 2. end coordinates (x.y) 3. map%/

<<set $pf_start = $args[0]>>
<<set $pf_end = $args[1]>>
<<set $pf_map = $args[2]>>

/%convert map to collision map. positive numbers are converted to open nodes, negative numbers to not closed nodes (not passable)%/

<<for $pf_i_x to 0, $pf_i_y to 0; $pf_i_y lt $pf_map.length;>>

<<if $pf_map[$pf_i_y][$pf_i_x] gt 0>>
<<set $pf_map[$pf_i_y][$pf_i_x] to "O">>
<<else>>
<<set $pf_map[$pf_i_y][$pf_i_x] to "N">>
<<endif>>

<<if $pf_i_x lt $pf_map[$pf_i_y].length -1>>
<<set $pf_i_x++>>
<<else>>
<<set $pf_i_y++>>
<<set $pf_i_x to 0>>
<<endif>>
<</for>>

/%calculates optimal number of steps necessary to reach each node%/

<<for $pf_smallest to [], $pf_list to [[$pf_start[0],$pf_start[1]]], $pf_path to [$pf_start]; def $pf_list[0]; $pf_smallest to []>>

<<if $pf_map[$pf_list[0][1]+1][$pf_list[0][0]] eq "O">>
<<set $pf_map[$pf_list[0][1]+1][$pf_list[0][0]] to "Li">>
<<set $pf_list.push([$pf_list[0][0],$pf_list[0][1]+1])>>
<<elseif $pf_map[$pf_list[0][1]+1][$pf_list[0][0]] gte 0>>
<<set $pf_smallest.push($pf_map[$pf_list[0][1]+1][$pf_list[0][0]])>>
<<endif>>

<<if $pf_map[$pf_list[0][1]-1][$pf_list[0][0]] eq "O">>
<<set $pf_map[$pf_list[0][1]-1][$pf_list[0][0]] to "Li">>
<<set $pf_list.push([$pf_list[0][0],$pf_list[0][1]-1]) >>
<<elseif $pf_map[$pf_list[0][1]-1][$pf_list[0][0]] gte 0>>
<<set $pf_smallest.push($pf_map[$pf_list[0][1]-1][$pf_list[0][0]])>>
<<endif>>

<<if $pf_map[$pf_list[0][1]][$pf_list[0][0]+1] eq "O">>
<<set $pf_map[$pf_list[0][1]][$pf_list[0][0]+1] to "Li">>
<<set $pf_list.push([$pf_list[0][0]+1,$pf_list[0][1]]) >>
<<elseif $pf_map[$pf_list[0][1]][$pf_list[0][0]+1] gte 0>>
<<set $pf_smallest.push($pf_map[$pf_list[0][1]][$pf_list[0][0]+1])>>
<<endif>>

<<if $pf_map[$pf_list[0][1]][$pf_list[0][0]-1] eq "O">>
<<set $pf_map[$pf_list[0][1]][$pf_list[0][0]-1] to "Li">>
<<set $pf_list.push([$pf_list[0][0]-1,$pf_list[0][1]]) >>
<<elseif $pf_map[$pf_list[0][1]][$pf_list[0][0]-1] gte 0>>
<<set $pf_smallest.push($pf_map[$pf_list[0][1]][$pf_list[0][0]-1])>>
<<endif>>

<<if def $pf_smallest[0]>>
<<set $pf_smallest.sort(function(a, b){return a-b})>>
<<set $pf_map[$pf_list[0][1]][$pf_list[0][0]] to $pf_smallest[0] +1>>
<<else>>
<<set $pf_map[$pf_list[0][1]][$pf_list[0][0]] to 0>>
<<endif>>

<<set $pf_list.shift()>>

/%saves optimal path to the final note to a path (a list of coordinates) and ends loop%/

<<if $pf_map[$pf_end[1]][$pf_end[0]] gte 0>>

<<for $pf_steps to $pf_map[$pf_end[1]][$pf_end[0]], $pf_pointer to $pf_end, $pf_select to [], $pf_path to [$pf_end]; $pf_steps gt 1; $pf_steps--, $pf_select to []>>

<<if $pf_map[$pf_pointer[1]][$pf_pointer[0]-1] eq $pf_steps -1>>
<<set $pf_select.push([$pf_pointer[0]-1,$pf_pointer[1]])>>
<<endif>>

<<if $pf_map[$pf_pointer[1]][$pf_pointer[0]+1] eq $pf_steps -1>>
<<set $pf_select.push([$pf_pointer[0]+1,$pf_pointer[1]])>>
<<endif>>

<<if $pf_map[$pf_pointer[1]+1][$pf_pointer[0]] eq $pf_steps -1>>
<<set $pf_select.push([$pf_pointer[0],$pf_pointer[1]+1])>>
<<endif>>

<<if $pf_map[$pf_pointer[1]-1][$pf_pointer[0]] eq $pf_steps -1>>
<<set $pf_select.push([$pf_pointer[0],$pf_pointer[1]-1])>>
<<endif>>

<<set $pf_path.unshift($pf_select.random())>>

<<set $pf_pointer to $pf_path[0]>>

<</for>>

<<break>>

<<endif>>

<</for>>

<</widget>>
I am not sure, if that's the most elegant way to do a pathfinding macro, but it works. By the way, is it of any use to unset the variables (exept $pf_path) at the end of the widget?

I really don't understand this problem. Help would be very appreciated. I am sure, other people might also take advantage of a pathfinding macro.





Comments

  • The array parameter of your macro is Passed by Reference (basically it means that it passes the location in memory of your array), when you assign $pf_map = $args[2] you end up with two variables pointing to the same array, not a copy which is why modifying one variable effects the other.

    What you want to do is make a Copy/Clone of the array, which you would normally do using the following code but this wont work correctly in your case because you have an array of arrays:

    <<set $pf_map = $args[2].slice(0)>>
    Instead you will need to do a Deep Copy using one of the following two methods, they both work.

    <<set $pf_map to jQuery.extend(true, [], $args[2])>>

    or

    <<set $pf_map to JSON.parse(JSON.stringify($args[2]))>>
  • Sweet! That works! Thank you!

    How can I tell, if a parameter is passed by reference? Or does it only happen when I pass data to a widget?
  • Technically the parameters are all being passed by value.

    The actual value in an array variable is NOT the array itself but an object reference (kinda like a number) to where the array is stored and you are passing this reference by value. The same behavior happens when passing any object as a parameter, and is standard in Javascript which is the programming language behind all the marcos and code you use in a Twine story format.
  • Ah, that explains a lot :) I'll have that in mind when handling arrays now. Thanks again.

    I hope, doing all that twine stuff will make it easier for me to eventually learn JS.
Sign In or Register to comment.