How to serialize a metatable in Lua? - serialization

local binser = require "binser"
local log, floor, ceil, min, random = math.log, math.floor, math.ceil, math.min, math.random
local makeNode = function(value,size)
return {
value=value,
next={},
width={},
size=size
}
end
local End ={}
local NIL = makeNode(End,0)
local insert = function(self,value)
local node, chain, stepsAtLevel = self.head, {}, {}
for i=1, self.maxLevel do stepsAtLevel[i]=0 end
for level = self.maxLevel, 1, -1 do
while node.next[level] ~= NIL and node.next[level].value <= value do
stepsAtLevel[level] = ( stepsAtLevel[level] or 0 ) + node.width[level]
node = node.next[level]
--print(level, stepsAtLevel[level],value)
end
chain[level]=node
end
local nodeLevel = min( self.maxLevel, - floor(log(random()) / log(2) ) )
local newNode = makeNode( value, nodeLevel)
local steps, prevNode = 0
for level= 1, nodeLevel do
prevNode = chain[level]
newNode.next[level] = prevNode.next[level]
prevNode.next[level] = newNode
newNode.width[level] = prevNode.width[level] - steps
prevNode.width[level] = steps + 1
steps = steps + stepsAtLevel[level]
end
for level = nodeLevel + 1, self.maxLevel do
chain[level].width[level] = chain[level].width[level] +1
end
self.size = self.size + 1
end
local first = function(self)
return self.head.next[1].value
end
local tostring = function (self)
local t = {}
for k,v in self:ipairs() do table.insert(t,v) end
return "( "..table.concat(t,", ").. " )"
end
local islMT = {
__index = function(self,i)
if type(i) ~= "number" then return end
if i > self.size then return end
local node = self.head
for level=self.maxLevel, 1, -1 do
while node.width[level] <= i do
i = i - node.width[level]
node = node.next[level]
end
end
return node.value
end,
__tostring=tostring
}
local ipairs = function (self)
local node, size = self.head.next[1] , self.size
count = 0
return function()
value=node.value
node = node.next[1]
count = count+1
return count <= size and count or nil, value
end
end
math.randomseed(os.time())
local size = expected_size or 16
if not expected_size then
expected_size = 16
end
local maxLevel = floor( log(expected_size) / log(2) )
local head = makeNode("HEAD",maxLevel)
for i=1,maxLevel do
head.next[i] = NIL
head.width[i] = 1
end
local insdel = setmetatable( {
size = 0,
head = head,
maxLevel = maxLevel,
insert = insert,
tostring = tostring,
ipairs=ipairs,
}, islMT
)
insdel:insert('foo')
print(insdel)
-- how to serialize metatable insdel? this fail
local ser = binser.serialize(insdel)
insdel = binser.deserialize(ser)
insdel:insert('bar')
print(insdel)
I have tried countless serialization module (serpent, json, pluto) and none works with metatable, does someone know how to serialize metatables? Which module to use to serialize/deserialize metatable and how?
The full code is included, the binser module is from luarocks :
https://luarocks.org/modules/bakpakin/binser

Related

Files in Lua program not able to reference each other's variables

