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.
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 ) 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
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.
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.
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.
[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.
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?