💾 Archived View for thrig.me › blog › 2024 › 03 › 16 › distance.c captured on 2024-03-21 at 15:41:18.

View Raw

More Information

-=-=-=-=-=-=-

// distance - compare various distance calculations (something *not* to
// be figured out during a 7DRL)
//
//   CFLAGS="-lm -lncurses" make distance && ./distance

#include <locale.h>
#include <math.h>
#include <ncurses.h>
#include <stdlib.h>

#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define ABS(a) ((a) < 0 ? -(a) : (a))

typedef struct {
	int yy;
	int xx;
} coord;

int chebyshev(coord *p1, coord *p2);
int distance(coord *p1, coord *p2);
int taxicab(coord *p1, coord *p2);

// this romanizes into various different spellings. "standard 8-way
// distance metric" (so will get you a square field of view)
int
chebyshev(coord *p1, coord *p2)
{
	int dy = ABS(p2->yy - p1->yy);
	int dx = ABS(p2->xx - p1->xx);
	return MAX(dy, dx);
}

// Pythagorean, or the bean haters. may vary depending on how the value
// is rounded. rounding up to a number outside of the level map might
// be bad, while always rounding towards zero might be bad for
// different reasons
int
distance(coord *p1, coord *p2)
{
	int dy = p2->yy - p1->yy;
	int dx = p2->xx - p1->xx;
	return lround(sqrt(dx * dx + dy * dy));
}

// or the "Manhattan" distance
int
taxicab(coord *p1, coord *p2)
{
	return ABS(p2->yy - p1->yy) + ABS(p2->xx - p1->xx);
}

// Bresenham's line algorithm (from Rosetta Code)
int
line(coord *p1, coord *p2)
{
	int y0       = p1->yy;
	int x0       = p1->xx;
	const int dx = abs(p2->xx - x0), sx = x0 < p2->xx ? 1 : -1;
	const int dy = abs(p2->yy - y0), sy = y0 < p2->yy ? 1 : -1;
	int err      = (dx > dy ? dx : -dy) / 2, e2;
	int distance = 0;
	while (1) {
		if (x0 == p2->xx && y0 == p2->yy) break;
		distance++;
		mvaddch(y0, x0, '.');
		e2 = err;
		if (e2 > -dx) {
			err -= dy;
			x0 += sx;
		}
		if (e2 < dy) {
			err += dx;
			y0 += sy;
		}
	}
	return distance;
}

int
main(int argc, char *argv[])
{
	setlocale(LC_ALL, "");
	initscr();
	curs_set(FALSE);
	keypad(stdscr, TRUE);
	leaveok(stdscr, TRUE);
	meta(stdscr, TRUE);
	//nodelay(stdscr, TRUE);
	noecho();
	nonl();
	raw();

	// where the HERO (Hapless Explorer of Random Orthographies) is
	coord hero;
	hero.yy = 1;
	hero.xx = 1;

	coord kitten;
	kitten.yy = LINES / 2;
	kitten.xx = COLS / 2;

	mvaddstr(0, 0, "use h j k l or arrows to move, q or Q to quit");

	int running = 1;
	while (running) {
		int dirty = 0;
		mvaddch(kitten.yy, kitten.xx, 'M'); // 'M' is for 'mlatu'
		mvaddch(hero.yy, hero.xx, '@');
		int ch = getch();
		switch (ch) {
		case 'h':
			hero.xx -= 1;
			if (hero.xx < 0) hero.xx = 0;
			dirty = 1;
			break;
		case KEY_DOWN:
		case 'j':
			hero.yy += 1;
			if (hero.yy >= LINES) hero.yy = LINES - 1;
			dirty = 1;
			break;
		case KEY_UP:
		case 'k':
			hero.yy -= 1;
			if (hero.yy < 1) hero.yy = 1;
			dirty = 1;
			break;
		case KEY_RIGHT:
		case 'l':
			hero.xx += 1;
			if (hero.xx >= COLS) hero.xx = COLS - 1;
			dirty = 1;
			break;

		case 'y':
			hero.xx -= 1;
			if (hero.xx < 0) hero.xx = 0;
			hero.yy -= 1;
			if (hero.yy < 1) hero.yy = 1;
			dirty = 1;
			break;
		case 'u':
			hero.xx += 1;
			if (hero.xx >= COLS) hero.xx = COLS - 1;
			hero.yy -= 1;
			if (hero.yy < 1) hero.yy = 1;
			dirty = 1;
			break;
		case 'b':
			hero.xx -= 1;
			if (hero.xx < 0) hero.xx = 0;
			hero.yy += 1;
			if (hero.yy >= LINES) hero.yy = LINES - 1;
			dirty = 1;
			break;
		case 'n':
			hero.xx += 1;
			if (hero.xx >= COLS) hero.xx = COLS - 1;
			hero.yy += 1;
			if (hero.yy >= LINES) hero.yy = LINES - 1;
			dirty = 1;
			break;

		case 'Q':
		case 'q':
			running = 0;
			break;
		default:;
		}
		if (dirty) {
			clear();
			int bdist = line(&hero, &kitten);
			move(0, 0);
			clrtoeol();
			char *msg;
			if (asprintf(&msg, "8way %d bres %d taxi %d pyth %d",
			             chebyshev(&hero, &kitten), bdist,
			             taxicab(&hero, &kitten),
			             distance(&hero, &kitten)) >= 0) {
				addstr(msg);
				free(msg);
			}
		}
	}
	endwin();
}