How would I compress a 3-dimensional array in Lua? - lua

So, I have what is basically a three-dimensional array in Lua, really a voxel system. It looks like this:
local VoxelTable = {
[1] = { --X
[5] = { --Y
[2] = { --Z
["Type"] = "Solid",
["Rotation"] = "InverseX",
["Material"] = "Grass",
["Size"] = Vector3.new(1,1,1)
--A 1x1x1 Solid grass block with rotation "InverseX"
}
}
}
}
The Voxels are generated, and because of that I can't compress them manually. But without compression rendering lags the game down a lot.
What I want to do is if there are three grass blocks right above/below eachother with the same rotation value, I combine them into one voxel, with a size of Vector3.new(1,3,1), with the position of the middle voxel.
So
[1] = { --X
[5] = { --Y
[2] = { --Z
["Type"] = "Solid",
["Rotation"] = "InverseX",
["Material"] = "Grass",
["Size"] = Vector3.new(1,1,1)
}
},
[6] = { --Y
[2] = { --Z
["Type"] = "Solid",
["Rotation"] = "InverseX",
["Material"] = "Grass",
["Size"] = Vector3.new(1,1,1)
}
},
[7] = { --Y
[2] = { --Z
["Type"] = "Solid",
["Rotation"] = "InverseX",
["Material"] = "Grass",
["Size"] = Vector3.new(1,1,1)
}
}
}
becomes
[1] = { --X
[6] = { --Y
[2] = { --Z
["Type"] = "Solid",
["Rotation"] = "InverseX",
["Material"] = "Grass",
["Size"] = Vector3.new(1,3,1)
}
}
}

Here’s a somewhat simplified example. I’ve created a 10 x 10 x 10 cube of voxels, giving each voxel a vec3 size attribute (as you have it) and a random letter attribute (a, b, or c). I then iterate over the voxels, looking up and down. If the voxel I’m on has the same letter attribute as the voxel above and below, then I set the above and below voxels to nil, and increase the size attribute of the middle voxel. I am sure this could all be optimized, and I’m sure more sophisticated logic could look for other voxel relationships besides this hard-coded stack-of-three identical voxels. But this is a start:
local world = {}
local letters = {"a", "b", "c"}
function setup()
-- Create simplified test data
for x = 1, 10 do
world[x] = {}
for y = 1, 10 do
world[x][y] = {}
for z = 1, 10 do
world[x][y][z] = {}
local randomIndex = math.random(1, 3)
world[x][y][z].letter = letters[randomIndex]
world[x][y][z].size = vec3(1, 1, 1)
end
end
end
-- Combine common stacks of three
for x = 1, 10 do
for y = 2, 9 do -- Ensure there is at least a level below (y == 1) or above (y == 10)
for z = 1, 10 do
combineStacks(x, y, z)
end
end
end
end
function combineStacks(x, y, z)
local low = world[x][y - 1][z]
local mid = world[x][y][z]
local high = world[x][y + 1][z]
if low ~= nil and mid ~= nil and high ~= nil then
if low.letter == mid.letter and mid.letter == high.letter then
world[x][y - 1][z] = nil -- low
world[x][y + 1][z] = nil -- high
mid.size = vec3(1, 3, 1)
print("Stack of three identical voxels found!")
end
end
end
The above was written and tested (and visualized, shown below) in Codea. The vec3 construct is native to that environment and not to Lua in general, so keep that in mind.
Here’s a 2D visualization of the results, with each square showing a slice of the voxel cube. If you see a yellow point (representing a stack of three!), look at the square slice to the left and right, and at the same location you will see the voxels there are nil:

Related

What is the problem with my Gradient Descent Algorithm or how its applied?

