#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <sys/time.h>
#include "Random.h"

#define MIN_GET_ONBOARD    500
#define MIN_STAY_AT        300
//minimum dice to roll on if not to MIN_STAY_AT
#define MIN_WILL_ROLL 3
//don't change
#define SIDES 6
//5 or 6
#define DICE 6
#define ZILCH 0

const float Prob_no_zilch[7] = {
	0.0000,
    33.3333,
    55.5556,
    70.3704,
    80.2469,
    86.8313,
    91.2209
};

const uint16_t Combinations[7] = {
	0,
	6,
	21,
	56,
	126,
	252,
	462
};

const uint16_t Same_of_kind_scores[7][7] = {
	{0,    0,    0,    0,    0,    0,    0},
	{0,  100,    0,    0,    0,   50,    0},
	{0,  200,    0,    0,    0,  100,    0},
	{0, 1000,  200,  300,  400,  500,  600},
	{0, 2000,  400,  600,  800, 1000, 1200},
	{0, 4000,  800, 1200, 1600, 2000, 2400},
	{0, 8000, 1600, 2400, 3200, 4000, 4800}
};

const uint16_t Straight_score = 1500;

const uint16_t Three_pairs_score = 1000;


static uint8_t Has_Straight(uint8_t *roll_results, uint8_t dice){
	/* return 1 if dice are a straight, else 0 
		should only be run when dice == full amount (first roll)*/
	if(dice == 1)
		return 1;
	if(dice == 0)
		return 0;
	uint8_t num1 = 0;
	uint8_t num2 = SIDES;
	uint8_t match_found = 0;
	uint8_t *roll_results_ordered = malloc(dice*sizeof(uint8_t) );

	//find lowest rolled number
	for(num1=0; num1<dice; num1++){
		if(roll_results[num1] < num2)
			num2 = roll_results[num1];
	}
	roll_results_ordered[0] = num2;

	//search for roll that is 1 higher than previous for dice, return 0 if broken
	for(num1=1; num1<dice; num1++){
		match_found = 0;
		for(num2=0; num2<dice; num2++){
			if(roll_results[num2] == roll_results_ordered[num1-1]+1){
				roll_results_ordered[num1] = roll_results[num2];
				match_found = 1;
				break;
			}
		}
		if(match_found == 0)
			return 0;
	}
	free(roll_results_ordered);
	return 1;
}

static uint8_t Has_Three_Pair(uint8_t *num_counts, uint8_t dice){
	/* return 1 if has 3 pair else 0 */
	if(dice < 6)
		return 0;
	uint8_t num1 = 0;
	uint8_t num2 = 0;
	for(num1=0; num1<SIDES; num1++){
		if(num_counts[num1] == 2)
			num2++;
		}
	}
	if(num2 >= 3)
		return 1;
	return 0;
}

