Lua Separation Steering algorithm groups overlapping rooms into one corner - lua

I'm trying to implement a dungeon generation algorithm (presented here and demo-ed here ) that involves generating a random number of cells that overlap each other. The cells then are pushed apart/separated and then connected. Now, the original poster/author described that he is using a Separation Steering Algorithm in order to uniformly distribute the cells over an area. I haven't had much experience with flocking algorithm and/or separation steering behavior, thus I turned to google for an explanation (and found this ). My implementation (based on the article last mentioned) is as follows:
function pdg:_computeSeparation(_agent)
local neighbours = 0
local rtWidth = #self._rooms
local v =
{
x = self._rooms[_agent].startX,
y = self._rooms[_agent].startY,
--velocity = 1,
}
for i = 1, rtWidth do
if _agent ~= i then
local distance = math.dist(self._rooms[_agent].startX,
self._rooms[_agent].startY,
self._rooms[i].startX,
self._rooms[i].startY)
if distance < 12 then
--print("Separating agent: ".._agent.." from agent: "..i.."")
v.x = (v.x + self._rooms[_agent].startX - self._rooms[i].startX) * distance
v.y = (v.y + self._rooms[_agent].startY - self._rooms[i].startY) * distance
neighbours = neighbours + 1
end
end
end
if neighbours == 0 then
return v
else
v.x = v.x / neighbours
v.y = v.y / neighbours
v.x = v.x * -1
v.y = v.y * -1
pdg:_normalize(v, 1)
return v
end
end
self._rooms is a table that contains the original X and Y position of the Room in the grid, along with it's width and height (endX, endY).
The problem is that, instead of tiddly arranging the cells on the grid, it takes the overlapping cells and moves them into an area that goes from 1,1 to distance+2, distance+2 (as seen in my video [youtube])
I'm trying to understand why this is happening.
In case it's needed, here I parse the grid table, separate and fill the cells after the separation:
function pdg:separate( )
if #self._rooms > 0 then
--print("NR ROOMS: "..#self._rooms.."")
-- reset the map to empty
for x = 1, self._pdgMapWidth do
for y = 1, self._pdgMapHeight do
self._pdgMap[x][y] = 4
end
end
-- now, we separate the rooms
local numRooms = #self._rooms
for i = 1, numRooms do
local v = pdg:_computeSeparation(i)
--we adjust the x and y positions of the items in the room table
self._rooms[i].startX = v.x
self._rooms[i].startY = v.y
--self._rooms[i].endX = v.x + self._rooms[i].endX
--self._rooms[i].endY = v.y + self._rooms[i].endY
end
-- we render them again
for i = 1, numRooms do
local px = math.abs( math.floor(self._rooms[i].startX) )
local py = math.abs( math.floor(self._rooms[i].startY) )
for k = self.rectMinWidth, self._rooms[i].endX do
for v = self.rectMinHeight, self._rooms[i].endY do
print("PX IS AT: "..px.." and k is: "..k.." and their sum is: "..px+k.."")
print("PY IS AT: "..py.." and v is: "..v.." and their sum is: "..py+v.."")
if k == self.rectMinWidth or
v == self.rectMinHeight or
k == self._rooms[i].endX or
v == self._rooms[i].endY then
self._pdgMap[px+k][py+v] = 1
else
self._pdgMap[px+k][py+v] = 2
end
end
end
end
end

I have implemented this generation algorithm as well, and I came across more or less the same issue. All of my rectangles ended up in the topleft corner.
My problem was that I was normalizing velocity vectors with zero length. If you normalize those, you divide by zero, resulting in NaN.
You can fix this by simply performing a check whether your velocity's length is zero before using it in any further calculations.
I hope this helps!

Uhm I know it's an old question, but I noticed something and maybe it can be useful to somebody, so...
I think there's a problem here:
v.x = (v.x + self._rooms[_agent].startX - self._rooms[i].startX) * distance
v.y = (v.y + self._rooms[_agent].startY - self._rooms[i].startY) * distance
Why do you multiply these equations by the distance?
"(self._rooms[_agent].startX - self._rooms[i].startX)" already contains the (squared) distance!
Plus, multiplying everything by "distance" you modify your previous results stored in v!
If at least you put the "v.x" outside the bracket, the result would just be higher, the normalize function will fix it. Although that's some useless calculation...
By the way I'm pretty sure the code should be like:
v.x = v.x + (self._rooms[_agent].startX - self._rooms[i].startX)
v.y = v.y + (self._rooms[_agent].startY - self._rooms[i].startY)
I'll make an example. Imagine you have your main agent in (0,0) and three neighbours in (0,-2), (-2,0) and (0,2). A separation steering behaviour would move the main agent toward the X axis, at a normalized direction of (1,0).
Let's focus only on the Y component of the result vector.
The math should be something like this:
--Iteration 1
v.y = 0 + ( 0 + 2 )
--Iteration 2
v.y = 2 + ( 0 - 0 )
--Iteration 3
v.y = 2 + ( 0 - 2 )
--Result
v.y = 0
Which is consistent with our theory.
This is what your code do:
(note that the distance is always 2)
--Iteration 1
v.y = ( 0 + 0 + 2 ) * 2
--Iteration 2
v.y = ( 4 + 0 - 0 ) * 2
--Iteration 3
v.y = ( 8 + 0 - 2 ) * 2
--Result
v.y = 12
And if I got the separation steering behaviour right this can't be correct.

Related

How can I set transition.to to continue in the same direction with the same velocity once it has arrived at the set point?

Hi I am new to coding and I am using Lua and solar2d, trying to transition object1 via another object2's co-ordinates and for object1 to continue along the same path with the same velocity if it doesn't hit object2.
I can easily transition to the obeject but I don't know how to then go beyond that.
transition.to( object1, { x=object2.x, y=object2.y, time=3000, })
I feel I will have to add an oncomplete but not sure what.
any help would be greatly appreciated.
You have to calculate the equation of the line (y = m * x + b) that you are traveling.
Formulas:
m = (y2 - y1) / (x2 - x1)
b = y1 - m * x1
So in your case:
m = (object2.y - object1.y) / (object2.x - object1.x)
b = object1.y - m * object1.x
Now you have the equation of the path (line) to keep if object1 doesn't hit object2.
When the transition ends, you want to check if the object2 is still there (object1 hit it) or not (object1 keeps moving), so you need to include an onComplete listener to check for that.
As for the speed, you have to decide if you want a constant speed and then you have to calculate the time for each transition or if you are using always 3 seconds no matter if the object2 is close or far away from the object1. I guess you probably want the first option, so it doesn't go pretty slow if objects are close and too fast if the object are far away. In that case you have to set a constant speed s, that you want.
Formulas:
Speed = Distance / Time
Time = Distance / Speed
Distance between 2 points:
d = squareRoot( (x2 - x1)^2 + (y2 - y1)^2 )
In summary, it would be something like that:
s = 10 --Constant speed
m = (object2.y - object1.y) / (object2.x - object1.x)
b = object1.y - m * object1.x
direction = 1 --assume it's traveling to the right
if(object2.x < object1.x)then
direction = -1 --it's traveling to the left
end
local function checkCollision( obj )
if(obj.x == object2.x and obj.y == object2.y)then
-- Object1 hit Object2
else
-- Object2 is not here anymore, continue until it goes offscreen
-- following the line equation
x3 = -10 -- if it's traveling to the left
if(direction == 1)then
--it's traveling to the right
x3 = display.contentWidth + 10
end
y3 = m * x3 + b
d2 = math.sqrt( (x3 - obj.x)^2 + (y3 - obj.y)^2 )
t2 = d2 / s
transition.to( obj, {x=x3, y=y3, time=t2} )
end
end
d1 = math.sqrt( (object2.x - object1.x)^2 + (object2.y - object1.y)^2 )
t1 = d1 / s
transition.to( object1, { x=object2.x, y=object2.y, time=t1, onComplete=checkCollision} )
You should try different values for the speed s until you get the desired movement.

Error Trying to Draw Platforms in Lua/Love2D

New to Lua/Game dev in general here. I'm trying to write a code to draw the background of my game. I have four different spritesheets for things related to setting:
Background - which contains the drawn "background image" of the game
Platforms - which contain the platform sprites of the game
objectsBig - which contain the relatively bigger drawn objects in the background (example rocks, seaweed, etc)
objectsSmall - which contain the relatively smaller drawn objects (smaller rocks, plants, etc)
I made a function, called generateQuads() which is in my Util.lua file, which, given a spritesheet, splices it up into the different sprites in the spritesheet and returns those sprites. It is as follows:
-- Function to generate quads/cut the spritesheet
function generateQuads(atlas, tileWidth, tileHeight)
local sheetWidth = atlas:getWidth() / tileWidth
local sheetHeight = atlas:getHeight() / tileHeight
local sheetCounter = 1
local quads = {}
-- for every line of the sprite sheet
for y = 0, sheetHeight - 1 do
-- for every piece in the width
for x = 0, sheetWidth - 1 do
-- quads generated will be the ones specified by tileWidth and tileHeight
quads[sheetCounter] = love.graphics.newQuad(x * tileWidth, y * tileHeight, tileWidth,
tileHeight, atlas:getDimensions())
sheetCounter = sheetCounter + 1
end
end
return quads
end
I also have a class called Map, which is meant to contain information for the Map. The following are the functions in it:
FUNCTION TO SET UP THE CLASS
function Map:init()
-- Load the background sprite into variable
-- Each is 500x500, total is 1500x500
self.background = love.graphics.newImage('Graphics/platform/background/full.png')
-- Load all the ground platforms into variable
-- Each is 60x30, total is 240x30
self.platforms = love.graphics.newImage('Graphics/platform/ground/all.png')
-- Load all the bigger misc objects into variable
-- Each is 15x20, total is 15x120
self.objectsBig = love.graphics.newImage('Graphics/platform/objects/15x20/all.png')
-- Load all the smaller misc objects into variable
-- Each is 15x15, total is 60x15
self.objectsSmall = love.graphics.newImage('Graphics/platform/objects/15x15/all.png')
-- Setting the size for each of the variables IN PIXELS
self.backgroundWidth = 500
self.backgroundHeight = 1500
self.platformWidth = 60
self.platformHeight = 30
self.objectsBigWidth = 15
self.objectsBigHeight = 20
self.objectsSmallWidth = 15
self.objectsSmallHeight = 15
-- Setting the map size IN TILES
-- EXPERIMENTAL, CHECK AGAIN
self.mapWidth = 432
self.mapHeight = 243
-- Setting the width of the map IN PIXELS (in relation to platforms)
self.mapWidthPixels = self.backgroundWidth
self.mapHeightPixels = self.backgroundHeight
-- Storing our map features
self.tiles = {}
-- Storing the camera points, starting from the bottom
-- The 243 is the VIRTUAL_HEIGHT, which we needed to subtract to account for the
-- offset of the screen
self.camX = 0
self.camY = self.backgroundHeight - 243
-- Cutting each of the spritesheets
self.backgroundSprite = generateQuads(self.background, self.backgroundWidth, self.backgroundHeight)
self.platformSprite = generateQuads(self.platforms, self.platformWidth, self.platformHeight)
self.objectsBigSprite = generateQuads(self.objectsBig, self.objectsBigWidth, self.objectsBigHeight)
self.objectsSmallSprite = generateQuads(self.objectsSmall, self.objectsSmallWidth, self.objectsSmallHeight)
-- 'Setting' the tiles for the map to be background
for y = 1, self.mapHeight do
for x = 1, self.mapWidth do
self:setTile(x, y, BACKGROUND)
end
end
-- 'Setting' the platform tiles for where we start
for x = 1, self.mapWidth do
self:setTile(x, self.mapHeight - 303, GROUND_MIDDLE)
end
end
FUNCTION TO 'SET' WHERE IN THE X, Y COORDINATE THE TILES SHOULD BE
function Map:setTile(x, y, tile)
-- The table 'tiles' is going to store what tile should be in what x and y position
-- subtracting y by 1 so that it's 0 indexed, not 1 indexed
self.tiles[(y - 1) * self.mapWidth + x] = tile
end
FUNCTION TO RETURN WHAT TILE IS IN THE X, Y COORDINATE
function Map:getTile(x, y, tile)
-- This function is going to tell us what tile is set at this x and y position
return self.tiles[(y - 1) * self.mapWidth + x]
end
FUNCTION TO UPDATE
function Map:update(dt)
-- Moving the camera depending on the key being pressed
if love.keyboard.isDown('w') then
-- up movement, clamped between 0 and the other
self.camY = math.max(0, math.floor(self.camY - SCROLL_SPEED * dt))
elseif love.keyboard.isDown('a') then
-- left movement
self.camX = math.max(0, math.floor(self.camX - SCROLL_SPEED * dt))
elseif love.keyboard.isDown('s') then
-- down movement, subtracting VIRTUAL_HEIGHT to account for the screen offset
self.camY = math.min(self.backgroundHeight - VIRTUAL_HEIGHT, math.floor(self.camY + SCROLL_SPEED * dt))
elseif love.keyboard.isDown('d') then
-- right movement
self.camX = math.min(self.backgroundWidth - VIRTUAL_WIDTH, math.floor(self.camX + SCROLL_SPEED * dt))
end
end
FUNCTION TO RENDER
function Map:render()
for y = 1, self.mapHeight do
for x = 1, self.mapWidth do
-- Drawing the background first
love.graphics.draw(self.background, self.backgroundSprite[self:getTile(x, y)],
(x - 1) * self.backgroundWidth, (y - 1) * self.backgroundHeight)
love.graphics.draw(self.platform, self.platformSprite[self:getTile(x, y)],
(x - 1) * self.platformWidth, (y - 1) * self.platformHeight)
end
end
end
What I'm trying to do is splice up each spritesheet, and for set the background and bottom tiles so that I can see a bottom floor. I'm trying to do this through giving numbers to the components in my spritesheet, so that, for example, in a 1500x500 spritesheet for the background, the entire thing is considered 'one' sprite, and treated as such. Its number, 1, is given in a variable. Another example is a 240x30 platform spritesheet, where each 60x30 is considered 'one' sprite, and given a corresponding number, like so:
-- Know where the platforms are in the spritesheet
GROUND_LEFT = 1
GROUND_RIGHT = 2
GROUND_SMALL = 3
GROUND_MIDDLE = 4
-- Know where the backgrounds are in the spritesheet
BACKGROUND = 1
[...]
I want to then store each in the self.tiles list, so that I can access them at any time from the table (Do note that most of this is taken from the CS50 implementation of mario, and so I understand if I've gotten any concepts wrong). When I run this code, though, I get the following error:
Error
Error
Map.lua:135: bad argument #1 to 'draw' (Texture expected, got nil)
Traceback
[C]: in function 'draw'
Map.lua:135: in function 'render'
main.lua:48: in function 'draw'
[C]: in function 'xpcall'
Essentially, I just want to know a way in which I can set the bottom platform tiles, and then be able to iterate over the map and 'set' where the other object sprites should be. The background alone works fine, but once I added the:
-- 'Setting' the platform tiles for where we start
for x = 1, self.mapWidth do
self:setTile(x, self.mapHeight - 303, GROUND_MIDDLE)
end
and
love.graphics.draw(self.platform, self.platformSprite[self:getTile(x, y)],
(x - 1) * self.platformWidth, (y - 1) * self.platformHeight)
lines, the program wouldn't run.
Any help is appreciated!
EDIT: The line I'm getting an error on is the self.platform one.
i had the same problem before, and it turned out to be a typo when typing the variable. looking at your code, i saw that there was no self.platform, instead, there was self.platforms.
try:
love.graphics.draw(self.platforms, self.platformSprite[self:getTile(x, y)],
(x - 1) * self.platformWidth, (y - 1) * self.platformHeight)

How to find and update levels accordingly based on points?

I am creating a rails application which is like a game. So it has points and levels. For example: to become level one the user has to get atleast 100 points and again for level two the user has to reach level 2 the user has to collect 200 points. The level difference changes after every 10 levels i.e., The difference between each level changes after 10 levels always. By that I mean the difference in points between level one and two is 100 and the difference in points in level 11 and 12 is 150 and so on. There is no upper bound for levels.
Now my question is let's say a user's total points is 3150 and just got updated to 3155. What's the optimal solution to find the current level and update it if needed?
I can get a solution using while loops and again looping inside it which will give a result in O(n^2). I need something better.
I think this code works but I'm not sure if this is the best way to go about it
def get_level(points)
diff = 100
sum = 0
level = -1
current_level = 0
while level.negative?
10.times do |i|
current_level += 1
sum += diff
if points > sum
next
elsif points <= sum
level = current_level
break
end
end
diff += 50
end
puts level
end
I wrote a get_points function (it should not be difficult). Then based on it get_level function in which it was necessary to solve the quadratic equation to find high value, and then calc low.
If you have any questions, let me know.
Check output here.
#!/usr/bin/env python3
import math
def get_points(level):
high = (level + 1) // 10
low = (level + 1) % 10
high_point = 250 * high * high + 750 * high # (3 + high) * high // 2 * 500
low_point = (100 + 50 * high) * low
return low_point + high_point
def get_level(points):
# quadratic equation
a = 250
b = 750
c = -points
d = b * b - 4 * a * c
x = (-b + math.sqrt(d)) / (2 * a)
high = int(x)
remainder = points - (250 * high * high + 750 * high)
low = remainder // (100 + 50 * high)
level = high * 10 + low
return level
def main():
for l in range(0, 40):
print(f'{l:3d} {get_points(l - 1):5d}..{get_points(l) - 1}')
for level, (l, r) in (
(1, (100, 199)),
(2, (200, 299)),
(9, (900, 999)),
(10, (1000, 1149)),
(11, (1150, 1299)),
(19, (2350, 2499)),
(20, (2500, 2699)),
):
for p in range(l, r + 1): # for in [l, r]
assert get_level(p) == level, f'{p} {l}'
if __name__ == '__main__':
main()
Why did you set the value of a=250 and b = 750? Can you explain that to me please?
Let's write out every 10 level and the difference between points:
lvl - pnt (+delta)
10 - 1000 (+1000 = +100 * 10)
20 - 2500 (+1500 = +150 * 10)
30 - 4500 (+2000 = +200 * 10)
40 - 7000 (+2500 = +250 * 10)
Divide by 500 (10 levels * 50 difference changes) and received an arithmetic progression starting at 2:
10 - 2 (+2)
20 - 5 (+3)
30 - 9 (+4)
40 - 14 (+5)
Use arithmetic progression get points formula for level = k * 10 equal to:
sum(x for x in 2..k+1) * 500 =
(2 + k + 1) * k / 2 * 500 =
(3 + k) * k * 250 =
250 * k * k + 750 * k
Now we have points and want to find the maximum high such that point >= 250 * high^2 + 750 * high, i. e. 250 * high^2 + 750 * high - points <= 0. Value a = 250 is positive and branches of the parabola are directed up. Now we find the solution of quadratic equation 250 * high^2 + 750 * high - points = 0 and discard the real part (is high = int(x) in python script).

What does the distance in machine learning signify?

In Neural networks, we regularly use the equation:
w1*x1 + w2*x2 + w3*x3 ...
We can interpret this as equation of a line with each x as a dimension. To make things more clearer, lets take an example of a simple perceptron network.
Imagine a single layer perceptron with 2 ip's/features (x1 & x2) and one output (y). (Sorry stackoverflow didn't allow me to post an additional image)
Let
R = w1*x1 + w2 * x2
y = 0 if R >= threshold
y = 1 if R < threshold
Scenario 1:
Threshold = 0
w1 = 2, w2 = -1
The line separating class 0 and 1 has the equation 2*x1 - x2 = 0
Suppose we get a test sample
P = (1,1)
R = 2*1 - 1 = 1 > 0
Sample P belongs to class 1
My questions is what is this R?
From the figure, its horizontal distance from the line.
Scenario 2:
Threshold = 0
w1 = 2, w2 = 1
The line separating class 0 and 1 has the equation 2*x1 + x2 = 0
P = (1,1)
R = 2*1 + 1 = 3 > 0
Sample P belongs to class 1
From the figure, its vertical distance from the line.
R is supposed to mean some form of distance from the classifying line. More the distance, more farther away from the line and we are more confident about the classification.
Just want to know what kind of distance from the line is R?

Triangular Vertices - Lua calculation?

I am current determined to complete a problem in Lua and have no idea where to begin. I was thinking about beginning with a modulus operator. I am searching for advice from experienced Lua programmers on how to program this and mainly how I can calculate the theoretically mathematical side of the problem.
Source of the question... (http://www.eecs.qmul.ac.uk/~pbo/ACM/archive/00209.html)
Gratitude will be shown to anyone who answers correctly.
Thank-you.
function get_left(max)
i = 0
j = 1
ls = {}
repeat
i = i + 1
j = j + i - 1
ls[i] = j
print (ls[i], " ls")
until j >= max
return ls
end
a = get_left(27)
(need to format that as code -.-)
if the point1 is between a[i] and a[i+1]
if then point2 is still between a[i] and a[i+1] it is on the same line
else if point2 is between a[i + n] and a[i + n + 1]
then if point2 is at a[i + n] + (a[i] - point1) + n + 1 its in a strait line right above it
else if point2 is at a[i + n] + (a[i] - point1) + n - 1 its in a strait line left above it
if for all points n or n*(-1) is equal the distance between the points is equal.
That is if I didn't make any logical errors and you probably have to check more for it to work properly.
This is more a mathematical question than lua I recommend adding a math tag to it.

Resources