/*
 *  Turing -- A simple two-dimensional turing machine.
 *
 *  Written by Steven T. McClure @ University of New Hampshire, 1989
 *
 *  This program can be distributed freely in any non-commercial
 *  environment, and can be used in no way for a profit by anyone
 *  (except me, of course :) ), and can be modified in any way that
 *  you like.  I take no responsibility for the readablness of this
 *  code (or this comment, for that matter :) ), the correctness of
 *  this code, or any damages occuring from the use of this code.
 *  Have fun with it, I know I did.
 */
/*
 *  This program uses the screen as a 'tape' for a two-dimensional
 *  turing machine.  Instructions for use are in 'turing.inf'.
 */
/*
 *  4 colors in 320X200 mode (0x04), 2 colors in 640X400 mode (0x42)
 *  This was developed on an AT&T6300, so these were the only two
 *  choices in set_scr.  To set a different video mode, just replace
 *  RUN_VID with your choice of video, as the program makes no check
 *  of screen boundaries.
 */

#include <stdio.h>
#include <dos.h>

#define		RUN_VID		0x04	/* Video to use during run-time */
#define		END_VID		0x02	/* Video to reset to after run */

#define		BLACK		0	/* GUESSES FOR THESE */
#define		GREEN		1	/* These are probably different */
#define		BLUE		2	/* for their real color, but on a */
#define		RED		3	/* monochrome monitor, it doesn't */
					/* matter.  If the real values are */
					/* discovered and changed for the */
					/* system being used, the values in */
					/* 'turing.doc' should be changed */
					/* also. */
					/* Also, other colors can be added */
					/* eaisly by just defining them */
					/* here, as they are used nowhere */
					/* in the program, just as a guide */
					/* to actual colors. */
#define		UP		0	/* Physical directions to move */
#define		RGT		2
#define		DOWN		4
#define		LFT		6

#define		LEFT		10	/* Turn and move directions */
#define		RIGHT		20
#define		FORWARD		30
#define		STAY		40
#define		BACKUP		50

struct	tape	{
	int	t_lin;
	int	t_col;
	int	t_dir;
	int	t_state;
	int	t_color;
} tp;

struct	state	{
	int	s_got;
	int	s_paint;
	int	s_dirgo;
	int	s_sgo;

	struct	state	*s_prev;
	struct	state	*s_next;
};

struct	list	{
	int	l_state;

	struct	state	*l_first;
	struct	state	*l_last;
	struct	list	*l_prev;
	struct	list	*l_next;
};

struct	list	*firstlist;
struct	list	*lastlist;
struct	tape	t;

typedef	union	REGS	REGS;

main(argc, argv)
int	argc;
char	*argv[];
{
	REGS	r;
	REGS	or;
	char	c;
	int	tmp;

	if (argc < 2) {
		printf("Usage: turing [ file ] ...\n");
		exit(0);
	}
	read_tuples(argv[1]);
	set_scr(2);
	putdot(tp.t_lin, tp.t_col, tp.t_color);
	do {
		do_turing();
		c = getch();
	} while (c == 'r' || c == 'R');
	set_scr(1);
}

do_turing()
{
	struct	list	*l;
	struct	state	*s;
	int	color;

	while (!kbhit()) {
		color = getdot(tp.t_lin, tp.t_col);
		for (l = firstlist; l; l = l->l_next)
			if (l->l_state == tp.t_state)
				break;
		if (!l)
			break;
		for (s = l->l_first; s; s = s->s_next)
			if (color == s->s_got)
				break;
		if (!s)
			break;
		putdot(tp.t_lin, tp.t_col, s->s_paint);
		if (s->s_dirgo == RIGHT)
			tp.t_dir = (tp.t_dir + 2 > 6 ? 0 : tp.t_dir + 2);
		else if (s->s_dirgo == LEFT)
			tp.t_dir = (tp.t_dir - 2 < 0 ? tp.t_dir + 6 : tp.t_dir - 2);
		else if (s->s_dirgo == BACKUP)
			tp.t_dir = (tp.t_dir - 4 < 0 ? tp.t_dir + 4 : tp.t_dir - 4);
		if (s->s_dirgo != STAY) {
			if (tp.t_dir == UP)
				tp.t_lin = (tp.t_lin - 1 < 0 ? 0 : tp.t_lin - 1);
			else if (tp.t_dir == DOWN)
				tp.t_lin = (tp.t_lin + 1 > 199 ? 199 : tp.t_lin + 1);
			else if (tp.t_dir == LFT)
				tp.t_col = (tp.t_col - 1 < 0 ? 0 : tp.t_col - 1);
			else if (tp.t_dir == RGT)
				tp.t_col = (tp.t_col + 1 > 319 ? 319 : tp.t_col + 1);
			else
				break;
		}
		if (s->s_dirgo == BACKUP)
			tp.t_dir = (tp.t_dir - 4 < 0 ? tp.t_dir + 4 : tp.t_dir - 4);
		tp.t_state = s->s_sgo;
	}
	getch();
}

read_tuples(str)
char	*str;
{
	struct	state	*s;
	struct	list	*l;
	FILE	*fp;
	int	cstate;
	int	godir;
	int	gotcol;
	int	paint;
	int	gostate;

	if ((fp = fopen(str, "r")) == NULL) {
		printf("Cannot open '%s' for reading.\n", str);
		exit(-1);
	}
	fscanf(fp, "%d %d %d %d %d\n", &tp.t_state, &tp.t_lin, &tp.t_col, &tp.t_dir, &tp.t_color);
	while (!feof(fp)) {
		fscanf(fp, "%d %d %d %d %d\n", &cstate, &gotcol, &paint, &godir, &gostate);
		for (l = firstlist; l; l = l->l_next)
			if (cstate == l->l_state)
				break;
		if (!l) {
			l = malloc(sizeof(struct list));
			l->l_prev = firstlist;
			l->l_next = 0;
			if (!firstlist)
				firstlist = l;
			if (lastlist)
				lastlist->l_next = l;
			lastlist = l;
			l->l_first = 0;
			l->l_last = 0;
			l->l_state = cstate;
		}
		s = malloc(sizeof(struct state));
		s->s_prev = l->l_first;
		s->s_next = 0;
		if (!l->l_first)
			l->l_first = s;
		if (l->l_last)
			l->l_last->s_next = s;
		l->l_last = s;
		s->s_got = gotcol;
		s->s_paint = paint;
		s->s_dirgo = godir;
		s->s_sgo = gostate;
	}
	fclose(fp);
}

int
getdot(x, y)
int	x;
int	y;
{
	REGS	r;
	REGS	or;

	r.h.ah = 0x0D;
	r.h.bh = 0;
	r.x.cx = y;
	r.x.dx = x;
	int86(0x10, &r, &or);
	return (int)or.h.al;
}

putdot(x, y, color)
int	x;
int	y;
int	color;
{
	REGS	r;
	REGS	or;

	r.h.ah = 0x0C;
	r.h.al = color;
	r.h.bh = 0;
	r.x.cx = y;
	r.x.dx = x;
	int86(0x10, &r, &or);
}

set_scr(x)
int	x;
{
	REGS	r;
	REGS	or;

	if (x == 1) {
		r.h.ah = 0x00;
		r.h.al = END_VID;
		int86(0x10, &r, &or);
	} else if (x == 2) {
		r.h.ah = 0x00;
		r.h.al = RUN_VID;
		int86(0x10, &r, &or);
	}
}
