Stuck in an Infinite Loop?

Twine Version: 2.3.5
Story Format: Harlowe

Okay, I’m trying to simulate a store and things are getting complicated… The math already seems to be slowing down the refresh rate of displaying quantities and dollars, and when we get into high numbers, things start to get weird. Like infinite loops popping up and stuff. I’m self-taught so I think it’s time to call in some help. I’ll just paste my code and explain it and you’ll hopefully see the problems.

Okay, so I first set up a customer modifier variable and a max customers variable somewhere else. Here, I set up an array of customers and then randomize it, then I make them into datamaps that have a certain number of slots to buy items, randomized between 1 and 3. Finally, those buy slots are all added up in the total $buyslots

(set: $customers to (range: 1,(ceil: $maxcustomers * $customermod)))
(set: $customers to (range: 1,(random: (round: $customers's length - ($customers's length * .10)), (round: $customers's length + ($customers's length * .10)))))
(set: $buyslots to 0)
(for: each _index, ...(range: 1, $customers's length))[
	(set: $customers's (_index) to (dm:
		"name","default",
		"buyslots", 1
	))
	(set: $customers's (_index)'s buyslots to (random: 1,3))
	(set: $buyslots to it + $customers's (_index)'s buyslots)
]

So next I need to take the list of products the store has in stock and shuffle it, because if it’s in the same order every time the first items on the list will be bought more often. So for each product on the now shuffled list, I first zero out a $salesmod (holds the number of sales for that item) and a “calc” in the items datamap (the final number of sales for that item). Anyway, then I calculate the popularity of the item (it needs to be limited to a range of 1-10, which the items can go outside of, but they are capped for this calculation - that’s what $poplimit is)

Next comes the part I’ve been having the most trouble with and I think the source of the infinite loop issue but I’m really not sure. For each buy slot, it picks a number from 1-10, and if that falls within the popularity of the item, the customer buys it, and a buy slot is removed.

Finally, the number of that item bought is calculated, that number is taken out of the store’s stock, the money is added to the store’s money, and the total sale is recorded.

(set: $products to (shuffled: ...$products))
(for: each _index, ...(range: 1, $products's length))[
	(set: $salesmod to 0)
	(set: $products's (_index)'s calc to 0)
	(if: $products's (_index)'s number > 0)[
		(if: $products's (_index)'s popularity < 0)[
			(set: $poplimit to 0)
		]
		(else-if: $products's (_index)'s popularity > 10)[
			(set: $poplimit to 10)
		]
		(else:)[
			(set: $poplimit to $products's (_index)'s popularity)
		]
		(for: each _slot, ...(range: 1, $buyslots))[
			(if: $buyslots >= 1)[
				(set: $salecheck to (random: 1, 10))
				(if: $salecheck <= $poplimit)[
					(set: $salesmod to it + 1)
					(set: $buyslots to it - 1)
				]
			]
		]
		(if: $salesmod <= $products's (_index)'s number)[
			(set: $products's (_index)'s calc to $salesmod)
		]
		(else:)[
				(set: $products's (_index)'s calc to $products's (_index)'s number)
		]
		(if: $products's (_index)'s calc <= 0 and $products's (_index)'s pop is "not popular")[
			(set: $products's (_index)'s calc to 0)
		]
		(else-if: $products's (_index)'s calc <= 0)[
			(set: $products's (_index)'s calc to 1)
		]
		(set: $products's (_index)'s number to (it - $products's (_index)'s calc))
		(set: $dollarDiff to ($products's (_index)'s calc * $products's (_index)'s price))
		(set: $dollars to it + $dollarDiff)
		(set: $totalsale to it + $dollarDiff)
	]
]

And here’s a pretty clunky way to get the list back in alphabetical order, by putting the names of the products in an array, sorting it, then matching the datamap of products to the alphabetized list (to display in many other areas of the game along with their number, price, etc.). If anyone knows a better way to do this, please tell me.

(set: $alphabetizer to (a:))
(set: $tempProducts to (a:))
(for: each _name, ...$products)[
	(set: $alphabetizer to it + (a: _name's name))
]
(set: $alphabetizer to (sorted: ...$alphabetizer))
(for: each _name, ...$alphabetizer)[
	(for: each _index, ...(range: 1, $products's length))[
		(set: _item to $products's (_index))
		(if: _item's name is _name)[
			(set: $tempProducts to it + (a: _item))
		]
	]
]

(set: $products to $tempProducts)

Woooo! That’s it. What a mess. Hope someone can help.

warnings:

  1. Harlowe comes with its own custom implementations of the Array and DataMap collection objects, and these implementations have efficiency issues when it comes to things like looping, updating property values, property accessing, etc…
  2. Harlowe will display an “infinite loop” related error message whenever the collection object (eg. Array) passed to a (for:) macro contains more than 48 elements/items.

eg. The following 48 element test works…

(set: $numbers to (range: 1, 48))
(for: each _i, ...$numbers)[
	_i\
]

…while the follow 49 element test will result in an “infinite loop” related error message.

(set: $numbers to (range: 1, 49))
(for: each _i, ...$numbers)[
	_i\
]

If your project will need to perform looping on large Arrays or to do bulk manipulate of DataMaps then I strongly suggest to consider switching to another story format that has better support for those object types. eg, SugarCube.

1 Like

Thank you so much for your answer, but uf… is it difficult or even possible to convert a twine story to SugarCube, or would I have to do it all manually? Is there a way to use Javascript or HTML 5 to handle arrays in a Harlowe story?

There is no automatic or scripted method I know of to convert one story format to another, as they each have their own unique macro language and functionality. So yes, you would need to do it by ‘hand’.

Harlowe has been deliberately designed by its developer to restrict an Author’s ability to use JavaScript to access/extend the engine’s functionality.

So as Chapel’s Harlowe extensions have proven it is possible to do but there is no documented JavaScript API for this story format and you will need to resort to hacking the engine to access things like the Variable State sub-system.

Ah, yeah I figured from other things I’ve seen you say. I’m converting it now and it’s chaos, but at least I’m learning Sugarcube now. Thanks, you and TheMadExile are like the patron saints of Twine, and have helped me figure out hundreds of problems.