Testing Counterfeit Monkey on Iphone?

If the game sticks at the compass rose, there was almost certainly an error during the loading process.

We’ve had trouble with very large blorb files in the past, so this may just not be runnable with Quixe as it stands.

iOS Safari has a developer console (look under Settings/Safari/Advanced) but you have to hook it into MacOS Safari to see what’s going on.

The first thing to try would be running the same web page with MacOS Safari, to see if that works.

Yay, it works! Turns out I had private surf mode on. With that switched off, it runs. Not exactly well, but playable.

EDIT: Well, playable except for the frequent crashes, that is.

EDIT 2: Recurring bugs in mobile Safari seem to be: Room descriptions sometimes not printed, occasional “A problem occurred with this web page so it was reloaded” (and the game restarts), keyboard not responding on restart.

None of these show up in desktop Safari, or in Firefox on my Windows computer. Out-of-memory issues, perhaps? The developer console seems to be wiped clean when the page reloads. It would be interesting to try it out on the latest and greatest Iphone or similar (mine is a 5s).

Right, if anyone wants to try it out, here is a NeoCities link. Note that the map isn’t working – there will be a black rectangle in its place. There also seems to be a problem where the initial question (“Can you hear me?”) won’t show if there is a startup precomputation file present. If the game starts at an empty prompt, just type YES.

Out of memory errors are almost certainly the problem. The code itself is well-tested (and iOS Safari is the same codebase as MacOS Safari to begin with).

I guess that means it’s running out of disk space to page to as well? My phone has 1 GB RAM and 4,5 GB free disk space, and that is apparently not enough.

I wonder where in the game we are wasting most memory. Do unused properties use a lot of space?

This is memory managed by the Javascript interpreter, so it’s more limited than general OS memory. I don’t know what the interpreter’s policy on limits is.

The immediate problem is a product of two factors: CM uses a very large amount of RAM, and Quixe uses inefficient Javascript arrays for storing the game file and undo snapshots. The latter is my problem (I will eventually switch over to Uint8Arrays). The former, you’ll have to dig into.

(Attributes use space. Properties use space, particularly if they’re defined for larger kind-classes than necessary. Tables use space. Relations can use a lot of space.) (Optimizing RAM use in Inform is an old problem because there are no easy answers.)

One thing that I’m sure would help: instead of using a startup precomputation save-file, put the precomputed values directly into the Inform source code. That doesn’t change RAM usage but it cuts the undo-snapshot files way down.

Thanks, that’s good advice!

Another stupid question: what would the proper way be to represent all that data in Inform 6 code? I was considering putting the FWMatrix route-finding data in the source code, but I had no idea how to do it.

Array path_finding -> 1 3 1 1 12 4 ...
with a total of 10 000 numbers on a single line, or 10 000 lines of

path_finding->0 = 1; path_finding->1 = 3; ...
?

The former.

Undo snapshots are compressed (in a very simple way) by comparing to the original data file. So any array which is never changed will not take up (much) snapshot space. If you change the array contents (at game startup or later) then it has to be snapshotted as different.

Now, it’s possible that the I6 compiler will choke on arrays that size. I haven’t tested that case. I’m willing to treat that as a compiler bug and fix it, if it happens.

Yes, it seems that the compiler can handle it. Thanks! Another case of “should have tried it before asking”, but I suppose I was hoping there was some nicer syntax for this that I had missed.

If the arrays could be put in a dedicated extension then it would be easier to regenerate them if necessary. It also wouldn’t be too hard to make a function which would output that array.

Do we know which rules are making the biggest difference to the memory/size of saves or undos? Maybe we could make it save between each of the rules to tell where the big jumps are.

The memory layout will also affect the savefile size, and it might be possible to define some arrays earlier.

I’ve added a testing extension to the repo, which (by manually listing every rule) saves before each rule.

The results:

  • By the time of the Flexible Windows sort the Table of User Styles rule the save file is already at 17KB. Presumably most of this comes from INITIALISE_MEMORY_R() (which I’m guessing also needs to run before it will save files for some reason) through its creation of the heap etc. I doubt we can do anything to cut this.
  • The alternative position player in model world rule adds 7KB
  • The new declare everything initially unmentioned rule adds 6KB
  • The move all quips to the quip-repository rule adds 3KB
  • The initialize route-finding rule adds 31KB
  • The initialize drawers rule adds 8KB
  • The setting proffered rule adds 6KB
  • The initialize hash codes rule adds 12KB

