#include #include #include #include #include #include #include "mastermind.h" #include "apptogui.h" #include "configure.h" #include "list.h" #include "types.h" #include "mymath.h" #include "debug.h" int current_indentation = 0; char board[WIDTH][HEIGHT]; char output[WIDTH][HEIGHT]; char input[WIDTH]; char solution[WIDTH]; int current_line = 0; BOOLEAN not_solved = TRUE; void mastermind_initialize(void); void mastermind_print_board(void); int mastermind_total_combinations(void); int mastermind_combinations(void); BOOLEAN mastermind_solved(void); void mastermind_generate_answer(void); void mastermind_user_wants_to_quit(void); void mastermind_user_wants_help(void); void mastermind_user_entered_try(long); void mastermind_show_correct_solution(void); void mastermind_user_wants_new_game(void); void mastermind_key_pressed(char ch, int position); void mastermind_initialize(void) { int x; int y; int i; DEBUG_BEGIN(mastermind_initialize, 10); for (x = 0; x < WIDTH ; x++) for (y = 0; y < HEIGHT; y++) { board [x][y] = '_'; output[x][y] = ' '; } current_line = 0; not_solved = TRUE; srand(time(NULL)); for (i = 0; i < WIDTH; i++) { solution[i] = 'A' + (rand() % COLORS); } /* solution[0] = 'A'; solution[1] = 'C'; solution[2] = 'C'; solution[3] = 'B'; solution[4] = 'C'; */ DEBUG_END(mastermind_initialize, 10); } int mastermind_total_combinations() { int i; int result = 1; DEBUG_BEGIN(mastermind_total_combinations, 10); for (i = 0; i < WIDTH; i++) result *= COLORS; DEBUG_END(mastermind_total_combinations, 10); return result; } int mastermind_combinations_of_one_entry(int value[WIDTH]) { int i; int j; int result = 1; int set_bits = 0; for (i = 0; i < WIDTH; i++) { set_bits = 0; for (j = 0; j < COLORS; j++) { set_bits += (value[i] >> j) % 2; } #ifdef DEBUG if (0 == set_bits) printf("Internal error: a set with no 1 bit occured!\n"); #endif result *= set_bits; } return result; } int mastermind_black_stones_num(int line) { int i; int erg = 0; DEBUG_BEGIN(mastermind_black_stones_num, 50); for (i = 0; i < WIDTH; i++) if ('*' == output[i][line]) erg++; DEBUG_END(mastermind_black_stones_num, 50); return erg; } int mastermind_white_stones_num(int line) { int i; int erg = 0; DEBUG_BEGIN(mastermind_white_stones_num, 50); for (i = 0; i < WIDTH; i++) if ('O' == output[i][line]) erg++; DEBUG_END(mastermind_white_stones_num, 50); return erg; } BOOLEAN mastermind_is_entry_legal(list_item *entry) { BOOLEAN erg = TRUE; int i; for (i = 0; (i < WIDTH) && erg; i++) erg = /* erg && */ (entry->value[i] != 0); return erg; } void mastermind_handle_empties(list_item **current, int row, int line, BOOLEAN handeled_positions[WIDTH]) { char ch; int shift; int i; int only_this_bits; ch = board[row][line]; shift = ch - 'A'; only_this_bits = ~(1 << shift); for (i = 0; i < WIDTH; i++) if (!handeled_positions[i]) (*current)->value[i] = (((*current)->value[i]) & (only_this_bits)); } void mastermind_handle_blacks(list_item **current, int row, int line) { /* blackify: assume that a black stone is there in the answer exactly for 'row' */ char ch; int shift; DEBUG_BEGIN(mastermind_handle_blacks, 150); ch = board[row][line]; shift = ch - 'A'; (*current)->value[row] = (((*current)->value[row]) & (1 << shift)); DEBUG_END(mastermind_handle_blacks, 150); } void mastermind_handle_whites_and_empties(list_item *in_state, int num_of_blacks, int num_of_whites, index_list *index_of_whites, int *remap_bypass_blacks, int line, BOOLEAN handeled_positions[WIDTH], list_item **output_list) { /* here we have to handle sources and destinations of white stones */ char ch = 0; int shift = 0; int row = 0; int i = 0; int j = 0; BOOLEAN legal = FALSE; int nth = 0; int source_row = 0; int destination_row = 0; index_list *n_over_k = NULL; index_list *n_over_k_cursor = NULL; index_list *permuts = NULL; index_list *permuts_cursor = NULL; list_item *temp_item = NULL; list_item *base = NULL; BOOLEAN currently_handeled_positions[WIDTH]; /*-----------------------------------------------------------*/ /* initialization stuff */ list_new_item(list_item, base); memcpy(base->value, in_state->value, WIDTH * sizeof(int)); /* handle sources */ /* indexes of sources are saved in index_of_whites */ /* so we have only to disable the color at this position */ /* this color also cannot occur at any other position where */ /* the user tried to set the same color (except there was */ /* a black stone as answer (we can see it in handled positions) */ for (i = 0; i < num_of_whites; i++) { row = remap_bypass_blacks[index_of_whites->value[i]]; ch = board[row][line]; shift = ch - 'A'; for (j = 0; j < (WIDTH - num_of_blacks); j++) { if ((!handeled_positions[remap_bypass_blacks[j]]) && (board[remap_bypass_blacks[j]][line] == ch)) { base->value[remap_bypass_blacks[j]] = ((base->value[remap_bypass_blacks[j]]) & (~(1 << shift))); } } } /* handle destination */ /* you have number of free positions over num_of_whites */ /* combinations to select where the destinationpositions */ /* are. And for every combination of that art you have */ /* num_of_whites factorial posiibilities to fill it with */ /* the contents of that white stones (don't fill a white */ /* stone into his primary location) */ mymath_cached_generate_n_over_k(WIDTH - num_of_blacks, num_of_whites, &n_over_k); mymath_cached_permut(num_of_whites, &permuts); for(n_over_k_cursor = n_over_k; n_over_k_cursor != NULL; n_over_k_cursor = n_over_k_cursor->list_next_item) { for(permuts_cursor = permuts; permuts_cursor != NULL; permuts_cursor = permuts_cursor->list_next_item) { legal = TRUE; memcpy(currently_handeled_positions, handeled_positions, WIDTH * sizeof(int)); list_new_item(list_item, temp_item); memcpy(temp_item->value, base->value, WIDTH * sizeof(int)); for (i = 0; (i < num_of_whites); i++) { nth = permuts_cursor->value[i]; source_row = remap_bypass_blacks[index_of_whites->value[nth]]; destination_row = remap_bypass_blacks[n_over_k_cursor->value[i]]; legal = legal && ((board[source_row][line]) != (board[destination_row][line])); legal = legal && (!currently_handeled_positions[destination_row]); if (legal) { /* Put this white stone from source to dest */ ch = board[source_row][line]; shift = ch - 'A'; temp_item->value[destination_row] = temp_item->value[destination_row] & (1 << shift); currently_handeled_positions[destination_row] = TRUE; } } if (legal) { /* handle unhandeled positions */ /* to simplify code, but need more time to run */ /* go over all not black positions and handel them like */ /* they got nothing (neither black nor white) */ for (j = 0; j < (WIDTH - num_of_blacks); j++) { if (!currently_handeled_positions[remap_bypass_blacks[j]]) { mastermind_handle_empties(&temp_item, remap_bypass_blacks[j], line, currently_handeled_positions); } } /* append this stuff to the outlist */ if (mastermind_is_entry_legal(temp_item)) { list_add_element_at_head(list_item, *output_list, temp_item); } else { list_delete_all_items(list_item, temp_item); } } else { list_delete_all_items(list_item, temp_item); } } } } void mastermind_generate_list_for_current_line(list_item **out_list, int line) { list_item *base; list_item *temp_item; index_list *indexes_blacks_list = NULL; index_list *indexes_whites_list = NULL; index_list *index_black_entry = NULL; index_list *index_white_entry = NULL; int black_stones; int white_stones; BOOLEAN handeled_positions[WIDTH]; int maxvalue; int i; DEBUG_BEGIN(mastermind_generate_list_for_current_line, 10); /* Generate one entry that contains all */ /* possible combinations like if no try entered before */ list_new_item(list_item, base); maxvalue = 0; for (i = 0; i < COLORS; i++) maxvalue = maxvalue | (1 << i); for(i = 0; i < WIDTH; i++) base->value[i] = maxvalue; black_stones = mastermind_black_stones_num(line); white_stones = mastermind_white_stones_num(line); /* Returns a list with entries of the following form: an array of k elements with ints in it. if you call for 3 over 2 you will get: first_entry : {0, 1} second_entr : {0, 2} third_entry : {1, 2} */ DEBUG_BEGIN(calling_mathematics_functions, 100); mymath_cached_generate_n_over_k(WIDTH, black_stones, &indexes_blacks_list); mymath_cached_generate_n_over_k(WIDTH - black_stones, white_stones, &indexes_whites_list); DEBUG_END(calling_mathematics_functions, 100); if ((black_stones != 0) && (white_stones != 0)) { DEBUG_BEGIN(black_and_whites, 100); for (index_black_entry = indexes_blacks_list; index_black_entry != NULL; index_black_entry = index_black_entry->list_next_item) { for (index_white_entry = indexes_whites_list; index_white_entry != NULL; index_white_entry = index_white_entry->list_next_item) { int remap[WIDTH]; int current_offset; int i; int j; list_new_item(list_item, temp_item); memcpy(temp_item->value, base->value, WIDTH * sizeof(int)); for (i = 0; i < WIDTH; i++) handeled_positions[i] = FALSE; for (i = 0; i < black_stones; i++) { mastermind_handle_blacks(&temp_item, index_black_entry->value[i], line); handeled_positions[index_black_entry->value[i]] = TRUE; } for (i = 0; i < WIDTH; i++) { if (i == 0) remap[0] = 0; else remap[i] = remap[i - 1] + 1; current_offset = remap[i]; for (j = 0; handeled_positions[current_offset + j]; j++); remap[i] += j; } mastermind_handle_whites_and_empties (temp_item, black_stones, white_stones, index_white_entry, remap, line, handeled_positions, out_list); /* This is done in handle_whites_and_empties */ /* list_append(list_item, out_list, temp_item); */ } } DEBUG_END(black_and_whites, 100); } else if (black_stones != 0) { int i; DEBUG_BEGIN(only_blacks, 100); for (index_black_entry = indexes_blacks_list; index_black_entry != NULL; index_black_entry = index_black_entry->list_next_item) { DEBUG_BEGIN(in_loop_initialization, 110); for (i = 0; i < WIDTH; i++) handeled_positions[i] = FALSE; list_new_item(list_item, temp_item); memcpy(temp_item->value, base->value, WIDTH * sizeof(int)); DEBUG_END(in_loop_initialization, 110); DEBUG_BEGIN(in_loop_first_loop, 110); for (i = 0; i < black_stones; i++) { DEBUG_BEGIN(mastermind_handle_blacks_call, 150); mastermind_handle_blacks(&temp_item, index_black_entry->value[i], line); DEBUG_END(mastermind_handle_blacks_call, 150); handeled_positions[index_black_entry->value[i]] = TRUE; } DEBUG_END(in_loop_first_loop, 110); DEBUG_BEGIN(in_loop, 110); for (i = 0; i < WIDTH; i++) { if (!handeled_positions[i]) mastermind_handle_empties(&temp_item, i, line, handeled_positions); } if (mastermind_is_entry_legal(temp_item)) list_add_element_at_head(list_item, *out_list, temp_item); DEBUG_END(in_loop, 110); } DEBUG_END(only_blacks, 100); } else if (white_stones != 0) { int i; int remap[WIDTH]; DEBUG_BEGIN(only_whites, 100); for (i = 0; i < WIDTH; i++) remap[i] = i; for (index_white_entry = indexes_whites_list; index_white_entry != NULL; index_white_entry = index_white_entry->list_next_item) { for (i = 0; i < WIDTH; i++) { handeled_positions[i] = FALSE; } list_new_item(list_item, temp_item); memcpy(temp_item->value, base->value, WIDTH * sizeof(int)); mastermind_handle_whites_and_empties (temp_item, 0, white_stones, index_white_entry, remap, line, handeled_positions, out_list); /* This is done in handle_whites_and_empties */ /* list_append(list_item, out_list, temp_item); */ } DEBUG_END(only_whites, 100); } else { int i; DEBUG_BEGIN(only_empties, 100); /* (black_stones == 0) && (white_stones == 0) */ list_new_item(list_item, temp_item); memcpy(temp_item->value, base->value, WIDTH * sizeof(int)); for (i = 0; i < WIDTH; i++) handeled_positions[i] = FALSE; for (i = 0; i < WIDTH; i++) { mastermind_handle_empties(&temp_item, i, line, handeled_positions); } if (mastermind_is_entry_legal(temp_item)) list_add_element_at_head(list_item, *out_list, temp_item); DEBUG_END(only_empties, 100); } /* list_delete_all_items(list_item, base); */ DEBUG_END(mastermind_generate_list_for_current_line, 10); } void mastermind_merge_old_and_new_list(list_item *old_list, list_item *new_list, list_item **out_list) { list_item *old_list_cursor; list_item *new_list_cursor; list_item *temp_item; int i; DEBUG_BEGIN(mastermind_merge_old_and_new_list, 10); *out_list = NULL; list_new_item(list_item, temp_item); for(old_list_cursor = old_list; old_list_cursor != NULL; old_list_cursor = old_list_cursor->list_next_item) { for(new_list_cursor = new_list; new_list_cursor != NULL; new_list_cursor = new_list_cursor->list_next_item) { for (i = 0; i < WIDTH; i++) temp_item->value[i] = (old_list_cursor->value[i] & new_list_cursor->value[i]); if (mastermind_is_entry_legal(temp_item)) { list_add_element_at_head(list_item, *out_list, temp_item); list_new_item(list_item, temp_item); } } } DEBUG_END(mastermind_merge_old_and_new_list, 10); } void mastermind_print_bitfield(list_item *item) { int j; int i; /* printf("Bitfield: "); */ for (j = 0; j < WIDTH; j++) { for (i = 0; i < COLORS; i++) printf("%d", (item->value[j] & (1 << i))>>i ); printf("|"); } printf("\n"); } void mastermind_delete_unnecessary_items(list_item **root_of_list) { /* Delete one element if at any position no bit set anymore or if this entry is a really subset of any other */ list_item *out_list = NULL; list_item *temp_item = NULL; list_item *cursor_a = NULL; list_item *cursor_b = NULL; BOOLEAN copy_it; BOOLEAN delete_it; int i; DEBUG_BEGIN(mastermind_delete_impossible_items, 20); copy_it = TRUE; for (cursor_a = *root_of_list; cursor_a != NULL; cursor_a = cursor_a->list_next_item) { for (cursor_b = cursor_a->list_next_item; (cursor_b != NULL) && copy_it; cursor_b = cursor_b->list_next_item) { if (cursor_a != cursor_b) { /* if (b | a == b) delete a */ delete_it = TRUE; for (i = 0; (i < WIDTH) && (delete_it); i++) delete_it = delete_it && ((cursor_b->value[i] | cursor_a->value[i]) == (cursor_b->value[i])); copy_it = copy_it && (!delete_it); } } if (copy_it) { list_new_item(list_item, temp_item); memcpy(temp_item->value, cursor_a->value, WIDTH * sizeof(int)); list_add_element_at_head(list_item, out_list, temp_item); #ifdef DEBUG mastermind_print_bitfield(temp_item); #endif } else { copy_it = TRUE; } } list_delete_all_items(list_item, *root_of_list); *root_of_list = out_list; out_list = NULL; DEBUG_END(mastermind_delete_impossible_items, 20); } void mastermind_split_list(list_item **root_of_old_list, int line) { /* Generally: Generate a list for the currently entered try */ /* and merge it with the old one. */ list_item *root_of_new_list = NULL; list_item *root_of_out_list = NULL; DEBUG_BEGIN(mastermind_split_list, 10); /* This function assumes that there are no restrictions from */ /* earlier tries. */ mastermind_generate_list_for_current_line(&root_of_new_list, line); /* Now we merge both lists the old and the new list and */ /* eliminate all unpossible entries */ mastermind_delete_unnecessary_items(&root_of_new_list); mastermind_merge_old_and_new_list(*root_of_old_list, root_of_new_list, &root_of_out_list); #ifdef DEBUG printf("Length of merged unoptimized\n"); list_print_length(list_item, root_of_out_list); #endif mastermind_delete_unnecessary_items(&root_of_out_list); /* free some space */ DEBUG_BEGIN(mastermind_split_list_freeing_space1, 10); list_delete_all_items(list_item, *root_of_old_list); DEBUG_END(mastermind_split_list_freeing_space1, 10); DEBUG_BEGIN(mastermind_split_list_freeing_space2, 10); list_delete_all_items(list_item, root_of_new_list); DEBUG_END(mastermind_split_list_freeing_space2, 10); /* return the new list */ *root_of_old_list = root_of_out_list; DEBUG_END(mastermind_split_list, 10); } int mastermind_combinations(void) { static int last_calculated_line = 0; static list_item *root_of_list = NULL; list_item *list_cursor = NULL; int result = 0; DEBUG_BEGIN(mastermind_combinations, 20); /* if a new game begins, we have to clean the list and reinitialize it */ if (current_line == 0) { int i; int j; int maxvalue; last_calculated_line = 0; list_delete_all_items(list_item, root_of_list); list_new_item(list_item, root_of_list); maxvalue = 0; for (j = 0; j < COLORS; j++) { maxvalue += (1 << j); } for (i = 0; i < WIDTH; i ++) { root_of_list->value[i] = maxvalue; } result = mastermind_combinations_of_one_entry(root_of_list->value); } else { if (last_calculated_line < current_line) { mastermind_split_list(&root_of_list, current_line - 1); last_calculated_line ++; } list_cursor = root_of_list; while (NULL != list_cursor) { result += mastermind_combinations_of_one_entry(list_cursor->value); list_get_next_item(list_item, list_cursor); } } DEBUG_END(mastermind_combinations, 20); return (result); } BOOLEAN mastermind_solved() { BOOLEAN result = TRUE; int i; DEBUG_BEGIN(mastermind_solved, 10); for (i = 0; i < WIDTH; i++) result = result && (board[i][current_line - 1] == solution[i]); DEBUG_END(mastermind_solved, 10); return result; } void mastermind_generate_answer() { int i; int j; int schwarze = 0; int weisse = 0; char testing[WIDTH]; DEBUG_BEGIN(mastermind_generate_answer, 10); /* initialize the array testing */ for (i = 0; i < WIDTH; i++) testing[i] = solution[i]; /* Search for exactly right placement */ for (i = 0; i < WIDTH; i++) { if (testing[i] == board[i][current_line]) { testing[i] = 'b'; output[schwarze++][current_line] = '*'; } } /* Search for right colors */ for (i = 0; i < WIDTH; i++) { if (testing[i] != 'b') { for (j = 0; j < WIDTH; j++) { if (board[i][current_line] == testing[j]) { testing[j] = 'w'; output[schwarze + weisse][current_line] = 'O'; weisse++; /* close the loop over j and go through the next i */ j = WIDTH; } } } /* else: This position got a black rating before */ } DEBUG_END(mastermind_generate_answer, 10); } void mastermind_user_wants_to_quit(void) { exit(0); } void mastermind_user_wants_help(void) { printf("Sorry, help is not implemeted yet!\n"); } void mastermind_user_entered_try(long number) { int i; char to_show[WIDTH]; DEBUG_BEGIN(mastermind_user_entered_try, 10); if (number == current_line) { apptogui_get_entered_try(number, input); apptogui_flat_try_button(number); for (i = 0; i < WIDTH; i++) board[i][current_line] = input[i]; mastermind_generate_answer(); for (i = 0; i < WIDTH; i++) to_show[i] = output[i][current_line]; apptogui_show_answer(to_show, current_line); current_line ++; apptogui_current_state(mastermind_total_combinations(), mastermind_combinations(), mastermind_total_combinations() - mastermind_combinations()); } if ((current_line >= (HEIGHT)) || mastermind_solved()) { apptogui_show_correct_solution(solution); } DEBUG_END(mastermind_user_entered_try, 10); } void mastermind_show_correct_solution(void) { DEBUG_BEGIN(mastermind_show_correct_solution, 10); apptogui_show_correct_solution(solution); DEBUG_END(mastermind_show_correct_solution, 10); } void mastermind_user_wants_new_game(void) { DEBUG_BEGIN(mastermind_user_wants_new_game, 10); mastermind_initialize(); apptogui_reset_all(mastermind_total_combinations(), mastermind_combinations(), 0); DEBUG_END(mastermind_user_wants_new_game, 10); } void mastermind_key_pressed(char ch, int position) { if (((current_line * WIDTH) <= position) && (COLORS > ch)) apptogui_change_choice_item(position, ch); }