I'm hoping somebody experienced can point out where/if I am missing some crucial syntax.
My program works fine (because 95% of it was created by a CS50 professor), with one exception - I cannot seem to get the player and flag files/classes to reference each other to show that my Mario character has reached the final flag.
Specifically, the function "Flag:victory(player)" does not appear to be pulling information from the player file/class, while player has a similar function that is able to access Flag, although I don't see any difference between the two files as to why Flag is not behaving properly.
Flag = Class{}
function Flag:init(map)
-- reference to map for checking tiles
self.texture = map.spritesheet
-- animation frames
self.frames = {}
-- current animation frame
self.currentFrame = nil
self.player = Player(map)
-- used to determine behavior and animations
self.state = 'waving'
-- x and y velocity
self.dy = 0
-- position on top of map tiles
self.y = map.tileHeight * ((map.mapHeight / 2) - 4)
self.x = (map.mapWidth - 3) * map.tileWidth
-- initialize all player animations
self.animations = {
['waving'] = Animation({
texture = self.texture,
frames = {
love.graphics.newQuad(0, 48, 16, 16, self.texture:getDimensions()),
love.graphics.newQuad(16, 48, 16, 16, self.texture:getDimensions()),
love.graphics.newQuad(0, 48, 16, 16, self.texture:getDimensions()),
},
interval = 0.30
}),
['falling'] = Animation({
texture = self.texture,
frames = {
love.graphics.newQuad(32, 48, 16, 16, self.texture:getDimensions())
}
})
}
-- initialize animation and current frame we should render
self.animation = self.animations['waving']
self.currentFrame = self.animation:getCurrentFrame()
end
function Flag:update(dt)
self.animation:update(dt)
self.currentFrame = self.animation:getCurrentFrame()
self:victory()
end
function Flag:render()
local scaleX = - 1
-- draw sprite with scale factor and offsets
love.graphics.draw(self.texture, self.currentFrame, math.floor(self.x + 8 / 2),
math.floor(self.y + 8 / 2), 0, scaleX, 1, 8 / 2, 8 / 2)
end
function Flag:victory(player)
if self.player.victory == true then -- when player reaches flag
self.state = 'falling' -- change flag animation to down sprite
self.animation = self.animations['falling']
self.y = self.y + 2 -- descend the flag
end
end
--[[
Represents our player in the game, with its own sprite.
]]
Player = Class{}
local WALKING_SPEED = 140
local JUMP_VELOCITY = 400
function Player:init(map)
self.x = 0
self.y = 0
self.width = 16
self.height = 20
-- offset from top left to center to support sprite flipping
self.xOffset = 8
self.yOffset = 10
-- reference to map for checking tiles
self.map = map -- just a convenience because map comes in as a param/arg so the self should be dropped
self.texture = love.graphics.newImage('graphics/blue_alien.png')
-- sound effects
self.sounds = {
['jump'] = love.audio.newSource('sounds/jump.wav', 'static'),
['hit'] = love.audio.newSource('sounds/hit.wav', 'static'),
['coin'] = love.audio.newSource('sounds/coin.wav', 'static'),
}
-- variable for playing sound just once when game ends
self.musicplayed = false
-- animation frames
self.frames = {}
-- current animation frame
self.currentFrame = nil
-- used to determine behavior and animations
self.state = 'idle'
-- determines sprite flipping
self.direction = 'left'
-- x and y velocity
self.dx = 0
self.dy = 0
-- position on top of map tiles
self.y = map.tileHeight * ((map.mapHeight - 2) / 2) - self.height
self.x = map.tileWidth * 10
self.xmax = self.x -- farthest that mario has ever gone to the right
self.xmin = 1 -- farthest mario may go to the left, based on xmax and left border of map
-- initialize all player animations
self.animations = {
['idle'] = Animation({
texture = self.texture,
frames = {
love.graphics.newQuad(0, 0, 16, 20, self.texture:getDimensions())
}
}),
['walking'] = Animation({
texture = self.texture,
frames = {
love.graphics.newQuad(128, 0, 16, 20, self.texture:getDimensions()),
love.graphics.newQuad(144, 0, 16, 20, self.texture:getDimensions()),
love.graphics.newQuad(160, 0, 16, 20, self.texture:getDimensions()),
love.graphics.newQuad(144, 0, 16, 20, self.texture:getDimensions()),
},
interval = 0.15
}),
['jumping'] = Animation({
texture = self.texture,
frames = {
love.graphics.newQuad(32, 0, 16, 20, self.texture:getDimensions())
}
}),
['victory'] = Animation({
texture = self.texture,
frames = {
love.graphics.newQuad(0, 0, 16, 20, self.texture:getDimensions()),
love.graphics.newQuad(48, 0, 16, 20, self.texture:getDimensions()),
love.graphics.newQuad(0, 0, 16, 20, self.texture:getDimensions()),
love.graphics.newQuad(160, 0, 16, 20, self.texture:getDimensions()),
love.graphics.newQuad(0, 0, 16, 20, self.texture:getDimensions()),
},
interval = .25
})
}
-- initialize animation and current frame we should render
self.animation = self.animations['idle']
self.currentFrame = self.animation:getCurrentFrame()
-- behavior map we can call based on player state
self.behaviors = {
['idle'] = function(dt)
-- add spacebar functionality to trigger jump state
if love.keyboard.wasPressed('space') then
self.dy = -JUMP_VELOCITY
self.state = 'jumping'
self.animation = self.animations['jumping']
self.sounds['jump']:play()
elseif love.keyboard.isDown('left') then
self.direction = 'left'
self.dx = -WALKING_SPEED
self.state = 'walking'
self.animations['walking']:restart()
self.animation = self.animations['walking']
self:checkLeftBoundary()
elseif love.keyboard.isDown('right') then
self.direction = 'right'
self.dx = WALKING_SPEED
self.state = 'walking'
self.animations['walking']:restart()
self.animation = self.animations['walking']
else
self.dx = 0
end
self:checkLeftBoundary()
self:checkRightBoundary()
end,
['walking'] = function(dt)
-- keep track of input to switch movement while walking, or reset
-- to idle if we're not moving
if love.keyboard.wasPressed('space') then
self.dy = -JUMP_VELOCITY
self.state = 'jumping'
self.animation = self.animations['jumping']
self.sounds['jump']:play()
elseif love.keyboard.isDown('left') then
self.direction = 'left'
self.dx = -WALKING_SPEED
elseif love.keyboard.isDown('right') then
self.direction = 'right'
self.dx = WALKING_SPEED
else
self.dx = 0
self.state = 'idle'
self.animation = self.animations['idle']
end
self:checkLeftBoundary()
self:checkRightBoundary()
-- check for collisions moving left and right
self:checkRightCollision()
self:checkLeftCollision()
-- check if there's a tile directly beneath us
if not self.map:collides(self.map:tileAt(self.x, self.y + self.height)) and
not self.map:collides(self.map:tileAt(self.x + self.width - 1, self.y + self.height)) then
-- if so, reset velocity and position and change state
self.state = 'jumping'
self.animation = self.animations['jumping']
end
end,
['jumping'] = function(dt)
if love.keyboard.isDown('left') then
self.direction = 'left'
self.dx = -WALKING_SPEED
elseif love.keyboard.isDown('right') then
self.direction = 'right'
self.dx = WALKING_SPEED
end
-- apply map's gravity before y velocity
self.dy = self.dy + self.map.gravity
-- check if there's a tile directly beneath us
if self.map:collides(self.map:tileAt(self.x, self.y + self.height)) or
self.map:collides(self.map:tileAt(self.x + self.width - 1, self.y + self.height)) then
-- if so, reset velocity and position and change state
self.dy = 0
self.state = 'idle'
self.animation = self.animations['idle']
self.y = (self.map:tileAt(self.x, self.y + self.height).y - 1) * self.map.tileHeight - self.height
end
self:checkLeftBoundary()
self:checkRightBoundary()
-- check for collisions moving left and right
self:checkRightCollision()
self:checkLeftCollision()
end,
['victory'] = function(dt)
self.dx = 0
-- check if there's a tile directly beneath us
if self.map:collides(self.map:tileAt(self.x, self.y + self.height)) or
self.map:collides(self.map:tileAt(self.x + self.width - 1, self.y + self.height)) then
-- if so, reset velocity and position and change state
self.dy = 0
self.y = (self.map:tileAt(self.x, self.y + self.height).y - 1) * self.map.tileHeight - self.height
end
end
}
end
function Player:update(dt)
self.behaviors[self.state](dt)
self.animation:update(dt)
self.currentFrame = self.animation:getCurrentFrame()
self.x = self.x + self.dx * dt
self.xmax = math.max(self.x, self.xmax) -- tester to update xmax if player moves further to right than ever before
self.xmin = math.max(1, math.min(self.xmax - VIRTUAL_WIDTH / 2, map.mapWidthPixels - VIRTUAL_WIDTH)) -- farthest mario may go to the left, based on xmax and left border of map
self:calculateJumps()
self:gameover()
self:reachflag()
if self.map:victory() == true then
self.victory = true
end
-- apply velocity
self.y = self.y + self.dy * dt
end
function Player:gameover()
if self.y > 300 then
self.game_over = true
end
end
function Player:reachflag(Flag)
if self.x >= flag.x then
self.victory = true
end
end
-- jumping and block hitting logic
function Player:calculateJumps()
-- if we have negative y velocity (jumping), check if we collide
-- with any blocks above us
if self.dy < 0 then
if self.map:tileAt(self.x, self.y).id ~= TILE_EMPTY or
self.map:tileAt(self.x + self.width - 1, self.y).id ~= TILE_EMPTY then
-- reset y velocity
self.dy = 0
-- change block to different block
local playCoin = false
local playHit = false
if self.map:tileAt(self.x, self.y).id == JUMP_BLOCK then
self.map:setTile(math.floor(self.x / self.map.tileWidth) + 1,
math.floor(self.y / self.map.tileHeight) + 1, JUMP_BLOCK_HIT)
playCoin = true
else
playHit = true
end
if self.map:tileAt(self.x + self.width - 1, self.y).id == JUMP_BLOCK then
self.map:setTile(math.floor((self.x + self.width - 1) / self.map.tileWidth) + 1,
math.floor(self.y / self.map.tileHeight) + 1, JUMP_BLOCK_HIT)
playCoin = true
else
playHit = true
end
if playCoin then
self.sounds['coin']:play()
elseif playHit then
self.sounds['hit']:play()
end
end
end
end
-- checks two tiles to our left to see if a collision occurred
function Player:checkLeftCollision()
if self.dx < 0 then
-- check if there's a tile directly beneath us
if self.map:collides(self.map:tileAt(self.x - 1, self.y)) or
self.map:collides(self.map:tileAt(self.x - 1, self.y + self.height - 1)) then
-- if so, reset velocity and position and change state
self.dx = 0
self.x = self.map:tileAt(self.x - 1, self.y).x * self.map.tileWidth
end
end
end
-- checks two tiles to our right to see if a collision occurred
function Player:checkRightCollision()
if self.dx > 0 then
-- check if there's a tile directly beneath us
if self.map:collides(map:tileAt(self.x + self.width, self.y)) or
self.map:collides(map:tileAt(self.x + self.width, self.y + self.height - 1)) then
-- if so, reset velocity and position and change state
self.dx = 0
self.x = (self.map:tileAt(self.x + self.width, self.y).x - 1) * map.tileWidth - self.width
end
end
end
function Player:checkLeftBoundary()
if self.x <= self.xmin then -- went too far left
self.x = self.xmin
if self.direction == 'left'then
self.dx = 0 -- set speed to zero but keep state, ie walking, jumping/falling
end
end
end
function Player:checkRightBoundary()
if self.x >= map.mapWidthPixels - 16 then -- went too far right
self.x = map.mapWidthPixels - 16
if self.direction == 'right'then
self.dx = 0 -- set speed to zero but keep state, ie walking, jumping/falling
end
end
end
function Player:render()
local scaleX
-- set negative x scale factor if facing left, which will flip the sprite
-- when applied
if self.direction == 'right' then
scaleX = 1
else
scaleX = -1
end
-- draw sprite with scale factor and offsets
love.graphics.draw(self.texture, self.currentFrame, math.floor(self.x + self.xOffset),
math.floor(self.y + self.yOffset), 0, scaleX, 1, self.xOffset, self.yOffset)
if self.game_over == true then
self.state = 'idle'
self.dy = 0
self.dx = 0
map.music:stop()
if self.musicplayed == false then
love.audio.newSource('sounds/Laser_Shoot2.wav', 'static'):play()
self.musicplayed = true
end
love.graphics.print('Oh Snap! Try again? (y/n)', self.xmin + 40, 40)
if love.keyboard.isDown('y') then
self.map:init()
self.map:render()
elseif love.keyboard.isDown('n')then
love.event.quit()
end
end
if self.victory == true then
self.state = 'victory'
self.animation = self.animations['victory']
if self.musicplayed == false then
love.audio.newSource('sounds/Powerup23.wav', 'static'):play()
self.musicplayed = true
end
love.graphics.print('Congratulations! Play again? (y/n)', self.xmin + 35, 40)
if love.keyboard.isDown('y') then
self.map:init()
self.map:render()
map.music:stop()
elseif love.keyboard.isDown('n')then
love.event.quit()
end
end
end
--[[
Super Mario Bros. Demo
Author: Colton Ogden
Original Credit: Nintendo
Demonstrates rendering a screen of tiles.
]]
Class = require 'class'
push = require 'push'
require 'Animation'
require 'Map'
require 'Player'
require 'Flag'
-- close resolution to NES but 16:9
VIRTUAL_WIDTH = 432
VIRTUAL_HEIGHT = 243
-- actual window resolution
WINDOW_WIDTH = 1280
WINDOW_HEIGHT = 720
-- seed RNG
math.randomseed(os.time())
-- makes upscaling look pixel-y instead of blurry
love.graphics.setDefaultFilter('nearest', 'nearest')
-- an object to contain our map data
map = Map()
flag = Flag(map)
player = Player(map)
-- performs initialization of all objects and data needed by program
function love.load()
-- sets up a different, better-looking retro font as our default
love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 16))
-- sets up virtual screen resolution for an authentic retro feel
push:setupScreen(VIRTUAL_WIDTH, VIRTUAL_HEIGHT, WINDOW_WIDTH, WINDOW_HEIGHT, {
fullscreen = false,
resizable = true
})
love.window.setTitle('Super Mario 50')
love.keyboard.keysPressed = {}
love.keyboard.keysReleased = {}
end
-- called whenever window is resized
function love.resize(w, h)
push:resize(w, h)
end
-- global key pressed function
function love.keyboard.wasPressed(key)
if (love.keyboard.keysPressed[key]) then
return true
else
return false
end
end
-- global key released function
function love.keyboard.wasReleased(key)
if (love.keyboard.keysReleased[key]) then
return true
else
return false
end
end
-- called whenever a key is pressed
function love.keypressed(key)
if key == 'escape' then
love.event.quit()
end
love.keyboard.keysPressed[key] = true
end
-- called whenever a key is released
function love.keyreleased(key)
love.keyboard.keysReleased[key] = true
end
-- called every frame, with dt passed in as delta in time since last frame
function love.update(dt)
map:update(dt)
-- reset all keys pressed and released this frame
love.keyboard.keysPressed = {}
love.keyboard.keysReleased = {}
end
-- called each frame, used to render to the screen
function love.draw()
-- begin virtual resolution drawing
push:apply('start')
-- clear screen using Mario background blue
love.graphics.clear(108/255, 140/255, 255/255, 255/255)
-- renders our map object onto the screen
love.graphics.translate(math.floor(-map.camX + 0.5), math.floor(-map.camY + 0.5))
map:render()
-- end virtual resolution
push:apply('end')
end
--[[
Contains tile data and necessary code for rendering a tile map to the
screen.
]]
require 'Util'
Map = Class{}
TILE_BRICK = 1
TILE_EMPTY = -1
-- cloud tiles
CLOUD_LEFT = 6
CLOUD_RIGHT = 7
-- bush tiles
BUSH_LEFT = 2
BUSH_RIGHT = 3
-- mushroom tiles
MUSHROOM_TOP = 10
MUSHROOM_BOTTOM = 11
-- jump block
JUMP_BLOCK = 5
JUMP_BLOCK_HIT = 9
-- flagpole
FLAGPOLE_TOP = 8
FLAGPOLE_MIDDLE = 12
FLAGPOLE_BOTTOM = 16
PYRAMID_BLOCK = 17
-- a speed to multiply delta time to scroll map; smooth value
local SCROLL_SPEED = 62
-- constructor for our map object
function Map:init()
self.spritesheet = love.graphics.newImage('graphics/spritesheet.png')
self.sprites = generateQuads(self.spritesheet, 16, 16)
self.music = love.audio.newSource('sounds/music.wav', 'static')
self.pyramid_block = love.graphics.newImage('graphics/pyramidblock.png')
self.tileWidth = 16
self.tileHeight = 16
self.mapWidth = 300
self.mapHeight = 28
self.tiles = {}
-- applies positive Y influence on anything affected
self.gravity = 15
-- associate player with map
self.player = Player(self)
self.flag = Flag(self)
-- camera offsets
self.camX = 0
self.camY = -3
-- cache width and height of map in pixels
self.mapWidthPixels = self.mapWidth * self.tileWidth
self.mapHeightPixels = self.mapHeight * self.tileHeight
-- first, fill map with empty tiles
for y = 1, self.mapHeight do
for x = 1, self.mapWidth do
-- support for multiple sheets per tile; storing tiles as tables
self:setTile(x, y, TILE_EMPTY)
end
end
-- begin generating the terrain using vertical scan lines
local x = 1
while x < self.mapWidth - 20 do
-- 2% chance to generate a cloud
-- make sure we're 2 tiles from edge at least
if x < self.mapWidth - 2 then
if math.random(20) == 1 then
-- choose a random vertical spot above where blocks/pipes generate
local cloudStart = math.random(self.mapHeight / 2 - 6)
self:setTile(x, cloudStart, CLOUD_LEFT)
self:setTile(x + 1, cloudStart, CLOUD_RIGHT)
end
end
-- 5% chance to generate a mushroom
if math.random(20) == 1 then
-- left side of pipe
self:setTile(x, self.mapHeight / 2 - 2, MUSHROOM_TOP)
self:setTile(x, self.mapHeight / 2 - 1, MUSHROOM_BOTTOM)
-- creates column of tiles going to bottom of map
for y = self.mapHeight / 2, self.mapHeight do
self:setTile(x, y, TILE_BRICK)
end
-- next vertical scan line
x = x + 1
-- 10% chance to generate a bush, being sure to generate away from edge
elseif math.random(10) == 1 and x < self.mapWidth - 3 then
local bushLevel = self.mapHeight / 2 - 1
-- place bush component and then column of bricks
self:setTile(x, bushLevel, BUSH_LEFT)
for y = self.mapHeight / 2, self.mapHeight do
self:setTile(x, y, TILE_BRICK)
end
x = x + 1
self:setTile(x, bushLevel, BUSH_RIGHT)
for y = self.mapHeight / 2, self.mapHeight do
self:setTile(x, y, TILE_BRICK)
end
x = x + 1
-- 10% chance to not generate anything, creating a gap
elseif math.random(10) ~= 1 then
-- creates column of tiles going to bottom of map
for y = self.mapHeight / 2, self.mapHeight do
self:setTile(x, y, TILE_BRICK)
end
-- chance to create a block for Mario to hit
if math.random(15) == 1 then
self:setTile(x, self.mapHeight / 2 - 4, JUMP_BLOCK)
end
-- next vertical scan line
x = x + 1
else
-- increment X so we skip two scanlines, creating a 2-tile gap
x = x + 2
end
end
while x <= self.mapWidth do
-- draw in pyramid
if x == self.mapWidth - 13 then
local row = 1 -- how wide pyramid to be
while row < 7 do
local column = 1 -- might need to not say local everytime
while column <= row do
self:setTile(x, self.mapHeight / 2 - column, PYRAMID_BLOCK)
column = column + 1
end
-- creates column of tiles going to bottom of map
for y = self.mapHeight / 2, self.mapHeight do
self:setTile(x, y, TILE_BRICK)
end
row = row + 1
x = x + 1
end
end
-- draw in flagpole
if x == self.mapWidth - 2 then
self:setTile(x, self.mapHeight / 2 - 3, FLAGPOLE_TOP)
self:setTile(x, self.mapHeight / 2 - 2, FLAGPOLE_MIDDLE)
self:setTile(x, self.mapHeight / 2 - 1, FLAGPOLE_BOTTOM)
end
-- creates column of tiles going to bottom of map
for y = self.mapHeight / 2, self.mapHeight do
self:setTile(x, y, TILE_BRICK)
end
-- next vertical scan line
x = x + 1
end
-- start the background music
self.music:setLooping(true)
self.music:play()
end
-- return whether a given tile is collidable
function Map:collides(tile)
-- define our collidable tiles
local collidables = {
TILE_BRICK, JUMP_BLOCK, JUMP_BLOCK_HIT,
MUSHROOM_TOP, MUSHROOM_BOTTOM, PYRAMID_BLOCK
}
-- iterate and return true if our tile type matches
for _, v in ipairs(collidables) do
if tile.id == v then
return true
end
end
return false
end
-- this function doesn't appear to be doing anything...
function Map:victory(player, flag)
if self.player.x >= self.flag.x * map.tileWidth then
return true
else
return false
end
end
-- function to update camera offset with delta time
function Map:update(dt)
self.player:update(dt)
self.flag:update(dt)
-- keep camera's X coordinate following the player, preventing camera from
-- scrolling past 0 to the left and the map's width
self.camX = math.min(self.mapWidthPixels - VIRTUAL_WIDTH, math.max(self.camX, math.min(self.player.x - VIRTUAL_WIDTH / 2,
math.min(self.mapWidthPixels - VIRTUAL_WIDTH, self.player.x))))
end
-- gets the tile type at a given pixel coordinate
function Map:tileAt(x, y)
return {
x = math.floor(x / self.tileWidth) + 1,
y = math.floor(y / self.tileHeight) + 1,
id = self:getTile(math.floor(x / self.tileWidth) + 1, math.floor(y / self.tileHeight) + 1)
}
end
-- returns an integer value for the tile at a given x-y coordinate
function Map:getTile(x, y)
return self.tiles[(y - 1) * self.mapWidth + x]
end
-- sets a tile at a given x-y coordinate to an integer value
function Map:setTile(x, y, id)
self.tiles[(y - 1) * self.mapWidth + x] = id
end
-- renders our map to the screen, to be called by main's render
function Map:render()
for y = 1, self.mapHeight do
for x = 1, self.mapWidth do
local tile = self:getTile(x, y)
if tile ~= TILE_EMPTY then
if tile > 16 then
love.graphics.draw(self.pyramid_block,
(x - 1) * self.tileWidth, (y - 1) * self.tileHeight)
else
love.graphics.draw(self.spritesheet, self.sprites[tile],
(x - 1) * self.tileWidth, (y - 1) * self.tileHeight)
end
end
end
end
self.player:render()
self.flag:render()
end
You're using 'self.player' which is a brand new player at the init() (which is a different player than the one you want)
You need to use 'player' that you passed as an argument to check if player.victory is true.