Some of these things could presumably be relatively easily changed.

  • Instead of running the new declare everything initially unmentioned rule can we just make that a default property?
  • Testing code could output instructions which set the startup location of all quips to be in the quip-repository (in the same way that Conversation Builder does). And eventually we could even automatically run and regenerate that code as part of the testing framework.
  • The initial hash codes could also be set in the same way

Great! Plenty to work with, there.

I have some functioning code now that hard-codes the route-finding into the source, which obviously is the big one.

EDIT: Here, if anyone wants to have a look: https://github.com/angstsmurf/counterfeit-monkey/tree/data-in-source

The I7 compiler really wants to include " with room_index -1" in every room declaration, so I had to create a new property for this (room_index_2) that could be hardcoded to something else.

Now I have something to do this Christmas. :wink:

Zarf: to really optimise for smaller memory snapshots, it would help if Inform 6 gave us more control over the memory map, such as setting particular arrays as static. But that would be a really big change to introduce, so I don’t expect it to happen.

Angstsmurf, what do you know about the proferring relationship? It produces a 1.1MB array, which from what I can tell is only rarely modified, and I think it is most of the 6KB snapshot increase I listed above. I think it could probably be replaced with a function based system, probably through an overloaded phrase “to decide whether (X) proffers (Y):”

Yes, the proffering relation is of course what keeps track of how things are created from other things. If we letter-remove the apple to create ale, the apple now proffers the ale and the ale is proffered by the apple, and I always get these confused.

It is complicated by the fact that a thing can be proffered by (and proffer) an arbitrary number of things. There are safety mechanisms in place that make sure that we don’t create infinite loops of things that proffer themselves.

Just like we have done with the mentioning relation, it could be replaced with a list property (or two in in this case, one for proffered by and one for proffers) that every thing has. When we replaced the mentioning relation, it made things much faster, and I have a hunch it might have saved memory as well. But perhaps there are downsides that I haven’t thought of. Well, apart from being a lot of work and a source of lots of potential bugs.

EDIT: If we just want to reduce the snapshot size, it would be much easier to declare in the source that things already have the proffering relations that the initialization rule (the setting proffered rule) gives them. A thousand lines like “The spill proffers the spill” etc.

This is a standing item on my “would be nice to have” list for the I6 compiler. But there aren’t that many uses for it.

Just a small update on this: I tried replacing the proffering relation with list properties, and while it reduced the number of opcodes called by quite a lot, nearly 14 million fewer in a full game test, it actually increased the amount of memory used: the startup data file grew from 42K to 98K, and the final undo files from 84K to 143K. This also meant that the total speed increase was no more than about 4 seconds for the entire game, i.e. the increase from fewer opcodes is offset by the overhead of creating and saving those larger undo snapshots.

This is likely because I used two list properties rather than one, in order to not have to iterate through every thing in the game world to check if something is proffered by something else. I don’t know if it would be worth the trouble to try doing it with just one list property instead.

In other news, the map and the initial question now work at tass.neocities.org. It still crashes on my phone, though.

EDIT: With undo file saving forced on (rather than saving undo snapshots to memory) the game actually seems to work fine on my phone. So far I’ve completed the first two acts. Any testing of the url above on mobile devices is welcome!

EDIT 2: Oops, I tried to save and the game crashed with the QuotaExceededError mentioned earlier. Don’t try that!

Right, I think I have a rough idea of what is going on.

QuotaExceededError obviously means that the allotted local storage space for the current browser session is full. Actually, if private mode is active in Safari, no local storage at all is allowed. There are apparently ways to check for this and avoid crashing. I guess I should open a feature request on the Glkote Github page.

If private mode is off, there is still only just enough storage for our ten allowed undo snapshots and a save file or two at the beginning of the game. As we get further and more of the game state changes, the size grows of both undo snapshots and save files. Then the storage space will no longer be enough, and we eventually get the QuotaExceededError crash again.

I’ve reduced the maximum number of undo snapshots from 10 to 5 now and use only a single save file. We’ll see how far I get this time.

I raised an issue on Github for the uncaught errors: github.com/erkyrath/glkote/issues/20

Though Quixe should not be running the ultra undo code… it should only be needed for Git.

Well, (ab)using Ultra Undo snapshots is the only way I have found so far to avoid the out of RAM related crashes mentioned above (“A problem occurred with this web page so it was reloaded”). Only to run into out of local storage crashes, of course. These seem to be easier to handle, though. Not only can we reduce the number of allowed undo files, it is also possible to delete leftover files with the built-in Quixe file manager.

EDIT: Thanks for reporting the problem, by the way!