I've been trying figure out what I have done wrong for many hours, but just can't figure out. I've even looked at other basic Neural Network libraries to make sure that my gradient descent algorithms were correct, but it still isn't working.
I'm trying to teach it XOR but it outputs -
input (0 0) | 0.011441891321516094
input (1 0) | 0.6558508610135193
input (0 1) | 0.6558003273099053
input (1 1) | 0.6563021185296245
after 1000 trainings, so clearly there's something wrong.
The code is written in lua and I created the Neural Network from raw data so you can easily understand how the data is formatted.
- Training code -
math.randomseed(os.time())
local nn = require("NeuralNetwork")
local network = nn.newFromRawData({
["activationFunction"] = "sigmoid",
["learningRate"] = 0.3,
["net"] = {
[1] = {
[1] = {
["value"] = 0
},
[2] = {
["value"] = 0
}
},
[2] = {
[1] = {
["bias"] = 1,
["netInput"] = 0,
["value"] = 0,
["weights"] = {
[1] = 1,
[2] = 1
}
},
[2] = {
["bias"] = 1,
["netInput"] = 0,
["value"] = 0,
["weights"] = {
[1] = 1,
[2] = 1
}
},
[3] = {
["bias"] = 1,
["netInput"] = 0,
["value"] = 0,
["weights"] = {
[1] = 1,
[2] = 1
}
},
[4] = {
["bias"] = 1,
["netInput"] = 0,
["value"] = 0,
["weights"] = {
[1] = 1,
[2] = 1
}
}
},
[3] = {
[1] = {
["bias"] = 1,
["netInput"] = 0,
["value"] = 0,
["weights"] = {
[1] = 1,
[2] = 1,
[3] = 1,
[4] = 1
}
}
}
}
})
attempts = 1000
for i = 1,attempts do
network:backPropagate({0,0},{0})
network:backPropagate({1,0},{1})
network:backPropagate({0,1},{1})
network:backPropagate({1,1},{0})
end
print("Results:")
print("input (0 0) | "..network:feedForward({0,0})[1])
print("input (1 0) | "..network:feedForward({1,0})[1])
print("input (0 1) | "..network:feedForward({0,1})[1])
print("input (1 1) | "..network:feedForward({1,1})[1])
- Library -
local nn = {}
nn.__index = nn
nn.ActivationFunctions = {
sigmoid = function(x) return 1/(1+math.exp(-x/1)) end,
ReLu = function(x) return math.max(0, x) end,
}
nn.Derivatives = {
sigmoid = function(x) return x * (1 - x) end,
ReLu = function(x) return x > 0 and 1 or 0 end,
}
nn.CostFunctions = {
MSE = function(outputs, expected)
local sum = 0
for i = 1, #outputs do
sum += 1/2*(expected[i] - outputs[i])^2
end
return sum/#outputs
end,
}
function nn.new(inputs, outputs, hiddenLayers, neurons, learningRate, activationFunction)
local self = setmetatable({}, nn)
self.learningRate = learningRate or .3
self.activationFunction = activationFunction or "ReLu"
self.net = {}
local net = self.net
local layers = hiddenLayers+2
for i = 1, layers do
net[i] = {}
end
for i = 1, inputs do
net[1][i] = {value = 0}
end
for i = 2, layers-1 do
for x = 1, neurons do
net[i][x] = {netInput = 0, value = 0, bias = math.random()*2-1, weights = {}}
for z = 1, #net[i-1] do
net[i][x].weights[z] = math.random()*2-1
end
end
end
for i = 1, outputs do
net[layers][i] = {netInput = 0, value = 0, bias = math.random()*2-1, weights = {}}
for z = 1, #net[layers-1] do
net[layers][i].weights[z] = math.random()*2-1
end
end
return self
end
function nn.newFromRawData(data)
return setmetatable(data, nn)
end
function nn:feedForward(inputs)
local net = self.net
local activation = self.activationFunction
local layers = #net
local inputLayer = net[1]
local outputLayer = net[layers]
for i = 1, #inputLayer do
inputLayer[i].value = inputs[i]
end
for i = 2, layers do
local layer = net[i]
for x = 1, #layer do
local sum = layer[x].bias
for z = 1, #net[i-1] do
sum += net[i-1][z].value * layer[x].weights[z]
end
layer[x].netInput = sum
layer[x].value = nn.ActivationFunctions[activation](sum)
end
end
local outputs = {}
for i = 1, #outputLayer do
table.insert(outputs, outputLayer[i].value)
end
return outputs
end
function nn:backPropagate(inputs, expected)
local outputs = self:feedForward(inputs)
local net = self.net
local activation = self.activationFunction
local layers = #net
local lr = self.learningRate
local inputLayer = net[1]
local outputLayer = net[layers]
for i = 1, #outputLayer do
local delta = -(expected[i] - outputs[i]) * nn.Derivatives[activation](net[layers][i].value)
outputLayer[i].delta = delta
end
for i = layers-1, 2, -1 do
local layer = net[i]
local nextLayer = net[i+1]
for x = 1, #layer do
local delta = 0
for z = 1, #nextLayer do
delta += nextLayer[z].delta * nextLayer[z].weights[x]
end
layer[x].delta = delta * nn.Derivatives[activation](layer[x].value)
end
end
for i = 2, layers do
local lastLayer = net[i-1]
for x = 1, #net[i] do
net[i][x].bias -= lr * net[i][x].delta
for z = 1, #lastLayer do
net[i][x].weights[z] -= lr * net[i][x].delta * lastLayer[z].value
end
end
end
end
return nn
Any help would be highly appreciated, thanks!
All initial weights must be DIFFERENT numbers, otherwise backpropagation will not work. For example, you can replace 1 with math.random()
Increase number of attempts to 10000
With these modifications, your code works fine:
Results:
input (0 0) | 0.028138230938126
input (1 0) | 0.97809448578087
input (0 1) | 0.97785000216126
input (1 1) | 0.023128477689456