Why is setting the expiry time ineffective in Redis

Now i use redispipeline to set key to redis ,and set key timeout.After running codes and timeout,and the keys are still in redis?It seemed doesn't work.Is there sth wrong with my code.
X[1] is dict,such as dict["a"]=b dict["c"]=d
redis_pool = redis.ConnectionPool(host = redis_ip, port = redis_port, decode_responses = False)
redis_connection = redis.StrictRedis(connection_pool = redis_pool)
redis_pipeline = redis_connection.pipeline(transaction = False)
sync_count = 0
for x in iterator:
key = suffix + x[0]
#value = str(x['tagid'])+"\t"+x['tag_name']
value = x[1]
redis_pipeline.hmset(key, value)
redis_pipeline.expire(key,60)
sync_count += 1
if (sync_count % 100 == 0):
result = redis_pipeline.execute()
print (result)
time.sleep(0.001)
break
if sync_count == 10000:
break
redis_pipeline.execute()

the object variable in link list reverse codes

Hi this is a link list reverse code. Anyone can help me understand that the differecen between r = Solution().reverseList(n1) and r = Solution().reverseList(ListNode(1)).
I wonder why the output which is [1] and [3,2,1] respectively and what is the purpose of to assign the ListNode object to a variable.
class Solution(object):
def reverseList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
#if not head:
# return None
prev = head
curr = prev.next
while curr:
next =curr.next
curr.next = prev
prev = curr
curr = next
head.next = None
return prev
class ListNode(object):
def __init__(self,x):
self.val = x
self.next = None
def to_list(self):
return[self.val] + self.next.to_list() if self.next else [self.val]
if __name__ == "__main__":
n1 = ListNode(1)
n2 = ListNode(2)
n3 = ListNode(3)
n1.next = n2
n2.next = n3
r = Solution().reverseList(n1)
print r.to_list()
#assert r.to_list() == [3,2,1]
I understood now.
ListNode(1) has no "next" set, so it will forever return 1 as it's list, whereas n1 starts the same way (as just node of 1), but the "next" attribute is set to node of 2 whose "next" is set to node of 3, so n1 carries all of this information with it, and ListNode(1) does not.
welcome any comments.

