Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Minesweeper cartridge to site #128

Merged
merged 1 commit into from
Oct 23, 2021
Merged

Conversation

claudiomattera
Copy link
Contributor

This is a classic Minesweeper game.

Playable by mouse: left click to uncover a tile, right click to flag a tile, left+right click to uncover additional tiles.

Source at https://gitlab.com/claudiomattera/minesweeper

@netlify
Copy link

netlify bot commented Oct 23, 2021

✔️ Deploy Preview for wasm4 ready!

🔨 Explore the source changes: 3b8973a

🔍 Inspect the deploy log: https://app.netlify.com/sites/wasm4/deploys/6173f5ae084e560007a4e762

😎 Browse the preview: https://deploy-preview-128--wasm4.netlify.app

@aduros aduros merged commit 6e71e46 into aduros:main Oct 23, 2021
@aduros
Copy link
Owner

aduros commented Oct 23, 2021

Wow, amazing game! 💥 Nice use of mouse/touch controls!

The code is really well organized and instructive, definitely one of the larger Rust projects to date. Automatically generating images from the build is interesting.

How was your experience building this game? Anything we can improve for Rust or WASM-4 in general?

@claudiomattera
Copy link
Contributor Author

Wow, amazing game! boom Nice use of mouse/touch controls!

Thanks!

The hardest part was to figure out how to detect a simultaneous left and right click.
I tried at least three different approaches before I found a working one.

The code is really well organized and instructive, definitely one of the larger Rust projects to date.

It is way over engineered :D
But it was fun to make.
I do not really have experience in game development, so there are likely a lot of parts that can be done better.

Automatically generating images from the build is interesting.

I started working on a different game before this (which turned out to be too complex, so I decided to work on something simpler for starters), and I kept forgetting updating my sprites, so I wrote that small library.
The downside is that it adds quite a bit of dependencies, even if only at build time.

How was your experience building this game? Anything we can improve for Rust or WASM-4 in general?

One thing I find lacking was the support for randomness.
I found a mention of a seed() function exposed by the runtime in another issue, so I just tried to define it in the wasm4 module and it worked fine.
Is there any reason why it is not defined already?

I also wonder if there are any ways to check the cartridge size.

@aduros
Copy link
Owner

aduros commented Oct 28, 2021

One thing I find lacking was the support for randomness. I found a mention of a seed() function exposed by the runtime in another issue, so I just tried to define it in the wasm4 module and it worked fine. Is there any reason why it is not defined already?

seed currently exposes the device timer which I'm not too happy about. I might change it in the future to return a fixed number that's randomized at start up, and not the current time.

Another approach (used by real-world games on classic consoles) do things like counting the number of frames before the first button press to seed their RNG.

I also wonder if there are any ways to check the cartridge size.

You should just be able to check the file size. Note that we don't currently enforce the 64 KB file size limit but it might be enforced in the future (likely with a mode to disable it during cart development).

It looks like the minesweeper cart is pretty large, do you know where most of the space is coming from?

@claudiomattera
Copy link
Contributor Author

claudiomattera commented Oct 29, 2021

Another approach (used by real-world games on classic consoles) do things like counting the number of frames before the first button press to seed their RNG.

This is not really my area of expertise, but would not that approach have quite low entropy?
Is there anything better suitable as a random seed?
Perhaps a random number generated by the device random seed, so that the game does not have access to the device time?

It looks like the minesweeper cart is pretty large, do you know where most of the space is coming from?

There is no need to sugar coat it, that cartridge is straight gigantic :D

I tried to experiment a bit, and it seems that even when building for release there are still large debug-related sections in the final executable.

twiggy top -n 30 target/wasm32-unknown-unknown/release/minesweeper.wasm
 Shallow Bytes │ Shallow % │ Item
───────────────┼───────────┼───────────────────────────────────────────────
         38546 ┊    22.61% ┊ custom section '.debug_str'
         20513 ┊    12.03% ┊ data[2]
         16344 ┊     9.59% ┊ custom section '.debug_info'
         14981 ┊     8.79% ┊ custom section '.debug_line'
         13144 ┊     7.71% ┊ custom section '.debug_pubnames'
         11937 ┊     7.00% ┊ "function names" subsection
          9904 ┊     5.81% ┊ data[0]
          7625 ┊     4.47% ┊ update
          5816 ┊     3.41% ┊ custom section '.debug_ranges'
          ...

Apparently, modern Rust versions should remove those sections automatically when lto = true is set, but I could not make it work.
I could not even manually pass --gc-sections to rust-lld, because it would fail with "unknown argument error".

So I used wasm-gc to remove those sections, and the size got down to 73 KiB.
It is still very large, though.

twiggy top -n 30 target/wasm32-unknown-unknown/release/*.wasm
 Shallow Bytes │ Shallow % │ Item
───────────────┼───────────┼───────────────────────────────────────────────────
         20513 ┊    27.65% ┊ data[2]
         10195 ┊    13.74% ┊ "function names" subsection
          9904 ┊    13.35% ┊ data[0]
          7013 ┊     9.45% ┊ update
          1194 ┊     1.61% ┊ core::str::slice_error_fail::h2e3fd31345cd0cd6
          1158 ┊     1.56% ┊ core::fmt::Formatter::pad::h2c14c4abe79aa87d
          1107 ┊     1.49% ┊ <&T as core::fmt::Debug>::fmt::h66859a836ed27c61
          1066 ┊     1.44% ┊ <bare_io::error::ErrorKind as core::fmt::Debug>::fmt::h021dd1ff93806cf4
           817 ┊     1.10% ┊ core::unicode::printable::is_printable::h82bcfa07010ba343
           779 ┊     1.05% ┊ <buddy_alloc::non_threadsafe_alloc::NonThreadsafeAlloc as core::alloc::global::GlobalAlloc>::dealloc::hb690b3c45f61e9bb
           755 ┊     1.02% ┊ <core::fmt::builders::PadAdapter as core::fmt::Write>::write_str::h17901f0f345f4c66
           ...

I could remove that "function names" subsection using wasm-opt, but I do not know what are those two data sections that take the majority of space.

data[0] seems related to the sprite I use in game (a custom font table).
I removed all unused glyphs and the size went down from 9904 to 7408.
The source PNGs were respectively 1.5 KiB and 224 B, so it must contain other stuff.
I have no idea what is inside data[2].

It also looks like string formatting functions take quite a bit of space, there is probably room for improvement there.

@claudiomattera
Copy link
Contributor Author

claudiomattera commented Nov 3, 2021

I figured out why the cartridge was so large.
Static arrays used by buddy-alloc are the culprits.

Using raw memory addresses seems to solve the problem, I opened a pull request with more details #138.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants