module-based literate programming

This commit is contained in:
りき萌 2024-02-17 14:56:17 +01:00
parent 9ef9d57f13
commit b9218c8ace
7 changed files with 183 additions and 106 deletions

View file

@ -270,38 +270,42 @@ styles = ["tairu.css"]
% id = "01HPQCCV4R557T2SN7ES7Z4EJ7"
- we can verify this logic with a bit of code; with a bit of luck, we should be able to narrow down our tileset into something a lot more manageable.
% id = "01HPSY4Y19NQ6DZN10BP1KQEZN"
+ we'll start off by defining a bunch of variables to represent our ordinal directions:
```javascript ordinal-directions
const E = 0b00000001;
const SE = 0b00000010;
const S = 0b00000100;
const SW = 0b00001000;
const W = 0b00010000;
const NW = 0b00100000;
const N = 0b01000000;
const NE = 0b10000000;
const ALL = E | SE | S | SW | W | NW | N | NE;
export const E = 0b0000_0001;
export const SE = 0b0000_0010;
export const S = 0b0000_0100;
export const SW = 0b0000_1000;
export const W = 0b0001_0000;
export const NW = 0b0010_0000;
export const N = 0b0100_0000;
export const NE = 0b1000_0000;
export const ALL = E | SE | S | SW | W | NW | N | NE;
```
as I've already said, we represent each direction using a single bit.
% id = "01HPSY4Y19AW70YX8PPA7AS4DH"
- I'm using JavaScript by the way, because it's the native programming language of your web browser. read on to the end of this tangent to see why.
% id = "01HPSY4Y19HPNXC54VP6TFFHXN"
- now I don't know about you, but I find the usual C-style way of checking whether a bit is set extremely hard to read, so let's take care of that:
```javascript ordinal-directions
function isSet(integer, bit) {
export function isSet(integer, bit) {
return (integer & bit) == bit;
}
```
% id = "01HPSY4Y1984H2FX6QY6K2KHKF"
- now we can write a function that will remove the aforementioned redundancies.
the logic is quite simple - for southeast, we only allow it to be set if both south and east are also set, and so on and so forth.
```javascript ordinal-directions
// t is a tile index; variable name is short for brevity
function removeRedundancies(t) {
export function removeRedundancies(t) {
if (isSet(t, SE) && (!isSet(t, S) || !isSet(t, E))) {
t &= ~SE;
}
@ -318,10 +322,11 @@ styles = ["tairu.css"]
}
```
% id = "01HPSY4Y19HWQQ9XBW1DDGW68T"
- with that, we can find a set of all unique non-redundant combinations:
```javascript ordinal-directions
function ordinalDirections() {
export function ordinalDirections() {
let unique = new Set();
for (let i = 0; i <= ALL; ++i) {
unique.add(removeRedundancies(i));
@ -330,11 +335,13 @@ styles = ["tairu.css"]
}
```
% id = "01HPSY4Y19KG8DC4SYXR1DJJ5F"
- by the way, I find it quite funny how JavaScript's [`Array.prototype.sort`] defaults to ASCII ordering *for all types.*
even numbers! ain't that silly?
[`Array.prototype.sort`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
% id = "01HPSY4Y19V62YKTGK3TTKEB38"
- and now it's time to _Let It Cook™_:
```javascript ordinal-directions
@ -346,18 +353,62 @@ styles = ["tairu.css"]
47
```
% id = "01HPSY4Y194DYYDGSAT83MPQFR"
- forty seven! that's how many unique tiles we actually need.
% id = "01HPSY4Y19C303Z595KNVXYYVS"
- you may find pixel art tutorials saying you need forty *eight* and not forty *seven*, but that is not quite correct -
the forty eighth tile is actually just the empty tile! saying it's part of the tileset is quite misleading IMO.
% id = "01HPSY4Y19TM2K2WN06HHEM3D0"
- phew... the nesting's getting quite unwieldy, let's wrap up this tangent and return back to doing some bitwise autotiling!
% id = "01HPSY4Y192FZ37K3KXZM90K9J"
- so in reality we actually only need 47 tiles and not 256 - that's a whole lot less, that's 81.640625% less tiles we have to draw!
% id = "01HPSY4Y19HEBWBTNMDMM0AZSC"
- and it's even possible to autogenerate most of them given just a few smaller 4x4 pieces - but for now, let's not go down that path.\
maybe another time.
- so we only need to draw 47 tiles, but to actually display them in a game we still need to pack them into an image.
- we *could* use a similar approach to the 16 tile version, but that would leave us with lots of wasted space!
- think that with this redundancy elimination approach most of the tiles will never even be looked up by the renderer, because the bit combinations will be collapsed into a more canonical form before the lookup.
- we could also use the approach I mentioned briefly [here][branch:01HPQCCV4RB65D5Q4RANJKGC0D], which involves introducing a lookup table - which sounds reasonable, so let's do it!
- I don't want to write the lookup table by hand, so let's generate it! I'll reuse the redundancy elimination code from before to make this easier.
- we'll start by obtaining our ordinal directions array again:
```javascript ordinal-directions
export let xToConnectionBitSet = ordinalDirections();
```
- then we'll turn that array upside down... in other words, invert the index-value relationship, so that we can look up which X position in the tile strip to use for a specific connection combination.
remember that our array has only 256 values, so it should be pretty cheap to represent using a `Uint8Array`:
```javascript ordinal-directions
export let connectionBitSetToX = new Uint8Array(256);
for (let i = 0; i < xToConnectionBitSet.length; ++i) {
connectionBitSetToX[xToConnectionBitSet[i]] = i;
}
```
- and there we go! we now have a mapping from our bitset to positions within the tile strip. try to play around with the code example to see which bitsets correspond to which position!
```javascript ordinal-directions
console.log(connectionBitSetToX[E | SE | S]);
```
```output ordinal-directions
4
```
TODO: The value from the previous output should not leak into this one. how do we do this? do we emit extra `pushMessage` calls inbetween the editors so that they know when to end?
maybe use a `classic` context instead of a module? or maybe have a way of sharing data between outputs? (return value?)
% id = "01HPD4XQPWT9N8X9BD9GKWD78F"
- bitwise autotiling is a really cool technique that I've used in plenty of games in the past.
@ -423,6 +474,7 @@ styles = ["tairu.css"]
% id = "01HPD4XQPWP847T0EAM0FJ88T4"
- then vines
% id = "01HPSY4Y19FA2HGYE4F3Y9NJ57"
- well... it's even simpler than that in terms of graphical presentation, but we'll get to that.
% id = "01HPD4XQPWK58Z63X6962STADR"