Important Dates

Currently Online

Latest Posts

Topic: Scenarios: File I/O

GunChleoc
Avatar
Joined: 2013-10-07, 15:56
Posts: 3159
Ranking
One Elder of Players
Location: RenderedRect
Posted at: 2018-04-28, 16:59

Can you provide me with the scenario that you are using for testing your branch?


Busy indexing nil values

Top Quote
ypopezios
Avatar
Joined: 2018-04-20, 00:22
Posts: 220
Ranking
Widelands-Forum-Junkie
Posted at: 2018-04-28, 21:13

GunChleoc wrote:

good players should still end up with a slight advantage as a reward, but not too much

I'm not sure if the following is relevant, but I thought of sharing it.

I understand the above desire as to bound a value between an acceptable range, so as it's not too big a dis/advantage. For any quantity which can have any value (val) between 0 and infinity, instead of comparing it against the desired bounds (minval and maxval) and equal all excessive values to them, we could bound it in a more challenging way through the following formula:

fairval = minval + val * (maxval - minval) / (val + maxval - minval)

Suppose that we want to bound a quantity between 40 and 60. The common way through comparisons would behave like this:

  • All values less than 40 will become 40.

  • All values between 40 and 60 will stay as they are.

  • All values greater than 60 will become 60.

While the formula would behave like this:

  • A 0 value will become 40.

  • All values between 0 and 780 will get compressed within 40 and 60 in a way of increasing difficulty.

  • Any value above 780 will round to 60.

If the central idea is relevant, the formula could be adjusted to bring its behavior closer to more special desires.


Top Quote
Nordfriese
Avatar
Joined: 2017-01-17, 18:07
Posts: 699
Ranking
One Elder of Players
Location: 0x55555d3a34c0
Posted at: 2018-04-29, 09:13

Can you provide me with the scenario that you are using for testing your branch?

Here´s my test script:

local game = wl.Game()

print("Starting to WRITE...")
game:save_campaign_data("TestCampaign", "test_scenario", {
   abc = "Hello",
   d = 8,
   efg = "World",
   h = -999,
   i = true,
   jklmno = 0,
   pq = "Hello",
   rstuv = 999,
   wxyz = false
})
print("Done.")

print("Starting to READ...")
local result = game:read_campaign_data("TestCampaign", "test_scenario")
print("Returned:")
if result == nil then
   print ("<nil>")
