Table value doesn't change - lua

I have a 2 dim array and all it's cells filled with zeros.
What i'm trying to do is to take some randomly chosen cells and fill it with 4 or 5
but what i get is either empty gird with all value equal to zero or i get just one value that has changed to 4 or 5 and that's my code below:
local grid = {}
for i=1,10 do
grid[i] = {}
for j=1,10 do
grid[i][j] = 0
end
end
local empty={}
for i=1,10 do
for j=1,10 do
if grid[i][j]==0 then
table.insert(empty,i ..'-'.. j)
end
end
end
local fp=math.floor(table.maxn(empty)/3)
local fx,fy
for i=1,fp do
math.randomseed(os.time())
math.random(0,1)
local fo=math.random(0,1)
math.random(table.maxn(empty))
local temp= empty[math.random(table.maxn(empty))]
local dashindex=string.find(temp,'-')
fx=tonumber(string.sub(temp,1,dashindex-1))
fy=tonumber(string.sub(temp,dashindex+1,string.len(temp)))
if fo==0 then
grid[fx][fy]=4
elseif fo==1 then
grid[fx][fy]=5
end
end
for i=1,10 do
for j=1,10 do
print(grid[i][j])
end
print('\n')
end

I'm not sure what the for i=1,fp loop is doing with temp and fo, for example the seed should only be set once, and also, the return value on line after local fo is ignored, seems very messy. But based on your post, if you really just want to randomly select N cells from your 2D array and set those to either 4 or 5 (randomly), this should work:
-- maybe N = fp
local N = 5
math.randomseed(os.time())
local i = 1
repeat
fx = math.random(1, 10)
fy = math.random(1, 10)
if grid[fx][fy] == 0 then
grid[fx][fy] = math.random(4,5)
i = i + 1
end
until i > N
Note however that the closer N is to number of items in array (100 in your example), the longer it will take for the loop to complete. If this is a concern, then for large N values, you could do the opposite: initialize each cell to 4 or 5 randomly, and then randomly set size - N of them to 0.
math.randomseed(os.time())
local rows = 10
local columns = 10
local grid = {}
if N > rows*columns/2 then
for i=1,rows do
grid[i] = {}
for j=1,columns do
grid[i][j] = math.random(4,5)
end
end
local i = 1
repeat
fx = math.random(1, 10)
fy = math.random(1, 10)
if grid[fx][fy] ~= 0 then
grid[fx][fy] = 0
i = i + 1
end
until i > N
else
for i=1,rows do
grid[i] = {}
for j=1,columns do
grid[i][j] = 0
end
end
local i = 1
repeat
fx = math.random(1, 10)
fy = math.random(1, 10)
if grid[fx][fy] == 0 then
grid[fx][fy] = math.random(4,5)
i = i + 1
end
until i > N
end

Related

Computercraft program printing new line instead of updating previous line

