/*
    Living Realms is a primate evolution simulator.
    Copyright (C) 2011  Sterling Pickens

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "Life.h"
static void Set_Region_Permutation(uint32_t rand);
static void KILL_Tribe(uint32_t y, uint32_t x);
static int Is_Same_Species(struct Species *one, struct Species *two);
static void Duplicate_Life(struct Species *one, struct Species *two);
static float Location_Temp(uint32_t src_y, uint32_t src_x);
static int Climate_Fatal(struct Species *one, uint32_t src_y, uint32_t src_x);
static void Create_Life(struct Species *one, struct Species *two, struct Species *three, uint64_t *rseed);
static void Moves_Populate(uint32_t src_y, uint32_t src_x, struct Moves *moves);
static int Tribe_Borders_Water(uint32_t src_y, uint32_t src_x);
static int Tribe_Water_Migrate(uint32_t src_y, uint32_t src_x, uint32_t *dest, uint64_t *rseed);
static int Moved_To_Land(uint32_t src_y, uint32_t src_x, uint64_t *rseed);
static int Tribe_Move_To_Water(uint32_t src_y, uint32_t src_x, uint32_t *dest, uint64_t *rseed);
static int Tribe_Migrate(uint32_t src_y, uint32_t src_x, uint32_t *dest, uint64_t *rseed);
static int Fight_Tribe(struct Species *one, struct Species *two, uint64_t *rseed);
static void Tribe_Interactions(uint32_t src_y, uint32_t src_x, uint32_t tid, uint64_t *rseed);
static void FM_Reset(uint32_t start, uint32_t end);
static void Life_Speed_Reset(void);

unsigned char Region_Offsets[5] = {0, 1, 2, 3, 4}; //Initialized

const unsigned char perm_4[12][4] = {
	{0, 1, 2, 3},
	{0, 2, 3, 1},
	{0, 3, 1, 2},
	//
	{1, 2, 3, 0},
	{1, 3, 0, 2},
	{1, 0, 2, 3},
	//
	{2, 3, 0, 1},
	{2, 0, 1, 3},
	{2, 1, 3, 0},
	//
	{3, 0, 1, 2},
	{3, 1, 2, 0},
	{3, 2, 0, 1}
};

const unsigned char perm_5[20][5] = {
	{0, 1, 2, 3, 4},
	{0, 2, 3, 4, 1},
	{0, 3, 4, 1, 2},
	{0, 4, 1, 2, 3},
	//
	{1, 2, 3, 4, 0},
	{1, 3, 4, 0, 2},
	{1, 4, 0, 2, 3},
	{1, 0, 2, 3, 4},
	//
	{2, 3, 4, 0, 1},
	{2, 4, 0, 1, 3},
	{2, 0, 1, 3, 4},
	{2, 1, 3, 4, 0},
	//
	{3, 4, 0, 1, 2},
	{3, 0, 1, 2, 4},
	{3, 1, 2, 4, 0},
	{3, 2, 4, 0, 1},
	//
	{4, 0, 1, 2, 3},
	{4, 1, 2, 3, 0},
	{4, 2, 3, 0, 1},
	{4, 3, 0, 1, 2}
};

static void Set_Region_Permutation(uint32_t rand){
	Region_Offsets[0] = perm_5[rand][0];
	Region_Offsets[1] = perm_5[rand][1];
	Region_Offsets[2] = perm_5[rand][2];
	Region_Offsets[3] = perm_5[rand][3];
	Region_Offsets[4] = perm_5[rand][4];
}

static void KILL_Tribe(uint32_t y, uint32_t x){
	SET_ZERO(Alive, y, x);
	SET_ZERO(Fought, y, x);
	SET_ZERO(Mated, y, x);
}

void Allocate_Life_Array(void){
	uint32_t n1, n2;
	life = (struct Species ***)malloc(sizeof(struct Species **) * HEIGHT);
	for(n1=0; n1<HEIGHT; n1++){
		life[n1] = (struct Species **)malloc(sizeof(struct Species *) * WIDTH);
		for(n2=0; n2<WIDTH; n2++){
			life[n1][n2] = (struct Species *)malloc(sizeof(struct Species));
			(void)memset( (void *)life[n1][n2], '\0', sizeof(struct Species) );
		}
	}
}

static int Is_Same_Species(struct Species *one, struct Species *two){
	// "dna" determined by all variables, no one variable can deviate more than 4
	// y=mx+b   y range 0-255,  if x = x same species, b == subspecies deviation ?
	//must allow for subspecies deviation

	if( one->intelligence/4 != two->intelligence/4 )
		return 0;
	if( one->strength/4 != two->strength/4 )
		return 0;
	if( one->speed/4 != two->speed/4 )
		return 0;
	if( one->temp_l/4 != two->temp_l/4 )
		return 0;
	if( one->temp_h/4 != two->temp_h/4 )
		return 0;
	if( one->max_age/4 != two->max_age/4 )
		return 0;

	return 1;
}

void Add_First_Life(struct Species *one){
	one->intelligence          = INTELLIGENCE;
	one->strength              = STRENGTH;
	one->speed                 = SPEED;
	one->imunity               = IMUNITY;
	one->temp_l                = TEMP_L;
	one->temp_h                = TEMP_H;
	one->max_age               = MAX_AGE;
	one->cur_age               = CUR_AGE;
	one->fight_flight_balance  = FIGHT_FLIGHT_BALANCE;
	one->interspecies_violence = INTERSPECIES_VIOLENCE;
	one->migration_factor      = MIGRATION_FACTOR;
	one->social_factor         = SOCIAL_FACTOR;
	one->water_turns           = 0;
	one->water_direction       = 0;
}

static void Duplicate_Life(struct Species *one, struct Species *two){
	//used by water migration as temporary solution
	two->intelligence          = one->intelligence;
	two->strength              = one->strength;
	two->speed                 = one->speed;
	two->imunity               = IMUNITY;
	two->temp_l                = one->temp_l;
	two->temp_h                = one->temp_h;
	two->max_age               = one->max_age;
	two->cur_age               = 0;
	two->fight_flight_balance  = one->fight_flight_balance;
	two->interspecies_violence = one->interspecies_violence;
	two->migration_factor      = one->migration_factor;
	two->social_factor         = one->social_factor;
	two->water_turns           = 0;
	two->water_direction       = 0;
}

static float Location_Temp(uint32_t src_y, uint32_t src_x){
	//return temperature at any given src_y src_x
	float location_temp = (float)stats_t.cur_temp;
	//using as elevation temp var
	float equator_dist = (float)ia->dem[src_y][src_x]-stats_t.cur_sealevel;

	//bordering water, degrees cooler maybe

	//elevation effect
	if(equator_dist < 11001){
		location_temp -= equator_dist/1000*TEMP_PER_1000M;
		//location_temp -= TEMP_PER_1000M*((float)(ia->dem[src_y][src_x]-stats_t.cur_sealevel)/1000);
	}else{
		location_temp -= 11*TEMP_PER_1000M;
	}
	//dealing with 1/12th scale of 30 arc-second grid
	//1 arc minute = 1/60 of 1deg
	//30 arc-second = 1/30 of 1deg
	//1px = 12x 30 arc-second  = 2/5 of 1deg
	//1800px = 90-0-90  900px is equator

	//equatorial distance effect
	equator_dist = abs( (float)src_y - HEIGHT/2 ) * 2 / 5;  //degrees from equator

	//45c different between eq and poles  .5C per 1deg  hmmmm
	location_temp -= equator_dist / DEM_SCALE;

	return location_temp;
}

static int Climate_Fatal(struct Species *one, uint32_t src_y, uint32_t src_x){
	//return 1 if climate would kill tribe at this location
	// 6.49C/1000m 
	// -56.5 °C above 11000m

	//need to account for intelligence and strength to withstand
	float location_temp = Location_Temp(src_y, src_x);

	if(location_temp > (float)one->temp_h){ //if temp is higher than tribe can withstand
		//2 parts intel 1 part strength
		// 127 * 3 = 381
		//
		// initial tribes will have +/-4 added to their tolerance range
		//
		if( ((float)one->temp_h + (float)floor( ((double)one->intelligence*2+one->strength) / 10) ) < location_temp ){ //if it's still higher
			return 1;
		}
	}
	if(location_temp < (float)one->temp_l){ //if temp is lower than tribe can withstand
		if( ((float)one->temp_l + (float)floor( ((double)one->intelligence*2+one->strength) / 10) ) > location_temp ){ //if it's still lower
			return 1;
		}
	}
	return 0;
}

static void Create_Life(struct Species *one, struct Species *two, struct Species *three, uint64_t *rseed){
	//mix sub species 1 and 2 to create 3 (average of 1 and 2) [ ~8.3% chance of (+/-1pt) species deviation ]
	//chance of new species:  ~1% 
	uint32_t rand1, rand2;
	int use_ceil = 0;
	float tmp;
	//double bigtmp;

	//struct timeval cur;
	//gettimeofday(&cur, NULL);
	//uint64_t s = (unsigned int)cur.tv_usec + 3;


	//rand1 = lrand(&s) % 2;
	rand1 = LRAND(*(uint64_t *)rseed) % 2;
	if(rand1)
		use_ceil = 1;
	three->water_turns = 0;
	three->water_direction = 0;
	//genetic
	tmp = (float)((float)one->intelligence + two->intelligence) / 2;
		three->intelligence = (unsigned char)( use_ceil ? ceil(tmp) : tmp );
	tmp = (float)((float)one->strength + two->strength) / 2;
		three->strength = (unsigned char)( use_ceil ? ceil(tmp) : tmp );
	tmp = (float)((float)one->speed + two->speed) / 2;
		three->speed = (unsigned char)( use_ceil ? ceil(tmp) : tmp );
	//keep within ranges
	tmp = (float)((float)one->temp_l + two->temp_l) / 2;
		three->temp_l = (unsigned char)( use_ceil ? ceil(tmp) : tmp );
	tmp = (float)((float)one->temp_h + two->temp_h) / 2;
		three->temp_h = (unsigned char)( use_ceil ? ceil(tmp) : tmp );
	tmp = (float)((float)one->max_age + two->max_age) / 2;
		three->max_age = (unsigned char)( use_ceil ? ceil(tmp) : tmp );

	//non-genetic
	tmp = (float)((float)one->social_factor + two->social_factor) / 2;
		three->social_factor = (unsigned char)( use_ceil ? ceil(tmp) : tmp );

//	tmp = (float)((float)one->interspecies_violence + two->interspecies_violence) / 2;
//		three->interspecies_violence = (unsigned char)( use_ceil ? ceil(tmp) : tmp );
//	tmp = (float)((float)one->fight_flight_balance + two->fight_flight_balance) / 2;
//		three->fight_flight_balance = (unsigned char)( use_ceil ? ceil(tmp) : tmp );

	three->interspecies_violence = INTERSPECIES_VIOLENCE;
	three->fight_flight_balance = FIGHT_FLIGHT_BALANCE;

	tmp = (float)((float)one->migration_factor + two->migration_factor) / 2;
		three->migration_factor = (unsigned char)( use_ceil ? ceil(tmp) : tmp );

	//starts at 0yrs old
	three->cur_age = 0;
	three->imunity = (unsigned char)IMUNITY;
	rand1 = (int32_t)ceil(CHANCE_OF_EVOLUTION*3/8);
	//rand2 = lrand(&s) % rand1;
	//rand1 = lrand(&s) % rand1;
	rand2 = LRAND(*(uint64_t *)rseed) % rand1;
	rand1 = LRAND(*(uint64_t *)rseed) % rand1;
	if(rand2 != rand1)
		return;
	//3/80 total chance of new species after here !!!!!

	//1/2 chance we do a deviation
	//unsigned char deviation = (unsigned char)(lrand(&s) % 2);
	unsigned char deviation = (unsigned char)(LRAND(*(uint64_t *)rseed) % 2);
	//rand1 = lrand(&s) % 9; //which feild will be modified
	rand1 = LRAND(*(uint64_t *)rseed) % 9;

	if(rand1 == 0){
		if(deviation){
			if(three->intelligence < 255)
				three->intelligence++;
		}else{
			if(three->intelligence > 4)
				three->intelligence--;
		}
		return;
	}

	if(rand1 == 1){
		if(deviation){
			if(three->strength < 255)
				three->strength++;
		}else{
			if(three->strength > 4)
				three->strength--;
		}
		return;
	}
	if(rand1 == 2){
		if(deviation){
			if(three->speed < 255)
				three->speed++;
		}else{
			if(three->speed > 4)
				three->speed--;
		}
		return;
	}
	if(rand1 == 3){
		//stay within ranges
		if(deviation){
			if(three->temp_h < 39){
				three->temp_h++;
				three->temp_l++;
			}
		}else{
			if(three->temp_l > 4){
				three->temp_h--;
				three->temp_l--;
			}
		}
		return;
	}
	if(rand1 == 4){
		//stays within range 30 - 128yrs
		if(deviation){
			 if(three->max_age < 100)
				three->max_age++;
		}else{
			if(three->max_age > 26)
				three->max_age--;
		}
		return;
	}
	if(rand1 == 5){
		if(deviation){
			if(three->social_factor < 255)
				three->social_factor++;
		}else{
			if(three->social_factor > SOCIAL_FACTOR)
				three->social_factor--;
		}
		return;
	}

	if(rand1 == 6){
		if(deviation){
			if(three->interspecies_violence < 255)
				three->interspecies_violence++;
		}else{
			if(three->interspecies_violence > INTERSPECIES_VIOLENCE)
				three->interspecies_violence--;
		}
		return;
	}
	if(rand1 == 7){
		if(deviation){
			if(three->fight_flight_balance < 255)
				three->fight_flight_balance++;
		}else{
			if(three->fight_flight_balance > FIGHT_FLIGHT_BALANCE)
				three->fight_flight_balance--;
		}
		return;
	}

	//if(rand1 == 8){
		if(deviation){
			if(three->migration_factor != 255)
				three->migration_factor++;
		}else{
			if(three->migration_factor > MIGRATION_FACTOR)
				three->migration_factor--;
		}
	//}
}

static void Moves_Populate(uint32_t src_y, uint32_t src_x, struct Moves *moves){
	//populate struct moves with 4 surrounding coordinates
	const uint32_t width = WIDTH - 1;
	const uint32_t height = HEIGHT - 1;
	if(src_x != 0)
		moves[0].x = src_x - 1;
	else
		moves[0].x = width;
	moves[0].y = src_y;
	moves[0].valid  = 1;
	moves[0].rating = 4;

	if(src_x != width)
		moves[1].x = src_x + 1;
	else
		moves[1].x = 0;

	moves[1].y = src_y;
	moves[1].valid  = 1;
	moves[1].rating = 4;
	moves[2].x = src_x;

	if(src_y != 0)
		moves[2].y = src_y-1;
	else
		moves[2].y = height;

	moves[2].valid  = 1;
	moves[2].rating = 4;
	moves[3].x = src_x;

	if(src_y != height)
		moves[3].y = src_y+1;
	else
		moves[3].y = 0;

	moves[3].valid  = 1;
	moves[3].rating = 4;

}

static int Tribe_Borders_Water(uint32_t src_y, uint32_t src_x){
	//return 1 if a tribe is bordering open water
	uint32_t n1 = 0;
	struct Moves moves[4];
	Moves_Populate(src_y, src_x, moves);
	for(n1=0; n1<4; n1++){
		if( !IS_SET(Alive, moves[n1].y, moves[n1].x) ){
			if(ia->dem[moves[n1].y][moves[n1].x] <= stats_t.cur_sealevel){
			//if( !IS_SET(Alive, moves[n1].y, moves[n1].x) ){
				return 1;
			}
		}
	}
	return 0;
}

static int Tribe_Water_Migrate(uint32_t src_y, uint32_t src_x, uint32_t *dest, uint64_t *rseed){
	//migrate while at sea
	//return 1 if migrated onto land, 0 if still at sea
	uint32_t n1 = 0;
	uint32_t n2 = 0;
	uint32_t rand1;
	struct Moves moves[4];
	Moves_Populate(src_y, src_x, moves);
	int ret_val = 0;

	if(life[src_y][src_x]->water_turns < 4){
		n2 = 0;
		//see if a neightbor is unoccupied land
		for(n1=0; n1<4; n1++){
			if( IS_SET(Alive, moves[n1].y, moves[n1].x) ){
				moves[n1].valid = 0;
				//n2++;
				continue;
			}
			if(ia->dem[moves[n1].y][moves[n1].x] <= stats_t.cur_sealevel){
				moves[n1].valid = 0;
				//n2++;
				continue;
			}
			n2++;
			moves[n1].valid = 1;
		}
	}

	if(n2 > 0){
		//if 1 or more land (unoccupied) choice choose one randomly
		rand1 = LRAND(*(uint64_t *)rseed) % n2;
		n2 = 0;
		for(n1=0; n1<4; n1++){
			if(moves[n1].valid){
				if(n2 == rand1){
					//this is your choice
					dest[0] = moves[n1].y;
					dest[1] = moves[n1].x;
					ret_val = 1;
					break;
				}
				n2++;
			}
		}
	}else{
		//if no valid land (unoccupied) choices
		n2 = 0;
		//choose a water spot randomly
		for(n1=0; n1<4; n1++){
			if( !IS_SET(Alive, moves[n1].y, moves[n1].x) ){
				if(ia->dem[moves[n1].y][moves[n1].x] <= stats_t.cur_sealevel){
				//if( !IS_SET(Alive, moves[n1].y, moves[n1].x) ){
					moves[n1].valid = 1;
					n2++;
					continue;
				}
			}
			moves[n1].valid = 0;
		}
		if(n2 > 0){ //one or more open water spots exists
			if(life[src_y][src_x]->water_turns < 6){ //guarantee straight line path first X turns if valid
				n1 = life[src_y][src_x]->water_direction;
				if(moves[n1].valid){ //if continuing direction is valid
					dest[0] = moves[n1].y;
					dest[1] = moves[n1].x;
					SET_ONE(Alive, dest[0], dest[1]);
					(void)memcpy((void *)life[dest[0]][dest[1]], (const void *)life[src_y][src_x], sizeof(struct Species));
					if(life[dest[0]][dest[1]]->imunity < 255)
						life[dest[0]][dest[1]]->imunity++;
					life[dest[0]][dest[1]]->water_turns++;
					KILL_Tribe(src_y, src_x);
					return 0;
				}
			}
			rand1 = (uint32_t)ceil((double)life[src_y][src_x]->intelligence/256*100);
			//use intelligence to determine if we take straight line path
			if(LRAND(*(uint64_t *)rseed) % 100 < rand1){
				//search for water spot continuing direction
				n1 = life[src_y][src_x]->water_direction;
				if(moves[n1].valid){ //if continuing direction is valid
					dest[0] = moves[n1].y;
					dest[1] = moves[n1].x;
					SET_ONE(Alive, dest[0], dest[1]);
					(void)memcpy((void *)life[dest[0]][dest[1]], (const void *)life[src_y][src_x], sizeof(struct Species));
					if(life[dest[0]][dest[1]]->imunity < 255)
						life[dest[0]][dest[1]]->imunity++;
					life[dest[0]][dest[1]]->water_turns++;
					KILL_Tribe(src_y, src_x);
					return 0;
				}
			}
			rand1 = LRAND(*(uint64_t *)rseed) % n2;
			n2 = 0;
			for(n1=0; n1<4; n1++){
				if(moves[n1].valid){
					if(n2 == rand1){
						dest[0] = moves[n2].y;
						dest[1] = moves[n2].x;
						break;
					}
					n2++;
				}
			}
		}else{
			dest[0] = src_y;  //Don't move
			dest[1] = src_x;  //Don't move
			life[dest[0]][dest[1]]->water_turns++;
			return ret_val;
		}
	}

	SET_ONE(Alive, dest[0], dest[1]);
	(void)memcpy((void *)life[dest[0]][dest[1]], (const void *)life[src_y][src_x], sizeof(struct Species));
	if(life[dest[0]][dest[1]]->imunity < 255)
		life[dest[0]][dest[1]]->imunity++;
	KILL_Tribe(src_y, src_x);
	//set water_turns
	if(ret_val){ // found land
	life[dest[0]][dest[1]]->water_turns = 0;
	}else{
		life[dest[0]][dest[1]]->water_turns++;
	}
	return ret_val;
}

static int Moved_To_Land(uint32_t src_y, uint32_t src_x, uint64_t *rseed){
	//return 0 if no open spots for duplication
	//return 1 if duplicated
	uint32_t n1 = 0;
	uint32_t n2 = 0;
	uint32_t rand1;
	struct Moves moves[4];
	Moves_Populate(src_y, src_x, moves);

	//see if a neightbor is unoccupied land
	for(n1=0; n1<4; n1++){
		if( IS_SET(Alive, moves[n1].y, moves[n1].x) ){
			moves[n1].valid = 0;
			//n2++;
			continue;
		}
		if(ia->dem[moves[n1].y][moves[n1].x] <= stats_t.cur_sealevel){
			moves[n1].valid = 0;
			//n2++;
			continue;
		}
		n2++;
		moves[n1].valid = 1;
	}
	//pick one random
	if(n2 > 0){
		rand1 = LRAND(*(uint64_t *)rseed) % n2;
		n2 = 0;
		for(n1=0; n1<4; n1++){
			if(moves[n1].valid){
				if(n2 == rand1){
					//this is destination for duplicated tribe
					SET_ONE(Mated, src_y, src_x);
					SET_ONE(Mated, moves[n1].y, moves[n1].x);
					SET_ONE(Fought, moves[n1].y, moves[n1].x);
					SET_ONE(Alive, moves[n1].y, moves[n1].x);
					Duplicate_Life(life[src_y][src_x], life[moves[n1].y][moves[n1].x]);
					life_speed_up_Y[moves[n1].y] = 1;
					life_speed_up_X[moves[n1].x] = 1;
					return 1;
				}
				n2++;
			}
		}
	}
	return 0;
}

static int Tribe_Move_To_Water(uint32_t src_y, uint32_t src_x, uint32_t *dest, uint64_t *rseed){
	//move a tribe onto water, return 1 if no open water available
	uint32_t n1 = 0;
	uint32_t n2 = 0;
	uint32_t rand1;
	struct Moves moves[4];
	Moves_Populate(src_y, src_x, moves);

	//set to invalid moves that aren't water, or are occupied
	for(n1=0; n1<4; n1++){
		if( IS_SET(Alive, moves[n1].y, moves[n1].x) ){
			moves[n1].valid = 0;
			continue;
		}
		if(ia->dem[moves[n1].y][moves[n1].x] > stats_t.cur_sealevel){
			moves[n1].valid = 0;
			continue;
		}
		if( Climate_Fatal(life[src_y][src_x], moves[n1].y, moves[n1].x) ){
			moves[n1].valid = 0;
			continue;
		}
		n2++;
	}

	//error condition ?
	if(n2 == 0){
		dest[0] = src_y;
		dest[1] = src_x;
		return 1;
	}
	//randomly choose one of the valid choices
	rand1 = LRAND(*(uint64_t *)rseed) % n2;
	n2 = 0;
	for(n1=0; n1<4; n1++){
		if(moves[n1].valid){
			if(n2 == rand1){
				//this is your choice
				dest[0] = moves[n1].y;
				dest[1] = moves[n1].x;
				life[src_y][src_x]->water_direction = n1;
				break;
			}
			n2++;
		}
	}

	SET_ONE(Alive, dest[0], dest[1]);
	(void)memcpy((void *)life[dest[0]][dest[1]], (const void *)life[src_y][src_x], sizeof(struct Species));
	if(life[dest[0]][dest[1]]->imunity < 255)
		life[dest[0]][dest[1]]->imunity++;
	//set water_turns to 1
	life[dest[0]][dest[1]]->water_turns = 1;
	//clear old data
	KILL_Tribe(src_y, src_x);
	return 0;
}

static int Tribe_Migrate(uint32_t src_y, uint32_t src_x, uint32_t *dest, uint64_t *rseed){
	//return 1 if moved
	//put new location in dest
	uint32_t n1, n2;
	const uint32_t width = WIDTH - 1;
	const uint32_t height = HEIGHT - 1;
	struct Moves moves[4];

	//if any of these are greater/less than height or width they need to be set to 0
	//same goes for all neighbors calculations

	//4 potential destination initialization
	Moves_Populate(src_y, src_x, moves);
	//4 neighors of a move destination
	struct Neighbors{unsigned int x; unsigned int y;}neighbors[4];

	//to store candidate destination with highest rating
	struct Dest_Cand{int h_rating; unsigned int m_index;}dest_cand = {0, 0};
	int cand_ties[4];

    //struct timeval cur;
    uint32_t rand1;
    //gettimeofday(&cur, NULL);
	//uint64_t s = (uint64_t)cur.tv_usec + 3;

	for(n1=0; n1<4; n1++){
		//rule out moving to occupied coordinates
		if( IS_SET(Alive, moves[n1].y, moves[n1].x) ){
			moves[n1].valid = 0;
			continue;
		}
		//rule out moving to water
		if(stats_t.cur_sealevel >= ia->dem[moves[n1].y][moves[n1].x]){
			moves[n1].valid = 0;
			continue;
		}
		//rule out moving to fatal climates
		if( Climate_Fatal(life[src_y][src_x], moves[n1].y, moves[n1].x) )
			moves[n1].valid = 0;
	}
	for(n1=0; n1<4; n1++){
		//if all ruled out return 0;
		if(moves[n1].valid == 1)
			break;
		if(n1 == 3)
			return 0;
	}
	//calculate ratings for valid destinations
	for(n1=0; n1<4; n1++){
		if(!moves[n1].valid)
			continue;
		if(moves[n1].x != 0)
			neighbors[0].x = moves[n1].x-1;
		else
			neighbors[0].x = width;
		neighbors[0].y = moves[n1].y;
		if(moves[n1].x != width)
			neighbors[1].x = moves[n1].x+1;
		else
			neighbors[1].x = 0;
		neighbors[1].y = moves[n1].y;
		neighbors[2].x = moves[n1].x;
		if(moves[n1].y != 0)
			neighbors[2].y = moves[n1].y-1;
		else
			neighbors[2].y = height;
		neighbors[3].x = moves[n1].x;
		if(moves[n1].y != height)
			neighbors[3].y = moves[n1].y+1;
		else
			neighbors[3].y = 0;
		//calculate rating for this move threat/reward
			//if neighbor is empty terrain inc if climate is more favorable
			//ie: more toward the middle of temp_l - temp_h range than current position
        for(n2=0; n2<4; n2++){
            if(neighbors[n2].y == src_y){  //if a neighbor is src_x/src_y don't count self
                if(neighbors[n2].x == src_x)
                    continue;
            }
			if( !IS_SET(Alive, neighbors[n2].y, neighbors[n2].x) ){
				moves[n1].rating++;
				continue;
			}
			if(!Is_Same_Species(life[src_y][src_x], life[neighbors[n2].y][neighbors[n2].x]) ){
				moves[n1].rating--;
				continue;
			}
			moves[n1].rating++;
			//unless already on water ...
		}
	}
	dest_cand.h_rating = -1;
	//find destination with highest rating
	for(n1=0; n1<4; n1++){
		if(!moves[n1].valid){
			moves[n1].rating = 0;
			continue;
		}
		if(	moves[n1].rating >= dest_cand.h_rating){
			dest_cand.h_rating = moves[n1].rating;
			dest_cand.m_index  = n1;
		}
	}
	n2=0;
	//if rating tie should pick a random choice out of them
	//count rating ties
	for(n1=0; n1<4; n1++)
		cand_ties[n1] = 0;
	for(n1=0; n1<4; n1++){
		if(!moves[n1].valid)
			continue;
		if(dest_cand.h_rating == moves[n1].rating){
			cand_ties[n1] = 1;
			n2++;
		}
	}
	if(n2){ //was no tie
		dest[0] = moves[dest_cand.m_index].y;
		dest[1] = moves[dest_cand.m_index].x;
	}

	if(n2 > 1){ //was a tie
		//n2 can be  2  3  or 4
		//rand1 = lrand(&s) % n2; // 0 to 1, 0 to 2, or 0 to 3
		rand1 = LRAND(*(uint64_t *)rseed) % n2;
		n2 = 0;
		for(n1=0; n1<4; n1++){
			if(!cand_ties[n1])
				continue;
			if(n2 == rand1){
				//moves[n1] is dest choice
				dest[0] = moves[n1].y;
				dest[1] = moves[n1].x;
				break;
			}
			n2++;
		}
	}

	if(stats_t.cur_sealevel < ia->dem[dest[0]][dest[1]]){
		//Move to destination
		SET_ONE(Alive, dest[0], dest[1]);
		(void)memcpy((void *)life[dest[0]][dest[1]], (const void *)life[src_y][src_x], sizeof(struct Species));
		if(life[dest[0]][dest[1]]->imunity < 255)
			life[dest[0]][dest[1]]->imunity++;
		//clear old data
		KILL_Tribe(src_y, src_x);
		return 1;
	}
	return 0;
}

static int Fight_Tribe(struct Species *one, struct Species *two, uint64_t *rseed){
	//simulate outcome of a potential combat between these two tribes
	// return 1 *one won, 0 *one lost
	uint32_t rand1, rand2;
	//struct timeval cur;
    //gettimeofday(&cur, NULL);
    //uint64_t s = (uint64_t)cur.tv_usec + 3;
	int advanced = 0;
	//fight determined by  1 part strength, 1 part speed, 2 parts intelligence, and part chance
	uint32_t rating1 = (uint32_t)one->intelligence*2 + one->strength;
	uint32_t rating2 = (uint32_t)two->intelligence*2 + two->strength;
	rating1 += one->speed;
	rating2 += two->speed;
	advanced = (rating1 > rating2) ? 1 : 0;
	rating1 /= 16;
	rating2 /= 16;
	//rand1 = lrand(&s) % rating1;
	//rand2 = lrand(&s) % rating2;
	rand1 = LRAND(*(uint64_t *)rseed) % rating1;
	rand2 = LRAND(*(uint64_t *)rseed) % rating2;

	if(rand1 == rand2){ // tie goes to more advanced tribe, if same rating random wins ...
		if(advanced)
			return 1;
		else
			//return (int)(lrand(&s) % 2);
			return (int)(LRAND(*(uint64_t *)rseed) % 2);
	}
	if(rand1 > rand2){
		return 1;
	}
	return 0;
}

static void Tribe_Interactions(uint32_t src_y, uint32_t src_x, uint32_t tid, uint64_t *rseed){
	//evaluate all surrounding tribes for threat/reward take proper actions
	uint32_t n1, n2;
	uint32_t rand1;
	int out_come;
	//struct timeval cur;
	struct Neighbors{uint32_t y; uint32_t x; uint32_t occupied;}neighbors[4];
	struct Neighbors_T{uint32_t y; uint32_t x;}tmp[4];
	const uint32_t width = WIDTH - 1;
	const uint32_t height = HEIGHT - 1;
    //gettimeofday(&cur, NULL);
    //uint64_t s = (uint64_t)cur.tv_usec + 3;
	/*
	const unsigned char perm_4[12][4] = {
		{0, 1, 2, 3},
		{0, 2, 3, 1},
		{0, 3, 1, 2},
		{1, 2, 3, 0},
		{1, 3, 0, 2},
		{1, 0, 2, 3},
		{2, 3, 0, 1},
		{2, 0, 1, 3},
		{2, 1, 3, 0},
		{3, 0, 1, 2},
		{3, 1, 2, 0},
		{3, 2, 0, 1}
	};
	*/
	//need to be randomly assigned, 12 possible permutations
	//n×(n - 1)×(n - 2)×...×2×1

	//for map wrap
	tmp[0].y = src_y;
	if(src_x != 0)
		tmp[0].x = src_x - 1;
	else
		tmp[0].x = width;

	tmp[1].y = src_y;
	if(src_x != width)
		tmp[1].x = src_x + 1;
	else
		tmp[1].x = 0;

	if(src_y != 0)
		tmp[2].y = src_y - 1;
	else
		tmp[2].y = height;
	tmp[2].x = src_x;

	if(src_y != height)
		tmp[3].y = src_y + 1;
	else
		tmp[3].y = 0;
	tmp[3].x = src_x;

	//mix them randomly
	//rand1 = lrand(&s) % 12;
	rand1 = LRAND(*(uint64_t *)rseed) % 12;
	for(n1=0; n1<4; n1++){
		neighbors[n1].y = tmp[perm_4[rand1][n1]].y;
		neighbors[n1].x = tmp[perm_4[rand1][n1]].x;
		neighbors[n1].occupied = 0;
	}

	for(n1=0; n1<4; n1++){
		if( IS_SET(Alive, neighbors[n1].y, neighbors[n1].x) ){
			if(life[neighbors[n1].y][neighbors[n1].x]->water_turns == 0) //don't interact with water migraters
				neighbors[n1].occupied = 1;
		}
	}

	for(n1=0; n1<4; n1++){
		if(neighbors[n1].occupied){
			if( Is_Same_Species(life[src_y][src_x], life[neighbors[n1].y][neighbors[n1].x]) ){
				//check to see if we'll fight first !
				if( !IS_SET(Fought, src_y, src_x) ){
					rand1 = (uint32_t)ceil((double)life[src_y][src_x]->interspecies_violence/256*100);
					//if(lrand(&s) % 100 < rand1){
					if(LRAND(*(uint64_t *)rseed) % 100 < rand1){
						out_come = Fight_Tribe( life[src_y][src_x], life[neighbors[n1].y][neighbors[n1].x], rseed );
						if(out_come){//if the fight was won
							SET_ONE(Fought, src_y, src_x);
							KILL_Tribe(neighbors[n1].y, neighbors[n1].x);
							stats[tid].died_defending++;
							if(life[src_y][src_x]->imunity < 255)
								life[src_y][src_x]->imunity++;
							continue;
						}
						if(!out_come){ //died
							SET_ONE(Fought, neighbors[n1].y, neighbors[n1].x);
							KILL_Tribe(src_y, src_x);
							stats[tid].died_attacking++;
							if(life[neighbors[n1].y][neighbors[n1].x]->imunity < 255)
								life[neighbors[n1].y][neighbors[n1].x]->imunity++;
							return;
						}
					}
				}
				if(IS_SET(Mated, src_y, src_x))
					continue;
				if(IS_SET(Mated, neighbors[n1].y, neighbors[n1].x))
					continue;
				rand1 = (uint32_t)ceil((double)life[src_y][src_x]->social_factor/256*100);
				//if(lrand(&s) % 100 < rand1){
				if(LRAND(*(uint64_t *)rseed) % 100 < rand1){
					out_come = 0;
					//choose open space for offspring
					for(n2=0; n2<4; n2++){
						if(neighbors[n2].occupied == 0){
							if(stats_t.cur_sealevel < ia->dem[neighbors[n2].y][neighbors[n2].x] ){
								out_come = 1;
								break;
							}
						}
					}
					//choose random open space if multiple
					//if space available create life
					if(out_come){
						SET_ONE(Mated, src_y, src_x);
						SET_ONE(Mated, neighbors[n1].y, neighbors[n1].x);
						SET_ONE(Mated, neighbors[n2].y, neighbors[n2].x);
						SET_ONE(Fought, neighbors[n2].y, neighbors[n2].x);
						SET_ONE(Alive, neighbors[n2].y, neighbors[n2].x);
						Create_Life(life[src_y][src_x], life[neighbors[n1].y][neighbors[n1].x], life[neighbors[n2].y][neighbors[n2].x], rseed);
						stats[tid].new_life++;
						life_speed_up_Y[neighbors[n2].y] = 1;
						life_speed_up_X[neighbors[n2].x] = 1;
						if( !Is_Same_Species(life[src_y][src_x], life[neighbors[n2].y][neighbors[n2].x]) )
							stats[tid].new_species++;
						if(life[src_y][src_x]->imunity < 255)
							life[src_y][src_x]->imunity++;
						if(life[neighbors[n1].y][neighbors[n1].x]->imunity < 255)
							life[neighbors[n1].y][neighbors[n1].x]->imunity++;
					}
				}
			}else{
				if( !IS_SET(Fought, src_y, src_x) ){
					rand1 = (uint32_t)ceil((double)life[src_y][src_x]->fight_flight_balance/256*100);
					//if(lrand(&s) % 100 < rand1){
					if(LRAND(*(uint64_t *)rseed) % 100 < rand1){
						out_come = Fight_Tribe( life[src_y][src_x], life[neighbors[n1].y][neighbors[n1].x], rseed );
						if(out_come){//fight was won
							SET_ONE(Fought, src_y, src_x);
							KILL_Tribe(neighbors[n1].y, neighbors[n1].x);
							stats[tid].died_defending++;
							if(life[src_y][src_x]->imunity < 255)
								life[src_y][src_x]->imunity++;
						}
						if(!out_come){ //died
							SET_ONE(Fought, neighbors[n1].y, neighbors[n1].x);
							KILL_Tribe(src_y, src_x);
							stats[tid].died_attacking++;
							if(life[neighbors[n1].y][neighbors[n1].x]->imunity < 255)
								life[neighbors[n1].y][neighbors[n1].x]->imunity++;
							return;
						}
					}
				}
			}
		}
		if( IS_SET(Fought, src_y, src_x) ){
			if( IS_SET(Mated, src_y, src_x) ){
				return;
			}
		}
	}
}

static void FM_Reset(uint32_t start, uint32_t end){
	uint32_t h;
	for(h=start; h<end; h++){
		(void)memset( (void *)Fought[h], '\0', sizeof(unsigned char)*(size_t)ceil(WIDTH/8));
		(void)memset( (void *)Mated[h], '\0', sizeof(unsigned char)*(size_t)ceil(WIDTH/8));
	}
}

static void Life_Speed_Reset(void){
	uint32_t h;
	uint32_t w;
	(void)memset( (void *)life_speed_up_X, '\0', sizeof(unsigned char)*(size_t)WIDTH);
	(void)memset( (void *)life_speed_up_Y, '\0', sizeof(unsigned char)*(size_t)HEIGHT);
	for(h=0; h<HEIGHT; h++){
		for(w=0; w<WIDTH; w++){
			if( IS_SET(Alive, h, w) ){
				life_speed_up_X[w] = 1;
				life_speed_up_Y[h] = 1;
			}
		}
	}
}

#ifndef LRSTATS_COMPILE
void *Tribe_Actions(void *tid){
	//master thread
	//initiate actions migrate/combat/desease/create_life/migrate/socialize/etc.
	uint32_t t = *(unsigned int *)tid;
	uint32_t y = 0;
	uint32_t x = 0;
	uint32_t x_s = 0;
	int right_to_left = 0;
	int stop = 0;
	int first_member = 0;
	uint32_t dest[] = {0, 0}; //y x
	char did_move = 0;
	struct timeval cur;
    uint32_t rand1 = 0;
	uint32_t rand2 = 0;
    gettimeofday(&cur, NULL);
    //uint64_t s = (uint64_t)cur.tv_usec + 3;
	//Actions_t_info[t].rseed =
	uint64_t rseed = (uint64_t)cur.tv_usec + 3 + t;
	uint32_t region_offset = 0;
	uint32_t region = 0;
	//printf("t: %u\n", t);

	if(t == 0)
		gettimeofday(&Year_Starttime, NULL);
	while(No_Exit == 1){
		for(region_offset=0; region_offset<5; region_offset++){
			//add (Actions_t_info[t].start_h/5) to auto stagger on each run (2.5% fewer trylock fails with 4 threads)
			for(y = (Actions_t_info[t].start_h/5)+Region_Offsets[region_offset]; y<HEIGHT-5; y+=5){
				region = (uint32_t)y/5;
				if( pthread_mutex_trylock( &Region_Mutexes[region] ) != 0)
					continue;
				if(!life_speed_up_Y[y])
					continue;
					//randomly choose to start from right or left of row
					//rand1 = lrand(&s) % 2;
					rand1 = LRAND(rseed) % 2;
				if( rand1 ){
					right_to_left = 0;
					x_s = WIDTH - 1;
					x = 0;
				}else{
					right_to_left = 1;
					x_s = 0;
					x = WIDTH - 1;
				}
				stop = 0;
				first_member = 1;
				while(!stop){
					if(right_to_left){
						if(!first_member)
							x--;
						else
							first_member = 0;
					}else{
						if(!first_member)
							x++;
						else
							first_member = 0;
					}
					if(x == x_s)
						stop = 1;
					if(!life_speed_up_X[x])
						continue;
					if( IS_SET(Alive, y, x) ){
						//get one year older
						if(life[y][x]->water_turns == 0){
							life[y][x]->cur_age++;

							//die from old age?
							if(life[y][x]->cur_age == life[y][x]->max_age ){
								KILL_Tribe(y, x);
								stats[t].old_age++;
								continue;
							}
							rand1 = (uint32_t)ceil((double)life[y][x]->imunity/256*100);
							//rand2 = lrand(&s) % 100;
							rand2 = LRAND(rseed) % 100;
							if(rand2 > rand1){
								KILL_Tribe(y, x);
								stats[t].disease++;
								continue;
							}
						//}
						//if(life[y][x]->water_turns == 0){
							if( Tribe_Borders_Water(y, x) ){
								rand1 = (uint32_t)ceil((double)WATER_MIGRATION_RANGE/256*100);
								//if(lrand(&s) % 100 < rand1){
								if(LRAND(rseed) % 100 < rand1){
									if( !Tribe_Move_To_Water(y, x, dest, &rseed) ){ //if found Open water
										if( Climate_Fatal(life[dest[0]][dest[1]], dest[0], dest[1]) ){
											KILL_Tribe(dest[0], dest[1]);
											stats[t].exposure++;
											continue;
										}
										//moved
										life_speed_up_Y[dest[0]] = 1;
										life_speed_up_X[dest[1]] = 1;
										//no interactions for water migraters
										continue;
									}
									//did not move 
									if(stats_t.cur_sealevel >= ia->dem[y][x]){ //drown
										KILL_Tribe(y, x);
										stats[t].drown++;
										continue;
									}
									if( Climate_Fatal(life[y][x], y, x) ){
										KILL_Tribe(y, x);
										stats[t].exposure++;
										continue;
									}
									life_speed_up_Y[y] = 1;
									life_speed_up_X[x] = 1;
									Tribe_Interactions(y, x, t, &rseed);
									continue;
								}
							}
							//continue;
						//}
						//if(life[y][x]->water_turns == 0){ //if tribe is not water migrating
							rand1 = (uint32_t)ceil((double)life[y][x]->migration_factor/256*100);
							//if(lrand(&s) % 100 < rand1){
							if(LRAND(rseed) % 100 < rand1){
								did_move = Tribe_Migrate(y, x, dest, &rseed);
								if(did_move == 0){ //couldn't find a spot, try to water migrate
									rand1 = (uint32_t)ceil((double)WATER_MIGRATION_RANGE/256*100);
									//if(lrand(&s) % 100 < rand1){
									if(LRAND(rseed) % 100 < rand1){
										if( Tribe_Borders_Water(y, x) ){
											if( !Tribe_Move_To_Water(y, x, dest, &rseed) ){ //if found Open water
												if( Climate_Fatal(life[dest[0]][dest[1]], dest[0], dest[1]) ){
													KILL_Tribe(dest[0], dest[1]);
													stats[t].exposure++;
													continue;
												}
												life_speed_up_Y[dest[0]] = 1;
												life_speed_up_X[dest[1]] = 1;
												continue;
												//no interactions for water migraters
											}
											did_move = 0; //didn't move
										}
									}
								}
							}
							//this tribe won't/can't migrate regardless 
							if(stats_t.cur_sealevel >= ia->dem[y][x]){ //drown
								KILL_Tribe(y, x);
								stats[t].drown++;
								continue;
							}
							if(did_move){
								if( Climate_Fatal(life[dest[0]][dest[1]], dest[0], dest[1]) ){
									KILL_Tribe(dest[0], dest[1]);
									stats[t].exposure++;
									continue;
								}
								Tribe_Interactions(dest[0], dest[1], t, &rseed);
							}else{
								if( Climate_Fatal(life[y][x], y, x) ){
									KILL_Tribe(y, x);
									stats[t].exposure++;
									continue;
								}
								Tribe_Interactions(y, x, t, &rseed);
							}
							continue;
						} // end of if not water migrating
						did_move = Tribe_Water_Migrate(y, x, dest, &rseed); //1 if found land 

						life_speed_up_Y[dest[0]] = 1;
						life_speed_up_X[dest[1]] = 1;

						if(!did_move){
							if(life[dest[0]][dest[1]]->water_turns == WATER_MIGRATION_T_MAX){
								KILL_Tribe(dest[0], dest[1]);
								stats[t].drown++;
								continue;
							}
						}
						if(Moved_To_Land(y, x, &rseed)){ //if duplicated
							stats[t].new_life++;
						}
						if( Climate_Fatal(life[dest[0]][dest[1]], dest[0], dest[1]) ){
							KILL_Tribe(dest[0], dest[1]);
							stats[t].exposure++;
						}
					}//end of if life != NULL
				}//end of X loop
			}//end of Y loop
			//3.8% of thread time spent waiting here
			//should theoretically be no longer than 1 y pass ...
#ifndef WITHOUT_HOST_PT_BARRIER
			(void)pthread_barrier_wait(&Barrier1);  //region offset change 0-4
#else
			(void)lrpthread_barrier_wait(&Barrier1);
#endif
            //for(y = Region_Offsets[region_offset]; y<HEIGHT-5; y+=5){
            //    region = (uint32_t)y/5;

			for(y=(uint32_t)(Actions_t_info[t].start_h/5); y<(uint32_t)(Actions_t_info[t].end_h/5); y++){
				//if(t == 0){ //thread this
				//for(y=0; y<((uint32_t)HEIGHT/5); y++){
					//printf("t: %u y: %u\n", t, y);
					if( pthread_mutex_unlock( &Region_Mutexes[y] ) != 0){
						Stats_Handled = 1;
						No_Exit = 2;
						pthread_exit(NULL);
					}
				//}
			}
			//2% of thread time spent waiting here
#ifndef WITHOUT_HOST_PT_BARRIER
			(void)pthread_barrier_wait(&Barrier2);
#else
			(void)lrpthread_barrier_wait(&Barrier2);
#endif
			//start next region_offset
		}//end of year
		//printf("t: %u FM_Reset a\n", t);
		FM_Reset(Actions_t_info[t].start_h, Actions_t_info[t].end_h);
		//printf("t: %u FM_Reset b\n", t);
		//[ Follow could result in better than 2x reduction in barrier overall waits ]
		//fm reset threaded
		//life speed reset threaded

		//mutex locked stats += for vars

		//barrier
		//mutex
		//1 thread
			//sea level
			//climate
			//year increment
			//year_runtime
				//potentially handle from opengl thread
					//gen detailed counts (reduced)
					//save current state
					//handle exit conditions
		//mutex unlock
		//barrier

		//first thread to lock
		(void)pthread_mutex_lock( &Mutex1 );
		if(!Stats_Handled){
			Stats_Handled = 1;
			stats_t.year++;
			//FM_Reset(0, HEIGHT); //can thread
			gettimeofday(&Year_Endtime, NULL);
			Year_Runtime += ((double)(Year_Endtime.tv_sec*1000000-Year_Starttime.tv_sec*1000000+Year_Endtime.tv_usec-Year_Starttime.tv_usec))/1000000;
			gettimeofday(&Year_Starttime, NULL);
			if(stats_t.year % CLIMATE_YEARS == 0){
				Adjust_Sea_Level(0);
				Adjust_Climate(0);
				if(stats_t.sea_rising)
					Life_Speed_Reset();  //can thread
			}
			if(stats_t.year % Backup_Freq == 0){
				Backup_Freq_Runtime += Year_Runtime;
				Gen_Detailed_Counts();
				pthread_mutex_unlock( &Mutex3 );
				No_Exit = !Save_Current_State();  //cause an exit if failed
				(void)pthread_mutex_unlock( &Mutex3 );
			}else if(No_Exit == 0){  //save and exit
				Backup_Freq_Runtime += Year_Runtime;
				Gen_Detailed_Counts();
				(void)Save_Current_State();  //will still exit gracefully
			}
			//Set_Region_Permutation(lrand(&s) % 20);
			Set_Region_Permutation(LRAND(rseed) % 20);
		}
		(void)pthread_mutex_unlock( &Mutex1 );
		//.8% of thread time spent waiting here
#ifndef WITHOUT_HOST_PT_BARRIER
		(void)pthread_barrier_wait(&Barrier3); //barrier for full year
#else
		(void)lrpthread_barrier_wait(&Barrier3);
#endif
		(void)pthread_mutex_lock( &Mutex2 );
		Stats_Handled = 0;
		(void)pthread_mutex_unlock( &Mutex2 );
	}//main thread loop
	pthread_exit(NULL);
}
#endif