else
   print ("  " .. #result .. " item(s)")
   for k,v in ipairs(result) do
      print ("    " .. k .. " = " .. v)
   end
end
print("Done.")

The desired output would be

Returned:
  9 item(s)
   abc = "Hello",
   d = 8,
   efg = "World",
   h = -999,
   i = true,
   jklmno = 0,
   pq = "Hello",
   rstuv = 999,
   wxyz = false

But I always get:

Returned:
  0 item(s)

The .wcd file is written to correctly, though in wrong order.

@ypopezios: That´s a very good idea! I was going to use 1/x or exp(-x) functions to balance difficulties, but your approach will work much better (the only difficulty in scripting will be finding good min/max values). I´ll definitely use it in scenario #4 (though it will be some time until even #3 is complete…) face-smile.png


Top Quote
GunChleoc
Avatar
Joined: 2013-10-07, 15:56
Posts: 3159
Ranking
One Elder of Players
Location: RenderedRect
Posted at: 2018-04-29, 17:34

Thanks for the formula face-smile.png

The C++ code was actually correct, except for some test output that caused it to push strings instead of numbers.

Lua is a bit tricky when your tables don't have integer keys:

  1. You can't count the number of items
  2. You have to iterate with pairs instead of ipairs.
  3. Order of keys is not stable, so don't waste your time trying to get them back in the same order that you wrote them

I have fixed up your test script and added it to the test suite, in the plain.wmf map. You can run it to see the console output like this:

./widelands --verbose=true --datadir=data --datadir_for_testing=. --disable_fx=true --disable_music=true --language=en_US --scenario=test/maps/plain.wmf --script=test/maps/plain.wmf/scripting/test_campaign_data.lua

You're doing a good job here venturing into C++ face-smile.png

Edited: 2018-04-29, 17:36

Busy indexing nil values

Top Quote
Nordfriese
Avatar
Joined: 2017-01-17, 18:07
Posts: 699
Ranking
One Elder of Players
Location: 0x55555d3a34c0
Posted at: 2018-05-02, 12:29

I hate spending so much time debugging my code when only the test script is wrong… now it works. Thanks face-smile.png

I pulled out the code that parses/creates the table into a recursive read/write function, so tables can now be used as values. Scenarios can save subtables, e.g.

game:save_campaign_data("test_campaign", "test_scenario", {
   a_key = 0,
   another_key = {
      a = 0,
      b = "abc",
      c = {
         a = 4,
         b = false,
         xxx = {},
         yyy = "Hello",
         zzz = {
            key1 = "Hello",
            key2 = "You",
            key3 = "World",
            key4 = "!",
            subtbl = {
               ...
            },
            also_a_subtable = {
               ...
            }
         }
      }
   }
})

One other thing it would be nice to have is to permit arrays as well as tables. Using an array currently causes a coroutine error ("invalid key to 'next'"). I don´t know how to fix that though…


Top Quote
GunChleoc
Avatar
Joined: 2013-10-07, 15:56
Posts: 3159
Ranking
One Elder of Players
Location: RenderedRect
Posted at: 2018-05-03, 08:46

Nordfriese wrote:

I hate spending so much time debugging my code when only the test script is wrong… now it works. Thanks face-smile.png

Yep. It led me a merry chase too face-wink.png

I pulled out the code that parses/creates the table into a recursive read/write function, so tables can now be used as values. Scenarios can save subtables

Very nice!

One other thing it would be nice to have is to permit arrays as well as tables. Using an array currently causes a coroutine error ("invalid key to 'next'"). I don´t know how to fix that though…

What do you mean by that - tables that use ints instead of strings as keys? I guess you'd have to iterate over the current table to check if all keys can be reverted to consecutive ints, starting from 1. This check could be done when saving if you write a hint to the file, or during loading.


Busy indexing nil values

Top Quote
Nordfriese
Avatar
Joined: 2017-01-17, 18:07
Posts: 699
Ranking
One Elder of Players
Location: 0x55555d3a34c0
Posted at: 2018-05-03, 18:48

Experimented some more and it turned out that actually the error message is incorrect. It´s luaL_checkstring() that can´t cope with integer keys for whatever reason, not lua_next(). Checking for lua_type(L, -1) fixes that problem.

I guess you'd have to iterate over the current table to check if all keys can be reverted to consecutive ints, starting from 1.

As far as I know, tables can either be arrays with indices 1..n, or have only strings as keys.
I now implemented a solution where integer keys are ignored when saving; and entries with no keys defined are assigned to an integer key when reading. If my guess is incorrect, this design is fragile though…

Finally, one small bug remaining: In arrays, nil values are silently omitted while saving; so any indices after nil values are decremented in reading the data. Example:

game:save_campaign_data("test_campaign", "test_scenario", {
   "Hello", nil, "World" -- index of "World" is 3
}

local data = game:read_campaign_data("test_campaign", "test_scenario")
--> data = {"Hello", "World"} -- index of "World" is 2

I have no idea where this could come from…


Top Quote
GunChleoc
Avatar
Joined: 2013-10-07, 15:56
Posts: 3159
Ranking
One Elder of Players
Location: RenderedRect
Posted at: 2018-05-03, 21:42

You could check for nil values when saving and trigger report_error if there are any. If somebody is saving nil values, chances are good that there is something wrong with the data being saved anyway.


Busy indexing nil values

Top Quote
Nordfriese
Avatar
Joined: 2017-01-17, 18:07
Posts: 699
Ranking
One Elder of Players
Location: 0x55555d3a34c0
Posted at: 2018-05-04, 09:37

But if a nil value exists, the mandatory typecheck should return neither "string", "number", "boolean" nor "table", so the if/else block would already trigger an error, just like it does when an object is used as a value. Perhaps this is the fault of lua_next(), it appears to skip keys that are mapped to nil. I don´t know why it confuses the int-keys then, though.
I wrote a warning for this bug in the documentation of save_campaign_data(), perhaps we could leave it as it is?


Top Quote
GunChleoc
Avatar
Joined: 2013-10-07, 15:56
Posts: 3159
Ranking
One Elder of Players
Location: RenderedRect
Posted at: 2018-05-04, 16:57

I'll have to take a look at the code. Please ping me if you don't hear from me by Monday.


Busy indexing nil values

Top Quote