I increment y (the y index of the monitor) by one each time I print a line to advance the y value (basically a newline). I'm running into an issue however, when I have done mon_1.setCursorPos(x, y+1) it updates the previous line as it should, however when I do y = y+1 mon_1.setCursorPos(x, y) it prints a new line without updating the previous line. Any suggestions?
Here's the full code
--Define some colors
color_fuel = 16
color_ok = 2243
color_error = 224455
color_blank = 0
color_active = 2240
--Tables
reactors={}
max_energy={}
produced={}
percent_capacity={}
--Globals
x = 1
y = 1
i = 0
--Lamps
lamp_1 = peripheral.wrap("colorful_lamp_0")
lamp_2 = peripheral.wrap("colorful_lamp_1")
lamp_3 = peripheral.wrap("colorful_lamp_2")
--Monitors
mon_1 = peripheral.wrap("monitor_3")
--Cap banks
cap_1 = peripheral.wrap("capacitor_bank_2")
--Helper functions
function flash_fuel_lamp()
lamp_2.setLampColor(color_blank)
sleep(0.01)
lamp_2.setLampColor(color_error)
for i = 10,1,-1 do
lamp_1.setLampColor(color_fuel)
sleep(1)
lamp_1.setLampColor(color_blank)
sleep(1)
end
lamp_1.setLampColor(color_fuel)
end
function clear_lamps()
lamp_1.setLampColor(color_blank)
lamp_2.setLampColor(color_blank)
lamp_3.setLampColor(color_blank)
end
--Boot Sequence
--Set lamps to black and clear screen
clear_lamps()
mon_1.clear()
--Turn on the reactors and set the status light to green
for k,v in pairs(peripheral.getNames()) do
if(string.find(v, "BigReactors")) then
reactors[i] = peripheral.wrap(v)
i = i + 1
else
--print("Other Peripheral Detected")
end
end
for k,v in pairs(reactors) do
v.setActive(true)
end
lamp_2.setLampColor(color_ok)
lamp_3.setLampColor(color_active)
--Main loop
for key,value in pairs(max_energy) do
mon_1.setCursorPos(x, y)
mon_1.write("Reactor " .. key .. " Max Energy: " .. value)
y = y + 1
end
while true do
--Check the fuel level
if(redstone.getAnalogInput("bottom") == 0) then
lamp_3.setLampColor(color_blank)
for k,v in pairs(reactors) do
v.setActive(false)
end
flash_fuel_lamp()
end
for k,v in pairs(reactors) do
if(v.getActive() == false) then
lamp_3.setLampColor(color_blank)
end
--Calculate the energies
max_energy[k] = v.getEnergyStored()
produced[k] = v.getEnergyProducedLastTick()
percent_capacity[k] = (max_energy[k] / produced[k]) * 100
end
--[[max_energy = reactor.getEnergyStored()
energy_produced = reactor.getEnergyProducedLastTick()
percent_capacity = (max_energy / energy_produced) * 100]]
max_energy_cap = cap_1.getMaxEnergyStored()
energy_stored = cap_1.getEnergyStored()
percent_capacity_cap = (energy_stored / max_energy_cap) * 100
y=y+1
mon_1.setCursorPos(x, y)
for key,value in pairs(percent_capacity) do
for k,v in pairs(reactors) do
mon_1.write("Reactor " .. k .. " Percent Capacity: " .. value)
y=y+1
mon_1.setCursorPos(x, y)
mon_1.write("Capacitor output per tick: " .. cap_1.getAverageOutputPerTick())
y=y+1
mon_1.setCursorPos(x, y)
mon_1.write("Capacitor input per tick: " .. cap_1.getAverageInputPerTick())
y=y+1
mon_1.setCursorPos(x, y)
end
end
sleep(0.01)
end```
So I think that you might be forgetting that when you do mon_1.setCursorPos(x, y+1) you're not incrementing the y variable, you're only passing a higher value to the called function, while with y = y+1 mon_1.setCursorPos(x, y) you are incrementing y.
This explains why the previous line is not being updated when incrementing y, since the nested for-loops at the bottom of your code use that y variable as well.

bmp aspect ratio issue

I've been trying to understand how bmp files work so I can render some Mandelbrot set pictures and output them as bmp files since that seems to be one of the easiest methods but for some reason when I use an aspect ratio that isn't 1:1 even though its something to the power of 4 (so no padding is needed) I get weird artifacts like these 200:100 48:100 what I'm trying to do is turning an array of pixels that has white for even numbers and black for odd numbers into a bmp, this (100:100) is what it looks like with 1:1 aspect ratio.
I've tried reading through the wikipedia article to see if I can figure out what I'm doing wrong but I still don't get what I'm missing.
This is the script I've written in Lua so far:
ResolutionX = 100
ResolutionY = 100
local cos, atan, sin, atan2, sqrt, floor = math.cos, math.atan, math.sin, math.atan2, math.sqrt, math.floor
local insert, concat = table.insert, table.concat
local sub, char, rep = string.sub, string.char, string.rep
io.output("Test.bmp")
function Basen(n,b)
n = floor(n)
if not b or b == 10 then return tostring(n) end
local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
local t = {}
repeat
local d = (n % b) + 1
n = floor(n / b)
insert(t, 1, digits:sub(d,d))
until n == 0
return rep("0",32-#t)..concat(t,"")
end
FileSize = Basen(ResolutionY*ResolutionX*3 + 54,2)
FileSize4 = tonumber(sub(FileSize,1,8),2) or 0
FileSize3 = tonumber(sub(FileSize,9,16),2) or 0
FileSize2 = tonumber(sub(FileSize,17,24),2) or 0
FileSize1 = tonumber(sub(FileSize,25,32),2) or 0
Width = Basen(ResolutionX,2)
print("Width: ",Width)
Width4 = tonumber(sub(Width,1,8),2) or 0
Width3 = tonumber(sub(Width,9,16),2) or 0
Width2 = tonumber(sub(Width,17,24),2) or 0
Width1 = tonumber(sub(Width,25,32),2) or 0
Height = Basen(ResolutionY,2)
print("Height: ",Height)
Height4 = tonumber(sub(Height,1,8),2) or 0
Height3 = tonumber(sub(Height,9,16),2) or 0
Height2 = tonumber(sub(Height,17,24),2) or 0
Height1 = tonumber(sub(Height,25,32),2) or 0
BMPSize = Basen(ResolutionY*ResolutionX*3,2)
BMPSize4 = tonumber(sub(BMPSize,1,8),2) or 0
BMPSize3 = tonumber(sub(BMPSize,9,16),2) or 0
BMPSize2 = tonumber(sub(BMPSize,17,24),2) or 0
BMPSize1 = tonumber(sub(BMPSize,25,32),2) or 0
print("TotalSize: ",FileSize1,FileSize2,FileSize3,FileSize4,"\nWidth: ",Width1,Width2,Width3,Width4,"\nHeight: ",Height1,Height2,Height3,Height4,"\nImage data: ",BMPSize1,BMPSize2,BMPSize3,BMPSize4)
Result = {"BM"..char( --File type
FileSize1,FileSize2,FileSize3,FileSize4,--File size
0,0,0,0, --Reserved
54,0,0,0, --Where the pixel data starts
40,0,0,0, --DIB header
Width1,Width2,Width3,Width4, --Width
Height1,Height2,Height3,Height4, --Height
1,0, --Color planes
24,00, --Bit depth
0,0,0,0, --Compression
BMPSize1,BMPSize2,BMPSize3,BMPSize4, --The amount of bytes pixel data will consume
Width1,Width2,Width3,Width4,
Height1,Height2,Height3,Height4,
0,0,0,0, --Number of colors in palatte
0,0,0,0
)}
for X = 0, ResolutionX - 1 do
for Y = 0, ResolutionY - 1 do
insert(Result,rep(char(255 * ((X + 1) % 2) * ((Y + 1) % 2)),3))
end
end
io.write(table.concat(Result))
Ok, here's a BMP version. I've put things in a module so it might be easier to use.
local writeBMP = {}
local floor = math.floor
local insert, concat = table.insert, table.concat
local sub, char, rep = string.sub, string.char, string.rep
local function Basen(n,b)
n = floor(n)
if not b or b == 10 then return tostring(n) end
local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
local t = {}
repeat
local d = (n % b) + 1
n = floor(n / b)
insert(t, 1, digits:sub(d,d))
until n == 0
return rep("0",32-#t)..concat(t,"")
end
local function nextMul4(x)
if ( x % 4 == 0 ) then
return x
else
return x+4-(x%4)
end
end
local function clamp(x)
local y = x
if ( x > 255 ) then
y = 255
elseif ( x < 0 ) then
y = 0
end
return floor(y)
end
-- Accepts array of type pixelsXYC[X][Y][C] of numbers 0-255
-- C=1,2,3 are the red, green and blue channels respectively
-- X increases left to right, and Y increases top to bottom
function writeBMP.data(pixelsXYC, resolutionX, resolutionY)
local Pixels = pixelsXYC
local ResolutionX = resolutionX
local ResolutionY = resolutionY
assert(#Pixels == ResolutionX, "Table size and X resolution mismatch")
assert(#Pixels[1] == ResolutionY, "Table size and Y resolution mismatch")
local FileSize = Basen(ResolutionY*nextMul4(3*ResolutionX) + 54,2)
local FileSize4 = tonumber(sub(FileSize,1,8),2) or 0
local FileSize3 = tonumber(sub(FileSize,9,16),2) or 0
local FileSize2 = tonumber(sub(FileSize,17,24),2) or 0
local FileSize1 = tonumber(sub(FileSize,25,32),2) or 0
local Width = Basen(ResolutionX,2)
local Width4 = tonumber(sub(Width,1,8),2) or 0
local Width3 = tonumber(sub(Width,9,16),2) or 0
local Width2 = tonumber(sub(Width,17,24),2) or 0
local Width1 = tonumber(sub(Width,25,32),2) or 0
local Height = Basen(ResolutionY,2)
local Height4 = tonumber(sub(Height,1,8),2) or 0
local Height3 = tonumber(sub(Height,9,16),2) or 0
local Height2 = tonumber(sub(Height,17,24),2) or 0
local Height1 = tonumber(sub(Height,25,32),2) or 0
local BMPSize = Basen(ResolutionY*nextMul4(3*ResolutionX),2)
local BMPSize4 = tonumber(sub(BMPSize,1,8),2) or 0
local BMPSize3 = tonumber(sub(BMPSize,9,16),2) or 0
local BMPSize2 = tonumber(sub(BMPSize,17,24),2) or 0
local BMPSize1 = tonumber(sub(BMPSize,25,32),2) or 0
local Result = {}
Result[1] = "BM" .. char( --File type
FileSize1,FileSize2,FileSize3,FileSize4,--File size
0,0, --Reserved
0,0, --Reserved
54,0,0,0, --Where the pixel data starts
40,0,0,0, --DIB header
Width1,Width2,Width3,Width4, --Width
Height1,Height2,Height3,Height4, --Height
1,0, --Color planes
24,0, --Bit depth
0,0,0,0, --Compression
BMPSize1,BMPSize2,BMPSize3,BMPSize4, --The amount of bytes pixel data will consume
37,22,0,0, --Pixels per meter horizontal
37,22,0,0, --Pixels per meter vertical
0,0,0,0, --Number of colors in palatte
0,0,0,0
)
local Y = ResolutionY
while( Y >= 1 ) do
for X = 1, ResolutionX do
local r = clamp( Pixels[X][Y][1] )
local g = clamp( Pixels[X][Y][2] )
local b = clamp( Pixels[X][Y][3] )
Result[#Result+1] = char(b)
Result[#Result+1] = char(g)
Result[#Result+1] = char(r)
end
-- byte alignment
if ( ( (3*ResolutionX) % 4 ) ~= 0 ) then
local Padding = 4 - ((3*ResolutionX) % 4)
Result[#Result+1] = rep(char(0),Padding)
end
Y = Y - 1
end
return table.concat(Result)
end
function writeBMP.write(pixelsXYC, resolutionX, resolutionY, filename)
local file = io.open(filename,"wb")
local data = writeBMP.data(pixelsXYC, resolutionX, resolutionY)
file:write(data)
end
return writeBMP
A simple test:
-- writeBMP example
local writeBMP = require "writeBMP"
local resolutionX = 100
local resolutionY = 100
-- Pixel data
local pixels = {}
for x=1,resolutionX do
pixels[x] = {}
for y=1, resolutionY do
pixels[x][y] = {}
local red = 255*(resolutionX-x+resolutionY-y)/(resolutionX+resolutionY)
local green = 255*y/resolutionY
local blue = 255*x/resolutionX
pixels[x][y][1] = red
pixels[x][y][2] = green
pixels[x][y][3] = blue
end
end
writeBMP.write(pixels,resolutionX,resolutionY,"testwritebmp.bmp")
return
Note: In BMP, the Y axis starts on the bottom. I am more used to Y axis going from top down in computer graphics (so I wrote it that way).
Thanks HAX for the code.
Welcome to Stack Exchange :)
I suggest having a look at PPM files, they are easy. They can be converted with other tools to png or bmp with other tools.
Wikipedia PPM Specification
Here is a PPM solution:
ResolutionX = 48
ResolutionY = 100
local cos, atan, sin, atan2, sqrt, floor = math.cos, math.atan, math.sin, math.atan2, math.sqrt, math.floor
local insert, concat = table.insert, table.concat
local sub, char, rep = string.sub, string.char, string.rep
local file = io.open("Test.ppm","w")
-- PPM File headers
local Output = { }
Output[1] = "P3"
Output[2] = tostring(ResolutionX) .. " " .. tostring(ResolutionY)
Output[3] = "255"
-- Image body
for Y = 0, ResolutionY - 1 do
for X = 0, ResolutionX - 1 do
local value = 255 * ((X + 1) % 2) * ((Y + 1) % 2)
Output[#Output+1] = rep(tostring(floor(value)),3," ")
end
end
-- Join lines together
local Result = table.concat(Output,"\n")
file:write(Result)
Notes: I could not get your code to write to file (see my usage of file). The inner loop must be X (columns) and the outer loop must be Y (rows) if the write order is English reading order (left-right down-up).

Attempt to index field (a nil value), for an Object

I have a problem with trying to add things to a LOVE2D version of Match-3. (From the CS50 course)
I added a function swapTiles() into my Board class and made a class object called self.board in a Class called PlayState. Then when I try to access the new function, it says this error:
Error
src/states/PlayState.lua:155: attempt to index field 'board' (a nil value)
I'll provide my Board and PlayState class below:
Board: (keep in mind the new function is literally in the code)
Board = Class{}
function Board:init(x, y, level) -- Added "level" as an integer for the block variations.
self.x = x
self.y = y
self.matches = {}
self.level = level
self:initializeTiles()
end
function Board:swapTiles(tile1, tile2)
-- swap grid positions of tiles
local tempX = tile1.gridX
local tempY = tile1.gridY
tile1.gridX = tile2.gridX
tile1.gridY = tile2.gridY
tile2.gridX = tempX
tile2.gridY = tempY
-- swap tiles in the tiles table
self.tiles[tile1.gridY][tile1.gridX] = tile1
self.tiles[tile2.gridY][tile2.gridX] = tile2
end
function Board:initializeTiles()
self.tiles = {}
-- There should only be two shiny tiles.
for tileY = 1, 8 do
-- empty table that will serve as a new row
table.insert(self.tiles, {})
for tileX = 1, 8 do
self.isPowerup = false
if math.random(1, 25) == 4 then
self.isPowerup = true
end
-- create a new tile at X,Y with a random color and variety
table.insert(self.tiles[tileY], Tile(tileX, tileY, math.random(18), math.min(8, math.random(1, self.level)), self.isPowerup))
end
end
while self:calculateMatches() do
-- recursively initialize if matches were returned so we always have
-- a matchless board on start
self:initializeTiles()
end
end
--[[
Goes left to right, top to bottom in the board, calculating matches by counting consecutive
tiles of the same color. Doesn't need to check the last tile in every row or column if the
last two haven't been a match.
]]
function Board:calculateMatches()
local matches = {}
-- how many of the same color blocks in a row we've found
local matchNum = 1
-- horizontal matches first
for y = 1, 8 do
local colorToMatch = self.tiles[y][1].color
matchNum = 1
-- every horizontal tile
for x = 2, 8 do
-- if this is the same color as the one we're trying to match...
if self.tiles[y][x].color == colorToMatch then
matchNum = matchNum + 1
else
-- set this as the new color we want to watch for
colorToMatch = self.tiles[y][x].color
-- if we have a match of 3 or more up to now, add it to our matches table
if matchNum >= 3 then
local match = {}
-- go backwards from here by matchNum
for x2 = x - 1, x - matchNum, -1 do
-- add each tile to the match that's in that match
table.insert(match, self.tiles[y][x2])
-- Shiny Check
if self.tiles[y][x2].isShiny == true then
for i = 1, 8 do
table.insert(match, self.tiles[y][i])
end
end
end
-- add this match to our total matches table
table.insert(matches, match)
end
-- don't need to check last two if they won't be in a match
if x >= 7 then
break
end
matchNum = 1
end
end
-- account for the last row ending with a match
if matchNum >= 3 then
local match = {}
-- go backwards from end of last row by matchNum
for x = 8, 8 - matchNum + 1, -1 do
table.insert(match, self.tiles[y][x])
end
table.insert(matches, match)
end
end
-- vertical matches
for x = 1, 8 do
local colorToMatch = self.tiles[1][x].color
matchNum = 1
-- every vertical tile
for y = 2, 8 do
if self.tiles[y][x].color == colorToMatch then
matchNum = matchNum + 1
else
colorToMatch = self.tiles[y][x].color
if matchNum >= 3 then
local match = {}
for y2 = y - 1, y - matchNum, -1 do
table.insert(match, self.tiles[y2][x])
if self.tiles[y2][x].isShiny == true then
for i = 1, 8 do
table.insert(match, self.tiles[i][x])
end
end
end
table.insert(matches, match)
end
matchNum = 1
-- don't need to check last two if they won't be in a match
if y >= 7 then
break
end
end
end
-- account for the last column ending with a match
if matchNum >= 3 then
local match = {}
-- go backwards from end of last row by matchNum
for y = 8, 8 - matchNum, -1 do
table.insert(match, self.tiles[y][x])
end
table.insert(matches, match)
end
end
-- store matches for later reference
self.matches = matches
-- return matches table if > 0, else just return false
return #self.matches > 0 and self.matches or false
end
--[[
Remove the matches from the Board by just setting the Tile slots within
them to nil, then setting self.matches to nil.
]]
function Board:removeMatches()
for k, match in pairs(self.matches) do
for k, tile in pairs(match) do
self.tiles[tile.gridY][tile.gridX] = nil
end
end
self.matches = nil
end
--[[
Shifts down all of the tiles that now have spaces below them, then returns a table that
contains tweening information for these new tiles.
]]
function Board:getFallingTiles()
-- tween table, with tiles as keys and their x and y as the to values
local tweens = {}
-- for each column, go up tile by tile till we hit a space
for x = 1, 8 do
local space = false
local spaceY = 0
local y = 8
while y >= 1 do
-- if our last tile was a space...
local tile = self.tiles[y][x]
if space then
-- if the current tile is *not* a space, bring this down to the lowest space
if tile then
-- put the tile in the correct spot in the board and fix its grid positions
self.tiles[spaceY][x] = tile
tile.gridY = spaceY
-- set its prior position to nil
self.tiles[y][x] = nil
-- tween the Y position to 32 x its grid position
tweens[tile] = {
y = (tile.gridY - 1) * 32
}
-- set space back to 0, set Y to spaceY so we start back from here again
space = false
y = spaceY
spaceY = 0
end
elseif tile == nil then
space = true
if spaceY == 0 then
spaceY = y
end
end
y = y - 1
end
end
-- create replacement tiles at the top of the screen
for x = 1, 8 do
for y = 8, 1, -1 do
local tile = self.tiles[y][x]
-- if the tile is nil, we need to add a new one
if not tile then
local tile = Tile(x, y, math.random(18), math.random(1, self.level))
tile.y = -32
self.tiles[y][x] = tile
tweens[tile] = {
y = (tile.gridY - 1) * 32
}
end
end
end
return tweens
end
function Board:getNewTiles()
return {}
end
function Board:testForMatches()
for y = 1, 8 do
for x = 1, 8 do
-- Test for left swap
if x > 1 then
end
end
end
end
function Board:render()
for y = 1, #self.tiles do
for x = 1, #self.tiles[1] do
self.tiles[y][x]:render(self.x, self.y)
end
end
end
Here's my PlayState: (look for PlayState:swapTiles(), keep in mind that the self.board is being used several times for and works fine, except when i try calling self.board:swapTiles().)
PlayState = Class{__includes = BaseState}
function PlayState:init()
-- start our transition alpha at full, so we fade in
self.transitionAlpha = 255
-- position in the grid which we're highlighting
self.boardHighlightX = 0
self.boardHighlightY = 0
-- timer used to switch the highlight rect's color
self.rectHighlighted = false
-- flag to show whether we're able to process input (not swapping or clearing)
self.canInput = true
-- tile we're currently highlighting (preparing to swap)
self.highlightedTile = nil
self.score = 0
self.timer = 60
-- set our Timer class to turn cursor highlight on and off
Timer.every(0.5, function()
self.rectHighlighted = not self.rectHighlighted
end)
-- subtract 1 from timer every second
Timer.every(1, function()
self.timer = self.timer - 1
-- play warning sound on timer if we get low
if self.timer <= 5 then
gSounds['clock']:play()
end
end)
end
function PlayState:enter(params)
-- grab level # from the params we're passed
self.level = params.level
-- spawn a board and place it toward the right
self.board = params.board or Board(VIRTUAL_WIDTH - 272, 16)
-- grab score from params if it was passed
self.score = params.score or 0
-- score we have to reach to get to the next level
self.scoreGoal = self.level * 1.25 * 1000
end
function PlayState:update(dt)
if love.keyboard.wasPressed('escape') then
love.event.quit()
end
-- go back to start if time runs out
if self.timer <= 0 then
-- clear timers from prior PlayStates
Timer.clear()
gSounds['game-over']:play()
gStateMachine:change('game-over', {
score = self.score
})
end
-- go to next level if we surpass score goal
if self.score >= self.scoreGoal then
-- clear timers from prior PlayStates
-- always clear before you change state, else next state's timers
-- will also clear!
Timer.clear()
gSounds['next-level']:play()
-- change to begin game state with new level (incremented)
gStateMachine:change('begin-game', {
level = self.level + 1,
score = self.score
})
end
if self.canInput then
-- move cursor around based on bounds of grid, playing sounds
if love.keyboard.wasPressed('up') then
self.boardHighlightY = math.max(0, self.boardHighlightY - 1)
gSounds['select']:play()
elseif love.keyboard.wasPressed('down') then
self.boardHighlightY = math.min(7, self.boardHighlightY + 1)
gSounds['select']:play()
elseif love.keyboard.wasPressed('left') then
self.boardHighlightX = math.max(0, self.boardHighlightX - 1)
gSounds['select']:play()
elseif love.keyboard.wasPressed('right') then
self.boardHighlightX = math.min(7, self.boardHighlightX + 1)
gSounds['select']:play()
end
-- if we've pressed enter, to select or deselect a tile...
if love.keyboard.wasPressed('enter') or love.keyboard.wasPressed('return') then
-- if same tile as currently highlighted, deselect
local x = self.boardHighlightX + 1
local y = self.boardHighlightY + 1
-- if nothing is highlighted, highlight current tile
if not self.highlightedTile then
self.highlightedTile = self.board.tiles[y][x]
-- if we select the position already highlighted, remove highlight
elseif self.highlightedTile == self.board.tiles[y][x] then
self.highlightedTile = nil
-- if the difference between X and Y combined of this highlighted tile
-- vs the previous is not equal to 1, also remove highlight
elseif math.abs(self.highlightedTile.gridX - x) + math.abs(self.highlightedTile.gridY - y) > 1 then
gSounds['error']:play()
self.highlightedTile = nil
else
self:swapTiles(self.highlightedTile, self.board.tiles[y][x], true)
end
end
end
Timer.update(dt)
end
function PlayState:swapTiles(tile1, tile2, swapBackAtNoMatch)
local tile1 = tile1
local tile2 = tile2
local swapBackAtNoMatch = swapBackAtNoMatch
self.board:swapTiles(tile1, tile2) -- Causes the nil error.
if swapBackAtNoMatch then
-- tween coordinates between two swapping tiles
Timer.tween(0.1, {
[tile1] = {x = tile2.x, y = tile2.y},
[tile2] = {x = tile1.x, y = tile1.y}
})
-- once they've swapped, tween falling blocks
:finish(function ()
local matches = self.board:calculateMatches()
if matches then
self.calculateMatches(matches)
else
-- swap back if there's no match
self.swapTiles(tile1, tile2, false)
gSounds['error']:play()
end
end)
else
-- tween coordinates between the two so they swap
Timer.tween(0.1, {
[tile1] = {x = tile2.x, y = tile2.y},
[tile2] = {x = tile1.x, y = tile1.y}})
end
end
--[[
Calculates whether any matches were found on the board and tweens the needed
tiles to their new destinations if so. Also removes tiles from the board that
have matched and replaces them with new randomized tiles, deferring most of this
to the Board class.
]]
function PlayState:calculateMatches()
self.highlightedTile = nil
-- if we have any matches, remove them and tween the falling blocks that result
local matches = self.board:calculateMatches()
if matches then
gSounds['match']:stop()
gSounds['match']:play()
-- add score for each match
for k, match in pairs(matches) do
local varietyPoints = 0 -- We'll keep track of the bonus variety points here
-- We'll use vareity to calculate points for each tile within a match
for j, tiles in pairs(match) do
varietyPoints = varietyPoints + tiles.variety * 25
end
self.score = self.score + (#match * 50) + varietyPoints
-- Also add one second times the number of match to the timer
self.timer = self.timer + #match * 1
end
-- remove any tiles that matched from the board, making empty spaces
self.board:removeMatches()
-- gets a table with tween values for tiles that should now fall
local tilesToFall = self.board:getFallingTiles()
-- first, tween the falling tiles over 0.25s
Timer.tween(0.25, tilesToFall):finish(function()
local newTiles = self.board:getNewTiles()
-- then, tween new tiles that spawn from the ceiling over 0.25s to fill in
-- the new upper gaps that exist
Timer.tween(0.25, newTiles):finish(function()
-- recursively call function in case new matches have been created
-- as a result of falling blocks once new blocks have finished falling
self:calculateMatches()
end)
end)
-- if no matches, we can continue playing
else
self.canInput = true
end
end
Honestly doesn't make much sense. Please help!
function PlayState:swapTiles(tile1, tile2, swapBackAtNoMatch) end
is syntactic sugar for
PlayState.swaptiles = function(self, tile1, tile2, swapBackAtNoMatch) end
That's the reason why you can work with self inside that function.
A function defined like that needs to be called using the colon operator as well to make this work. Or you explicitly provide the table as first parameter.
Hence in your call self.swapTiles(tile1, tile2, false)
self is going to be tile1
tile1.board is nil so self.board is nil in this function call which causes the error
You have to call self:swapTiles(tile1, tile2, false) or self.swapTiles(self, tile1, tile2, false)
Please make sure you fully understand the colon syntax.

Lua #Table returning 0, despite Table containing 3 elements (tables)? [duplicate]

Sounds like a "let me google it for you" question, but somehow I can't find an answer. The Lua # operator only counts entries with integer keys, and so does table.getn:
tbl = {}
tbl["test"] = 47
tbl[1] = 48
print(#tbl, table.getn(tbl)) -- prints "1 1"
count = 0
for _ in pairs(tbl) do count = count + 1 end
print(count) -- prints "2"
How do I get the number of all entries without counting them?
You already have the solution in the question -- the only way is to iterate the whole table with pairs(..).
function tablelength(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
Also, notice that the "#" operator's definition is a bit more complicated than that. Let me illustrate that by taking this table:
t = {1,2,3}
t[5] = 1
t[9] = 1
According to the manual, any of 3, 5 and 9 are valid results for #t. The only sane way to use it is with arrays of one contiguous part without nil values.
You can set up a meta-table to track the number of entries, this may be faster than iteration if this information is a needed frequently.
The easiest way that I know of to get the number of entries in a table is with '#'. #tableName gets the number of entries as long as they are numbered:
tbl={
[1]
[2]
[3]
[4]
[5]
}
print(#tbl)--prints the highest number in the table: 5
Sadly, if they are not numbered, it won't work.
There's one way, but it might be disappointing: use an additional variable (or one of the table's field) for storing the count, and increase it every time you make an insertion.
count = 0
tbl = {}
tbl["test"] = 47
count = count + 1
tbl[1] = 48
count = count + 1
print(count) -- prints "2"
There's no other way, the # operator will only work on array-like tables with consecutive keys.
function GetTableLng(tbl)
local getN = 0
for n in pairs(tbl) do
getN = getN + 1
end
return getN
end
You're right. There are no other way to get length of table
You could use penlight library. This has a function size which gives the actual size of the table.
It has implemented many of the function that we may need while programming and missing in Lua.
Here is the sample for using it.
> tablex = require "pl.tablex"
> a = {}
> a[2] = 2
> a[3] = 3
> a['blah'] = 24
> #a
0
> tablex.size(a)
3
local function CountedTable(x)
assert(type(x) == 'table', 'bad parameter #1: must be table')
local new_t = {}
local mt = {}
-- `all` will represent the number of both
local all = 0
for k, v in pairs(x) do
all = all + 1
end
mt.__newindex = function(t, k, v)
if v == nil then
if rawget(x, k) ~= nil then
all = all - 1
end
else
if rawget(x, k) == nil then
all = all + 1
end
end
rawset(x, k, v)
end
mt.__index = function(t, k)
if k == 'totalCount' then return all
else return rawget(x, k) end
end
return setmetatable(new_t, mt)
end
local bar = CountedTable { x = 23, y = 43, z = 334, [true] = true }
assert(bar.totalCount == 4)
assert(bar.x == 23)
bar.x = nil
assert(bar.totalCount == 3)
bar.x = nil
assert(bar.totalCount == 3)
bar.x = 24
bar.x = 25
assert(bar.x == 25)
assert(bar.totalCount == 4)
I stumbled upon this thread and want to post another option. I'm using Luad generated from a block controller, but it essentially works by checking values in the table, then incrementing which value is being checked by 1. Eventually, the table will run out, and the value at that index will be Nil.
So subtract 1 from the index that returned a nil, and that's the size of the table.
I have a global Variable for TableSize that is set to the result of this count.
function Check_Table_Size()
local Count = 1
local CurrentVal = (CueNames[tonumber(Count)])
local repeating = true
print(Count)
while repeating == true do
if CurrentVal ~= nil then
Count = Count + 1
CurrentVal = CueNames[tonumber(Count)]
else
repeating = false
TableSize = Count - 1
end
end
print(TableSize)
end
seems when the elements of the table is added by insert method, getn will return correctly. Otherwise, we have to count all elements
mytable = {}
element1 = {version = 1.1}
element2 = {version = 1.2}
table.insert(mytable, element1)
table.insert(mytable, element2)
print(table.getn(mytable))
It will print 2 correctly

How to get number of entries in a Lua table?

Sounds like a "let me google it for you" question, but somehow I can't find an answer. The Lua # operator only counts entries with integer keys, and so does table.getn:
tbl = {}
tbl["test"] = 47
tbl[1] = 48
print(#tbl, table.getn(tbl)) -- prints "1 1"
count = 0
for _ in pairs(tbl) do count = count + 1 end
print(count) -- prints "2"
How do I get the number of all entries without counting them?
You already have the solution in the question -- the only way is to iterate the whole table with pairs(..).
function tablelength(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
Also, notice that the "#" operator's definition is a bit more complicated than that. Let me illustrate that by taking this table:
t = {1,2,3}
t[5] = 1
t[9] = 1
According to the manual, any of 3, 5 and 9 are valid results for #t. The only sane way to use it is with arrays of one contiguous part without nil values.
You can set up a meta-table to track the number of entries, this may be faster than iteration if this information is a needed frequently.
The easiest way that I know of to get the number of entries in a table is with '#'. #tableName gets the number of entries as long as they are numbered:
tbl={
[1]
[2]
[3]
[4]
[5]
}
print(#tbl)--prints the highest number in the table: 5
Sadly, if they are not numbered, it won't work.
There's one way, but it might be disappointing: use an additional variable (or one of the table's field) for storing the count, and increase it every time you make an insertion.
count = 0
tbl = {}
tbl["test"] = 47
count = count + 1
tbl[1] = 48
count = count + 1
print(count) -- prints "2"
There's no other way, the # operator will only work on array-like tables with consecutive keys.
function GetTableLng(tbl)
local getN = 0
for n in pairs(tbl) do
getN = getN + 1
end
return getN
end
You're right. There are no other way to get length of table
You could use penlight library. This has a function size which gives the actual size of the table.
It has implemented many of the function that we may need while programming and missing in Lua.
Here is the sample for using it.
> tablex = require "pl.tablex"
> a = {}
> a[2] = 2
> a[3] = 3
> a['blah'] = 24
> #a
0
> tablex.size(a)
3
local function CountedTable(x)
assert(type(x) == 'table', 'bad parameter #1: must be table')
local new_t = {}
local mt = {}
-- `all` will represent the number of both
local all = 0
for k, v in pairs(x) do
all = all + 1
end
mt.__newindex = function(t, k, v)
if v == nil then
if rawget(x, k) ~= nil then
all = all - 1
end
else
if rawget(x, k) == nil then
all = all + 1
end
end
rawset(x, k, v)
end
mt.__index = function(t, k)
if k == 'totalCount' then return all
else return rawget(x, k) end
end
return setmetatable(new_t, mt)
end
local bar = CountedTable { x = 23, y = 43, z = 334, [true] = true }
assert(bar.totalCount == 4)
assert(bar.x == 23)
bar.x = nil
assert(bar.totalCount == 3)
bar.x = nil
assert(bar.totalCount == 3)
bar.x = 24
bar.x = 25
assert(bar.x == 25)
assert(bar.totalCount == 4)
I stumbled upon this thread and want to post another option. I'm using Luad generated from a block controller, but it essentially works by checking values in the table, then incrementing which value is being checked by 1. Eventually, the table will run out, and the value at that index will be Nil.
So subtract 1 from the index that returned a nil, and that's the size of the table.
I have a global Variable for TableSize that is set to the result of this count.
function Check_Table_Size()
local Count = 1
local CurrentVal = (CueNames[tonumber(Count)])
local repeating = true
print(Count)
while repeating == true do
if CurrentVal ~= nil then
Count = Count + 1
CurrentVal = CueNames[tonumber(Count)]
else
repeating = false
TableSize = Count - 1
end
end
print(TableSize)
end
seems when the elements of the table is added by insert method, getn will return correctly. Otherwise, we have to count all elements
mytable = {}
element1 = {version = 1.1}
element2 = {version = 1.2}
table.insert(mytable, element1)
table.insert(mytable, element2)
print(table.getn(mytable))
It will print 2 correctly

Resources