Check every value in a list in TI-Basic - optimization

I'm writing a snake game in TI-Basic, and every time I move I need to see if the head of the snake has hit any point in the tail. The tail is stored as a circular list-based queue, and I can add the beginning and end in constant time.
The only hard part is that I have to do something similar to this on every iteration:
(S = Size of the list)
For(I,1,S)
If X=LX(I) and Y=LY(I)
Then
Disp "GAME OVER"
Return
End
End
It's a fairly short loop, but it takes forever even on a list of 10 items. I tried the sequence way:
If sum(seq(X=LX(I) and Y=LY(I),I,1,S))
...
The only other optimization I can think of is to not check values for N to N+2 (because the first part of your tail that's possible to hit is at N+3), but that just puts off the problem after 4 points, and having the game unplayable with 14 points is no better than being unplayable after 10 points.
Using assembly isn't an option because I don't have a link cable (or the desire to write assembly).

Never used TI-Basic...
but how about also storing a 2D array of the game board. Each element in that array indicates if the snake is present. When you move forward, set the value of the array at the head point, and clear the value at the old tail end point. Then to test for collision, you can just do one lookup into the 2D array.

The entire block:
For(I,1,S)
If X=LX(I) and Y=LY(I)
Then
Disp "GAME OVER"
Return
End
End
can be replaced with:
If sum(X=LX and Y=LY)
Then
Disp "Game Over"
Return
End
X=LX applies the test piecewise to every element of LX, and the same goes for Y=LY. The sum() checks if there is a 1 in the intersection of the two lists.

What I did, when I was programming Snake, was to check if the pixel in front of the snake was on. If it was, I would check if this pixel is the "food" pixel, otherwise, the game would stop.
Example, with I and J being head and tail positions, (F, G) being the direction of the snake, and (M, N) being the food.
if Pxl-Test(I+F, J+G) #pixel in front of snake
then
if I+F=M and J+G=N
stop
end
Much more memory-conservant than a 2D array.

Related

Trying to create simple jump game in scratch found a bug that I can't figure out

Here is the link to the game so you can see the code..
https://scratch.mit.edu/projects/668072441
I am utterly confused the current setup uses blocks but I have tried dozens of configurations and none of them are working. Goal of the game is when the dog hits the black line (both are sprites) it ups the score by one and resets the dog this part works but when the dog hits cat it's supposed to broadcast the score and end the game. Then I am going to have the cat sprite say the final score later after I figure this out. But it doesn't end the game and I can't figure out why
The reason it isn't working is because of the block that says:
[glide (pick random (1) to (1.4)) seconds to x: (-240) y: (-100)]
The scripts below it won't be executed until the block finishes. At that point, the dog will be past the cat, so contact won't be detected. The fix is pretty simple: simply take the two if loops and move them into a single, different forever loop, like this:
when flag clicked:
forever:
if (touching (Sprite1)) then:
End Game
if (touching (Line)) then:
Score

What's the fastest way to find if a point is in one of many rectangles?

So basically im doing this for my minecraft spigot plugin (java). I know there are already some land claim plugins but i would like to make my own.
For this claim plugin i'd like to know how to get if a point (minecraft block) is inside a region (rectangle). i know how to check if a point is inside a rectangle, the main problem is how to check as quickly as possible when there are like lets say 10.000 rectangles.
What would be the most efficient way to check 10.000 or even 100.000 without having to manually loop through all of them and check every single rectangle?
Is there a way to add a logical test when the rectangles get generated in a way that checks if they hold that point? In that case you could set a boolean to true if they contain that point when generated, and then when checking for that minecraft block the region (rectangle) replies with true or false.
This way you run the loops or checks when generating the rectangles, but when running the game the replies should happen very fast, just check if true or false for bool ContainsPoint.
If your rectangles are uniformly placed neighbors of each other in a big rectangle, then finding which rectangle contains point is easy:
width = (maxX-minX)/num_rectangles_x;
height = same but for y
idx = floor( (x - minX)/width );
idy = floor( (y - minY)/height );
id_square = idx + idy*num_rectangles_x;
If your rectangles are randomly placed, then you should use a spatial acceleration structure like octree. Then check if point is in root, then check if point is in one of its nodes, repeat until you find a leaf that includes the point. 10000 tests per 10milliseconds should be reachable on cpu. 1 million tests per 10ms should be ok for a gpu. But you may need to implement a sparse version of the octree and a space filling curve order for leaf nodes to have better caching, to reach those performance levels.

Player doesn't spawn correctly in procedural generated map

I've followed "Procedural Generation in Godot: Dungeon Generation" by KidsCanCode #https://www.youtube.com/watch?v=o3fwlk1NI-w and find myself unable to debug the current problem.
This specific commit has the code, but I'll try to explain in more detail bellow.
My main scene has a Camera2D node, a generic Node2D calles Rooms and a TileMap, everything is empty.
When the script starts, it runs a
func make_room(_pos, _size):
position = _pos
size = _size
var s = RectangleShape2D.new()
s.custom_solver_bias = 0.5
s.extents = size
$CollisionShape2D.shape = s
A few times and it fills $Rooms using .add_child(r) where r is a instance of the node that has the make_room() function. It will then iterate over $Rooms.get_children() a few times to create a AStar node to link all the rooms:
The magic comes when make_map() is called after afterwards, it fills the map with non-walkable blocks and then it carves the empty spaces, which works fine too:
There is a find_start_room() that is called to find the initial room, it also sets a global variable to the Main script start_room, which is used to write 'Start' on the map using draw_string(font, start_room.position - Vector2(125,0),"start",Color(3,4,8))
When I hit 'esc' it runs this simple code to instance the player:
player = Player.instance()
add_child(player)
player.position = start_room.position + Vector2(start_room.size.x/2, start_room.size.y/2)
play_mode = true
The problem comes when spawning the player. I tried doing some 'blind' fixing, such as adding or subtracting a Vector2(start_room.size.x/2, start_room.size.y/2) to player.position to see if I could make it fall within the room, to no avail.
Turning to the debugger didn't help, as the positions expressed by the variable inspectors don't seem to mean anything.
I tried implementing a simple 'mouse click print location':
print("Mouse Click/Unclick at: ", event.position)
print("Node thing",get_node("/root/Main/TileMap").world_to_map(event.position))
And also a 'start_room' print location:
print(get_node("/root/Main/TileMap").world_to_map(start_room.position))
And a when player moves print location, written directly into the Character script:
print(get_node("/root/Main/TileMap").world_to_map(self.position))
Getting results like the ones bellow:
Mouse Click/Unclick at: (518, 293)
Node thing(16, 9)
(-142, 0)
(-147, -3)
So, the player doesn't spawn on the same position as the start_room and the mouse position information is not the same as anything else.
Why is the player now spawning correctly? How can I debug this situation?
EDIT1: User Theraot mentioned about how the RigidBody2D is doing some weird collisions, and from what I understood, changing their collision behavior should fix the whole thing.
There's a section on the code that -after generating the random rooms- it removes some of the rooms like this:
for room in $Rooms.get_children():
if randf() < cull:
room.queue_free()
else:
room.mode = RigidBody2D.MODE_STATIC
room_positions.append(Vector3(room.position.x, room.position.y, 0))
From what I understand, if the room is randomly selected it will be deleted using queue_free() OR it will be appended to a room_positions for further processing. This means if I shift all the rooms to a different collision layer, the player/character instance would be alone with the TileMap on the same collision layer.
So I just added a simple room.collision_layer = 3 changing this section of the code to
for room in $Rooms.get_children():
if randf() < cull:
room.queue_free()
else:
room.mode = RigidBody2D.MODE_STATIC
room.collision_layer = 3
room_positions.append(Vector3(room.position.x, room.position.y, 0))
It doesn't seem to have changed anything, the player still spawns outside the room.
Do you see the rooms spread outwards?
You didn't write code to move the rooms. Sure, the code gives them a random position. But even if you set their position to Vector2.ZERO they move outwards, avoiding overlaps.
Why? Because these rooms are RigidBody2D, and they do push other physics objects. Such as other rooms or the player character.
That's the problem: These rooms are RigidBody2D, and you put your KinematicBody2D player character on top of one of them. The RigidBody2D pushes it out.
The tutorial you followed is exploiting this behavior of RigidBody2Ds to spread the rooms. However you don't need these RigidBody2D after you are done populating your TileMap.
Instead, you can store the start position in a variable for later placing the player character (you don't need offsets - by the way - the position of the room is the center of the room), and then remove the RigidBody2Ds. If you want to keep the code that writes the text, you would also have to modify it, so it does not fail when the room no longer exists.
Alternatively, you can edit their collision layer and mask so they don't collide with the player character (or anything for that matter, but why would you want these RigidBody2Ds that collide with nothing?).
Addendum post edit: Collision layers and mask don't work as you expect.
First of all, the collision layer and mask are flags. The values of the layers are powers of two (1, 2, 4, 8...). So, when you set it to 3, it is the layer 1 plus the layer 2. So it still collides with a collision mask of 1.
And second, even if you changed the collision layer of the rooms to 2 (so it does not match the collision mask of 1 that the player character has). The player character still has a layer 1 which match the collision mask of the rooms.
See also the proposal Make physics layers and masks logic simple and consistent.
Thus, you would need to change the layer and mask. Both. in such way that they don't collide. For example, you can set layer and mask to 0 (which disable all collisions). The algorithm that populates the TileMap does not use the layer and mask.

Teleport command in Minecraft data pack is ignoring parameters

I have this command that's supposed to teleport players when they reach a specific point on the x axis (regardless of y or z), for some reason it teleports everyone regardless of their position on the x axis
Here's the command - execute at #p[x=-5371] run tp #p[x=-5371] 5365 ~ ~
It's running on a custom data pack as a loop for each tick
It doesn't work like that. You cannot use x= alone in a selector, (x= and dx= must be used tgt; whereas dx is known as the volume selector). Also, you are scanning for the nearest player only and I would suggest for you to use #a which perform the task for all players.
To further elaborate, the volume selector takes resemblance to how you use the fill command, where you will enter the coordinates of the starting point and ending point respectively and the game will calculate the volume and the blocks affected depending on the points you entered, allowing you to fill an entire space with blocks of whatever kind. Similarly, working with the volume selector requires you to fill in 2 parameters, the starting coordinates (x=,y=,z=)
and the coordinate displacements (dx=,dy=,dz=).
Suppose starting coordinates (x, y, z)
Then ending coordinates = (x+dx, y+dy, z+dz)
This allows the game to draft a volume (block region) and evaluate the player position to see if it matches the volume.
An example:
To test for players at point (x=64, y=64, z=64) use the command
execute as #a[x=64,dx=0,y=64,dy=0,z=64,dz=0] run say DetectedPlayer
This draws a block region of 1x1 at point (64, 64, 64) and if the feet coordinate of a player lands in its proximity the player would say in chat "DetectedPlayer"
Albeit I really am not sure if you can have an infinitely extending volume of block region.
I interpret your question as how to set a border for players. In that case I recommend you to use /worldborder which is way more user friendly and conventional```
Here is the page you are looking for if you are hell bent on using the volume selector
https://minecraft.fandom.com/wiki/Target_selectors

How to place half-block slabs in Minecraft with MakeCode

This is a bit of a long-shot. I really don't know where to ask this question.
I've been trying out CodeConnection + MakeCode with Minecraft and I haven't been able to figure out if there is correct way to place half-slabs at 0.5 step y axes increments.
I tried using a line between 2 points, but it left gaps between each slab.
If I try moving up 0.5, then it rounds it up to 1, and again leaves gaps.
It appears that all of the builder functions seem operate at a resolution of 1 block. However in-game I can obviously place slabs in 0.5 block increments to make stairs etc.
Blocks only exist at integer coordinates. Half slabs that exist in the top half of their space are still at a full integer coordinate. They just have a BlockState value of bottom=top (or top_slot_bit=true on Bedrock, represented by the integer value 8 as a bitflag, eg: 0b1... where the . bits are the integer representation of what type of slab (wood, stone, quartz...)).
What you're looking for is this widget, under Blocks:
You can set the block and then an integer representation of the desired data value (see the wiki on data values) in the numerical slot. This widget can then be dragged into the (block) portion of any block widget:
You'll probably have to some variable fiddling to get the data value to swap back and forth as you need it to, but that should solve the hurdle you've been facing.