static uint32_t Roll_Dice(uint64_t *s, char onboard){
	/*Roll 6 dice and play out a turn of 10,000*/
	uint32_t rand_num1 = 0;
	uint8_t roll_results[DICE];
	uint8_t num_counts[SIDES];
	uint8_t dice_rolling = DICE;
	uint8_t bust = 0;
	uint8_t has_straight = 0;
	uint8_t has_three_pair = 0;
	uint32_t score = 0;
	uint32_t num1 = 0;
	uint32_t num2 = 0;
	uint32_t score_if_kept[DICE];

	uint8_t of_kind[SIDES][DICE+1];

	while(bust != 1){
		//roll dice
		(void)memset(roll_results, '\0', sizeof(uint8_t) * DICE);
		for(num1=0; num1<dice_rolling; num1++){
			roll_results[num1] = lrand(s) % SIDES + 1;
		}
		//count amount of each number
		(void)memset(num_counts, '\0', sizeof(uint8_t) * DICE);
		for(num1=0; num1<dice_rolling; num1++){
			num_counts[ roll_results[num1] ]++;
		}
		//search for all possible points
			//combinations that use all dice (first roll only)
		if(dice_rolling == DICE){
			has_straight   = Has_Straight(roll_results, dice_rolling)
			has_three_pair = Has_Three_Pair(num_counts, dice_rolling)
		}
		/*
			for(num1=1; num1<DICE; num1++){
				if(roll_results[num1] != roll_results[0])
					break;
				if(num1 == (DICE-1))
					of_kind[5][roll_results[num1]] = 1;
					//comb.has_six_of_kind = 1;
			}
		}
		*/
			//combinations that use fewer than all dice
			//cycle thru them ?
		(void)memset((void *)of_kind, '\0', SIDES*DICE*sizeof(uint8_t));
		for(num1=0; num1<SIDES; num1++){
			//if(num_counts[num1] != 0)
			of_kind[num1][num_counts[num1]] = 1;
		}

		for(num1=0; num1<dice_rolling; num1++){
			of_kind[][num1]

		for(num1=0; num1<SIDES; num1++){

		can_keep[]

		dice_rolling

		(void)memset((void *)score_if_kept, '\0', DICE*sizeof(uint32_t));
		//highest single dice score
		//1
		if(num_counts[0] > 0){
			//one is highest 100
			score_if_kept[0] = Same_of_kind_scores[1][1];
		}else if(num_counts[4] > 0){
			//five is highest 50
			score_if_kept[0] = Same_of_kind_scores[1][5];
		}else{
			//no single dice option
			score_if_kept[0] = 0;
		}

		//highest 2 dice option, can only include combinations of 1 and 5
		//2
		//if(num_counts[0] + num_counts[4] > 1){
			//
		if(num_counts[0] >= 2){
			//200
			score_if_kept[1] = Same_of_kind_scores[2][1];
		}else if( num_counts[0] >= 1 && num_counts[4] >= 1){
			//150
			score_if_kept[1] = Same_of_kind_scores[1][1] + Same_of_kind_scores[1][5];
		}else if( num_counts[4] >= 2){
			//100
			score_if_kept[1] = Same_of_kind_scores[2][5];
		}else{
			//no 2 dice option
			score_if_kept[1] = 0;
		}

		//3
		score_if_kept[2] = Highest_Three_Combination(num_counts);
		//4
		score_if_kept[3] = Highest_Four_Combination(num_counts);
		//5
		score_if_kept[4] = Highest_Five_Combination(num_counts);
		//6
		score_if_kept[5] = Highest_Six_Combination(num_counts);

	}
}


static uint32_t Highest_Three_Combination(uint8_t *num_counts){
		//3 of kind or 1 and 5 combinations
	//uint32_t score;
	if(num_counts[0] >= 3)
		return Same_of_kind_scores[3][1];
	if(num_counts[5] >= 3)
		return Same_of_kind_scores[3][6];
	if(num_counts[4] >= 3)
		return Same_of_kind_scores[3][5];
	if(num_counts[3] >= 3)
		return Same_of_kind_scores[3][4];
	if(num_counts[2] >= 3)
		return Same_of_kind_scores[3][3];
	if(num_counts[0] >= 2 && num_counts[4] >= 1)
		//250
		return(2*Same_of_kind_scores[1][1] + Same_of_kind_scores[1][5]);
	if(num_counts[1] >= 3)
		//200 = 222 or 551
		return Same_of_kind_scores[3][2];
	if(num_counts[4] >= 2 && num_counts[0] >= 1)
		return(2*Same_of_kind_scores[1][5] + Same_of_kind_scores[1][1]);
	return 0;
}

static uint32_t Highest_Four_Combination(uint8_t *num_counts){
	//4 of kind, 3 of kind + 1 or 5, 2 1's + 2 5's
	//{0, 2000,  400,  600,  800, 1000, 1200},
	if(num_counts[0] >= 4) //2000
		return Same_of_kind_scores[4][1];
	if(num_counts[5] >= 4) //1200
		return Same_of_kind_scores[4][6];
	if(num_counts[0] >= 3 && num_counts[4] >= 1) //1050
		return(Same_of_kind_scores[3][1] + Same_of_kind_scores[1][5]);
	if(num_counts[4] >= 4) //1000
		return Same_of_kind_scores[4][5];
	if(num_counts[3] >= 4) //800
		return Same_of_kind_scores[4][4];
	if(num_counts[5] >= 3 && num_counts[0] >= 1) //700
		return(Same_of_kind_scores[1][1] + Same_of_kind_scores[3][6]);
	if(num_counts[5] >= 3 && num_counts[4] >= 1) //650
		return(Same_of_kind_scores[1][5] + Same_of_kind_scores[3][6]);
	if(num_counts[2] >= 4) //600
		return Same_of_kind_scores[4][3];
	if(num_counts[4] >= 3 && num_counts[0] >= 1) //600
		return(Same_of_kind_scores[3][5] + Same_of_kind_scores[1][1]);
	if(num_counts[3] >= 3 && num_counts[0] >= 1) //500
		return(Same_of_kind_scores[3][4] + Same_of_kind_scores[1][1]);
	if(num_counts[3] >= 3 && num_counts[4] >= 1) //450
		return(Same_of_kind_scores[3][4] + Same_of_kind_scores[1][5]);
	if(num_counts[1] >= 4) //400
		return Same_of_kind_scores[4][2];
	if(num_counts[2] >= 3 && num_counts[0] >= 1) //400
		return(Same_of_kind_scores[3][3] + Same_of_kind_scores[1][1]);
	if(num_counts[2] >= 3 && num_counts[4] >= 1) //350
		return(Same_of_kind_scores[3][3] + Same_of_kind_scores[1][5]);
	if(num_counts[1] >= 3 && num_counts[0] >= 1) //300
		return(Same_of_kind_scores[3][2] + Same_of_kind_scores[1][1]);
	if(num_counts[0] >= 2 && num_counts[4] >= 2) //300
		return(Same_of_kind_scores[2][1] + Same_of_kind_scores[2][5]);
	if(num_counts[1] >= 3 && num_counts[4] >= 1) //250
		return(Same_of_kind_scores[3][2] + Same_of_kind_scores[1][5]);

	return 0;
}

static uint32_t Highest_Five_Combination(uint8_t *num_counts){
	//5 of kind, all of previous + 1 or 5
	

	return 0;
}

static uint32_t Highest_Six_Combination(uint8_t *num_counts){
	//6 of kind, all of previous + 1 or 5
	

	return 0;
}

		//has_straight
		//has_three_pair

		//got nothing bust return 0;
		
		//find if all can be used
			//take points and roll again  continue;

		//find highest score from keeping all # of dice options
			//if not onboard skip MIN_* unless over MIN_GET_ONBOARD

			//maximize chance of not busting
			//ie: better to keep 1 instead of 1 + 5  or 1 + 1
				//don't take 

			//must look at MIN_WILL_ROLL and MIN_STAY_AT
				//if at MIN_WILL_ROLL take points and end turn
				//if at MIN_STAY_AT take points and end turn

		//will only keep more than 1 dice if ...
			//score gets onboard (end turn)
			//score meet MIN_STAY_AT (end turn)
			//no 1 dice option available
			//score is sufficiently high ?
			//does not leave remaining less than MIN_WILL_ROLL

		/*will only roll fewer dice than MIN_WILL_ROLL
		  if MIN_STAY_AT or (!onboard && MIN_GET_ONBOARD <= score) is satisfied [mutually exclusive]*/

		//factor in onboard

		//factor in MIN_WILL_ROLL

		//factor in MIN_STAY_AT


		//

	}
}


static uint32_t Roll_Dice(uint64_t *s, char onboard){
	/*Roll 6 dice and play out a turn of 10,000*/
	//uint64_t rand_seed = 
	uint32_t rand_num1 = 0;
	//uint32_t lrand(uint64_t *s)
	uint32_t score = 0;
	uint32_t score_tmp = 0;
	uint32_t num1 = 0;
	uint32_t num2 = 0;
	uint32_t dice_to_roll = 6;
	char turn_over = 0;
	char roll_results[6] = {0, 0, 0, 0, 0, 0};
	char num_counts[6]   = {0, 0, 0, 0, 0, 0};
	char *c_ptr;
	struct Comb comb;
	(void)memset(&comb, '\0', sizeof(struct Comb));
/*
	char has_straight      = 0;
	char has_three_pairs   = 0;
	char has_single_five   = 0;
	char has_single_one    = 0;
	char has_three_of_kind = 0;
	char has_four_of_kind  = 0;
	char has_five_of_kind  = 0;
	char has_six_of_kind   = 0;
	char has_two_one       = 0;
	char has_two_five      = 0;
	char zilch = 0;
	char dice_left_over    = 0;
*/

	while(!turn_over){
		(void)memset(&comb, '\0', sizeof(struct Comb));
		(void)memset(roll_results, '\0', 6);
		//roll dice
		for(num1=0; num1<dice_to_roll; num1++){
			roll_results[num1] = lrand(s) % 6 + 1;
		}
		//analyse results
		(void)memset(num_counts, '\0', 6);
		for(num1=0; num1<dice_to_roll; num1++){
			num_counts[ roll_results[num1] ]++;
		}
			//results only possible if 6 rolled
		//has_straight = 0;
		//has_three_pairs = 0;
		//has_six_of_kind = 0;

		if(dice_to_roll == 6){
				//check for straight
			//has_straight = 0;
			for(num1=0; num1<6; num1++){
				if(!num_counts[num1])
					break;
				if(num1 == 5)
					comb.has_straight = 1;
			}
				//check for three pairs
			//has_three_pairs = 0;
			num2 = 0;
			for(num1=0; num1<6; num1++){
				if(num_counts[num1] == 2)
					num2++;
			}
			if(num2 == 3)
				comb.has_three_pairs = 1;
				//check for 6 of a kind
			//has_six_of_kind = 0;
			for(num1=1; num1<6; num1++){
				if(roll_results[num1] != roll_results[0])
					break;
				if(num1 == 5)
					comb.has_six_of_kind = 1;
			}
		}
		//check for a single 5
		//has_single_five = 0;
		for(num1=0; num1<dice_to_roll; num1++){
			if(roll_results[num1] == 5){
				comb.has_single_five = 1;
				break;
			}
		}
		//check for a single 1
		//has_single_one = 0;
		for(num1=0; num1<dice_to_roll; num1++){
			if(roll_results[num1]){
				comb.has_single_one = 1;
				break;
			}
		}
		//check for 5 of a kind
		//has_five_of_kind = 0;
		if(dice_to_roll > 4){
			for(num1=0; num1<dice_to_roll; num1++){
				if(num_counts[num1] == 5){
					comb.has_five_of_kind = 1;
					break;
				}
			}
		}
		//check for 4 of a kind
		//has_four_of_kind = 0;
		if(dice_to_roll > 3){
			for(num1=0; num1<dice_to_roll; num1++){
				if(num_counts[num1] == 4){
					comb.has_four_of_kind = 1;
					break;
				}
			}
		}
		//check for two 5's or 1's
		//has_two_five = 0;
		//has_two_one = 0;
		if(dice_to_roll > 1){
			if(num_counts[4] == 2)
				comb.has_two_five = 1;
			if(num_counts[0] == 2)
				comb.has_two_one = 1;
		}
		//don't have anything: "zilch"
		zilch = 1;
		num2 = sizeof(struct Comb);
		c_ptr = (char *)&comb;
		for(num1=0; num1<num2; num1++){
			if(*(char *)(c_ptr+num1) != '\0'){
				zilch = 0;
				break;
			}
		}
		//
		if(zilch)
			return 0;
		//find the highest possible score without breaking MIN_WILL_ROLL or MIN_STAY_AT
		//ignore MIN_WILL_ROLL and MIN_STAY_AT if not onboard
		while(1){
			if(comb.has_six_of_kind){
				turn_over = 0;
				score += s_six_of_kind[roll_results[0]-1];
				break;
			}
			if(comb.has_straight){
				turn_over = 0;
				score += 1500;
				break;
			}
			if(comb.has_three_pairs){
				turn_over = 0;
				score += 1000;
				break;
			}
			score_tmp = 0;
			//check combinations of conditions against rules
			if(comb.has_five_of_kind){
				if(dice_to_roll == 6){
					//check for sweep
					

					//if the 1 dice not part of series is not 1 or 5 then bust
					for(num1=0; num1<6; num1++){
						if(roll_results[num1]){
							if(num1){
								
							}
							if(num1 == 5){
								
							}
						}
					}
				}else{

				}
			}
		}

		/*
		if(has_single_five || has_single_one || has_three_of_kind || has_four_of_kind || has_five_of_kind
			|| has_six_of_kind || has_three_pairs || has_straight)
			zilch = 0;
		if(zilch)
			return 0;
		//highest result
		dice_left_over = 0;
		while(1){
			if(has_six_of_kind){
				if(roll_results[0]){
					score+=8000;
					break;
				}
				if(roll_results[0] == 5){

				}
				if(roll_results[0] == 5){
					score+=4000;
					break;
				}
			}
			if(has_five_of_kind){

			}
			if(has_four_of_kind){

			}
			if(has_straight){
			1500
			}
			if(has_three_pairs){

			}
			if(has_three_of_kind){

			}
			if(has_two_one){

			}
			if(has_two_five){

			}
			if(has_single_one){

			}
			if(has_single_five){

			}
			zilch = 1;
			break;
		}
		*/

		//a sweep

		//bank the score and pass?

		turn_over = 1;

	}

	return score;
}

int main(int argc, char **argv){
	uint32_t score = 0;
	uint32_t score_tmp = 0;
	uint32_t turn = 0;
	struct timeval cur;
	gettimeofday(&cur, NULL);
	uint64_t rseed = (uint64_t)cur.tv_usec + 3 + t;


	//get on the board
	while(score_tmp < MIN_GET_ONBOARD){
		score_tmp = Roll_Dice(&rseed, 0);
		turn++;
	}
	score += score_tmp;
	//play to 10,000
	while(score < 10000){
		score += Roll_Dice(&rseed, 1);
		turn++;
	}
	return 0;
}