Why does my LOVE2D program keep resetting the score?

So, I'm currently in a problem with updating my Super Mario Bros. Yeah, sure, it works fine, but the problem is this:
Whenever I touch the flagpole at the end, it literally resets my score.
It makes no sense with this program since I made a params for PlayState:enter, and I don't exactly know why my score goes back to 0.
This shows up in PlayState.lua:
function PlayState:enter(params)
self.score = params.score
self.lastLevelWidth = params.lastLevelWidth
if self.lastLevelWidth == 0 then
self.lastLevelWidth = 100
else
self.lastLevelWidth = self.lastLevelWidth + 50
end
self.camX = 0
self.camY = 0
self.level = LevelMaker.generate(100, 10)
self.tileMap = self.level.tileMap
self.background = math.random(3)
self.backgroundX = 0
self.gravityOn = true
self.gravityAmount = 6
self.player = Player({
x = 0, y = 0,
width = 16, height = 20,
texture = 'green-alien',
stateMachine = StateMachine {
['idle'] = function() return PlayerIdleState(self.player) end,
['walking'] = function() return PlayerWalkingState(self.player) end,
['jump'] = function() return PlayerJumpState(self.player, self.gravityAmount) end,
['falling'] = function() return PlayerFallingState(self.player, self.gravityAmount) end
},
map = self.tileMap,
level = self.level,
})
self:spawnEnemies()
self.player:changeState('falling')
end
I used the params to get to a new score, 0, but I don't want it to let it stay like that. This is what I have done in LevelMaker.lua:
keyCollected = false
function LevelMaker.generate(width, height)
local tiles = {}
local entities = {}
local objects = {}
local tileID = TILE_ID_GROUND
-- whether we should draw our tiles with toppers
local topper = true
local tileset = math.random(20)
local topperset = math.random(20)
-- insert blank tables into tiles for later access
for x = 1, height do
table.insert(tiles, {})
end
-- make positions for the lock box and key in the level
local lockBoxPosition = math.random(1, width)
local keyPosition = math.random(1, width)
local keySkin = math.random(1, 4)
-- column by column generation instead of row; sometimes better for platformers
for x = 1, width do
local tileID = TILE_ID_EMPTY
-- lay out the empty space
for y = 1, 6 do
table.insert(tiles[y],
Tile(x, y, tileID, nil, tileset, topperset))
end
-- chance to just be emptiness
if math.random(7) == 1 and x ~= 1 and lockBoxPosition ~= x and keyPosition ~= x then
for y = 7, height do
table.insert(tiles[y],
Tile(x, y, tileID, nil, tileset, topperset))
end
else
tileID = TILE_ID_GROUND
local blockHeight = 4
for y = 7, height do
table.insert(tiles[y],
Tile(x, y, tileID, y == 7 and topper or nil, tileset, topperset))
end
-- chance to generate a pillar
if math.random(8) == 1 then
blockHeight = 2
-- chance to generate bush on pillar
if math.random(8) == 1 then
table.insert(objects,
GameObject {
texture = 'bushes',
x = (x - 1) * TILE_SIZE,
y = (4 - 1) * TILE_SIZE,
width = 16,
height = 16,
-- select random frame from bush_ids whitelist, then random row for variance
frame = BUSH_IDS[math.random(#BUSH_IDS)] + (math.random(4) - 1) * 7
}
)
end
-- pillar tiles
tiles[5][x] = Tile(x, 5, tileID, topper, tileset, topperset)
tiles[6][x] = Tile(x, 6, tileID, nil, tileset, topperset)
tiles[7][x].topper = nil
-- chance to generate bushes
elseif math.random(8) == 1 and keyPosition ~= 1 then
table.insert(objects,
GameObject {
texture = 'bushes',
x = (x - 1) * TILE_SIZE,
y = (6 - 1) * TILE_SIZE,
width = 16,
height = 16,
frame = BUSH_IDS[math.random(#BUSH_IDS)] + (math.random(4) - 1) * 7,
collidable = false
}
)
end
if x == keyPosition then
table.insert(objects,
GameObject {
texture = 'keys-and-locks',
x = (x - 1) * TILE_SIZE,
y = (blockHeight + 1) * TILE_SIZE,
width = 16,
height = 16,
frame = keySkin,
collidable = true,
consumable = true,
solid = false,
onConsume = function(player, object)
gSounds['pickup']:play()
player.score = player.score + 500
keyCollected = true
end
}
)
end
if x == lockBoxPosition then
table.insert(objects,
GameObject {
texture = 'keys-and-locks',
x = (x - 1) * TILE_SIZE,
y = (blockHeight - 1) * TILE_SIZE,
width = 16,
height = 16,
frame = keySkin + 4,
collidable = true,
consumable = true,
hit = false,
solid = true,
lockedBox = false,
objectRemove = false,
onCollide = function(obj)
if not obj.hit then
if keyCollected then
gSounds['pickup']:play()
obj.hit = true
obj.objectRemove = true
obj.consumable = true
local pole = GameObject {
texture = 'poles',
x = (width * TILE_SIZE) - 32,
y = (blockHeight - 1) * TILE_SIZE,
width = 16,
height = 48,
frame = math.random(#POLES),
collidable = true,
consumable = true,
solid = false,
onConsume = function(player, object)
gSounds['pickup']:play()
player.score = player.score + 1000
gStateMachine:change('play', {
score = player.score,
lastLevelWidth = width
})
end
}
local flag = GameObject {
texture = 'flags',
x = (width * TILE_SIZE) - 32 + 6,
y = blockHeight * TILE_SIZE,
width = 16,
height = 10,
frame = 1,
collidable = true,
consumable = true,
solid = false,
onConsume = function(player, object)
gSounds['pickup']:play()
player.score = player.score + 1000
gStateMachine:change('play', {
score = player.score,
lastLevelWidth = width
})
end
}
Timer.tween(2.0 , {
[flag] = {y = ((blockHeight - 1) * TILE_SIZE) + 4}
})
gSounds['powerup-reveal']:play()
table.insert(objects, pole)
table.insert(objects, flag)
end
keyCollected = false
end
gSounds['empty-block']:play()
end
}
)
-- chance to spawn a block
elseif math.random(10) == 1 then
table.insert(objects,
-- jump block
GameObject {
texture = 'jump-blocks',
x = (x - 1) * TILE_SIZE,
y = (blockHeight - 1) * TILE_SIZE,
width = 16,
height = 16,
-- make it a random variant
frame = math.random(#JUMP_BLOCKS),
collidable = true,
hit = false,
solid = true,
-- collision function takes itself
onCollide = function(obj)
-- spawn a gem if we haven't already hit the block
if not obj.hit then
-- chance to spawn gem, not guaranteed
if math.random(5) == 1 then
-- maintain reference so we can set it to nil
local gem = GameObject {
texture = 'gems',
x = (x - 1) * TILE_SIZE,
y = (blockHeight - 1) * TILE_SIZE - 4,
width = 16,
height = 16,
frame = math.random(#GEMS),
collidable = true,
consumable = true,
solid = false,
-- gem has its own function to add to the player's score
onConsume = function(player, object)
gSounds['pickup']:play()
player.score = player.score + 100
end
}
-- make the gem move up from the block and play a sound
Timer.tween(0.1, {
[gem] = {y = (blockHeight - 2) * TILE_SIZE}
})
gSounds['powerup-reveal']:play()
table.insert(objects, gem)
end
obj.hit = true
end
gSounds['empty-block']:play()
end
}
)
end
end
end
local map = TileMap(width, height)
map.tiles = tiles
return GameLevel(entities, objects, map)
end
What's supposed to happen is that when I collected the key and unlocked the lock block, I get a flag, and when I collide with the flag at the end of the map, I get to a new level, with the same score, but not 0. Sadly, everytime I get to a new level, it turns into 0.
I don't know if the problem is in StartState.lua:
function StartState:update(dt)
if love.keyboard.wasPressed('enter') or love.keyboard.wasPressed('return') then
gStateMachine:change('play', {
score = 0,
lastLevelWidth = 0
})
end
end
Any ideas why it's like this?
If you collide with the pole and then change to StartState, then that is the problem. I suggest feeding the score through start with: function StartState:enter(params) self.score = params.score end
Then just pass self.score through to the playstate

Xonix Game Optimization Problems

I'm doing a Xonix game on corona sdk. As a playing field, I decided to use a grid of squares. Width 44, Height 71. As a result, I received a row consisting of 3124(44*71) squares along which the player moves. But I have problems with optimization. The engine can not cope with such a number of squares on the screen at the same time. Because of this, FPS on a smartphone is not more than 12. Help me find the best solution. All that concerns polygons and meshes, I am not strong in geometry ((((
Reducing the number of squares is a bad idea. Then the appearance is lost, and the field is captured too large pieces.
Thank you in advance!
-- Creating a playing field
M.create_pole = function()
-- init
local image = display.newImage
local rect = display.newRect
local rrect = display.newRoundedRect
local floor = math.floor
M.GR_ZONE = display.newGroup()
M.POLE = {}
-- bg
M.POLE.bg = rrect(0,0,150,150,5)
M.POLE.bg.width = M.width_pole
M.POLE.bg.height = M.height_pole
M.POLE.bg.x = _W/2
M.POLE.bg.y = _H/2
M.POLE.bg.xScale = _H*.00395
M.POLE.bg.yScale = _H*.00395
M.POLE.bg:setFillColor(API.hexToCmyk(M.color.land))
M.GR:insert(M.POLE.bg)
M.POLE.img = image(IMAGES..'picture/test.jpg')
M.POLE.img.width = M.width_pole-M.size_xonix
M.POLE.img.height = M.height_pole-M.size_xonix
M.POLE.img.x = _W/2
M.POLE.img.y = _H/2
M.POLE.img.xScale = _H*.00395
M.POLE.img.yScale = _H*.00395
M.POLE.img.strokeWidth = 1.3
M.POLE.img:setStrokeColor(API.hexToCmyk(M.color.img))
M.GR:insert(M.POLE.img)
-- control player
M.POLE.bg:addEventListener( 'touch', M.swipe )
M.POLE.bg:addEventListener( 'tap', function(e)
M.POLE.player.state = 0
end )
-- arr field
M.arr_pole = {} -- newRect
local jj = 0
for i=1,M.delta_height do -- 71 point
M.arr_pole[i] = {}
M.GUI.border[i] = {}
for k=1,M.delta_width do -- 44 point
jj = jj+1
M.arr_pole[i][k] = {nm = jj}
M.GUI.border[i][k] = {}
local xx = k*M.size_xonix
local yy = i*M.size_xonix
local _x = floor(xx-M.size_xonix/2)
local _y = floor(yy-M.size_xonix/2)
M.arr_pole[i][k][1] = image(IMAGES..'pole/land.jpg')
M.arr_pole[i][k][1] .x = _x
M.arr_pole[i][k][1] .y = _y
M.GR_ZONE:insert(M.arr_pole[i][k][1])
-- water at the edges
-- the rest is land
if (i==1 or i==M.delta_height)
or (k==1 or k==M.delta_width) then
M.arr_pole[i][k].type = 0
else
M.arr_pole[i][k].type = 2
M.arr_pole[i][k][1].isVisible = true
end
end
end
M.GR_ZONE.width = M.POLE.bg.width*M.POLE.bg.xScale
M.GR_ZONE.height = M.POLE.bg.height*M.POLE.bg.yScale
M.GR_ZONE.x = M.POLE.bg.x-M.POLE.bg.width*M.POLE.bg.xScale/2
M.GR_ZONE.y = M.POLE.bg.y-M.POLE.bg.height*M.POLE.bg.yScale/2
-- player
local _x = M.arr_pole[1][1][1].x
local _y = M.arr_pole[1][1][1].y
M.POLE.player = image(IMAGES..'ui/player.png')
M.POLE.player.x = _x
M.POLE.player.y = _y
M.POLE.player.width = M.size_xonix*1.5
M.POLE.player.height = M.size_xonix*1.5
M.POLE.player.state = 0
M.GR_ZONE:insert(M.POLE.player)
M.POLE.player:toFront()
end
-- get all free cells
- land
M.get_free = function(_i,_j,_start)
local _par = pairs
local _fill = M.filling_arr
local _arr = M.arr_pole
local _index = {
{-1,-1}, {0,-1}, {1,-1},
{-1, 0}, {1, 0},
{-1, 1}, {0, 1}, {1, 1},
}
-- mark as verified
_arr[_i][_j].is_check = true
_fill[#_fill+1] = {_i,_j}
for k,v in _par(_index) do
local i = _i+v[1]
local j = _j+v[2]
if i>1 and i<M.delta_height
and j>1 and j<M.delta_width then
if not _arr[i][j].is_check then
if _arr[i][j].type==2 then
M.get_free(i,j)
end
end
end
end
if _start then
return _fill
end
end
-- fill(capture)
M.filling = function()
-- init
local par = pairs
local tb_rem = table.remove
local arr = M.arr_pole
M.island_arr = {}
-- check indicator
for i,ii in par(arr) do
for j,jj in par(ii) do
if jj.type==2 then
jj.is_check = false
end
end
end
-- we divide land into islands
for i,ii in par(arr) do
for j,jj in par(ii) do
if jj.type==2 and not jj.is_check then
M.filling_arr = {}
M.island_arr[#M.island_arr+1] = M.get_free(i,j,true)
end
end
end
- find a larger island and delete it
local sel_max = {dir = '', count = 1}
for k,v in par(M.island_arr) do
if #v>=sel_max.count then
sel_max.dir = k
sel_max.count = #v
end
end
if #M.island_arr>0 then
tb_rem(M.island_arr,sel_max.dir)
end
-- fill
for k,v in par(M.island_arr) do
for kk,vv in par(v) do
local obj = arr[vv[1]][vv[2]]
obj.type = 0
obj[1].isVisible = false
end
end
-- turning the edges into water
for k,v in par(M.bread_crumbs) do
local arr = arr[v.arr_y][v.arr_x]
arr.type = 0
arr[1].isVisible = false
end
M.clear_history()
end

Why is this lua dijkstra's algorithm not working in some cases?

Im using Dijkstra algorythm code from this site: https://rosettacode.org/wiki/Dijkstra%27s_algorithm#Lua
Unfortunately It doesnt work for current edges table.
I have determined that the problem disappears when I delete the connection from 35 -> 36, but It doesnt solve the problem.
-- Graph definition
local edges = {
[34] = {[35] = 1,[37] = 1,},
[35] = {[34] = 1,[36] = 1,[46] = 1,},
[36] = {[35] = 1,[37] = 1,},
[37] = {[34] = 1,[36] = 1,},
[38] = {[46] = 1,},
[46] = {[35] = 1,[38] = 1,},
}
-- Fill in paths in the opposite direction to the stated edges
function complete (graph)
for node, edges in pairs(graph) do
for edge, distance in pairs(edges) do
if not graph[edge] then graph[edge] = {} end
graph[edge][node] = distance
end
end
end
-- Create path string from table of previous nodes
function follow (trail, destination)
local path, nextStep = destination, trail[destination]
while nextStep do
path = nextStep .. " " .. path
nextStep = trail[nextStep]
end
return path
end
-- Find the shortest path between the current and destination nodes
function dijkstra (graph, current, destination, directed)
if not directed then complete(graph) end
local unvisited, distanceTo, trail = {}, {}, {}
local nearest, nextNode, tentative
for node, edgeDists in pairs(graph) do
if node == current then
distanceTo[node] = 0
trail[current] = false
else
distanceTo[node] = math.huge
unvisited[node] = true
end
end
repeat
nearest = math.huge
for neighbour, pathDist in pairs(graph[current]) do
if unvisited[neighbour] then
tentative = distanceTo[current] + pathDist
if tentative < distanceTo[neighbour] then
distanceTo[neighbour] = tentative
trail[neighbour] = current
end
if tentative < nearest then
nearest = tentative
nextNode = neighbour
end
end
end
unvisited[current] = false
current = nextNode
until unvisited[destination] == false or nearest == math.huge
return distanceTo[destination], follow(trail, destination)
end
-- Main procedure
print("Directed:", dijkstra(edges, 34, 38, true))
print("Undirected:", dijkstra(edges, 34, 38, false))
I recieve the output of inf, 38 with current egdes table content but when I delete the connection between 35 -> 36 it gives an good output - 3, 34 35 46 38
For easier understand im uploading the graphic representation of edges table: https://i.imgur.com/FFF22C1.png
As you can see the route is corrent when we start from 34 -> 35 -> 46 -> 38 but as I sad It works only when connection from 35 to 36 is not existing.
Why it is not working in the case showed in my code?
This is an example of Dijkstra algorithm implementation
-- Graph definition
local edges = {
[34] = {[35] = 1,[37] = 1,},
[35] = {[34] = 1,[36] = 1,[46] = 1,},
[36] = {[35] = 1,[37] = 1,},
[37] = {[34] = 1,[36] = 1,},
[38] = {[46] = 1,},
[46] = {[35] = 1,[38] = 1,},
}
local starting_vertex, destination_vertex = 34, 38
local function create_dijkstra(starting_vertex)
local shortest_paths = {[starting_vertex] = {full_distance = 0}}
local vertex, distance, heap_size, heap = starting_vertex, 0, 0, {}
return
function (adjacent_vertex, edge_length)
if adjacent_vertex then
-- receiving the information about adjacent vertex
local new_distance = distance + edge_length
local adjacent_vertex_info = shortest_paths[adjacent_vertex]
local pos
if adjacent_vertex_info then
if new_distance < adjacent_vertex_info.full_distance then
adjacent_vertex_info.full_distance = new_distance
adjacent_vertex_info.previous_vertex = vertex
pos = adjacent_vertex_info.index
else
return
end
else
adjacent_vertex_info = {full_distance = new_distance, previous_vertex = vertex, index = 0}
shortest_paths[adjacent_vertex] = adjacent_vertex_info
heap_size = heap_size + 1
pos = heap_size
end
while pos > 1 do
local parent_pos = (pos - pos % 2) / 2
local parent = heap[parent_pos]
local parent_info = shortest_paths[parent]
if new_distance < parent_info.full_distance then
heap[pos] = parent
parent_info.index = pos
pos = parent_pos
else
break
end
end
heap[pos] = adjacent_vertex
adjacent_vertex_info.index = pos
elseif heap_size > 0 then
-- which vertex neighborhood to ask for?
vertex = heap[1]
local parent = heap[heap_size]
heap[heap_size] = nil
heap_size = heap_size - 1
if heap_size > 0 then
local pos = 1
local last_node_pos = heap_size / 2
local parent_info = shortest_paths[parent]
local parent_distance = parent_info.full_distance
while pos <= last_node_pos do
local child_pos = pos + pos
local child = heap[child_pos]
local child_info = shortest_paths[child]
local child_distance = child_info.full_distance
if child_pos < heap_size then
local child_pos2 = child_pos + 1
local child2 = heap[child_pos2]
local child2_info = shortest_paths[child2]
local child2_distance = child2_info.full_distance
if child2_distance < child_distance then
child_pos = child_pos2
child = child2
child_info = child2_info
child_distance = child2_distance
end
end
if child_distance < parent_distance then
heap[pos] = child
child_info.index = pos
pos = child_pos
else
break
end
end
heap[pos] = parent
parent_info.index = pos
end
local vertex_info = shortest_paths[vertex]
vertex_info.index = nil
distance = vertex_info.full_distance
return vertex
end
end,
shortest_paths
end
local vertex, dijkstra, shortest_paths = starting_vertex, create_dijkstra(starting_vertex)
while vertex and vertex ~= destination_vertex do
-- send information about all adjacent vertexes of "vertex"
for adjacent_vertex, edge_length in pairs(edges[vertex]) do
dijkstra(adjacent_vertex, edge_length)
end
vertex = dijkstra() -- now dijkstra is asking you about the neighborhood of another vertex
end
if vertex then
local full_distance = shortest_paths[vertex].full_distance
local path = vertex
while vertex do
vertex = shortest_paths[vertex].previous_vertex
if vertex then
path = vertex.." "..path
end
end
print(full_distance, path)
else
print"Path not found"
end

How do I drag and drop specific objects in Love2d?

I'm new to Lua and coding in general so I decided to write a Chess Program to learn. I have setup a class and created objects from it to represent the pieces. Now I want to begin moving the pieces with my mouse. I looked at a tutorial, but it only handled one rectangle. My first though was to use a "for" loop in the love.mousePressed() function to go though each of the objects until it found an object with a matching x, y coordinate. This obviously did not work the way I did it. Instead, it only goes to the next object every time the mouse is pressed or at least it would if the program didn't immediately crash once the button was released. So my question is, what is the right way to be going about this?
local blackPawn = love.graphics.newImage("Textures/Blackpawn.png")
local blackRook = love.graphics.newImage("Textures/Blackrook.png")
local blackKnight = love.graphics.newImage("Textures/Blackknight.png")
local blackBishop = love.graphics.newImage("Textures/Blackbishop.png")
local blackQueen = love.graphics.newImage("Textures/Blackqueen.png")
local blackKing = love.graphics.newImage("Textures/BlackKing.png")
local whitePawn = love.graphics.newImage("Textures/Whitepawn.png")
local whiteRook = love.graphics.newImage("Textures/Whiterook.png")
local whiteKnight = love.graphics.newImage("Textures/Whiteknight.png")
local whiteBishop = love.graphics.newImage("Textures/Whitebishop.png")
local whiteQueen = love.graphics.newImage("Textures/Whitequeen.png")
local whiteKing = love.graphics.newImage("Textures/WhiteKing.png")
local chessboard = love.graphics.newImage("Textures/ChessBoard.png")
local register = {}
local id = 0
piece = {
xSquare = 0, ySquare = 0,
x = 0, y = 0,
height = 64, width = 64,
pawn = false,
Rook = false,
Knight = false,
Bishop = false,
Queen = false,
King = false,
color = "",
texture = whitePawn,
dragging = {active = false, diffx = 0, diffy = 0}
}
function piece.new()
newPiece = {}
for k, v in pairs(piece) do
newPiece[k] = v
end
return newPiece
end
function piece:draw()
end
function getMouse()
local x, y = love.mouse.getPosition()
local isDown = love.mouse.isDown(1,2)
return x, y, isDown
end
function createBoard(id)
for x = 1, 8 do
for y = 1, 8 do
if y ~= 3 and y ~= 4 and y ~=5 and y ~= 6 then
id = id + 1
register[id] = piece.new()
register[id].x = x * 64 - 48
register[id].y = (y - 1) * 64
if y == 2 then
register[id].pawn = true
register[id].color = "white"
register[id].texture = whitePawn
print("item " .. id .. " is here x = " .. register[id].x .. " y = " .. register[id].y .. " Is pawn = " .. tostring(register[id].pawn) ..
" Color is " .. register[id].color)
elseif y == 7 then
register[id].pawn = true
register[id].color = "black"
register[id].texture = blackPawn
print("item " .. id .. " is here x = " .. register[id].x .. " y = " .. register[id].y .. " Is pawn = " .. tostring(register[id].pawn) ..
" Color is " .. register[id].color)
elseif y == 1 then
register[id].color = "white"
if x == 1 or x == 8 then
register[id].Rook = true
register[id].texture = whiteRook
elseif x == 2 or x == 7 then
register[id].Knight = true
register[id].texture = whiteKnight
print("knight is here")
elseif x == 3 or x == 6 then
register[id].Bishop = true
register[id].texture = whiteBishop
elseif x == 5 then
register[id].King = true
register[id].texture = whiteKing
elseif x == 4 then
register[id].Queen = true
register[id].texture = whiteQueen
end
elseif y == 8 then
register[id].color = "black"
if x == 1 or x == 8 then
register[id].Rook = true
register[id].texture = blackRook
elseif x == 2 or x == 7 then
register[id].Knight = true
register[id].texture = blackKnight
elseif x == 3 or x == 6 then
register[id].Bishop = true
register[id].texture = blackBishop
elseif x == 5 then
register[id].King = true
register[id].texture = blackKing
elseif x == 4 then
register[id].Queen = true
register[id].texture = blackQueen
end
end
end
end
end
end
function drawBoard(id, register)
love.graphics.draw(chessboard, 0, 0)
for id = 1, 32 do
love.graphics.draw(register[id].texture, register[id].x, register[id].y)
end
end
function love.load()
createBoard(id)
end
function love.update(dt)
for id = 1, 32 do
if register[id].dragging.active == true then
register[id].x = love.mouse.getX() - register[id].dragging.diffx
register[id].y = love.mouse.getY() - register[id].dragging.diffy
end
end
end
function love.draw()
drawBoard(id, register)
end
function love.mousepressed(x, y, button)
for id = 1, 32 do
if (button == 1 or button == 2)
and x > register[id].x and x < register[id].x + register[id].width
and y > register[id].y and y < register[id].y + register[id].height
then
register[id].dragging.active = true
register[id].dragging.diffx = x - register[id].x
register[id].dragging.diffy = y - register[id].y
end
end
end
function love.mousereleased(x, y, button)
for id = 1, 32 do
if button == 1 or button == 2 then register[id].dragging.active = false end
end
end
function love.keypressed(key, unicode)
end
function love.keyreleased(key)
end
function love.focus(bool)
end
function love.quit()
end
Update:
I fixed the crashing, but I still have the weird bug where it changes the dragged piece into a different piece
Update 2: After a little more debugging I have figured out that the major issue is that it for some reason does not correctly check if the dragging is active. As the code stands right now I need an else dragging.active = false after to correctly set it, but now that it is correctly set it won't drag anything at all despite the correct object have dragging set to active (unless I try and drag the object with value 32 where it drags everything at once). I am very confused as to what's wrong. Why isn't Lua able to check value like this?
First, I'd create a global boolean for if a piece has been selected and then a variable to hold the piece selected
local selected = false
local selectedPiece = {}
Then create a playing board and split it into a grid, with each square being of equal size. Something like this
board = {
size = { 8, 8 }, -- 8x8 grid
squareSize = 40, -- 40 pixels long sides
pieces = {
{ -- First row contains which pieces?
Piece:Rook(),
Piece:Bishop(),
...
},
{ -- Second row
Piece:Pawn(),
...
},
{ -- etc.
Piece:Empty(),
...
}
}
}
I advise not using nil in your table for empty squares due to the odd behavior of tables with nil indexes.
In your love.mousepressed() method, you check where the click was based on its position (this is assuming the board takes up the whole window)
function love.mousepressed(x, y, btn)
-- If a piece hasn't been clicked on.
if (not selected) then
-- This line is assuming that since all board squares are equal size, then the mouse click has to be in at least one square.
-- Therefore, if we take the floor of the position/board.squareSize, we will always get a value from 0 - 7 (8 values) on the board.
local piece = board.pieces[math.floor(x/board.squareSize)][math.floor(y/board.squareSize)]
-- If there is a piece here.
if (piece:isNotAnEmpty()) then
selectedPiece = piece -- Select the piece.
selected = not selected -- Notify program that a piece is selected to handle such things accordingly in other methods.
end
else
-- Assuming you wrote a method that determines if a piece can be moved to a certain spot on the board.
if (board:CanMovePieceHere(selectedPiece, x/board.size, y/board.size)) then
-- Do your stuff here.
...
-- Eventually, reset your variables.
selected = not selected
selectedPiece = {}
end
end
end
This is how I'd approach it, but your question is very open to interpretation.

Resources