/* cwmain.c */

#include <stdio.h>
#include "corewar.h"
#include "clparse.h"


/* Globals. */
char  cwerrstr[STR_ERRLEN] = "";
char  *opnames[] = {
	"DAT", "MOV", "ADD", "SUB", "JMP", "JMZ", "JMN", "DJN", "CMP", "SPL", "SLT",
	"END", "EQU", NULL};
char  *amodenames[] = {
	"immediate", "direct", "indirect", "predecrement", "postincrement"};
core_el_t  *core = NULL;

aval_t  coresize = 8000;
aval_t  maxprogsize = 100;
unsigned  maxthreads = 8000;
unsigned  max_cycles;
unsigned  amode_restrict = TRUE;  /* TRUE is ICWS '88 */
unsigned  postinc = FALSE;  /* FALSE is ICWS '88 */
unsigned  rounds = 0;  /* Nonzero here means a tournament game. */
unsigned  w_radius, w_radius_set = FALSE; /* Maximum distance to do a write. */
unsigned  interactive = FALSE;
unsigned  split_id = FALSE;

static unsigned  fl_set = FALSE;
static unsigned  *wins;
static char  geometry[CLP_MAXSTRLEN + 1] = "";
static char  display[CLP_MAXSTRLEN + 1] = "";
static unsigned  iconic = FALSE;

static clp_info_t  cmdline[] = {
	{"", clp_true, NULL, VERSION},
	{"-coresize", clp_int, &coresize, "Set size of core (default = 8000)"},
	{"-maxprogsize", clp_int, &maxprogsize,
		 "Maximum program size (default = 100)"},
	{"-maxprocs", clp_int, &maxthreads,
		 "Maximum processes per program (default = 8000)"},
	{"-fightlength", clp_int, &max_cycles,
		 "Cycles until tie is declared (default = 10 * coresize)"},
	{"-fightlength", clp_true, &fl_set, NULL},
	{"-rounds", clp_int, &rounds, "Number of tournament games to play"},
	{"", clp_true, NULL, ""},
	{"-noall_modes", clp_true, &amode_restrict,
		 "ICWS '88 addressing mode restrictions (default)"},
	{"-all_modes", clp_false, &amode_restrict, "Remove restrictions"},
	{"", clp_true, NULL, ""},
	{"-splitid", clp_true, &split_id, "ID matching needed on SPL"},
	{"-nosplitid", clp_false, &split_id, "No ID matching on SPL (default)"},
	{"", clp_true, NULL, ""},
	{"-nopostinc", clp_false, &postinc,
		 "Disallow \">\" addressing mode (default)"},
	{"-postinc", clp_true, &postinc, "Allow \">\" addressing mode"},
	{"", clp_true, NULL, ""},
	{"-wdist", clp_int, &w_radius,
		 "Set maximum distance for a write (default = coresize)"},
	{"-wdist", clp_true, &w_radius_set, NULL},
#ifdef  X11_DISP
	{"-geometry", clp_string, geometry, NULL},
	{"-geom", clp_string, geometry, NULL},
	{"-iconic", clp_true, &iconic, NULL},
	{"-display", clp_string, display, NULL},
#endif  /* X11_DISP */
	CLP_END};

static void  fight(program_t *progs, unsigned nprogs), err(char *pname),
             interact(char *progname);

static char  usage[] =
	"Usage:\n"
	"   %s -help\n"
#ifndef  NO_DISP
	"   %s [options]\n"
#endif  /* NO_DISP */
	"   %s [options] <test program>\n"
	"   %s [options] -rounds n <fighter 1> ... <fighter n>\n";

