Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 

Repository files navigation

MemBlocks

A falling-block puzzle game where gravity is driven by system memory pressure. The fuller your machine's memory, the faster the pieces drop.

Single C source file. Builds on Linux and Windows with no external library on Windows and only ncurses on Linux.

 memblocks - gravity from /proc/meminfo

 | . . . . . . . . . .|   SCORE 1400
 | . . . . . . . . . .|   LINES 6
 | . . . .[] . . . . .|
 | . . . .[] . . . . .|   NEXT
 | . . .[][] . . . . .|   [][]
 | . . . . . . . . . .|   [][]
 | . . . . . . . . . .|
 | . . . . . . . . . .|   SYSTEM MEMORY
 | . . . . . . . . . .|   2300 / 3997 MB used (57.5%)
 | . . . . . . . . . .|   [###########         ]
 | . . . . . . . . . .|   held 2048/2048 MB   free 1441 MB
 | . . . .:: . . . . .|
 | . . . .:: . . . . .|   GRAVITY
 | . . .:::: . . . . .|   [##########          ]
 -----------------------  386 ms  x2.1  top x26.7

Why

It started as a way to make operating system concepts visible. The gauge is a live readout of memory state, and the balloon allocator lets you push on that state and watch the game react. The interesting part is not the game loop, it is that reserving memory does nothing until pages are actually touched.

Build

Linux

sudo apt install libncurses-dev
cc -Wall -Wextra -O2 -o memblocks memblocks.c $(pkg-config --libs ncurses)

Use pkg-config rather than a bare -lncurses. Some distributions split terminfo into a separate library, and on those a plain -lncurses fails at link time with undefined references to setupterm or tgetent.

Windows

gcc -Wall -Wextra -O2 -o memblocks.exe memblocks.c

No -l flag and no library to install. The Win32 console backend is compiled into the source. Get a compiler from MSYS2 (pacman -S mingw-w64-x86_64-gcc), from w64devkit, or from the Code::Blocks installer whose filename contains mingw-setup. The plain Code::Blocks installer ships no compiler at all.

To use PDCurses instead of the built-in backend:

gcc -DUSE_PDCURSES -O2 -o memblocks.exe memblocks.c -lpdcurses

Windows executable, built on Linux

Often the easiest route if you already have a Linux box.

sudo apt install mingw-w64
x86_64-w64-mingw32-gcc -Wall -Wextra -O2 -o memblocks.exe memblocks.c

Copy the resulting .exe to the Windows machine. Verify it first:

file memblocks.exe        # want: PE32+ executable (console) x86-64

The result depends only on KERNEL32.dll and msvcrt.dll, both of which ship with Windows, so nothing needs to be distributed alongside it.

Run

./memblocks              # Linux
.\memblocks.exe          # Windows, from PowerShell

Needs a terminal at least 59 columns by 24 rows.

Run the self-test first if anything looks wrong. It skips the display entirely, so error messages stay readable:

./memblocks --selftest
meminfo: 3997 MB total, 320 MB used (8.0%)
defaults: reserve 256 MB, high anchor 0.872, drop 800 -> 30 ms (top x26.7)
all checks passed

Controls

Key Action
Left, Right Move
Up, x Rotate clockwise
c Rotate counter-clockwise
Down Soft drop
Space Hard drop
a Allocate one chunk of memory
A Allocate four chunks
z Release one chunk
Z Release everything
p Pause
r Restart
q Quit

Hold A for a few seconds and watch the gravity bar climb.

Options

Flag Default Meaning
-l LOW usage at startup Memory fraction mapped to the slowest drop
-h HIGH highest reachable Memory fraction mapped to the fastest drop
-B MS 800 Slowest drop interval
-F MS 30 Fastest drop interval
-g CURVE 1.5 Response shape. 0 is linear, higher ramps up sooner
-m MB 256 Size of one balloon allocation
-r MB 5% of RAM, min 256 Memory to leave available, never allocated
-M MB 0 Ceiling on total balloon size, 0 means no ceiling
-p MS 6 Page-in budget per frame
--selftest Run logic checks without opening a terminal

Examples:

./memblocks -F 15 -g 3        # much more aggressive speed-up
./memblocks -g 0              # linear response
./memblocks -r 1024           # leave a gigabyte alone
./memblocks -M 2048           # never hold more than 2 GB

How the speed works

Memory usage is sampled twice a second and smoothed with an exponential moving average, otherwise the interval jitters on every cache fluctuation. The usage fraction is mapped onto the drop interval between -B and -F.

The low anchor defaults to whatever usage is at launch. Without that, a machine sitting at 60% used would start the game already sprinting. The high anchor is computed from the reserve, so pressure 1.0 lands exactly at the last allocation the reserve permits. A fixed anchor left the top of the speed range unreachable.

The response curve is p * (1 + k) / (1 + k * p), which maps 0 to 0 and 1 to 1 for any k greater than -1, so bending it cannot break the endpoints. This is used in preference to pow() so the program does not need to link libm.

Measured on a 4 GB machine with default settings:

held      used            drop      speed
   0 MB    226 MB   5.7%   800 ms    x1.0
 523 MB    735 MB  18.4%   588 ms    x1.4
1052 MB   1279 MB  32.0%   404 ms    x2.0
1578 MB   1782 MB  44.6%   280 ms    x2.9
2257 MB   2474 MB  61.9%   159 ms    x5.0
3328 MB   3589 MB  89.8%    30 ms   x26.7

The last row is where the reserve stops further allocation, which is also where the speed range tops out.

Implementation notes

Available, not free. Free memory on Linux is nearly always small and says nothing useful, because the kernel uses spare RAM for page cache. The program reads MemAvailable from /proc/meminfo, which accounts for reclaimable cache and slab. On Windows it uses GlobalMemoryStatusEx and reads ullAvailPhys, which is what Task Manager calls Available and counts the standby list. The two are close analogues, though Windows keeps a much larger standby list, so the same physical machine reports different startup figures under each OS.

Reserving memory does not consume it. Both mmap and VirtualAlloc return address space that no physical frame backs until something writes to it. The balloon allocator therefore walks each allocation and stores one byte per page. That single line is what actually moves the gauge:

base[b->faulted] = 1;       /* one write per page faults it */

Faulting is spread across frames. Doing it up front with MAP_POPULATE measured about 700 ms for 256 MB, so pressing A would have frozen the game for nearly three seconds. Pages are touched under a per-frame time budget instead, which keeps input live and makes the memory bar fill visibly rather than jump.

The reserve accounts for pending pages. Memory that is mapped but not yet faulted has not appeared in MemAvailable yet, so it is subtracted before deciding whether another allocation is allowed. Without that, a burst of keypresses commits far past the limit.

Pieces carry their own background colour. Drawing them as a foreground colour on the terminal default made the white piece invisible on light themes and the blue one unreadable on dark ones. Solid cells are now black on the piece colour, so contrast does not depend on the terminal theme.

Platform layer

Four operations differ between the two systems. Everything else is one copy of the source.

Linux Windows
Memory /proc/meminfo GlobalMemoryStatusEx
Reserve pages mmap VirtualAlloc
Release pages munmap VirtualFree
Page size sysconf(_SC_PAGESIZE) GetSystemInfo
Clock clock_gettime QueryPerformanceCounter
Display ncurses built-in Win32 console backend

clock_gettime lives in libwinpthread on MinGW rather than the default libraries, which is why Windows uses the performance counter. getopt is replaced by a small hand-rolled parser because MSVC has none and MinGW's differs in error reporting. %zu is avoided throughout because MinGW's C runtime handles it inconsistently.

Builds clean under -Wall -Wextra -Wpedantic and against -std=c90, c99 and c11.

Troubleshooting

fatal error: ncurses.h: No such file or directory The development headers are missing. The runtime library being present is not enough. sudo apt install libncurses-dev, or dnf install ncurses-devel on Fedora.

undefined reference to 'stdscr', 'wmove', 'waddch' Compiling worked, linking did not. The ncurses library is not being linked. In an IDE, put ncurses under Linker settings, not compiler options, and set it on the top-level project rather than a single build target.

The exe will not run on Windows, or Windows asks which program to open it It is probably a Linux binary. Check the first four bytes:

$b = [IO.File]::ReadAllBytes(".\yourfile")[0..3]
(($b | % { '0x{0:X2}' -f $_ }) -join ' ')

0x4D 0x5A is a Windows executable. 0x7F 0x45 0x4C 0x46 is Linux ELF, which means it was compiled with a Linux toolchain and has to be rebuilt with a Windows one. The source is portable; the binary is not.

terminal too small: need at least 59 cols x 24 rows Resize the window. The classic 80x25 console clears it by a single row.

A console window flashes and vanishes The program exited immediately and took its error message with it. Run it from a terminal rather than double-clicking, and start with --selftest.

SmartScreen warns about the exe Signing does not fix this. Microsoft removed the instant reputation that EV certificates used to grant, so signed and unsigned files both have to build reputation, and every rebuild resets it. SmartScreen fires on Mark of the Web, which is attached to downloaded files. Copy the exe across the filesystem instead of downloading it, or run Unblock-File .\memblocks.exe.

The memory reading looks too small If you built and ran inside WSL, /proc/meminfo reports the VM's memory budget rather than the host's, typically half of physical RAM. Raise it with memory=12GB in .wslconfig, or build a Windows binary and run it from PowerShell, where it reports actual Windows memory.

Safety

The balloon allocator will not drive available memory below the reserve, which defaults to 5% of RAM with a floor of 256 MB. Hammering the allocate key stops at the limit rather than triggering the OOM killer, and all memory is returned on exit or on interrupt. Lower it with -r at your own risk.

About

A tetris game that shows memory usage.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages