#include "includes.h"
#include "knightcap.h"

/* the "brain" is an implementation of the permanent brain idea common 
   in chess programs.

   In KnightCap it is also used to develop its own opening book. This
   is done by pondering the positions in the brain while idle,
   constantly refining them.
   */

extern struct state *state;

#define MAX_BRAIN_SIZE 10000
#define MAX_BRAIN_MOVE 30
#define MIN_MULL_TIME (30)
#define BRAIN_TEMP_SIZE MAX_GAME_MOVES
#define WHITE_THRESHOLD (tanh(0.0*EVAL_SCALE*PAWN_VALUE))
#define BLACK_THRESHOLD (tanh(0.0*EVAL_SCALE*PAWN_VALUE))
#define FLAG_NEEDS_RESEARCH (1<<0)
#define FLAG_NEVER_RESEARCH (1<<1)
#define MIN_DEPTH 12
#define SAVE_FREQUENCY 300 /* in seconds */

static int mulled_one_opening;
static int max_mull_time[4] = {30, 30, 120, 120};
int mull_stage;
extern int fill_hash;

int testing = 0;

struct brain_entry {
	uint32 hash1;
	uint32 hash2;
	int eval_low;
	int eval_high;
	char depth_low;
	char depth_high;
	Square from, to;
	int search_time; /* how long we've spent searching this pos */
	time_t last_play; /* when we last used this entry */
	time_t last_search; /* when we did the last search */
	Piece board[NUM_SQUARES];
	uint32 flags;
	uint32 flags2;
	unsigned short move_num;
	Square enpassent;
	uint32 white_wins, black_wins;
	uint32 unused;
};


static int done_lookup;
static int brain_size;
static struct brain_entry brain[MAX_BRAIN_SIZE + BRAIN_TEMP_SIZE];
static struct brain_entry *brain_temp = &brain[MAX_BRAIN_SIZE];
static int brain_temp_size;
static int mull_entry;
static int last_hit;
static char *brain_fname;
static int load_failed;
static int refined_count;

int mulling=0;

extern int learning;

static void consistency_check1(struct brain_entry *br);
static void consistency_check2(struct brain_entry *br);

static inline int converged(struct hash_entry *t)
{
	return (t->low.v + MTD_THRESHOLD >= t->high.v) &&
	       (t->depth_low == t->depth_high);
}

static inline int consistent(struct hash_entry *t1, struct hash_entry *t2)
{
	return ((t1->from == t2->from && t1->to == t2->to) && 
		!(t1->low.v > t2->high.v || t1->high.v < t2->low.v));
}


static inline void save_blunder(Position *b, struct brain_entry *br, Move *next_move)
{
        static int initialised;
        static FILE *f;

        if (!initialised) {
                initialised = 1;
                f = fopen("blunder.dat", "a");
        }

        fprintf(f, "%s %s%s %s %d %d\n", position_to_ppn(b), posstr(br->from), posstr(br->to),
		short_movestr(b, next_move), br->depth_low, br->depth_high);

} 

static inline int play_offset(int move_num)
{
	return 60*60*24*move_num;
}

/* return a string describing a brain entry */
static char *entry_str(struct brain_entry *br)
{
	Move m1;
	static char s[1000];

	m1.from = br->from;
	m1.to = br->to;

	sprintf(s,"depth=%d/%d num=%d time=%d v=%d/%d move=%s hash1=%08x %s",
		br->depth_low, 
		br->depth_high, 
		br->move_num,
		br->search_time,
		br->eval_low,
		br->eval_high,
		short_movestr(NULL, &m1),
		br->hash1,
		ctime(&br->last_play));

	return s;
}

static int brain_compare(const void *bb1, const void *bb2)
{
	struct brain_entry *b1;
	struct brain_entry *b2;

	b1 = (struct brain_entry *)bb1;
	b2 = (struct brain_entry *)bb2;

	if (b1->hash1 == 0) return 1;
	if (b2->hash1 == 0) return -1;
	if (b1->depth_low == -1) return 1;
	if (b2->depth_low == -1) return -1;
	if (b2->hash1 < b1->hash1)
		return 1;
	if (b2->hash1 > b1->hash1)
		return -1;
	return memcmp(b1->board, b2->board, sizeof(b1->board));
}

/* this one is used when saving to disk so the file remains the same
   with no changes (good for rsync) */
static int brain_comp2(const void *bb1, const void *bb2)
{
	struct brain_entry *b1;
	struct brain_entry *b2;

	b1 = (struct brain_entry *)bb1;
	b2 = (struct brain_entry *)bb2;

	if (b1->hash1 == 0) return 1;
	if (b2->hash1 == 0) return -1;
	if (b1->last_play < b2->last_play)
		return 1;
	if (b1->last_play > b2->last_play)
		return -1;
	if (b2->move_num < b1->move_num)
		return 1;
	if (b2->move_num > b1->move_num)
		return -1;
	return memcmp(b1->board, b2->board, sizeof(b1->board));
}


static int setup_brain_pos(Position *b, struct brain_entry *br)
{
	memset(b, 0, sizeof(*b));
	memcpy(b->board, br->board, sizeof(b->board));
	b->move_num = br->move_num;
	b->flags = br->flags;
	b->enpassent = br->enpassent;
	b->fifty_count = 0;
	br->hash1 = 0;
	br->hash2 = 0;
	
	if (!create_pboard(b)) {
		lprintf(0,"brain entry corrupt\n");
		br->depth_low = -1;
		br->depth_high = -1;
		return 0;
	}
	br->hash1 = b->hash1;
	br->hash2 = b->hash2;

	return 1;
}

static void brain_resort(void)
{
	int i;

	for (i=0;i<brain_size;i++) {
		if (brain[i].depth_low == -1)
			memset(&brain[i], 0, sizeof(brain[i]));
	}

	qsort(brain, brain_size, sizeof(brain[0]), brain_compare);
	while (brain_size && brain[brain_size-1].hash1 == 0)
		brain_size--;
}


/* rehash the brain to recreate the hash1 and hash2 entries then sort
   on hash1 */
static void brain_rehash(void)
{
	int i;
	Position b;
	unsigned int sum1, sum2;
	time_t t1;

	/* first work out if the brain needs byte swapping */
	sum1 = sum2 = 0;
	for (i=0;i<brain_size;i++) {
		sum1 += brain[i].move_num;
		sum2 += swapu16(brain[i].move_num);
	}

	if (sum1 > sum2) {
		lprintf(0,"byte swapping brain\n");
		for (i=0;i<brain_size;i++) {
			brain[i].eval_low = swap32(brain[i].eval_low);
			brain[i].eval_high = swap32(brain[i].eval_high);
			brain[i].search_time = swapu32(brain[i].search_time);
			brain[i].last_play = swapu32(brain[i].last_play);
			brain[i].last_search = swapu32(brain[i].last_search);
			brain[i].flags = swapu32(brain[i].flags);
			brain[i].flags2 = swapu32(brain[i].flags);
			brain[i].move_num = swapu16(brain[i].move_num);
			brain[i].white_wins = swapu32(brain[i].white_wins);
			brain[i].black_wins = swapu32(brain[i].black_wins);
		}
	}
	

	t1 = time(NULL);

	for (i=0;i<brain_size;i++) {
		if (brain[i].hash1 == 0) {
			memset(&brain[i], 0, sizeof(brain[i]));
			continue;
		}

		setup_brain_pos(&b, &brain[i]);

		if (brain[i].last_play > t1 - play_offset(brain[i].move_num)) {
			brain[i].last_play = t1 - play_offset(b.move_num);
		}
	}

	brain_resort();

	lprintf(0,"rehashed brain\n");
}


/* find a brain entry using only hash1 */
static int brain_find_hash1(uint32 hash1)
{
	int low,high, t;

	if (brain_size == 0) return -1;

	low = 0;
	high = brain_size-1;
	while (low < high) {
		t = (low+high)/2;
		if (brain[t].hash1 == hash1) 
			return t;
		if (brain[t].hash1 < hash1) {
			low = t+1;
		} else {
			high = t-1;
		}
	}
	if (brain[low].hash1 == hash1)
		return low;

	if (state->brain_inserting) {
		for (t=MAX_BRAIN_SIZE;t<MAX_BRAIN_SIZE+brain_temp_size;t++)
			if (brain[t].hash1 == hash1)
				return t;
	}
		
	return -1;
}

/* use a bisection search to try to find a brain entry matching the
   specified position. We could possibly replace this with a hash table
   lookup later if this proves too expensive */
static int brain_find(Position *b)
{
	int t = brain_find_hash1(b->hash1);

	if (t == -1) return -1;

	if (brain[t].hash1 == b->hash1 &&
	    brain[t].hash2 == b->hash2) 
		return t;

	return -1;
}


void brain_fill_hash(Position *b)
{
	unsigned size;
	int i, num;
	struct hash_entry *t;
	if (brain_size == 0) return;

	if (mulling) 
		num = brain[mull_entry].move_num;
	else 
		num = state->position.move_num;

	for (i=0;i<brain_size;i++) {
		if (brain[i].move_num <= num || brain[i].move_num > num + 20)
			continue;

		if (mulling && i == mull_entry) {
			lprintf(0, "not inserted (mull entry): %08x %s", brain[i].flags2, entry_str(&brain[i]));
			continue;
		}

		if (brain[i].flags2 & FLAG_NEEDS_RESEARCH) {
			lprintf(0, "not inserted (needs research): %08x %s", brain[i].flags2, entry_str(&brain[i]));
			continue;
		}

		if (ABS(brain[i].eval_low) < FORCED_WIN && 
		    brain[i].eval_low + 0.01*PAWN_VALUE < brain[i].eval_high) {
			lprintf(0, "not inserted (unconverged): %08x %s", brain[i].flags2, entry_str(&brain[i]));
			brain[i].flags2 |= FLAG_NEEDS_RESEARCH;
			continue;
		}

		if (brain[i].depth_low == -1) {
			lprintf(0, "not inserted (?): %08x %s", brain[i].flags2, entry_str(&brain[i]));
			continue;
		}

		t = hash_ptr(brain[i].hash1);

		size = hts();

		if (num > 0 && t->move_num >= num) {
			/* hash collision */
			if (brain[i].move_num > t->move_num) {
				lprintf(0,"hash collision %08x %08x %d %d\n", brain[i].hash1, t->hash1, 
					brain[i].move_num, t->move_num);
				continue;
			}
		}
		
		t->hash1 = brain[i].hash1;
		t->hash2 = brain[i].hash2;
		t->from = brain[i].from;
		t->to = brain[i].to;
		t->tag = 0;
		t->move_num = brain[i].move_num;
		t->depth_low = brain[i].depth_low;
		t->low.v = brain[i].eval_low;
		t->depth_high = brain[i].depth_high;
		t->high.v = brain[i].eval_low;
	}

	lprintf(0,"filled hash table\n");

}

static void brain_save(void)
{
	int fd;
	char tmpname[200];

	if (brain_size+brain_temp_size == 0) return;

	sprintf(tmpname,"%s.tmp",brain_fname);

	fd = open(tmpname,O_WRONLY|O_CREAT|O_TRUNC, 0666);
	if (fd == -1) {
		lprintf(0,"can't open %s\n", tmpname);
		return;
	}

	brain_resort();

	qsort(brain, brain_size, sizeof(brain[0]), brain_comp2);

	if (state->brain_inserting && brain_temp_size) {
		int t = MAX_BRAIN_SIZE - brain_temp_size;
		if (t > brain_size) {
			t = brain_size;
		} else {
			int i;
			lprintf(0,"overwriting %d entries\n", brain_size - t);
			for (i=t;i<brain_size;i++)
				lprintf(0,"entry %s", entry_str(&brain[i]));
		}
		memcpy(&brain[t], brain_temp, sizeof(brain[0])*brain_temp_size);

		lprintf(0,"added %d brain entries\n", brain_temp_size);
		brain_size = t + brain_temp_size;
		brain_temp_size = 0;
	}

	if (Write(fd, (char *)brain, sizeof(brain[0])*brain_size) != 
	    sizeof(brain[0])*brain_size) {
		lprintf(0,"write to %s failed\n", brain_fname);
		close(fd);
		brain_resort();
		return;
	}
	
	close(fd);

	if (rename(tmpname, brain_fname) != 0) {
		lprintf(0,"rename %s %s failed\n",
			tmpname, brain_fname);
	}

	lprintf(0,"brain saved\n");
	brain_resort();
}

static void brain_timer(time_t t)
{
	static time_t lastt;

	if (lastt + SAVE_FREQUENCY > t && 
	    brain_temp_size < BRAIN_TEMP_SIZE/2)
		return;

	lastt = t;

	brain_save();
}

int brain_open(char *fname)
{
	struct stat st;
	int fd;

	brain_size = 0;
	load_failed = 1;

	brain_fname = fname;

	fd = open(fname,O_RDWR);
	if (fd == -1) {
		lprintf(0,"can't open %s\n", fname);
		return 0;
	}

	if (fstat(fd, &st) != 0) {
		lprintf(0,"fstat %s failed\n", fname);
		close(fd);
		return 0;
	}

	if (st.st_size % sizeof(brain[0]) != 0) {
		lprintf(0,"corrupt brain file %s\n", fname);
		close(fd);
		return 0;
	}

	brain_size = st.st_size / sizeof(brain[0]);

	if (Read(fd, (char *)brain, st.st_size) != st.st_size) {
		lprintf(0,"brain read failed\n");
		close(fd);
		return 0;
	}

	close(fd);

	load_failed=0;
	brain_rehash();
	brain_clean();
	brain_update();

	return 1;
}


/* lookup a position - using the brain as an opening book */
int brain_lookup(Position *b, Move *move, Eval *v, int pondering)
{
	int t, num_moves, m, bm;
	Position b1;
	etype v1, max;
	static Move moves[MAX_MOVES];

	if (mulling || !brain_size || !state->use_pbrain) return 0;

#if USE_EGTB
	if (egtb(b)) {
		EGInit();
		if (EGTBScore(b, &v1)) {
			num_moves = generate_moves(b, moves);
			max = -INFINITY;
			bm = -1;
			for (m=0;m<num_moves;m++) {
				if (!do_move(&b1, b, &moves[m])) continue;
				if (no_material(&b1) && max < 0) {
					max = 0;
					bm = m;
					continue;
				}
				if (EGTBScore(&b1, &v1)) {
					v1 = -v1;
					if (v1 > max) {
						max = v1;
						bm = m;
					}
				}
			}
			if (bm < 0) 
				return 0;
			
			(*v) = makeeval(b, v1);
			move->from = moves[bm].from;
			move->to = moves[bm].to;
			move->v = max;

			if (!legal_move(b, move) || !do_move(&b1, b, move)) {
				lprintf(0,"Illegal move from EGTB! move=%s\n", 
					short_movestr(b, move));
				return 0;
			}

			lprintf(0, "EGTB: %s %s\n", short_movestr(b, move), evalstr(move->v));

			if (!pondering && state->ics_robot) {
				prog_printf("whisper EGTB: %s %s\n", short_movestr(b, move),
					    evalstr(move->v));
			}

			return 2;
		}
	}
#endif

	if (done_lookup == 0)
		state->last_book_move = 0;

        done_lookup = b->move_num;


	t = brain_find(b);
	if (t == -1) {
		lprintf(0,"no brain entry\n");
		return 0;
	}

	brain[t].last_play = timer_start_time() - play_offset(b->move_num);

	if (brain[t].from == A1 && brain[t].to == A1) {
		lprintf(0, "null move in brain entry\n");
		return 0;
	}

	if (brain[t].eval_low + MTD_THRESHOLD < brain[t].eval_high) {
		lprintf(0, "unconverged brain lookup %s\n", entry_str(&brain[t]));
		return 0;
	}

#if REJECT_LOW_BRAIN_MOVES
	if (brain[t].eval_low < -0.4*PAWN_VALUE) {
		lprintf(0,"book move too weak: %s", entry_str(&brain[t]));
		return 0;
	}
#endif
		
	last_hit = b->move_num;
	
	(*v) = makeeval(b, brain[t].eval_low);
	move->from = brain[t].from;
	move->to = brain[t].to;
	move->v = v->v;

	brain_timer(time(NULL));

	if (!legal_move(b, move) || !do_move(&b1, b, move)) {
		lprintf(0,"Illegal move from book! move=%s\n", 
			short_movestr(b, move));
		return 0;
	}

	if (check_repitition(&b1, 1)) 
		return 0;

	lprintf(0, "book: %s low=%d high=%d ww/bw=%d/%d depth=%d/%d searcht=%d\n",
		short_movestr(b, move),
		brain[t].eval_low,
		brain[t].eval_high,
		brain[t].white_wins,
		brain[t].black_wins,
		brain[t].depth_low,
		brain[t].depth_high,
		brain[t].search_time);

	if (state->first_book_move) {
		state->first_book_move = 0;
		state->first_book_move_index = t;
	}

	if (!pondering) {
		brain_temp[brain_temp_size] = brain[t];
		lprintf(0,"inserted entry %s", entry_str(&(brain_temp[brain_temp_size])));
		brain_temp_size++;
		if (state->ics_robot) { 
			prog_printf("whisper Book: %s low=%5.3lf high=%5.3lf ww/bw=%d/%d depth=%d/%d searcht=%d\n",
				    short_movestr(b, move),
				    (double)brain[t].eval_low/PAWN_VALUE,
				    (double)brain[t].eval_high/PAWN_VALUE,
				    brain[t].white_wins,
				    brain[t].black_wins,
				    brain[t].depth_low,
				    brain[t].depth_high,
				    brain[t].search_time);
		}
	}
	return 1;
}


/* possibly insert an entry into the brain */
int brain_insert(Position *b, Move *move)
{
	int nohash = 0;
	int t;
	struct brain_entry *br;
	Position b1;
	struct hash_entry *h;

	if (load_failed) return 0;

	t = brain_find(b);
	
	if (mulling && t != mull_entry) {
		return 0;
	}

	if (state->brain_inserting && t != -1) {
		br = &brain[t];
		lprintf(0, "index: %d\n", t);
	} else { 
		br = &brain_temp[brain_temp_size];
		if (!mulling) {
			brain_temp_size++;
			memset(br, 0, sizeof(*br));
		}
	}

	if (!do_move(&b1, b, move)) {
		lprintf(0,"illegal move passed to brain_insert\n");
		return 0;
	}

	h = fetch_hash(&b1);

	if (!h) {
		h = fetch_hash(b);
		if (!h) {
			lprintf(0,"no hash entry for brain_insert move\n");
			return 0;
		}
		nohash = 1;
	}
	
	if (!converged(h)) {
		if (!nohash) {
			struct hash_entry *h1 = h;
			h = fetch_hash(b);
			if (!h) {
				h = h1;
				lprintf(0, "No hash entry for brain entry\n");
			} else 
				nohash = 1;
		}
	}

	if (!converged(h)) {
		if (mulling && ABS(h->low.v) < FORCED_WIN && h->low.v != -INFINITY) {
			lprintf(0, "not inserting: not converged %d/%d %d/%d\n",h->low.v, h->high.v, 
				h->depth_low, h->depth_high); 
			return 0;
		}
		lprintf(0, "inserting unconverged entry: %d/%d %d/%d %s%s\n",
			h->low.v, h->high.v, h->depth_low, h->depth_high, 
			posstr(h->from), posstr(h->to));
		state->converged = 0;
	}
	
	/* check if it is worth overwriting the old entry. at present
	   we just base the decision on the depths of the entries */

	/* we are happy with this entry: reset the flags */
	br->flags2 &= ~FLAG_BLUNDER;
	br->flags2 &= ~FLAG_NEEDS_RESEARCH;
		
	/* we've decided where to put it, finish it off */
	br->hash1 = b->hash1;
	br->hash2 = b->hash2;
	if (nohash) {
		br->eval_low =  h->low.v;
		br->eval_high = h->high.v;
	} else {
		br->eval_low =  -h->high.v;
		br->eval_high = -h->low.v;
	}

	if (nohash) {
		br->depth_low = h->depth_low;
		br->depth_high = h->depth_high;
	} else {
		br->depth_low = h->depth_high+1;
		br->depth_high = h->depth_low+1;
	}

	br->from = move->from;
	br->to = move->to;

	br->search_time = timer_elapsed();
	memcpy(br->board, b->board, sizeof(b->board));
	br->flags = b->flags;
	br->move_num = b->move_num;
	br->enpassent = b->enpassent;
	br->last_search = time(NULL);

	if (!mulling) {
		br->last_play = time(NULL) - play_offset(b->move_num);
	}
		
	lprintf(0,"inserted entry %s", entry_str(br));

	if (br->depth_low < state->current_depth)
		state->converged = 0;
	else state->converged = 1;

	if (mulling) {
		br->depth_low = MIN_DEPTH;
		br->depth_high = MIN_DEPTH;
		consistency_check1(br);
		consistency_check2(br);
		brain_save();
	}
	brain_timer(time(NULL));

	return 1;
}

void brain_close(void)
{
	brain_save();
}


static int choose_mull(void)
{
	int t, r;
	int bestt;
	int last_move;
	time_t last_play;
#if SEARCH_OPENING_VARIATIONS
	Position b;
	Move m;
#endif
	lprintf(0,"stage 0 mulling: blunders\n");
	state->open = 0;
	/* the first priority is blunders, most recent ones first */
	mull_stage= 0;
	bestt = 0;
	while (bestt != -1) {
		bestt = -1;
		last_play = time(NULL);

		for (t=0;t<brain_size;t++) {
			if (brain[t].hash1 == 0) continue;

			if ((brain[t].flags2 & FLAG_BLUNDER) && 
			    !(brain[t].flags2 & FLAG_NEVER_RESEARCH)) {
				if (bestt == -1 || 
				    brain[t].last_play < last_play) {
					bestt = t;
					last_play = brain[t].last_play;
				}
			}
		}
		if (bestt != -1) {
			return bestt;
		}
	}

	lprintf(0, "all blunders searched\n");

	/* opening searched, now search longer on any unsearched moves */
stage2: 
	lprintf(0, "longer search\n");
	mull_stage = 1;
	lprintf(0, "stage 2 mull\n");
	bestt = 0;
	while (bestt!= -1) {
		bestt = -1;
		last_move = 0;

		for (t=0;t<brain_size;t++) {
			if (brain[t].hash1 == 0) continue;

			if ((brain[t].flags2 & FLAG_NEEDS_RESEARCH) && 
			    !(brain[t].flags2 & FLAG_NEVER_RESEARCH)) {
			    if (brain[t].move_num >= last_move ||  
				bestt == -1) {
				    bestt = t;
				    last_move = brain[t].move_num;
				}
			}
		}
		if (bestt != -1) {
			return bestt;
		}
	}

	lprintf(0,"all entries searched\n");

	if (!brain_clean())
		goto stage2;

	state->open = 1;
	if (state->notified) {
		prog_printf("tell %s My brain is now clean and I am accepting challenges\n", 
			    state->notifyee);
		prog_printf("accept\n");
		state->notified = 0;
	}
	prog_printf("seeks\n");

	if ((random() % 1) == 0 && mulled_one_opening) {

		/* choose randomly, weighted towards guys near the start
		   and those that have not been searched for a while */

		mull_stage = 2;
		lprintf(0, "using random mull\n");
		bestt = 0;
		while (!state->quit && state->computer == 0) {
			t = random() % brain_size;

			if (brain[t].hash1 == 0) continue;
			
			if (brain[t].flags2 & FLAG_NEVER_RESEARCH) continue;
			
			if (imax(brain[t].depth_low, brain[t].depth_high) > MAX_DEPTH/2) continue;
			
			/* don't mull on mate! */
			if (brain[t].eval_low > (WIN-100) || 
			    brain[t].eval_high < -(WIN-100))
				continue;
			
			r = 100;
			
			if (brain[t].move_num < 20)
				r += (20 - brain[t].move_num)*(20 - brain[t].move_num)*100;
			
			if (random() % 40100 >= r)
				continue;
			
			return t;
		}
	} else {
		mulled_one_opening = 1;
#if SEARCH_OPENING_VARIATIONS

		/* pick a very early move in the brain, follow the
		   resulting variation as far as it goes and then add
		   the end position to the brain and mull it.
		   Choose the opening in the last game played first. */

		mull_stage = 3;

		lprintf(0, "mulling opening variations\n");

		memset(&b, 0, sizeof(b));

		while (!state->quit && state->computer == 0) {
			if (!state->first_book_move) {
				t = state->first_book_move_index;
				state->first_book_move = 1;
			}

			t = random() % brain_size;
			if (brain[t].move_num > 10) continue;
			
			r = (10 - brain[t].move_num)*(10 - brain[t].move_num);
			if (random() % 100 >= r) continue;
			
			if (brain[t].hash1 == 0) continue;
			
			if (brain[t].flags2 & FLAG_NEVER_RESEARCH) continue;
			
			if (imax(brain[t].depth_low, brain[t].depth_high) > MAX_DEPTH/2) continue;
			
			/* don't mull on mate! */
			if (brain[t].eval_low > (WIN-100) || 
			    brain[t].eval_high < -(WIN-100))
				continue;
			
			if ((brain[t].move_num & 1) != (brain[t].hash1 & 1)) {
				brain[t].depth_low = -1;
				continue;
			}
			
			/* now follow the variation */
			while (!state->quit && state->computer == 0 && t != -1) { 
				if (!setup_brain_pos(&b, &brain[t])) {
					lprintf(0,"removing brain entry\n");
					brain[t].depth_low = -1;
					break;
				}

				m.from = brain[t].from;
				m.to = brain[t].to;

				if (!legal_move(&b, &m)) {
					lprintf(0,"illegal move %s", entry_str(&brain[t]));
					brain[t].depth_low = -1;
					break;
				}
			
				if (!do_move(&b, &b, &m)) {
					lprintf(0,"removing brain entry\n");
					brain[t].depth_low = -1;
					break;
				}
		
				t = brain_find(&b);
			}
			
			if (t != -1) continue;
			
			create_pboard(&b);

			print_board(b.board);

			/* yucky hack to get the entry in the brain without too much effort */

			mulling = 0;
			state->mulling = 0;
			testing  = 1;
			timer_reset();
			hash_reset();
			order_reset();

			state->move_time = 1;
			zero_move(&m);

			state->brain_inserting = 1;
			make_move(&b, &m);
			mulling = 1;
			state->mulling = 1;
			testing = 0;
			
			brain_save();
		
			state->brain_inserting  = 0;

			t = brain_find(&b);
			if (t == -1) {
				lprintf(0, "brain insert unsuccessful\n");
				continue;
			}

			return t;
		}
#endif
	}

	return -1;
}


void brain_mull(void)
{
	
	Position b;
	int t;
	Move move;
	float old_move_time=0;
	static int first_mull = 1;

	if (brain_size == 0) {
		state->open = 1;
		return;
	}

	if (state->auto_exit && state->moves_mulled >= MAX_MOVES_MULLED && state->open) return;

	lprintf(0,"starting mulling on %d brain entries\n", brain_size);

	mulled_one_opening = 0;
	mulling = 1;
	state->mulling = 1;
	state->brain_inserting = 0;
	brain_temp_size = 0;
	brain_clean();
	while (!state->quit && state->computer == 0 && 
	       state->use_mulling && 
	       (!state->auto_exit || (state->moves_mulled < MAX_MOVES_MULLED || !state->open))) {
		t = choose_mull();
		if (first_mull && state->ics_robot && state->open && 
		    state->computer == 0 && state->autoplay) {
			prog_printf("seeks\n");
			first_mull = 0;
		}

		if (t == -1) continue;
		if (!setup_brain_pos(&b, &brain[t]))
			continue;

		old_move_time = state->move_time;

		state->move_time = max_mull_time[mull_stage];

		mull_entry = t;

		timer_reset();
		hash_reset();
		order_reset();

		move.from = brain[t].from;
		move.to = brain[t].to;

		if (imax(brain[t].depth_low, brain[t].depth_high) > 40)
			brain[t].depth_low = brain[t].depth_high = 1;

		lprintf(0,"mulling %d depth=%d/%d mtime=%2.1f ww=%d bw=%d move=%d v=%d/%d %s\n",
			t, 
			brain[t].depth_low, 
			brain[t].depth_high, 
			state->move_time,
			brain[t].white_wins,
			brain[t].black_wins,
			brain[t].move_num, 
			brain[t].eval_low,
			brain[t].eval_high,
			short_movestr(&b, &move));

		if (state->display_position) {
			dump_ppn(&b);
		}

		if (mull_stage == 0) {
			if ((brain[t].depth_low < MIN_DEPTH) || (brain[t].depth_high < MIN_DEPTH))
				state->current_depth = imax(7,imax(brain[t].depth_low, brain[t].depth_high));
		} else {
			state->current_depth = 7;
		}
		lprintf(0,"current depth: %d\n", state->current_depth);

		state->brain_inserting = 1;
		make_move(&b, &move);
		state->brain_inserting  = 0;

		brain[t].flags2 &= ~FLAG_NEEDS_RESEARCH;
		brain[t].flags2 &= ~FLAG_BLUNDER;

		brain_save();

		++state->moves_mulled;
		lprintf(0,"mulled ***%d\n",state->moves_mulled); 
		state->move_time = old_move_time;

		if (state->computer) continue;

		/* This resumes any available match in the adjourned list 
		   (possibly ICS only) */

		if (state->ics_robot && state->autoplay && state->open)
			prog_printf("resume\n");

	}


	mulling = 0;
	state->mulling = 0;
}

/* This is used when cleaning the brain */
static void consistency_check0(struct brain_entry *br)
{
	int t1, num_moves, m;
	Position b, b1;
	static Move moves[MAX_MOVES];
	uint32 research_flags; 

	if (mulling && mull_stage == 0)
		research_flags = (FLAG_NEEDS_RESEARCH | FLAG_BLUNDER);
	else 
		research_flags = FLAG_NEEDS_RESEARCH;

	/* first some basic sanity checks */
	if (br->search_time < 0)
		br->search_time = 0;

	if (!setup_brain_pos(&b, br)) {
		lprintf(0,"removing brain entry\n");
		br->depth_low = -1;
		return;
	}

	if ((br->move_num & 1) != (br->hash1 & 1)) {
		lprintf(0,"wrong player in consistency_check0 %s",
			entry_str(br));
		br->depth_low = -1;
		return;
	}


	num_moves = generate_moves(&b, moves);
		
	for (m=0;m<num_moves;m++) {
		if (!do_move(&b1, &b, &moves[m])) continue;
		
		t1 = brain_find(&b1);
		if (t1 == -1) continue;

		if (!(brain[t1].flags2 & FLAG_NEEDS_RESEARCH) && 
		    br->eval_high < -brain[t1].eval_high) {
			br->flags2 |= research_flags;
			brain[t1].flags2 |= research_flags;
			lprintf(0, "refined entry0: %s", entry_str(br));
			lprintf(0, "by: %s\n", entry_str(&brain[t1]));
		}
	}
}
			
		

/* this is used when inserting a move into the brain. The insert may
   obsolete later brain entries */