Find missing number in sorted array

Whats wrong with this code ? Not able able to search missing number in a consecutive array using binary search.
a = [1,2,3,4,5,7,8]
lent = len(a)
beg =0
end = lent-1
while beg < end:
mid = (beg + end) / 2
if (a[mid]-a[beg])==(mid - beg):
beg = mid + 1
else:
end = mid -1
if(beg == end):
mid = (beg + end) / 2
print "missing"
print a[0]+ beg
Update #1: Yes, there was another one mistake. You're right. Here's updated version
Try this variant:
a = [1,2,3,4,5,7,8]
lent = len(a)
beg =0
end = lent-1
while beg < end:
mid = (beg + end) / 2
if (a[mid]-a[beg])==(mid - beg):
beg = mid
else:
end = mid
if abs(beg-end) <= 1:
print "missing: %s" % (a[0] + max(beg, mid),)
Result:
missing: 6
Also, try use functions, so you could easily test and debug your code on different lists:
def find_missing(a):
lent = len(a)
beg =0
end = lent-1
while beg < end:
mid = (beg + end) / 2
if (a[mid]-a[beg])==(mid - beg):
beg = mid
else:
end = mid
if abs(beg-end) <= 1:
return a[0] + max(beg, mid)
a = [1,2,3,4,5,7,8]
print find_missing(a)
a = [1,3,4,5,6]
print find_missing(a)
a = [1,2,3,4,5,7,8,9,10]
print find_missing(a)
Result:
6
2
6
//Implementation of the algorithm using java.
static int missingNumber(int [] nums) {
int i=0;
while(i < nums.length) {
int correct = nums[i];
if (nums[i] < nums.length && nums[i] != nums[correct]) {
swap(nums, i, correct);
} else {
i++;
}
}
for( int index=0; index<nums.length; index++){
if(index != nums[index]) {
return index;
}
}
return nums.length;
}
static void swap(int[] nums, int first, int second) {
int temp = nums[first];
nums[first] = nums[second];
nums[second] = temp;
}
int findMiss(int arr[], int low, int high)
{
if(low>high)
return -1;
if(arr[low]-1 != low)
return arr[low]-1;
int mid = (low + high) / 2;
if(arr[mid]-1 != mid)
return findMiss(arr,low,mid);
else
return findMiss(arr,mid+1,high);
}

