Skip to content

Commit

Permalink
Add tractorgen-animator, by Peter Miller
Browse files Browse the repository at this point in the history
This is a major enhancement to the suite of tools available for the
study of ASCII tractor mechanics. It was first submitted by Peter Miller
on the 11th of December 2008, and has undergone rigorous testing on a
daily basis in the interim.

In its simplest form, Mr. Miller suggests the following usage:

    tractorgen | tractorgen-animator

Which will generate a standard ASCII tractor of seven wheels, and
animate its motion across the display terminal.

This serves the very useful purpose of enabling the use of an ASCII
tractor to appropriately remove extant error messages from a user's
terminal. Mr. Miller has employed this method in the reboot procedure of
an embedded device, wherein the ASCII tractor performs the important
task of cleansing the display of unsightly text.

I am personally very fond of the following apparatus:

    for i in `seq 1 100000`; do tractorgen $i | tractorgen-animator; done

Which will attempt to create a succession of all ASCII tractors of wheel
lengths from 1 to 100000 and animate the resulting successful instances
thereof. Apart from being an enthralling method of spending one's
weekend, this provides a "hands-off" experimental routine which is
surely welcome in these days of high-pressure research.
  • Loading branch information
kfish committed Mar 5, 2010
1 parent 5eab5a0 commit 9bde40a
Show file tree
Hide file tree
Showing 2 changed files with 292 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/Makefile.am
@@ -1,6 +1,7 @@
## Process this file with automake to produce Makefile.in

bin_PROGRAMS = tractorgen
bin_PROGRAMS = tractorgen tractorgen-animator

tractorgen_SOURCES = tractorgen.c

tractorgen_animator_SOURCES = tractorgen-animator.c
290 changes: 290 additions & 0 deletions src/tractorgen-animator.c
@@ -0,0 +1,290 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


static void *
xmalloc(size_t n)
{
void *p = malloc(n ? n : 1);
if (!p)
{
perror("malloc");
exit(1);
}
return p;
}


static void
xfree(void *p)
{
if (p)
free(p);
}


typedef struct line_t line_t;
struct line_t
{
size_t ncolumns;
size_t ncolumns_max;
unsigned char *text;
size_t min_x;
size_t max_x;
};


static void
line_constructor(line_t *lp)
{
lp->ncolumns = 0;
lp->ncolumns_max = 0;
lp->text = 0;
lp->min_x = 0;
lp->max_x = 0;
}


static void
line_destructor(line_t *lp)
{
xfree(lp->text);
}


static void
line_stash(line_t *lp, int sx, int c)
{
if (sx < 0)
return;
if (c == ' ')
return;
size_t x = sx;
while (x >= lp->ncolumns_max)
{
size_t new_ncolumns_max = lp->ncolumns_max * 2 + 32;
unsigned char *new_text = xmalloc(new_ncolumns_max);
memcpy(new_text, lp->text, lp->ncolumns_max);
memset(new_text + lp->ncolumns_max, ' ',
new_ncolumns_max - lp->ncolumns_max);
xfree(lp->text);
lp->text = new_text;
lp->ncolumns_max = new_ncolumns_max;
}

if (lp->ncolumns == 0)
{
lp->min_x = x;
lp->max_x = x + 1;
}
else
{
if (lp->min_x > x)
lp->min_x = x;
if (lp->max_x <= x)
lp->max_x = x + 1;
}

if (x >= lp->ncolumns)
lp->ncolumns = x + 1;
lp->text[x] = c;
}


static void
line_print(const line_t *lp)
{
printf("%.*s\n", (int)lp->ncolumns, lp->text);
}


static int
line_get(const line_t *lp, int sx)
{
if (sx < 0)
return ' ';
size_t x = sx;
if (x >= lp->ncolumns)
return ' ';
return lp->text[x];
}


typedef struct picture_t picture_t;
struct picture_t
{
size_t nlines;
size_t nlines_max;
line_t *line;
};


static void
picture_constructor(picture_t *pp)
{
pp->nlines = 0;
pp->nlines_max = 0;
pp->line = 0;
}


static void
picture_destructor(picture_t *pp)
{
size_t y;
for (y = 0; y < pp->nlines; ++y)
line_destructor(&pp->line[y]);
xfree(pp->line);
}


static void
picture_stash(picture_t *pp, int x, int sy, int c)
{
if (x < 0 || sy < 0)
return;
size_t y = sy;
if (y >= pp->nlines_max)
{
size_t j;
size_t new_nlines_max = pp->nlines_max * 2 + 8;
line_t *new_lines = xmalloc(new_nlines_max * sizeof(line_t));
for (j = 0; j < pp->nlines; ++j)
new_lines[j] = pp->line[j];
xfree(pp->line);
pp->line = new_lines;
pp->nlines_max = new_nlines_max;
for (; j < pp->nlines_max; ++j)
line_constructor(&pp->line[j]);
}
if (y >= pp->nlines)
pp->nlines = y + 1;
line_stash(&pp->line[y], x, c);
}


static void
picture_print(const picture_t *pp)
{
size_t y;
for (y = 0; y < pp->nlines; ++y)
line_print(&pp->line[y]);
}


static int
picture_get_min_x(const picture_t *pp)
{
if (!pp->nlines)
return 0;
int min_x = pp->line[0].min_x;
size_t y;
for (y = 1; y < pp->nlines; ++y)
{
if (pp->line[y].ncolumns)
{
int x = pp->line[y].min_x;
if (x < min_x)
min_x = x;
}
}
return min_x;
}


static int
picture_get_max_x(const picture_t *pp)
{
if (!pp->nlines)
return 0;
int max_x = 0;
size_t y;
for (y = 0; y < pp->nlines; ++y)
{
int x = pp->line[y].max_x;
if (x > max_x)
max_x = x;
}
return max_x;
}


static int
picture_get(const picture_t *pp, int x, int sy)
{
if (sy < 0)
return ' ';
size_t y = sy;
if (y >= pp->nlines)
return ' ';
return line_get(&pp->line[y], x);
}


static void
picture_offset(const picture_t *src, int dx, picture_t *dst)
{
int lox = picture_get_min_x(src);
int hix = picture_get_max_x(src);
size_t y;
for (y = 0; y < src->nlines; ++y)
{
int x;
for (x = lox; x < hix; ++x)
{
int c = picture_get(src, x, y);
int x2 = x + dx;
if (x2 < 80)
picture_stash(dst, x + dx, y, c);
}
}
}


int
main()
{
/*
* read the tractor from stdin.
*/
picture_t p;
picture_constructor(&p);
int x = 0;
int y = 0;
for (;;)
{
int c = getchar();
if (c == EOF)
break;
if (c == '\n')
{
x = 0;
++y;
continue;
}
picture_stash(&p, x, y, c);
++x;
}

int hix = picture_get_max_x(&p);

int first = 1;
int dx;
for (dx = 1 - hix; dx < 80 - hix; ++dx)
{
if (!first)
{
usleep(50000);
printf("\33[%dA", (int)p.nlines);
}
picture_t p2;
picture_constructor(&p2);
picture_offset(&p, dx, &p2);
picture_print(&p2);
picture_destructor(&p2);
fflush(stdout);
first = 0;
}
return 0;
}

0 comments on commit 9bde40a

Please sign in to comment.