static void consistency_check1(struct brain_entry *br)
{
	int t2, num_moves, m;
	Move m1;
	Position b, b1, b2;
	static Move moves[MAX_MOVES];
	struct hash_entry *h;
	uint32 research_flags;

	if (mulling && mull_stage == 0)
		research_flags = (FLAG_NEEDS_RESEARCH | FLAG_BLUNDER);
	else 
		research_flags = FLAG_NEEDS_RESEARCH;


	/* first some basic sanity checks */
	if (br->search_time < 0)
		br->search_time = 0;

	if (!setup_brain_pos(&b, br)) {
		lprintf(0,"removing brain entry\n");
		br->depth_low = -1;
		return;
	}

	if ((br->move_num & 1) != (br->hash1 & 1)) {
		lprintf(0,"wrong player in consistency_check1 %s",
			entry_str(br));
		br->depth_low = -1;
		return;
	}

	/* Now see if this brain entry leads to another brain entry.
	   If it does then the value of that brain entry is bounded by 
	   the value of the position in the hash table. This relies on
	   the fact that the brain entry would have been overwritten
	   by the hash table entry, and hence the hash table entry
	   must be "better". 

	   However, this may not be the best thing to do. For example,
	   if the hash table entry has the same move as the brain
	   entry, but a significantly wider gap between eval_high and
	   eval_low, and in addition has only been searched 1-ply
	   deeper, then it is not really a better entry. A hack at
	   improving this is given below */

	m1.from = br->from;
	m1.to = br->to;

	if (!do_move(&b1, &b, &m1)) {
		lprintf(0,"removing brain entry\n");
		br->depth_low = -1;
		return;
	}
		
	t2 = brain_find(&b1);

	if (t2 != -1 && (h = fetch_hash(&b1)) && brain[t2].move_num > br->move_num) {
		/* only replace the brain entry if the move has
		   changed or if the new eval is inconsistent with the
		   old eval */
		if (brain[t2].from != h->from ||
		    brain[t2].to != h->to ||
		    h->high.v < brain[t2].eval_low ||
		    h->low.v > brain[t2].eval_high) {
			brain[t2].eval_high = h->high.v;
			brain[t2].eval_low = h->low.v;
			brain[t2].depth_low = h->depth_low;
			brain[t2].depth_high = h->depth_high;
			brain[t2].from = h->from;
			brain[t2].to = h->to;
			if (!converged(h)) {
				brain[t2].flags2 |= research_flags;
			}
			lprintf(0,"refined entry1 %s", entry_str(&brain[t2]));
		} else {
			if (h->depth_low > brain[t2].depth_low &&
			    h->low.v > -INFINITY) {
				brain[t2].eval_low = h->low.v;
				brain[t2].depth_low = h->depth_low;
				brain[t2].flags2 |= research_flags;
				lprintf(0,"refined entry1 %s", entry_str(&brain[t2]));
			}
			if (h->depth_high > brain[t2].depth_high && 
			    h->high.v < INFINITY) {
				brain[t2].eval_high = h->high.v;
				brain[t2].depth_high = h->depth_high;
				brain[t2].flags2 |= research_flags;
				lprintf(0,"refined entry1 %s", entry_str(&brain[t2]));
			}
		}
	}

	/* now generate and try all moves from this position. If they
	   lead to positions in the brain then the hash entries for these
	   positions also bound the brain entry evals */

	num_moves = generate_moves(&b1, moves);
		
	for (m=0;m<num_moves;m++) {
		if (!do_move(&b2, &b1, &moves[m])) continue;
		
		t2 = brain_find(&b2);
		if (t2 == -1) continue;
		
		if (t2 != -1 && (h = fetch_hash(&b2)) && brain[t2].move_num > br->move_num) {
			/* only replace the brain entry if the move has
			   changed or if the new eval is inconsistent with the
			   old eval */
			if (brain[t2].from != h->from ||
			    brain[t2].to != h->to ||
			    h->high.v < brain[t2].eval_low ||
			    h->low.v > brain[t2].eval_high) {
				brain[t2].eval_high = h->high.v;
				brain[t2].eval_low = h->low.v;
				brain[t2].depth_low = h->depth_low;;
				brain[t2].depth_high = h->depth_high;
				brain[t2].from = h->from;
				brain[t2].to = h->to;
				if (!converged(h))
					brain[t2].flags2 |= research_flags;
				lprintf(0,"refined entry2 %s", entry_str(&brain[t2]));
			} else {
				if (h->depth_low > brain[t2].depth_low &&
				    h->low.v > -INFINITY) {
					brain[t2].eval_low = h->low.v;
					brain[t2].depth_low = h->depth_low;
					brain[t2].flags2 |= research_flags;
					lprintf(0,"refined entry2 %s", entry_str(&brain[t2])); 
				}
				if (h->depth_high > brain[t2].depth_high &&
				    h->high.v < INFINITY) {
					brain[t2].eval_high = h->high.v;
					brain[t2].depth_high = h->depth_high;
					brain[t2].flags2 |= research_flags;
					lprintf(0,"refined entry2 %s", entry_str(&brain[t2]));
				}
			}
		}
	}

	/* we don't go any deeper because it is very unlikely that any
	   brain entries this deep would have been overwritten in the hash table (?) */

}



/* this is used when thinking about using an entry in the brain, or
   when mulling entries. It may invalidate the current entry based on
   later entries */
static void consistency_check2(struct brain_entry *br)
{
	int t1, t2, num_moves, m;
	Move m1;
	Position b, b1, b2;
	static Move moves[MAX_MOVES];
	struct hash_entry *h;
	uint32 research_flags;

	if (mulling && mull_stage == 0)
		research_flags = (FLAG_NEEDS_RESEARCH | FLAG_BLUNDER);
	else 
		research_flags = FLAG_NEEDS_RESEARCH;


	/* first some basic sanity checks */
	if (br->search_time < 0)
		br->search_time = 0;

	/* now see if this brain entry leads to another brain entry.
	   If it does then the value of this position is bounded by
	   the value of the other position */
	if (!setup_brain_pos(&b, br)) {
		lprintf(0,"removing brain entry\n");
		br->depth_low = -1;
		return;
	}

	if ((br->move_num & 1) != (br->hash1 & 1)) {
		lprintf(0,"wrong player in consistency_check2 %s",
			entry_str(br));
		br->depth_low = -1;
		return;
	}


	m1.from = br->from;
	m1.to = br->to;

	if (!legal_move(&b, &m1)) {
		lprintf(0,"illegal move %s", entry_str(br));
		br->depth_low = -1;
		return;
	}

	if (!do_move(&b1, &b, &m1)) {
		lprintf(0,"removing brain entry\n");
		br->depth_low = -1;
		return;
	}
		
	t1 = brain_find(&b1);
	h = fetch_hash(&b1);

	if (t1 != -1 && !(brain[t1].flags2 & FLAG_NEEDS_RESEARCH) && brain[t1].move_num > br->move_num) {
		if (br->eval_low > -brain[t1].eval_high) {
			br->eval_low = -brain[t1].eval_high;
			if (br->eval_high + brain[t1].eval_high > 0.01*PAWN_VALUE) {
				br->flags2 |= research_flags;
				brain[t1].flags2 |= research_flags;
				lprintf(0,"refined entry3: %s", entry_str(br));
				lprintf(0, "by %s", entry_str(&brain[t1]));
				if (h) 
					lprintf(0, "hash entry: %d/%d %d/%d\n", h->low.v, h->high.v, 
						h->depth_low, h->depth_high);
				else 
					lprintf(0,"brain entry not in hash table!\n");
			}
		}
	}
	
	/* now generate and try all moves from this position. If they
	   lead to positions in the brain then those positions also
	   bound the eval at the current position */
	num_moves = generate_moves(&b1, moves);
		
	for (m=0;m<num_moves;m++) {
		if (!do_move(&b2, &b1, &moves[m])) continue;
		
		if (b2.hash1 == br->hash1 && 
		    b2.hash2 == br->hash2) 
			continue;
		t2 = brain_find(&b2);
		if (t2 == -1) continue;
			
		h = fetch_hash(&b2);
		if (!((brain[t2].flags2 & FLAG_NEEDS_RESEARCH) || 
		      (brain[t2].flags2 & FLAG_BLUNDER)) && brain[t2].move_num > br->move_num) {
			if (brain[t2].eval_low + 0.01*PAWN_VALUE < brain[t2].eval_high) {
				brain[t2].flags2 |= research_flags;
				lprintf(0, "unconverged entry: %s", entry_str(&brain[t2]));
			} else {
				if (br->eval_low > brain[t2].eval_low) {
					if (br->eval_low - brain[t2].eval_low > 
					    0.01*PAWN_VALUE) {
						br->flags2 |= research_flags;
						lprintf(0,"refined entry4: %s", entry_str(br));
						lprintf(0,"by: %s", entry_str(&brain[t2]));
						if (t1 != -1) {
							lprintf(0, "after: %s", entry_str(&brain[t1]));
							brain[t1].flags2 |= research_flags;
						}
						br->eval_low = brain[t2].eval_low;
						if (h) 
							lprintf(0, "hash entry: %d/%d %d/%d\n", 
								h->low.v, h->high.v, 
								h->depth_low, h->depth_high);
						else {
							lprintf(0,"brain entry not in hash table!\n");
						}
						brain[t2].flags2 |= research_flags;
						++refined_count;
						if (mulling) 
							state->stop_search = 1;
					}
				}
			}
		}
	}
}


