RubE On Conveyor Belts/Interpreter

#include <stdio.h>
#include <windows.h>
#include <ctype.h>
#include <conio.h>
#include <stdlib.h>
#include 
#define INITIAL_DELAY 200
#define FAST
//#define RIGHTWAYUP
int TICK_DELAY=100;
#define MAX_PROGRAM_WIDTH 1000
#define MAX_PROGRAM_HEIGHT 1000
#define MAX_CONTROL_LENGTH 1000
#define MAX_CONTROL_TAPE 1000
#pragma warning(disable:4305) // truncation
#pragma warning(disable:4309) // truncation of constant value
int PROGRAM_WIDTH=80;
int PROGRAM_HEIGHT=24;
int state=0;
// MUST BE UNSIGNED!
// Otherwise all sorts of weird things happen.
unsigned char program[MAX_PROGRAM_HEIGHT][MAX_PROGRAM_WIDTH]={
	" r ",
	" ! ",
	" ",
	" ",
	" <<<<<<<O>>>>> ",
	" 4 ",
	" <<<O = <<<O>>> ",
	" 1 7 ",
	" O = O> = O ",
	" 0 6 8 ",
	" = O> = = ",
	" 3 ",
	" = O ",
	" 5 ",
	" O> = ",
	" 2 ",
	" = ",
	" ",
	" ",
	" ",
	" T * T * T ",
	" F ]F ]F ",
	"===============================================================================",
	" "
};
char control[MAX_CONTROL_LENGTH]="+[dsti[o[-]]+]";
unsigned short control_tape[MAX_CONTROL_TAPE];
unsigned short xdata[MAX_PROGRAM_HEIGHT][MAX_PROGRAM_WIDTH];
FILE *out;
int isFinished=0;
int ticks_since_output=0;
char input_char=0;
bool iscrate(unsigned char c)
{
	return isdigit(c) || c=='b' || c=='r';
}
// Returns true if successful
bool pushBlocksLeft(int x,int y)
{
	int blocks_left_edge=x;
	if(y<0 || y>=PROGRAM_HEIGHT || x<0 || x>=PROGRAM_WIDTH)
		return false;
	while(iscrate(program[y][blocks_left_edge]) || iscrate(program[y][blocks_left_edge]&0x7F)
		&& blocks_left_edge>0)
		blocks_left_edge--;
	if(blocks_left_edge==0)
		return false; // Can't push into edge of program
	if(program[y][blocks_left_edge]!=' ' && program[y][blocks_left_edge]!='F'
		&& program[y][blocks_left_edge]!='\\')
		return false; // Can't push into other objects besides furnace or ramp
	if(program[y][blocks_left_edge]=='\\' && !pushBlocksLeft(y-1,blocks_left_edge-1))
		return false;
	bool was_furnace=(program[y][blocks_left_edge]=='F');
	bool was_ramp=(program[y][blocks_left_edge]=='\\');
	for(int k=blocks_left_edge;k<x;k++)
	{
		program[y][k]=program[y][k+1]|0x80;
		xdata[y][k]=xdata[y][k+1];
		program[y][k+1]=' ';
	}
	if(was_furnace)
		program[y][blocks_left_edge]='F';
	else if(was_ramp)
	{
		program[y-1][blocks_left_edge-1]=program[y][blocks_left_edge];
		program[y][blocks_left_edge]='\\';
	}
	return true; // Success!
}
// Returns true if successful
bool pushBlocksRight(int x,int y)
{
	int blocks_right_edge=x;
	if(y<0 || y>=PROGRAM_HEIGHT || x<0 || x>=PROGRAM_WIDTH)
		return false;
	while(iscrate(program[y][blocks_right_edge]) || iscrate(program[y][blocks_right_edge]&0x7F)
		&& blocks_right_edge<PROGRAM_WIDTH-1)
		blocks_right_edge++;
	if(blocks_right_edge==PROGRAM_WIDTH-1)
		return false; // Can't push into edge of program
	if(program[y][blocks_right_edge]!=' ' && program[y][blocks_right_edge]!='F'
		&& program[y][blocks_right_edge]!='/')
		return false; // Can't push into other objects besides empty space, ramp or furnace.
	if(program[y][blocks_right_edge]=='/' && !pushBlocksRight(y-1,blocks_right_edge+1))
		return false;
	bool was_furnace=(program[y][blocks_right_edge]=='F');
	bool was_ramp=(program[y][blocks_right_edge]=='/');
	for(int k=blocks_right_edge;k>x;k--)
	{
		if(program[y][k]!=' ')
			return false; // Error!
		program[y][k]=program[y][k-1]|0x80;
		xdata[y][k]=xdata[y][k-1];
		program[y][k-1]=' ';
	}
	if(was_furnace)
		program[y][blocks_right_edge]='F';
	else if(was_ramp)
	{
		program[y-1][blocks_right_edge+1]=program[y][blocks_right_edge];
		program[y][blocks_right_edge]='/';
	}
	return true; // Success!
}
// 0x80 is added to anything which has moved at least once during
// the tick.
void doGun(int x,int y,int dx,int dy,char target)
{
	char moving;
	if(x-dx < 0 || y-dy < 0 || x-dx >= PROGRAM_WIDTH || y-dy >= PROGRAM_HEIGHT)
		return;
	int ix=x, iy=y;
	moving=program[y-dy][x-dx];
	if(!iscrate(moving)) return;
	if(moving & 0x80) return;
	while(true) // Not really
	{
		if(x < 0 || y < 0 || x >= PROGRAM_WIDTH || y >= PROGRAM_HEIGHT)
			break;
		x+=dx;
		y+=dy;
		if(program[y][x] == target)
		{
			x+=dx;
			y+=dy;
			if(x < 0 || y < 0 || x >= PROGRAM_WIDTH || y >= PROGRAM_HEIGHT)
				break;
			if(program[y][x] != ' ')
				break;
			program[y][x]=moving | 0x80;
			program[iy-dy][ix-dx]=' ';
			break;
		}
	}
}
void doCell(int x,int y,char c)
{
	int p1;
	int p2;
	int result;
	switch(c)
	{
	case 'G': // left
		doGun(x,y,-1,0,'t');
		break;
	case 'H': // down
		doGun(x,y,0,1,'t');
		break;
	case 'J': // right
		doGun(x,y,1,0,'t');
		break;
	case 'Y': // up
		doGun(x,y,0,-1,'t');
		break;
	case '!':
		if(y<PROGRAM_HEIGHT-1 && y>0 && (iscrate(program[y-1][x]&0x7F)
			|| program[y-1][x]=='r') && program[y+1][x]==' ')
		{
#ifndef FAST
			if(xdata[y][x]==1)
			{
#endif
				program[y+1][x]=program[y-1][x]|0x80;
				xdata[y+1][x]=xdata[y-1][x];
				xdata[y][x]=0;
#ifndef FAST
			}
			else
				xdata[y][x]=1;
#endif
		}
		break;
	case 'i':
		if(y<PROGRAM_HEIGHT-1 && y>0 && (iscrate(program[y+1][x]&0x7F)
			|| program[y+1][x]=='r') && program[y-1][x]==' ')
		{
#ifndef FAST
			if(xdata[y][x]==1)
			{
#endif
				program[y-1][x]=program[y+1][x]|0x80;
				xdata[y+1][x]=xdata[y-1][x];
				xdata[y][x]=0;
#ifndef FAST
			}
			else
				xdata[y][x]=1;
#endif
		}
		break;
	case 'V':
		if(y>0 && y<PROGRAM_HEIGHT-1 && iscrate(program[y-1][x]) && (program[y+1][x]==' '
			|| program[y+1][x]=='V'))
		{
			int out_y = y;
			while(out_y < PROGRAM_HEIGHT-1 && program[out_y][x]=='V') out_y++;
			if(program[out_y][x]!=' ') break;
			program[out_y][x]=program[y-1][x]|0x80;
			xdata[out_y][x]=xdata[y-1][x];
			program[y-1][x]=' ';
		}
		break;
	case 'A':
		if(y>0 && y<PROGRAM_HEIGHT-1 && iscrate(program[y+1][x]) && (program[y-1][x]==' '
			|| program[y-1][x]=='A'))
		{
			int out_y = y;
			while(out_y > 0 && program[out_y][x]=='A') out_y--;
			if(program[out_y][x]!=' ') break;
			program[out_y][x]=program[y+1][x]|0x80;
			xdata[out_y][x]=xdata[y+1][x];
			program[y+1][x]=' ';
		}
		break;
	case 'W':
		if(y>0 && y<PROGRAM_HEIGHT-1 && iscrate(program[y-1][x]) && program[y+1][x]==' ')
		{
			program[y+1][x]=program[y-1][x]|0x80;
			xdata[y+1][x]=xdata[y-1][x];
			program[y-1][x]=' ';
			program[y][x]='M';
		}
		break;
	case 'M':
		if(y>0 && y<PROGRAM_HEIGHT-1 && iscrate(program[y+1][x]) && program[y-1][x]==' ')
		{
			program[y-1][x]=program[y+1][x]|0x80;
			xdata[y-1][x]=xdata[y+1][x];
			program[y+1][x]=' ';
			program[y][x]='W';
		}
		break;
	case '<':
		if(y>0 && x>0 && iscrate(program[y-1][x]) /*&& program[y-1][x-1]==' '*/)
		{
			/*program[y-1][x-1]=program[y-1][x]|0x80;
			xdata[y-1][x-1]=xdata[y-1][x];
			program[y-1][x]=' ';*/
			pushBlocksLeft(x,y-1);
		}
		break;
	case '>':
		if(y>0 && x<PROGRAM_WIDTH-1 && iscrate(program[y-1][x]) /*&& program[y-1][x+1]==' '*/)
		{
			/*program[y-1][x+1]=program[y-1][x]|0x80;
			xdata[y-1][x+1]=xdata[y-1][x];
			program[y-1][x]=' ';*/
			pushBlocksRight(x,y-1);
		}
		break;
	case 'O':
		if(y==0 || !iscrate(program[y-1][x]))
			break;
		if(y==PROGRAM_HEIGHT-1 || !iscrate(program[y+1][x]&0x7F))
			break;
		p1=program[y-1][x]-'0';
		p2=(program[y+1][x]&0x7F)-'0';
		if(p1>p2) // move right
		{
			if(x<PROGRAM_WIDTH-1 && program[y-1][x+1]==' ')
			{
				program[y-1][x+1]=program[y-1][x];
				xdata[y-1][x+1]=xdata[y-1][x];
				program[y-1][x]=' ';
			}
		}
		else // move left
		{
			if(x>0 && program[y-1][x-1]==' ')
			{
				program[y-1][x-1]=program[y-1][x];
				xdata[y-1][x-1]=xdata[y-1][x];
				program[y-1][x]=' ';
			}
		}
		break;
	case ']':
#ifndef FAST
		if(xdata[y][x]==0)
		{
			xdata[y][x]=1;
			break;
		}
#endif
		if(y<PROGRAM_HEIGHT-1 && program[y+1][x]==' ')
		{
			program[y+1][x]=']'|0x80;
			program[y][x]=' ';
			xdata[y+1][x]=0;
		}
		else if(y>1 && program[y-1][x]=='^')
		{
			program[y-2][x]=']'|0x80;
			program[y][x]=' ';
			xdata[y-2][x]=0;
		}
		else if(x>0)
		{
			if(iscrate(program[y][x-1]))
			{
				if(x<=1 || !pushBlocksLeft(x-1,y))
					break;
			}
			else if(y<0 && program[y][x-1]=='\\')
			{
				if(iscrate(program[y-1][x-1]))
					if(!pushBlocksLeft(x-1,y-1))
						break;
				program[y-1][x-1]=']'|0x80;
				program[y][x]=' ';
			}
			else if(program[y][x-1]!=' ')
				break; // don't move
			program[y][x-1]=']'|0x80;
			xdata[y][x-1]=0;
			program[y][x]=' ';
			if(y>0 && x>1 && program[y-1][x-2]=='T')
			{
				program[y][x-1]='['|0x80;
				xdata[y][x-1]=0;
			}
		}
		break;
	case '[':
#ifndef FAST
		if(xdata[y][x]==0)
		{
			xdata[y][x]=1;
			break;
		}
#endif
		if(y<PROGRAM_HEIGHT-1 && program[y+1][x]==' ')
		{
			program[y+1][x]='['|0x80;
			program[y][x]=' ';
			xdata[y+1][x]=0;
		}
		else if(y>1 && program[y-1][x]=='^')
		{
			program[y-2][x]='['|0x80;
			program[y][x]=' ';
			xdata[y-2][x]=0;
		}
		else if(x<PROGRAM_WIDTH-1)
		{
			if(iscrate(program[y][x+1]))
			{
				if(x>=PROGRAM_WIDTH-2 || !pushBlocksRight(x+1,y))
					break;
			}
			else if(y<0 && program[y][x+1]=='/')
			{
				if(iscrate(program[y-1][x+1]))
					if(!pushBlocksRight(x+1,y-1))
						break;
				program[y-1][x+1]='['|0x80;
				program[y][x]=' ';
			}
			else if(program[y][x+1]!=' ')
				break; // don't move
			program[y][x+1]='['|0x80;
			program[y][x]=' ';
			xdata[y][x+1]=0;
			if(y>0 && x<PROGRAM_WIDTH-2 && program[y-1][x+2]=='T')
			{
				program[y][x+1]=']'|0x80;
				xdata[y][x+1]=0;
			}
		}
		break;
	case '*':
		if(y<PROGRAM_HEIGHT-1 && iscrate(program[y+1][x]&0x7F) && !(program[y+1][x]&0x80))
		{
			if(program[y+1][x]=='r')
				break; // can't output random crate
			if(program[y+1][x]=='b')
				fprintf(out," %i",xdata[y+1][x]);
			else
				fprintf(out," %c",program[y+1][x]&0x7F);
			ticks_since_output=0;
		}
		break;
	case '?':
		if(y<PROGRAM_HEIGHT-1 && input_char!='\0' && program[y+1][x]==' ')
			program[y+1][x]=input_char;
		break;
	case 'r':
		if(!((y<PROGRAM_HEIGHT-1 && program[y+1][x]=='!') ||
			(y>0 && program[y-1][x]=='i')))
			// don't change if on a copier
			program[y][x]=(rand()%10+'0')|0x80;
		if(y<PROGRAM_HEIGHT-1 && program[y+1][x]==' ')
		{
			program[y+1][x]=program[y][x]|0x80;
			program[y][x]=' ';
		}
		break;
	case 'F':
		if(x<PROGRAM_WIDTH-1 && iscrate(program[y][x+1]&0x7F))
			program[y][x+1]=' ';
		if(x>0 && iscrate(program[y][x-1]&0x7F))
			program[y][x-1]=' ';
		if(y>0 && iscrate(program[y-1][x]&0x7F))
			program[y-1][x]=' ';
		if(y<PROGRAM_HEIGHT-1 && iscrate(program[y+1][x]&0x7F))
			program[y+1][x]=' ';
		break;
	case '-':
		if(x==0 || x==PROGRAM_WIDTH-1 || y==PROGRAM_HEIGHT-1)
			break;
		if(!iscrate(program[y+1][x-1]) || !iscrate(program[y+1][x]))
			break;
		if(program[y+1][x+1]!=' ')
			break;
		if(program[y+1][x+1]!=' ')
			break;
		if(program[y+1][x-1]!='b')
			p1=program[y+1][x-1]-'0';
		else
			p1=xdata[y+1][x-1];
		if(program[y+1][x]!='b')
			p2=program[y+1][x]-'0';
		else
			p2=xdata[y+1][x];
		result=(signed short)p1-(signed short)p2;
		if(result<10 && result>=0)
			program[y+1][x+1]=result+'0'|0x80;
		else
		{
			program[y+1][x+1]='b';
			xdata[y+1][x+1]=result;
		}
		program[y+1][x-1]=' ';
		program[y+1][x]=' ';
		break;
	case '+':
		if(x==0 || x==PROGRAM_WIDTH-1 || y==PROGRAM_HEIGHT-1)
			break;
		if(!iscrate(program[y+1][x-1]) || !iscrate(program[y+1][x]) ||
			program[y+1][x-1]=='r' || program[y+1][x]=='r')
			break;
		if(program[y+1][x+1]!=' ')
			break;
		if(program[y+1][x-1]!='b')
			p1=program[y+1][x-1]-'0';
		else
			p1=xdata[y+1][x-1];
		if(program[y+1][x]!='b')
			p2=program[y+1][x]-'0';
		else
			p2=xdata[y+1][x];
		result=(signed short)p1+(signed short)p2;
		if(result<10 && result>=0)
			program[y+1][x+1]=result+'0'|0x80;
		else
		{
			program[y+1][x+1]='b';
			xdata[y+1][x+1]=result;
		}
		program[y+1][x-1]=' ';
		program[y+1][x]=' ';
		break;
/*	case 'x':
		if(x==0 || x==PROGRAM_WIDTH-1 || y==PROGRAM_HEIGHT-1)
			break;
		if(!iscrate(program[y+1][x-1]) || !iscrate(program[y+1][x]))
			break;
		if(program[y+1][x+1]!=' ')
			break;
		if(program[y+1][x+1]!=' ')
			break;
		if(program[y+1][x-1]!='b')
			p1=program[y+1][x-1]-'0';
		else
			p1=xdata[y+1][x-1];
		if(program[y+1][x]!='b')
			p2=program[y+1][x]-'0';
		else
			p2=xdata[y+1][x];
		result=p1*p2;
		if(result<10)
			program[y+1][x+1]=result+'0'|0x80;
		else
		{
			program[y+1][x+1]='b';
			xdata[y+1][x+1]=result;
		}
		program[y+1][x-1]=' ';
		program[y+1][x]=' ';
		break;
	case 'd':
		if(x==0 || x==PROGRAM_WIDTH-1 || y==PROGRAM_HEIGHT-1)
			break;
		if(!iscrate(program[y+1][x-1]) || !iscrate(program[y+1][x]))
			break;
		if(program[y+1][x+1]!=' ')
			break;
		if(program[y+1][x+1]!=' ')
			break;
		if(program[y+1][x-1]!='b')
			p1=program[y+1][x-1]-'0';
		else
			p1=xdata[y+1][x-1];
		if(program[y+1][x]!='b')
			p2=program[y+1][x]-'0';
		else
			p2=xdata[y+1][x];
		if(p2==0) result=-1;
		else result=p1/p2;
		if(result>=0)
		{
			if(result<10)
				program[y+1][x+1]=result+'0'|0x80;
			else
			{
				program[y+1][x+1]='b';
				xdata[y+1][x+1]=result;
			}
		}
		program[y+1][x-1]=' ';
		program[y+1][x]=' ';
		break;*/
	case 'D':
		if(x>0 && x<PROGRAM_WIDTH-1)
		{
			if(program[y][x-1]!=' ' && program[y][x+1]==' ' && !(xdata[y][x]&2))
			{
				program[y][x+1]='=';
				xdata[y][x]|=1;
			}
			if(program[y][x-1]==' ' && xdata[y][x]&1)
			{
				program[y][x+1]=' ';
				xdata[y][x]&=~1;
			}
			if(program[y][x+1]!=' ' && program[y][x-1]==' ' && !(xdata[y][x]&1))
			{
				program[y][x-1]='=';
				xdata[y][x]|=2;
			}
			if(program[y][x+1]==' ' && xdata[y][x]&2)
			{
				program[y][x-1]=' ';
				xdata[y][x]&=~2;
			}
		}
		if(y>0 && y<PROGRAM_WIDTH-1)
		{
			if(program[y-1][x]!=' ' && program[y+1][x]==' ' && !(xdata[y][x]&8))
			{
				program[y+1][x]='|';
				xdata[y][x]|=4;
			}
			if(program[y-1][x]==' ' && xdata[y][x]&4)
			{
				program[y+1][x]=' ';
				xdata[y][x]&=~4;
			}
			if(program[y+1][x]!=' ' && program[y-1][x]==' ' && !(xdata[y][x]&4))
			{
				program[y-1][x]='|';
				xdata[y][x]|=8;
			}
			if(program[y+1][x]==' ' && xdata[y][x]&8)
			{
				program[y-1][x]=' ';
				xdata[y][x]&=~8;
			}
		}
		break;
	case 's':
		if(y<PROGRAM_HEIGHT-1 && iscrate(program[y+1][x]&0x7F) && x<PROGRAM_WIDTH-1)
		{
			bool moved=(program[y+1][x]&0x7F)?true:false;
			int num=program[y+1][x]&0x7F;
			if(num=='r')
				break;
			if(num!='b')
			{
				program[y+1][x+1]=program[y+1][x];
				xdata[y+1][x+1]=xdata[y+1][x];
				program[y+1][x]=' ';
				break;
			}
			char buf[10];
			sprintf(buf,"%i",xdata[y+1][x]);
			int len=strlen(buf);
			if(x<PROGRAM_WIDTH-len)
			{
				int found_obstruction=0;
				for(int k=x+1;k<x+len+1;k++)
				{
					if(program[y+1][k]!=' ')
					{
						found_obstruction=1;
						break;
					}
				}
				if(found_obstruction)
					break;
				memcpy(&program[y+1][x+1],buf,len);
				program[y+1][x]=' ';
			}
		}
		break;
	case 'a':
		if(y>0 && isdigit(program[y-1][x]&0x7F))
		{
			int respond=(program[y-1][x] & 0x7F)-'0';
			if(state!=respond)
			{
				if(program[y+1][x]==' ')
					program[y+1][x]='|';
				if(program[y][x+1]==' ')
					program[y][x+1]='=';
				if(program[y][x-1]==' ')
					program[y][x-1]='=';
			}
			if(state==respond)
			{
				if(program[y+1][x]=='|')
					program[y+1][x]=' ';
				if(program[y][x+1]=='=')
					program[y][x+1]=' ';
				if(program[y][x-1]=='=')
					program[y][x-1]=' ';
				xdata[y][x]=0;
			}
		}
		break;
	case '\\':
		if(y>0 && x<PROGRAM_WIDTH-1 && iscrate(program[y-1][x]))
		{
			program[y][x+1]=program[y-1][x]|0x80;
			xdata[y][x+1]=xdata[y-1][x];
			program[y-1][x]=' ';
		}
		break;
	case '/':
		if(y>0 && x>0 && iscrate(program[y-1][x]))
		{
			program[y][x-1]=program[y-1][x]|0x80;
			xdata[y][x-1]=xdata[y-1][x];
			program[y-1][x]=' ';
		}
		break;
	case '0':case '1':case '2':case '3':case '4':
	case '5':case '6':case '7':case '8':case '9':
	case 'b':
		if(y<PROGRAM_HEIGHT-1 && program[y+1][x]==' ')
		{
			program[y+1][x]=program[y][x]|0x80;
			xdata[y+1][x]=xdata[y][x];
			program[y][x]=' ';
		}
		break;
	}
}
const COORD topleft={0,0};
const HANDLE hStdout=GetStdHandle(STD_OUTPUT_HANDLE);
void ResetCursorPosition()
{
	SetConsoleCursorPosition(hStdout,topleft);
}
void ShowCurrentState()
{
	for(int k=0;k<PROGRAM_HEIGHT;k++)
		printf("%s\n",program[k]);
}
/*void doTick()
{
	ticks_since_output++;
	for(int x=0;x<PROGRAM_WIDTH;x++)
	{
#ifdef RIGHTWAYUP
		for(int y=0;y<PROGRAM_HEIGHT;y++)
#else
		for(int y=PROGRAM_HEIGHT-1;y>=0;y--)
#endif
		{
			doCell(x,y,program[y][x]);
		}
	}
	for(x=0;x<PROGRAM_WIDTH;x++)
	{
		for(int y=0;y<PROGRAM_HEIGHT;y++)
		{
			if(program[y][x]&0x80)
				program[y][x]&=0x7F;
		}
	}
	input_char=' ';
}*/
void doAllOfType(char c1=0,char c2=0,char c3=0,char c4=0,char c5=0)
{
	for(int x=0;x<PROGRAM_WIDTH;x++)
	{
		for(int y=PROGRAM_HEIGHT-1;y>=0;y--)
		{
			char c=program[y][x];
			if((c1==0 && iscrate(c)) || c==c1 || c==c2 || c==c3 || c==c4 || c==c5)
				doCell(x,y,c);
		}
	}
}
void doTick()
{
	ticks_since_output++;
	doAllOfType('*','?','s','a');
	doAllOfType('D','F','r');
	doAllOfType('A','V','^');
	doAllOfType(0,'\\','/');
	doAllOfType('i','!','M','W');
	doAllOfType('G','H','J','Y');
	doAllOfType('[',']','>','<');
	doAllOfType('O','+','-');
	for(int x=0;x<PROGRAM_WIDTH;x++)
	{
		for(int y=0;y<PROGRAM_HEIGHT;y++)
		{
			if(program[y][x]&0x80)
				program[y][x]&=0x7F;
		}
	}
	input_char=' ';
}
void readProgram(char *from)
{
	FILE *f=fopen(from,"r");
	if(!f)
	{
		perror(from);
		exit(1);
	}
	char *buf=new char[100];
	fgets(buf,100,f);
	int control_len=0;
	if(*buf=='c')
		sscanf(buf,"%*c%i %i %i",&PROGRAM_WIDTH,&PROGRAM_HEIGHT,&control_len);
	else
		sscanf(buf,"%i %i",&PROGRAM_WIDTH,&PROGRAM_HEIGHT);
	PROGRAM_WIDTH++; // the NUL terminator for each line
	delete [] buf;
	if(control_len>0)
	{
		if(control_len>=MAX_CONTROL_LENGTH)
		{
			fprintf(stderr,"Program control too long. Maximum length: %i\n",MAX_CONTROL_LENGTH-1);
			exit(2);
		}
		buf=new char[control_len+3];
		fgets(buf,control_len+3,f);
		char *pStr=strchr(buf,'\n');
		if(pStr) *pStr='\0';
		else
		{
			fprintf(stderr,"The control length must accurately reflect the length of the control.\n");
			delete [] buf;
			exit(3);
		}
		strcpy(control,buf);
		delete [] buf;
	}
	if(PROGRAM_HEIGHT>=MAX_PROGRAM_HEIGHT || PROGRAM_WIDTH>=MAX_PROGRAM_WIDTH)
	{
		fprintf(stderr,"Program too big. Maximum dimensions: %i by %i\n",MAX_PROGRAM_WIDTH-1,MAX_PROGRAM_HEIGHT-1);
		exit(2);
	}
	buf=new char[PROGRAM_WIDTH+3];
	for(int y=0;y<PROGRAM_HEIGHT;y++)
	{
		fgets(buf,PROGRAM_WIDTH+3,f);
		char *pStr=strchr(buf,'\n');
		if(pStr) *pStr='\0';
		else if(y<PROGRAM_HEIGHT-1)
		{
			fprintf(stderr,"Programs must have one line in the file for every program line.\n");
			delete [] buf;
			exit(3);
		}
		strcpy((char*)program[y],buf);
	}
	delete [] buf;
	fclose(f);
	memset(xdata,0,sizeof xdata);
}
int main(int argc,char **argv)
{
	if(argc>=2)
	{
		if(!strcmp(argv[1],"--help"))
		{
			printf("RubE On Conveyor Belts usage:\n");
			printf("\t%s\n\t\tExecute the default program, which is implementation-dependent\n",argv[0]);
			printf("\t%s FILENAME\n\t\tExecute the specified program\n",argv[0]);
			return 0;
		}
		if(!strcmp(argv[1],"--version"))
		{
			printf("RubE On Conveyor Belts\n");
			printf("Whichever version you want it to be\n");
			printf("By immibis\n");
			return 0;
		}
	}
	if(argc>2)
	{
		printf("Too many arguments - for help, use '%s --help'\n",argv[0]);
		return 1;
	}
	if(argc==2)
		readProgram(argv[1]);
	// else do nothing - use the default program
	SMALL_RECT windowSize;
	windowSize.Left=0;
	windowSize.Top=0;
	windowSize.Bottom=max(24,PROGRAM_HEIGHT);
	windowSize.Right=max(79,PROGRAM_WIDTH);
	COORD bufferSize;
	bufferSize.X=max(80,PROGRAM_WIDTH+1);
	bufferSize.Y=max(25,PROGRAM_HEIGHT+1);
	if(!SetConsoleScreenBufferSize(hStdout,bufferSize))
	{
		printf("SetConsoleScreenBufferSize: %i\n",GetLastError());
		return 1;
	}
	if(!SetConsoleWindowInfo(hStdout,TRUE,&windowSize))
	{
		printf("SetConsoleWindowInfo: %i\n",GetLastError());
		return 1;
	}
	int cc=0; // control counter
	int tc=0; // tape counter
	srand(time(NULL));
	ResetCursorPosition();
	ShowCurrentState();
	Sleep(INITIAL_DELAY);
	out=fopen("program.out","w");
	while(!isFinished)
	{
#ifndef BYPASS_CONTROL
		switch(control[cc])
		{
		case '+':
			control_tape[tc]++;
			break;
		case '-':
			control_tape[tc]--;
			break;
		case '[':
			if(control_tape[tc]==0)
			{
				int level=0;
				do
				{
					if(control[cc]=='\0')
					{
						isFinished=1;
						break;
					}
					if(control[cc]=='[') level++;
					if(control[cc]==']') level--;
					cc++;
				} while(level>0);
				cc--;
			}
			break;
		case ']':
			if(control_tape[tc]!=0)
			{
				int level=0;
				do
				{
					if(cc<0)
					{
						isFinished=1;
						break;
					}
					if(control[cc]=='[') level--;
					if(control[cc]==']') level++;
					cc--;
				} while(level>0);
				cc++;
			}
			break;
		case 'i':case ',':
			if(_kbhit())
			{
				char ch;
				ch=_getch();
				if(ch=='`') isFinished=1;
				if(ch=='+') TICK_DELAY=int(double(TICK_DELAY)*0.80);
				if(ch=='-') TICK_DELAY=int(double(TICK_DELAY+4)*1.25);
				control_tape[tc]=ch;
			}
			else
				control_tape[tc]=0;
			break;
		case '<':
			if(tc>0) tc--;
			break;
		case '>':
			if(tc<MAX_CONTROL_TAPE-1) tc++;
			break;
		case '\0':
			isFinished=1;
			break;
		case 's':
			Sleep(TICK_DELAY);
			break;
		case 't':
			doTick();
			break;
		case 'd':
			ResetCursorPosition();
			ShowCurrentState();
			break;
		case 'a':
			state=control_tape[tc]%10;
			break;
		case 'o':
			input_char=(char)control_tape[tc];
			if(input_char=='b' || !iscrate(input_char)) input_char='\0';
			break;
		case 'O':
			input_char=control_tape[tc]+'0';
			if(input_char<'0' || input_char>'9') input_char='\0';
			break;
		}
		cc++;
#else
		ResetCursorPosition();
		ShowCurrentState();
		Sleep(TICK_DELAY);
		doTick();
		if(_kbhit())
		{
			input_char=_getch();
			if(input_char=='`') isFinished=1;
			if(input_char=='+') TICK_DELAY=int(double(TICK_DELAY)*0.80);
			if(input_char=='-') TICK_DELAY=int(double(TICK_DELAY+4)*1.25);
			if(!iscrate(input_char)) input_char='\0';
		}
#endif
	}
	fclose(out);
	return 0;
}