Page 1 of 2
[solved] Resetting a table back to it's original values
Posted: Mon Sep 29, 2014 10:28 pm
by AlexCalv
I'm trying to 'restart' my game and the only way I can think of is calling the player table when you lose. I know I could turn the table into a function but if I do that, then I have to change all of my functions from "player.Draw()" to "playerDraw()" for example. If I don't then I get an error. Is there a way to I guess, recall a table with it's original set values?
This is my player table if you need it.
Code: Select all
player = {x = 10,
y = 636,
width = 80,
height = 80,
speed = 300,
xVel = 0,
yVel = 0,
jumpVel = -800,
score = 0,
lives = 3,
state = "stand"
}
Re: Resetting a table back to it's original values
Posted: Tue Sep 30, 2014 12:53 am
by davisdude
You could make another variable with the exact same values as the other table, and then at the end reset it. Note that this is
not allowed:
This doesn't work because the values in oldTable will be changed each time the "table's" values are changed. Instead, do something like this:
Code: Select all
local function DeepCopy( Table, Cache ) -- Robin's code.
if type( Table ) ~= 'table' then
return Table
end
Cache = Cache or {}
if Cache[Table] then
return Cache[Table]
end
local New = {}
Cache[Table] = New
for Key, Value in pairs( Table ) do
New[Globals.DeepCopy( Key, Cache)] = Globals.DeepCopy( Value, Cache )
end
return New
end
Then implement it:
Code: Select all
Table = { 1, 2, 3 }
OldTable = DeepCopy( Table )
-- Game stuff
if RestartGame then
Table = OldTable
-- All the other variables, in this style.
end
Re: Resetting a table back to it's original values
Posted: Tue Sep 30, 2014 4:01 am
by ivan
There are several ways to solve this.
Davisdude's method is very good although I prefer a slightly different implementation:
Code: Select all
--- Copies the contents from one table to another
--- Does not remove existing elements in the destination table
-- @param s Source table
-- @param d Destination table (optional)
-- @return The destination table
function table.copy(s, d)
assert(s, "table is nil")
assert(s ~= d, "source and destination tables must be different")
d = d or {}
for k, v in pairs(s) do
if type(v) == "table" then
if d[k] == nil then
d[k] = {}
end
table.copy(v, d[k])
else
d[k] = v
end
end
return d
end
Slightly different from Davisdude's example, since it does not overwrite the "current" reference:
Code: Select all
default = { 1, 2, 3 }
current = {}
function restartgame()
table.copy(default, current)
end
I have a generic "replace" function for tables that you might find useful:
Code: Select all
--- Replaces the existing values in a table
--- Optionally, converts the source values to match the destination types
-- @param s Source table
-- @param d Destination table
-- @param m Convert source values to destination types (optional)
-- @return The number of replaced values
local function toboolean(v)
if v == 'true' then
return true
elseif v == 'false' then
return false
end
end
function table.replace(s, d, m)
assert(s, "source table is nil")
assert(d, "destination table is nil")
assert(s ~= d, "source and destination tables must be different")
local n = 0
-- iterate destination table
for k, dv in pairs(d) do
local sv = s[k]
local dt = type(dv)
local st = type(sv)
if st == "table" and dt == "table" then
-- recursive
n = n + table.replace(sv, dv, m)
else
-- convert source value to destination type
if m == true and dt ~= st then
if dt == 'boolean' then
sv = toboolean(sv)
elseif dt == 'number' then
sv = tonumber(sv)
elseif dt == 'string' then
sv = tostring(sv)
else
sv = nil
end
end
-- replace destination value
if sv ~= nil and dv ~= sv then
d[k] = sv
n = n + 1
end
end
end
return n
end
It works similar to "table.copy" but it only overwrites existing values so:
Code: Select all
default = { 1, 2, 3 }
current = {}
table.replace(default, current) -- does nothing since the destination table has no values that can be replaced
current = { 4, 5, 6 }
table.replace(default, current) -- replaces "4, 5, 6" with "1, 2, 3"
If your "player" table includes functions then why not:
Code: Select all
local player = {}
local playermt = { __index = player }
function player:create()
local self = {}
setmetatable(self, playermt)
self:reset()
return self
end
function player:reset()
self.x = 10
self.y = 636
self.width = 80
self.height = 80
-- etc
end
Re: Resetting a table back to it's original values
Posted: Tue Sep 30, 2014 7:34 am
by Robin
Note that ivan's copy functions crash if you pass a table with cycles.
Now, what you probably want is none of this, but just a reset function:
Code: Select all
player = {}
function player.Reset()
player.x = 10
player.y = 636
player.width = 80
player.height = 80
player.speed = 300
player.xVel = 0
player.yVel = 0
player.jumpVel = -800
player.score = 0
player.lives = 3
player.state = "stand"
end
player.Reset()
Then you can simply call
player.Reset().
Re: Resetting a table back to it's original values
Posted: Tue Sep 30, 2014 11:22 am
by rmcode
I also prefer Robin's method. It's simple and it works. You could store the default values in "constants" at the top of your class. That way you only have to change them in one place later on.
Code: Select all
local HEALTH = 300;
local PLAYER_X = 10;
local PLAYER_Y = 636;
player = {
x = PLAYER_X,
y = PLAYER_Y,
...
}
function player.reset()
player.x = PLAYER_X;
player.y = PlAYER_Y;
...
end
Re: Resetting a table back to it's original values
Posted: Thu Oct 02, 2014 4:12 am
by kotwarrior
You could also try and set the original table as a metatable of the given table, just cache it using this function:
Code: Select all
local cache = function(tab)
local extra = tab;
setmetatable(tab, extra);
end;
and retrieve it using this one:
Code: Select all
local retrieve = function(tab)
return getmetatable(tab);
end;
To access these functions, you can use:
Code: Select all
local item = {"plr"; "important data";};
cache(item);
local item = {};
-- to retrieve
local item = retrieve(item);
Re: Resetting a table back to it's original values
Posted: Thu Oct 02, 2014 8:43 am
by Robin
That... doesn't work. Like, at all.
Re: Resetting a table back to it's original values
Posted: Thu Oct 02, 2014 10:02 am
by kikito
I need to point out that you actually don't need to reset the table to its original values. Like, at all. Just return a new table and override the old one.
Code: Select all
local player
...
function newPlayer()
return {
x = 10,
y = 636,
width = 80,
...
state = "stand"
}
end
Every time you need to "reset" the player you can do this:
Coding this way (creating new values instead of reusing existing ones) has less problems than what you are doing - for example, it's much more difficult to "forget resetting one value". It's also not possible to "get old values from the previous game" if you destroy and recreate everything.
Re: Resetting a table back to it's original values
Posted: Thu Oct 02, 2014 11:01 pm
by Clouds
kikito wrote:I need to point out that you actually don't need to reset the table to its original values. Like, at all. Just return a new table and override the old one.
....
Coding this way (creating new values instead of reusing existing ones) has less problems than what you are doing - for example, it's much more difficult to "forget resetting one value". It's also not possible to "get old values from the previous game" if you destroy and recreate everything.
Maybe it's good practice anyway, but if you do this, you have to also code the rest of your program to never ever set another variable to point to the table in question (unless it's always re-set before use). A new table, being a new object, won't update old references to the original table.
Re: Resetting a table back to it's original values
Posted: Fri Oct 03, 2014 6:31 am
by kikito
Clouds wrote:Maybe it's good practice anyway, but if you do this, you have to also code the rest of your program to never ever set another variable to point to the table in question (unless it's always re-set before use). A new table, being a new object, won't update old references to the original table.
No, you can have references to variables. It's just that your code needs to (re)assigns them when they need to be "reset". I think this is a good practice.