void analyse_game(void)
{
	int i, j, t;
	int winner=0;	
	int num_moves = state->position.move_num;
	int losing_move=0;
	int insert=0;
	int last_move=0;
	int first_move = -1;
	double threshold;
	FILE *result;


	done_lookup = 0;
	if (state->analysed) {
		if (!state->use_mulling)
			state->open = 1;
		return;
	}

	state->open = 0;

#if LEARN_EVAL
	if (learning) {
		td_update();
		td_dump("coeffs.dat");
	}
#endif

	/* couldn't think of a more reliable place to reset this */
	state->rating_change = -2;

	lprintf(0,"analysis: %d moves\n", num_moves);

	/* work out who won (or was winning when it ended) */
	for (i=num_moves-1;i>=0;i--) {
		lprintf(0,"move %d eval=%d\n", i, state->game_record[i].v);
		if (state->game_record[i].v != INFINITY) {
			if (state->game_record[i].v > 0.4*PAWN_VALUE) {
				winner = 1;
			} else if (state->game_record[i].v < -0.4*PAWN_VALUE) {
				winner = -1;
			}
			break;
		}
	}

	for (i=0; i<num_moves; i++) {
		if (first_move == -1 && state->game_record[i].v != 0)
			first_move = i;
		if (state->game_record[i].v != INFINITY && first_move != -1)
			lprintf(0,"%d %d\n", i - first_move, state->converged_record[i].v);
	}

	if (num_moves - first_move < 6) {
		if (!state->brain_inserting)
			brain_temp_size = 0;
		state->analysed = 1;
		if (!state->use_mulling) {
			state->open = 1;
			if (state->ics_robot && state->computer == 0 && state->autoplay) {
				prog_printf("seeks\n");
			}	
		}		
		return;
	}

	lprintf(0,"game analysis winner=%d moves=%d colour=%d\n",
		winner, num_moves, state->colour);

	result = fopen("result.txt", "a");
	if (winner == state->colour)
		fprintf(result, "1\n");
	else if (winner == 0)
		fprintf(result, "0\n");
	else 
		fprintf(result, "-1\n");
	fclose(result);
	dump_history();

	if (!state->use_mulling) {
		state->open = 1;
		state->analysed = 1;
		if (state->ics_robot && state->computer == 0 && state->notified) {
			prog_printf("tell %s My brain is now clean and I am accepting challenges\n", 
				    state->notifyee);
			prog_printf("accept\n");
			state->notified = 0;
		}
		if (state->ics_robot && state->computer == 0 && state->autoplay) {
			prog_printf("seeks\n");
		}	
		return;
	}

	if (!state->brain_inserting) { 
		if (winner == -state->colour || winner == 0) { 
			int forced_win=0;
			etype last_eval;
			double tanhv[MAX_GAME_MOVES]; 

			/* find the "losing move". we look for the
			   largest transition from above -half a pawn
			   to below 0 pawns. Do this for losses but
			   not the inverse for wins because often a
			   win comes through a mistake of the
			   opponent, a mistake the opponent could well
			   repeat. draws are tough because they often
			   only show up when the 50 move rule is
			   reached, so we handle them separately */
			for (i=first_move, j=0; i<num_moves; i++) {
				if (state->game_record[i].v != INFINITY) {
					/* kludge to fix up problem with wrong values after mate */
					if (!forced_win && ABS(state->converged_record[i].v) > 
					    FORCED_WIN && ABS(state->converged_record[i].v) < 
					    INFINITY) {
						forced_win = 1;
						last_eval = state->converged_record[i].v;
					}
					if (forced_win) 
						state->converged_record[i].v = last_eval;
					tanhv[j] = tanh(EVAL_SCALE*state->converged_record[i].v);
					lprintf(0,"%d %lf\n", j, tanhv[j]);
					if (i==state->last_book_move)
						state->last_book_move = j;
					++j;
				}
			}
			tanhv[j] = -1;
			last_move = j;
		
			memset(state->converged_record, 0, 
			       MAX_GAME_MOVES*sizeof(state->converged_record[0]));
			memset(state->game_record, 0, 
			       MAX_GAME_MOVES*sizeof(state->game_record[0]));

			/* now look for the last time the evaluation
                           was above 0.0 pawns for more than three
                           consecutive moves (-0.2 pawns for black) */
			losing_move = 0;
			if (state->colour == 1) {
				threshold = WHITE_THRESHOLD;
			} else {
				threshold = BLACK_THRESHOLD;
			}
			for (i=last_move; i>=2; i--) {
				if (tanhv[i] > threshold && 
				    tanhv[i-1] > threshold && 
				    tanhv[i-2] > threshold) {
					losing_move = i;
					break;
				}
			}

			lprintf(0,"losing move: %d \n", losing_move);
			result = fopen("losing_move.txt", "a");
			fprintf(result, "%d\n", losing_move);
			fclose(result);
			insert = 1;
		} else 	if (!state->auto_exit && state->won == STALEMATE) {
			/* just record the last position, we can't reliably 
			   conclude anything else 
			losing_move = brain_temp_size-1;
			lprintf(0,"losing move (draw): %d \n", losing_move);
			*/
		}

		if ((losing_move > 0 && losing_move < 120) || insert) {
			int new_entries = 0;
			state->brain_inserting = 0;
			if (losing_move < state->last_book_move) 
				last_move = imin(state->last_book_move+6, brain_temp_size);
			else 
				last_move = imin(losing_move+MAX_MOVES_MULLED, brain_temp_size);
			for (i=losing_move; i<last_move; i++) {
				t = brain_find_hash1(brain_temp[i].hash1);
				if (t != -1) {
					brain[t].flags2 |= FLAG_NEEDS_RESEARCH;
					brain[t].flags2 |= FLAG_BLUNDER;
				} else {
					brain_temp[new_entries] = brain_temp[i];
					brain_temp[new_entries].flags2 |= 
						FLAG_NEEDS_RESEARCH;
					brain_temp[new_entries].flags2 |= 
						FLAG_BLUNDER;
					++new_entries;
				}
			}
			brain_temp_size = new_entries;
			state->brain_inserting = 1;
			brain_save();
			state->brain_inserting = 0;
		}
		brain_temp_size = 0;
	}
		
	for (i=num_moves-3;i>=0;i--) {
		t = brain_find_hash1(state->hash_list[i]);

		if (t == -1) continue;

		if (winner == 1)
			brain[t].white_wins++;
		else if (winner == -1)
			brain[t].black_wins++;
	}	



	lprintf(0,"game analysis did %d moves\n",
		num_moves);

	brain_save(); 
	if (losing_move == 0 && state->auto_exit)
		state->moves_mulled = MAX_MOVES_MULLED;

	state->analysed  = 1;
}


