/* Xomb.xs - line drawing and random number generation utility functions */
#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include "jsf.h"
/* NOTE these MUST be kept in sync with similar in Xomb.pm */
#define MAP_COLS 78
#define MAP_ROWS 22
MODULE = Game::Xomb PACKAGE = Game::Xomb
PROTOTYPES: DISABLE
void
bypair (callback, ...)
SV *callback;
PREINIT:
int i;
SV *x, *y;
PPCODE:
if (!(items & 1)) croak("uneven number of arguments");
dSP;
for (i = 1; i < items; i += 2) {
x = ST(i);
y = ST(i + 1);
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, 2);
PUSHs(x);
PUSHs(y);
PUTBACK;
call_sv(callback, G_DISCARD);
SPAGAIN;
FREETMPS;
LEAVE;
}
UV
coinflip ()
CODE:
RETVAL = ranval() & 1;
OUTPUT:
RETVAL
# NOTE this distance differs from the iters count in linecb() as that
# function can do [0,0] .. [3,3] in 3 steps while this will calculate
# 4.24, lround'd to 4
UV
distance (uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1)
PREINIT:
int dx, dy;
CODE:
dx = x1 - x0;
dy = y1 - y0;
RETVAL = lround(sqrt(dx*dx + dy*dy));
OUTPUT:
RETVAL
# splice a random element out of an array reference
# NOTE this does not preserve the order of the array as that requires a
# series of copies anytime an item is extracted from not the end which
# for large lists will be most of the time (previous versions of this
# code did such a waterfall copy)
SV *
extract (avref)
AV *avref;
PREINIT:
SSize_t i, len, rnd;
SV *dunno, **swap;
CODE:
len = av_len(avref) + 1;
if (len == 0) XSRETURN_UNDEF;
rnd = ranval() % len;
dunno = av_delete(avref, rnd, 0);
if (rnd != len - 1) {
swap = av_fetch(avref, len - 1, FALSE);
av_store(avref, rnd, *swap);
AvFILLp(avref) -= 1;
AvMAX(avref) -= 1;
}
SvREFCNT_inc(dunno);
RETVAL = dunno;
OUTPUT:
RETVAL
# init_jsf - setup the RNG (see src/jsf.*)
void
init_jsf (seed)
UV seed
PPCODE:
raninit(seed);
UV
irand (uint32_t max)
CODE:
RETVAL = ranval() % max;
OUTPUT:
RETVAL
# linecb - Bresenham with some features to keep it from going off of
# the map and to skip the first point and abort should the callback
# return -1
void
linecb (callback, int x0, int y0, int x1, int y1)
SV *callback;
PREINIT:
int answer, dx, dy, err, e2, sx, sy, online, iters;
PPCODE:
dSP;
dx = abs(x1 - x0);
dy = abs(y1 - y0);
sx = x0 < x1 ? 1 : -1;
sy = y0 < y1 ? 1 : -1;
err = (dx > dy ? dx : -dy) / 2;
iters = 0;
online = 0;
while (1) {
if (x0 < 0 || x0 >= MAP_COLS || y0 < 0 || y0 >= MAP_ROWS) break;
if (online) {
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, 3);
mPUSHs(newSViv(x0));
mPUSHs(newSViv(y0));
mPUSHs(newSViv(iters));
PUTBACK;
call_sv(callback, G_SCALAR);
SPAGAIN;
answer = POPi;
FREETMPS;
LEAVE;
if (answer == -1) break;
}
if (x0 == x1 && y0 == y1) break;
e2 = err;
if (e2 > -dx) {
err -= dy;
x0 += sx;
}
if (e2 < dy) {
err += dx;
y0 += sy;
}
online = 1;
iters++;
}
UV
onein (uint32_t N)
CODE:
RETVAL = 0 == ranval() % N;
OUTPUT:
RETVAL
UV
roll (uint32_t count, uint32_t sides)
PREINIT:
uint32_t sum;
CODE:
sum = count;
while (count--) sum += ranval() % sides;
RETVAL = sum;
OUTPUT:
RETVAL
# pick a random element of an array ref
SV *
pick (avref)
AV *avref;
PREINIT:
SSize_t len, rnd;
SV **svp;
CODE:
len = av_len(avref) + 1;
if (len == 0) XSRETURN_UNDEF;
rnd = ranval() % len;
svp = av_fetch(avref, rnd, FALSE);
SvREFCNT_inc(*svp);
RETVAL = *svp;
OUTPUT:
RETVAL
# walkcb - linecb, but does not stop at x1,y1. used by Trolls to find
# what gets busted, which may or may not be the player
void
walkcb (callback, int x0, int y0, int x1, int y1)
SV *callback;
PREINIT:
int answer, dx, dy, err, e2, sx, sy, online, iters;
PPCODE:
dSP;
dx = abs(x1 - x0);
dy = abs(y1 - y0);
sx = x0 < x1 ? 1 : -1;
sy = y0 < y1 ? 1 : -1;
err = (dx > dy ? dx : -dy) / 2;
iters = 0;
online = 0;
while (1) {
if (x0 < 0 || x0 >= MAP_COLS || y0 < 0 || y0 >= MAP_ROWS) break;
if (online) {
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, 3);
mPUSHs(newSViv(x0));
mPUSHs(newSViv(y0));
mPUSHs(newSViv(iters));
PUTBACK;
call_sv(callback, G_SCALAR);
SPAGAIN;
answer = POPi;
FREETMPS;
LEAVE;
if (answer == -1) break;
}
e2 = err;
if (e2 > -dx) {
err -= dy;
x0 += sx;
}
if (e2 < dy) {
err += dx;
y0 += sy;
}
online = 1;
iters++;
}