void clear_scratch(){
    for (uint8_t x = 0; x < MATRIX_WIDTH; x++){
	for (uint8_t y = 0; y < MATRIX_HEIGHT; y++){
		set_scratch(x, y, 0);
	}
    }	
}
void setup(){
	//Show name plate
	for(uint8_t i = 0; i < 5; i++){
		show_name(GRN_1); _delay_ms(100);
		show_name(GRN_2); _delay_ms(100);
		show_name(GRN_3); _delay_ms(100);
		show_name(RED_1 | GRN_3); _delay_ms(100);
		show_name(RED_2 | GRN_3); _delay_ms(100);
		show_name(RED_3 | GRN_3); _delay_ms(100);
		show_name(RED_3 | GRN_2); _delay_ms(100);
		show_name(RED_3 | GRN_1); _delay_ms(100);
		show_name(RED_3); _delay_ms(100);
		show_name(RED_2); _delay_ms(100);
		show_name(RED_1); _delay_ms(100);
		show_name(RED_2); _delay_ms(100);
		show_name(RED_3); _delay_ms(100);
		show_name(RED_3 | GRN_1); _delay_ms(100);
		show_name(RED_3 | GRN_2); _delay_ms(100);
		show_name(RED_3 | GRN_3); _delay_ms(100);
		show_name(RED_2 | GRN_3); _delay_ms(100);
		show_name(RED_1 | GRN_3); _delay_ms(100);
		show_name(GRN_3); _delay_ms(100);
		show_name(GRN_2); _delay_ms(100);
	}
	
	//Clear board
	clear_scratch();
	flush();
	matrix_write_buffer();
	_delay_ms(100);

	//Random start positions
	for (uint8_t x = 0; x < MATRIX_WIDTH; x++){
		for (uint8_t y = 0; y < MATRIX_HEIGHT; y++){
			if ((random() & 0x3) == 0x3){		//25% chance
				set_scratch(x, y, GRN_1);
			}
		}
	}
	
	for (uint8_t i = 0; i < RECENT_HASH_COUNT; i++){
		recent_hashes[i] = i;
	}
	
	flush();
	matrix_write_buffer();
}
void setup(){
	srandom(analog_read_p(0) + timer_micros() + timer_millis());
	
	//Flash palette
	flash_palette(GRN_1); _delay_ms(100);
	flash_palette(GRN_2); _delay_ms(100);
	flash_palette(GRN_3); _delay_ms(100);
	flash_palette(RED_1 | GRN_3); _delay_ms(100);
	flash_palette(RED_2 | GRN_3); _delay_ms(100);
	flash_palette(RED_3 | GRN_3); _delay_ms(100);
	flash_palette(RED_3 | GRN_2); _delay_ms(100);
	flash_palette(RED_3 | GRN_1); _delay_ms(100);
	flash_palette(RED_3); _delay_ms(100);
	flash_palette(RED_2); _delay_ms(100);
	flash_palette(RED_1); _delay_ms(100);
	
	//Clear board
	clear_scratch();
	flush();
	matrix_write_buffer();
	_delay_ms(100);

	//Random start positions
	for (uint8_t x = 0; x < MATRIX_WIDTH; x++){
		for (uint8_t y = 0; y < MATRIX_HEIGHT; y++){
			if ((random() & 0x3) == 0x3){		//25% chance
				set_scratch(x, y, GRN_1);
			}
		}
	}
	
	for (uint8_t i = 0; i < RECENT_HASH_COUNT; i++){
		recent_hashes[i] = i;
	}
	
	flush();
	matrix_write_buffer();
}
int main (void){
	timer_init();
	uint8_t analog_pins[1] = {5};
	analog_init(analog_pins, 1, ANALOG_INTERNAL);
	matrix_init();
	matrix_set_mode(MATRIX_MODE_2BIT);
	
	setup();
		
	while (1) {
		for (uint8_t x = 0; x < MATRIX_WIDTH; x++){
			for (uint8_t y = 0; y < MATRIX_HEIGHT; y++){
				uint8_t count = get_neighbor_count(x, y);
				uint8_t alive = get_pixel(x, y);
				if (alive) {
					if (count < 2) set_scratch(x, y, 0x00);
					else if (count <= 3){
						if (alive == GRN_1) {
							set_scratch(x, y, GRN_2);
						}
						if (alive == GRN_2) {
							set_scratch(x, y, GRN_3);
						}
						if (alive == GRN_3) {
							set_scratch(x, y, GRN_3 | RED_1);
						}
						else if (alive == (GRN_3 | RED_1)) {
							set_scratch(x, y, GRN_3 | RED_2);
						}
						else if (alive == (GRN_3 | RED_2)) {
							set_scratch(x, y, GRN_3 | RED_3);
						}
						else if (alive == (GRN_3 | RED_3)) {
							set_scratch(x, y, GRN_2 | RED_3);
						}
						else if (alive == (GRN_2 | RED_3)) {
							set_scratch(x, y, GRN_1 | RED_3);
						}
						else if (alive == (GRN_1 | RED_3)) {
							set_scratch(x, y, RED_3);
						}
						else if (alive == (RED_3)) {
							set_scratch(x, y, RED_2);
						}
						else if (alive == (RED_2)) {
							set_scratch(x, y, RED_1);
						}
					}
					else if (count > 3) set_scratch(x, y, 0x00);
				}
				else if (count == 3){
					set_scratch(x, y, GRN_1);
				}
			}
		}
		
		flush();
		matrix_write_buffer();
		
		//Store board hash
		for (uint8_t i = RECENT_HASH_COUNT - 1; i > 0; i--){
			recent_hashes[i] = recent_hashes[i - 1];
		}
		recent_hashes[0] = get_board_hash();
		
		uint8_t matches = 0;
		for (uint8_t i = 0; i < RECENT_HASH_COUNT; i++){
			for (uint8_t j = i + 1; j < RECENT_HASH_COUNT; j++){
				if (recent_hashes[i] == recent_hashes[j]) matches++;
			}
		}
		if (matches == 0) recent_hash_match_count = 0;
		else recent_hash_match_count++;
		
		if (recent_hash_match_count >= RECENT_HASH_MATCH_COUNT){
			setup();
		}
		
		_delay_ms(70);
	}
}