Selecting a random tile in PulpScript

Hi. I'm loving the tooling provided, though it is a little hard to do certain things. I appreciate this is not the full SDK and I look forward to seeing that.

I am making a game where there is a collectable item that I need to move to a random location after it's been collected. I have a bit of inefficient code to achieve this at the moment that picks a random x and picks a random y then checks whether that's a valid tile to drop the item onto by seeing whether the tile is of type world or something else. If not, try again with two new random numbers. As fewer and fewer tiles become valid targets, I'm already pretty sure this is going to slow down. I also noticed, when I had bugs in my loop, that after 400 tries the game just stops and says the loop has no exit condition.

Is there a better way to pick a random tile that meets the criteria? I just want any tile that's still the white background.

Thanks!

Here’s a potentially really bad idea...how bad it is depends on how many candidate tiles you have. First you would create a new Item tile, leave it blank and name it “empty”. This is our candidate tile. Then flood fill all the current “white” tiles with “empty”. Add the following to “empty”’s PulpScript:

on pickme do
	if picked==0 then
		r = random 1,128
		if r==1 then
			swap "item"
			picked = 1
		end
	end
end

Then when you need to pick an item (I’m using the player’s confirm handler just as an example):

on confirm do
	picked = 0
	tries = 0
	while picked==0 do // yikes!
		emit "pickme"
		
		tries += 1
		if tries==400 then
			picked = 1 // complete miss
		end
	end
	log "found in {tries} tries"
end

What we’re doing here is asking all candidates if they’re the one. A random range of 1-128 produced decent results with an empty room filled with candidates except for walls on every side, so 299 candidates at the start. You could probably reduce the upper bound with fewer candidates. We drop it in a loop because we could miss (and definitely do as the number of candidates dwindle). We make sure we stop trying after our 400th failed attempt. In my tests the worst case (near the end) was 62 tries. On the hardware you’re definitely going to drop some frames but as long as you’re not calling it every frame it might be okay.

Another thing worth mentioning is that it does tend to favor the top left corner of the map initially (for what are probably obvious reasons).

emit is one of PulpScript's priciest functions (followed by while loops :playdate_cry_laugh:) so I almost hesitate to suggest this.

Thanks @shaun I like this a lot. It is targeting the available tiles instead of trying everywhere in a grid and hoping for a hit. I think I can probably reduce the random number down quite a lot as the randomness doesn't have to be superb. And to avoid maxing out the 400 loops, I could have another event called countMe or something that you can emit to find out how many candidates are left. Then you can reduce the random number to something more achievable when there are fewer targets to choose from. Ironically, I think your solution has the opposite problem to mine. It gets more efficient as the number of candidates goes down :joy:

This makes me think that PulpScript might benefit from having something like:

tell random "tileName"[,"anotherOptionalTileName","andAnother"] to
   ...
end

I could definitely see that being a benefit to the sorts of tile based games Pulp focuses on. There are obvious use cases for finding a random tile and the only way to get one right now is a bit round about.

Incidentally, this is why I wanted random tile locations. I always loved the Nokia snake game. This is using your approach now and it works well. Still need to get the score working of course.

2 Likes

You're right, if you first emit an event to have every tile increment a counter, you'd have a total count of the available tiles. After that, you can do random 1,count then decrement that number until 0, which will be the selected tile.

Edit: realized you only need one random call.

Edit2: Let's make some code to help others who show up. You'll have a function for counting tiles, and a function for picking one and turning it into a "gem". To pick one

The room script will count the "tile" sprites in the room, then turn one into a "gem"

on enter do
  tileCount = 0
  emit "countTiles"
  
  if tileCount > 0 then
    tileCount--
    selectedTile = random tileCount
    emit turnSelectedIntoGem
  end
end

the "tile" sprite script needs to increment the counter when called, plus turn into a gem if it is selected

on countTiles do
  tileCount++
end

on turnSelectedIntoGem do
  if selectedTile == 0 then
    swap "gem"
  end
  selectedTile--
end

If you manually decrement tileCount when you turn one into a "gem" you won't need to emit "countTiles" repeatedly.

1 Like

Clever! I will switch it to this since that sounds like the most efficient solution given the available tools. No while loop needed. Thanks.

I used this thread to finesse a working deck of cards! My initial brute force randomization attempt was like 5000 lines of code that allowed leaks (you can imagine what kind of awful checking I tried) but I think this thread provided a much better solution to faking an array of discrete random objects like a card deck.

2 Likes

[edit: i really ought to have scrolled to the latest comments before posting. my comment likely isn't a better solution for you, but might still be interesting?]

perhaps start with an 'emit' call to determine that at least SOME target tile exists... add 4 variables to be set by those tiles to say "i exist in quadrant a,b,c, or d". that way your next logic would only have to search for a candidate in a quarter of the play field - improving performance by decreasing the search space. with that you could randomly pick a quadrant to search in and mitigate bias toward the upper left corner?

I've recently been working on a game which creates random chambers within a room then adds random paths connecting between them. for this, i wrote some logic to select a tile in each chamber as its 'exit' point, then from each exit point i walk in randomized directions until i finally reach some randomly selected other chamber.

I used the word random an awful lot there. if it helps, here's the project:
Lootnscoot_2022-01-27T012711.623941.zip (64.2 KB)

anyway, i wonder if you could use a similar approach? to find your target tiles? i'm thinking that you would randomly walk through tiles within a selected quadrant, perhaps always picking the quadrant which has the most candidate targets within it?