Passing variables in python from radio buttons

I want to set values depends on the selected radio button and to use that values in other function.
Whatever i try, i always get the same answer
NameError: global name 'tX' is not defined #
import maya.cmds as cmds
from functools import partial
winID='MSDKID'
def init(*args):
print tX
print tY
print tZ
print rX
print rY
print rZ
return
def prozor():
if cmds.window(winID, exists = True):
cmds.deleteUI(winID);
cmds.window()
cmds.columnLayout( adjustableColumn=True, rowSpacing=10 )
cmds.button(label = "Init")
cmds.button(label = "MirrorSDK",command=init)
cmds.setParent( '..' )
cmds.setParent( '..' )
cmds.frameLayout( label='Position' )
cmds.columnLayout()
collection2 = cmds.radioCollection()
RButton0 = cmds.radioButton( label='Behavior' )
RButton1 = cmds.radioButton( label='Orientation' )
cmds.button(l='Apply', command = partial(script,RButton0,RButton1,))
cmds.setParent( '..' )
cmds.setParent( '..' )
print script(RButton0,RButton1)
cmds.showWindow()
def script(RButton0,RButton1,*_cb_val):
X = 0
rb0 = cmds.radioButton(RButton0, q = True, sl = True)
rb1 = cmds.radioButton(RButton1,q = True, sl = True)
if (rb0 == True):
tX = -1
tY = -1
tZ = -1
rX = 1
rY = 1
rZ = 1
if (rb1 == True):
tX = -1
tY = 1
tZ = 1
rX = 1
rY = -1
rZ = -1
return tX,tY,tZ,rX,rY,rZ
prozor()
The variables you are defining in script() are local to that function. The other functions don't see them.
If you need multiple UI elements to share data, you'll probably need to create a class to let them share variables. Some reference here and here