int  main(int argc, char *argv[])  {
	program_t  *prog = NULL;
	int  i, j;

	srandom(time(0L) + getpid());
	argc = clp_parse(argv, cmdline);
	if (!w_radius_set)
		w_radius = coresize / 2;
	if (argc == CLP_NOGOOD)
		exit(1);
	if (!fl_set)
		max_cycles = coresize * 10;
	--argc;

#ifdef  NO_DISP
	if (!rounds)  {
		printf("This KotH was compiled without display code.  "
		       "\"-rounds 1\" has been assumed.\n");
		rounds = 1;
	}
#endif  /* NO_DISP */

	if (rounds && (argc < 2))  {
		fprintf(stderr, "Tournament games must specify "
						"at least two program files.\n");
		fprintf(stderr, usage, argv[0], argv[0], argv[0], argv[0]);
		exit(1);
	}
	if (!rounds && (argc > 1))  {
		fprintf(stderr, "Interactive games cannot pre-load programs.\n");
		fprintf(stderr, usage, argv[0], argv[0], argv[0], argv[0]);
		exit(1);
	}
	if ((argc >= coresize / (maxprogsize * 2 - 1)) && rounds)  {
		fprintf(stderr, "Sorry; %d fighters will not fit in a core of size %d.\n",
						argc, coresize);
		exit(1);
	}

	if (argc)  {
		prog = (program_t *)tmalloc(argc * sizeof(program_t));
		wins = (unsigned *)tmalloc(argc * argc * sizeof(unsigned));
	}
	if (argc == 1)  {
		if (!assemble(prog, argv[1]))
			err(prog[0].pname);
		else  {
			printf("Program \"%s\" (length %d) by \"%s\"\n"
						 "(contact address \"%s\"):\n", prog->pname, prog->proglen,
						 prog->author, prog->retaddr);
			disasm(prog->listing, prog->proglen, prog->startaddr);
			exit(0);
		}
	}
	for (i = 0;  i < argc * argc;  ++i)
		wins[i] = 0;
	for (i = 0;  i < argc;  ++i)  {
		if (!assemble(prog+i, argv[i+1]))
			err(prog[i].pname);
	}
	if (argc > 0)  {
		for (i = 0;  i < rounds;  ++i)
			fight(prog, argc);
		for (i = 0;  i < argc;  ++i)  {
			for (j = i*argc;  j < i*argc + argc;  ++j)
				printf("%d ", wins[j]);
			putchar('\n');
		}
	} else  {
#ifndef  NO_DISP
		ld_init(DI_MAXPLAYERS);
		interact(argv[0]);
#endif  /* NO_DISP */
	}
	exit(0);
}


static void  err(char *pname)  {
	fprintf(stderr, "Error in program \"%s\":\n", pname);
	fprintf(stderr, "%s\n", cwerrstr);
	exit(1);
}


#ifndef  NO_DISP
static void  interact(char *progname)  {
	char  *geom, *dpynm;

	new_core();
	if (geometry[0])
		geom = geometry;
	else
		geom = NULL;
	if (display[0])
		dpynm = display;
	else
		dpynm = NULL;
	di_init(progname, geom, dpynm, iconic, max_cycles);
}
#endif  /* NO_DISP */


static void  fight(program_t *progs, unsigned nprogs)  {
	int  simlen, i, j;

	ld_init(nprogs);
	new_core();
	for (i = 0;  i < nprogs;  ++i)
		load_core(progs+i, ld_addr(i), i);
	simlen = simulate(max_cycles, nprogs);
	for (i = 0, j = -1;  i < nprogs;  ++i)
		if (proc_count(i))
			++j;
	for (i = 0;  i < nprogs;  ++i)
		if (proc_count(i))
			++wins[i*nprogs + j];
}


void  disasm(core_el_t *listing, aval_t len, aval_t start)  {
	static char  amname[] = "# @<>";
	aval_t  addr;

	for (addr = 0;  addr < len;  ++addr)  {
		if (addr == start)
			printf("START");
		printf("\t%s\t%c",
					 opnames[JTOP(listing->index)],
					 amname[JTAM1(listing->index)]);
		if (listing->val1 == AVAL_PROGID)
			printf("ID");
		else
			printf("%4d", listing->val1);
		printf(",%c", amname[JTAM2(listing->index)]);
		if (listing->val2 == AVAL_PROGID)
			printf("ID\n");
		else
			printf("%4d\n", listing->val2);
		++listing;
	}
}


void  *tmalloc(int size)  {
	void  *result;

	result = (void *)malloc(size);
	if (result == NULL)  {
		char  errstr[40];

		sprintf(errstr, "koth: malloc(%d) failed", size);
		perror(errstr);
		exit(1);
	}
	return(result);
}