static int clean_comp(const void *ii1, const void *ii2)
{
	int *i1;
	int *i2;

	i1 = (int *)ii1;
	i2 = (int *)ii2;

	if (brain[*i1].move_num != brain[*i2].move_num)
		return brain[*i1].move_num - brain[*i2].move_num;
	return memcmp(brain[*i1].board, brain[*i2].board, 
		      sizeof(brain[0].board));	
}


/* clean the brain. This means finding inconsisant brain entries
   and fixing them */
int brain_clean(void)
{
	int i, t;
	int clean=1;
	int brindex[MAX_BRAIN_SIZE];
	int duplicates=0;
	uint32 research_flags;

	brain_save();

	if (!brain_size) return clean;

	lprintf(0,"cleaning brain\n");

	if (mulling && mull_stage == 0)
		research_flags = (FLAG_NEEDS_RESEARCH | FLAG_BLUNDER);
	else 
		research_flags = FLAG_NEEDS_RESEARCH;

	for (i=0;i<brain_size-1;i++) {
		brain[i].flags2 &= ~FLAG_NEVER_RESEARCH;
		/* first sanity */
		if (ABS(brain[i].eval_low) > INFINITY || ABS(brain[i].eval_high) > INFINITY) {
			brain[i].flags2 &= ~FLAG_NEVER_RESEARCH;
			brain[i].flags2 |= research_flags;
			lprintf(0, "out of bounds: %s", entry_str(&brain[i]));
			clean = 0;
		} else if ((brain[i].depth_low == MAX_DEPTH || brain[i].depth_high == MAX_DEPTH) && 
			   !((ABS(brain[i].eval_low) >= FORCED_WIN && ABS(brain[i].eval_low) <= WIN) ||
			    (ABS(brain[i].eval_high) >= FORCED_WIN && ABS(brain[i].eval_high) <= WIN))) {
			brain[i].flags2 &= ~FLAG_NEVER_RESEARCH;
			brain[i].flags2 |= research_flags;
			clean = 0;
		} else {
			/* don't research mates */
			if ((ABS(brain[i].eval_low) >= FORCED_WIN && ABS(brain[i].eval_low) <= WIN) ||
			    (ABS(brain[i].eval_high) >= FORCED_WIN && ABS(brain[i].eval_high) <= WIN)) {
				brain[i].flags2 &= ~research_flags;
				brain[i].flags2 |= FLAG_NEVER_RESEARCH;
				brain[i].depth_low = brain[i].depth_high = MAX_DEPTH;
			} else if (brain[i].flags2 & FLAG_NEVER_RESEARCH) {
				brain[i].depth_low = brain[i].depth_high = -1;
			} else if (brain[i].eval_low + 0.01*PAWN_VALUE < brain[i].eval_high && 
				   ABS(brain[i].eval_low) < FORCED_WIN) {
				lprintf(0,"unconverged brain entry: %d %s", i,
					entry_str(&brain[i]));
				brain[i].flags2 |= research_flags;
				clean = 0;
			} else if (((brain[i].depth_low <= 5 && brain[i].depth_low > 0) ||
				    (brain[i].depth_high <= 5 && brain[i].depth_high > 0)) && 
				   ABS(brain[i].eval_low) < FORCED_WIN) { 
				lprintf(0,"shallow brain entry: %s", 
					entry_str(&brain[i]));
				brain[i].flags2 |= research_flags;
				clean = 0;
			}
		}

		if (brain[i].move_num > 200) {
			printf("removing late entry %s", entry_str(&brain[i]));
			brain[i].depth_low = -1;
			brain[i].depth_high = -1;
		}

#if STORE_ONLY_NEGATIVE
		if (!(brain[i].flags2 & FLAG_NEEDS_RESEARCH) && 
		    (((brain[i].eval_high > -0.25*PAWN_VALUE) && (brain[i].move_num & 1)) || 
		     ((brain[i].eval_high > 0) && !(brain[i].move_num & 1)))) {
			printf("removing positive entry %s", entry_str(&brain[i]));
			brain[i].depth_low = -1;
			brain[i].depth_high = -1;
		}
#endif
		if (brain[i].hash1 == brain[i+1].hash1) {
			if (brain[i].hash2 == brain[i+1].hash2) {
				lprintf(0,"duplicate entry %s",
					entry_str(&brain[i]));
				lprintf(0,"duplicate entry %s",
					entry_str(&brain[i+1]));
				brain[i+1].depth_low = -1;
				brain[i+1].depth_high = -1;
				duplicates++;
			} else {
				lprintf(0,"duplicate hash1 values\n");
			}
		}
	}

	brain_resort();

	/* build a list of indexes sorted by move number */
	for (i=0;i<brain_size;i++) brindex[i] = i;
	qsort(brindex, brain_size, sizeof(brindex[0]), clean_comp);

	
	/* now run through the moves in move order */
	for (i=0;i<brain_size;i++) {
		t = brindex[i];

		/* first some basic sanity checks */
		if (brain[t].search_time < 0)
			brain[t].search_time = 0;
		/* check consistency of this entry with later ones */
		if (!(brain[t].flags2 & FLAG_NEEDS_RESEARCH)) {
			consistency_check0(&brain[t]);
			consistency_check2(&brain[t]);
		}

		if (!(brain[t].flags2 & FLAG_NEEDS_RESEARCH)) {
			brain[t].depth_low = MIN_DEPTH;
			brain[t].depth_high = MIN_DEPTH;
		} else {
			clean = 0;
		}
		if (state->computer != 0)
			return clean;
	}
	
	brain_save();

	lprintf(0,"finished cleaning brain\n");

	return clean;
}


void brain_update()
{ 
	int i;

	for (i=0; i<brain_size; i++)
		if (brain[i].move_num > 15)
			brain[i].flags2 &= ~FLAG_NEEDS_RESEARCH;

	brain_save();
}
