Shadow Chess

Version 1 (2017/11/15) by @BipolarMike and @paniq

Beware: This is an alpha version. The rules are completely untested.

Shadow Chess (also known as Quantum Chess, Imaginary Chess or M.A.D. Chess) works like regular chess, but with additional rules that are designed to produce new strategical opportunities and interesting outcomes:

View minmaxbounds.glsl
bool in_wedge(vec2 p, vec2 u) {
return (p.x >= u[0]*p.y) && (p.x <= u[1]*p.y);
bool in_frustum(vec3 p, vec2 u, vec2 v) {
return in_wedge(p.xz, u) && in_wedge(p.yz, v);
void merge_plane_range(inout vec2 outer, vec2 p0, vec2 p1, vec2 u) {
outer[0] = in_wedge(p0, u)?min(outer[0], p0.y):outer[0];
outer[1] = in_wedge(p1, u)?max(outer[1], p1.y):outer[1];
View bounding.glsl
void compute_bounding_points_compact(surface2x3 surf, vec2 u, vec2 v, out bounds2x3 bounds) {
float surf_c7_c7 = surf.c[7]*surf.c[7];
float surf_2x_c0 = 2.0*surf.c[0];
float surf_2x_c1 = 2.0*surf.c[1];
float surf_2x_c3 = 2.0*surf.c[3];
float surf_2x_c6 = 2.0*surf.c[6];
float surf_4x_c1 = 4.0*surf.c[1];

After solving the compaction puzzle for parallel processing of values in a partitioned stream, the path to a screenspace CSG quadtree kernel is now open.

Instead of separate tiles, products and factors, we now keep a single array of factors, of which each factor also has a product index and a tile coordinate. We operate on a 256x256 tile so our tile coordinates fit into 16 bit, and allow only a maximum of 65536 products for this tile, with 2^31 addressable brushes. A factor then requires only 8 bytes: 2 bytes for its product index, 2 bytes for its tile coordinate and 4 bytes for its signed brush id.

We seed the array with all factors that matter for this 256x256 tile, sorted by tile coordinate and product index so that factors which belong to the same product are packed together, and all products which belong to the same tile are packed together as well.

We also init a brush id image with tile size 1x1.

In the beginning, there is typically


Massively Parallel Processing of Partitioned Streams

by @paniq

We wish to perform sum/max/filter operations on streams that have been partitioned into groups of varying size, and do this in a massively parallel fashion.

Finding Partitions

View csgtree_pruning.txt
using "none.libc"
; generate random CSG trees, attempt to optimize and evaluate robustness and
; effectivity in comparison with a full evaluation.
var INSIDE -1
View 1ddistances.txt
; provided a list of boolean range operations, find the nearest depth
; at which the ray hits a 100% occluding volume
using "none.libc"
+++++++++++ (20 30)
--- (19 21)

Ellipsoid Frustum Intersection

Yesterday I posted a problem to math stack exchange that bothered me for a while now, and right after I've had a few exchanges on Twitter, I got inspired to attempt a solution.

Here it goes. It's 100% untested but I'm fairly certain that it will work.

The problem is about a form of refining raytracing where we render a big list of convex 3D brushes (and I decided to start with Ellipsoids, since they're so useful) to the screen or a shadow map, without any prebuilt accelleration structure. How does it work? Well, if we had a way to figure out for a portion of the frustum whether it contained a brush, we could

  1. Start with a very low resolution
View gist:1e9f0aa78ff12d17c33af65f56499b59
Müsset im Naturbetrachten when observing nature, you must
Immer eins wie alles achten. mind the single thing as well as everything
Nichts ist drinnen, nichts ist draußen; nothing is inside; nothing is outside
Denn was innen, das ist außen. because what's inside is outside
So ergreifet ohne Säumnis so reach without delay for
Heilig öffentlich Geheimnis! the holy public secret (or mystery)
Freuet euch des wahren Scheins, enjoy the true illusiveness
Euch des ernsten Spieles! enjoy the serious play
Kein Lebend'ges ist ein Eins, nothing alive is just one thing
View gist:8dcb2231d4b1ab4857520e527fc747ce
when the entry label to a function is originally declared (during macro expansion),
the entry label is tagged with the label that was active when the label was declared;
this is called the scope label.
later, during evaluation, when an entry label is encountered as argument, and the
label has a scope label tag, the current frame (a cactus stack) is searched upwards
for the scope label, and truncated by that amount.
if the scope label tag can not be found in upwards frames (and this is the part that
gives me grief, because it happens too much) then the current frame is taken as-is.