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:

Code: Select all

oldTable = table
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:

Code: Select all

player = newPlayer()
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.