diff --git a/60_Mastermind/python/mastermind.py b/60_Mastermind/python/mastermind.py index cb89a97b..181a3e7e 100644 --- a/60_Mastermind/python/mastermind.py +++ b/60_Mastermind/python/mastermind.py @@ -37,121 +37,121 @@ computer_score = 0 def main() -> None: - global human_score, computer_score - current_round = 1 - while current_round <= NUM_ROUNDS: print(f"Round number {current_round}") - num_moves = 1 - guesses: List[List[Union[str, int]]] = [] - turn_over = False - print("Guess my combination ...") - secret_combination = int(POSSIBILITIES * random.random()) - answer = possibility_to_color_code(secret_combination) - while num_moves < 10 and not turn_over: - print(f"Move # {num_moves} Guess : ") - user_command = input("Guess ") - if user_command == "BOARD": - print_board(guesses) # 2000 - elif user_command == "QUIT": # 2500 - print(f"QUITTER! MY COMBINATION WAS: {answer}") - print("GOOD BYE") - quit() - elif len(user_command) != NUM_POSITIONS: # 410 - print("BAD NUMBER OF POSITIONS") - else: - invalid_letters = get_invalid_letters(user_command) - if invalid_letters > "": - print(f"INVALID GUESS: {invalid_letters}") - else: - guess_results = compare_two_positions(user_command, answer) - if guess_results[1] == NUM_POSITIONS: # correct guess - turn_over = True - print(f"You guessed it in {num_moves} moves!") - human_score = human_score + num_moves - print_score() - else: - print( - "You have {} blacks and {} whites".format( - guess_results[1], guess_results[2] - ) - ) - num_moves = num_moves + 1 - guesses.append(guess_results) - if not turn_over: # RAN OUT OF MOVES - print("YOU RAN OUT OF MOVES! THAT'S ALL YOU GET!") - print(f"THE ACTUAL COMBINATION WAS: {answer}") - human_score = human_score + num_moves - print_score() - - # COMPUTER TURN - turn_over = False - inconsistent_information = False - while not turn_over and not inconsistent_information: - all_possibilities = [1] * POSSIBILITIES - num_moves = 1 - inconsistent_information = False - print("NOW I GUESS. THINK OF A COMBINATION.") - input("HIT RETURN WHEN READY: ") - while num_moves < 10 and not turn_over and not inconsistent_information: - found_guess = False - possible_guess = int(POSSIBILITIES * random.random()) - if ( - all_possibilities[possible_guess] == 1 - ): # random guess is possible, use it - found_guess = True - else: - for i in range(possible_guess + 1, POSSIBILITIES): - if all_possibilities[i] == 1: - found_guess = True - possible_guess = i - break - if not found_guess: - for i in range(0, possible_guess): - if all_possibilities[i] == 1: - found_guess = True - possible_guess = i - break - if not found_guess: # inconsistent info from user - print("YOU HAVE GIVEN ME INCONSISTENT INFORMATION.") - print("TRY AGAIN, AND THIS TIME PLEASE BE MORE CAREFUL.") - turn_over = True - inconsistent_information = True - else: - computer_guess = possibility_to_color_code(possible_guess) - print(f"My guess is: {computer_guess}") - blacks_str, whites_str = input( - "ENTER BLACKS, WHITES (e.g. 1,2): " - ).split(",") - blacks = int(blacks_str) - whites = int(whites_str) - if blacks == NUM_POSITIONS: # Correct guess - print(f"I GOT IT IN {num_moves} MOVES") - turn_over = True - computer_score = computer_score + num_moves - print_score() - else: - num_moves += 1 - for i in range(0, POSSIBILITIES): - if all_possibilities[i] == 0: # already ruled out - continue - possible_answer = possibility_to_color_code(i) - comparison = compare_two_positions( - possible_answer, computer_guess - ) - if (blacks != comparison[1]) or (whites != comparison[2]): - all_possibilities[i] = 0 - if not turn_over: # COMPUTER DID NOT GUESS - print("I USED UP ALL MY MOVES!") - print("I GUESS MY CPU IS JUST HAVING AN OFF DAY.") - computer_score = computer_score + num_moves - print_score() + human_turn() + computer_turn() current_round += 1 print_score(is_final_score=True) sys.exit() +def human_turn() -> None: + global human_score + num_moves = 1 + guesses: List[List[Union[str, int]]] = [] + print("Guess my combination ...") + secret_combination = int(POSSIBILITIES * random.random()) + answer = possibility_to_color_code(secret_combination) + while True: + print(f"Move # {num_moves} Guess : ") + user_command = input("Guess ") + if user_command == "BOARD": + print_board(guesses) # 2000 + elif user_command == "QUIT": # 2500 + print(f"QUITTER! MY COMBINATION WAS: {answer}") + print("GOOD BYE") + quit() + elif len(user_command) != NUM_POSITIONS: # 410 + print("BAD NUMBER OF POSITIONS") + else: + invalid_letters = get_invalid_letters(user_command) + if invalid_letters > "": + print(f"INVALID GUESS: {invalid_letters}") + else: + guess_results = compare_two_positions(user_command, answer) + if guess_results[1] == NUM_POSITIONS: # correct guess + print(f"You guessed it in {num_moves} moves!") + human_score = human_score + num_moves + print_score() + return # from human turn, triumphant + else: + print( + "You have {} blacks and {} whites".format( + guess_results[1], guess_results[2] + ) + ) + guesses.append(guess_results) + num_moves += 1 + + if num_moves > 10: # RAN OUT OF MOVES + print("YOU RAN OUT OF MOVES! THAT'S ALL YOU GET!") + print(f"THE ACTUAL COMBINATION WAS: {answer}") + human_score = human_score + num_moves + print_score() + return # from human turn, defeated + + +def computer_turn() -> None: + global computer_score + while True: + all_possibilities = [1] * POSSIBILITIES + num_moves = 1 + print("NOW I GUESS. THINK OF A COMBINATION.") + input("HIT RETURN WHEN READY: ") + while True: + possible_guess = find_first_solution_of(all_possibilities) + if possible_guess < 0: # no solutions left :( + print("YOU HAVE GIVEN ME INCONSISTENT INFORMATION.") + print("TRY AGAIN, AND THIS TIME PLEASE BE MORE CAREFUL.") + break # out of inner while loop, restart computer turn + + computer_guess = possibility_to_color_code(possible_guess) + print(f"My guess is: {computer_guess}") + blacks_str, whites_str = input( + "ENTER BLACKS, WHITES (e.g. 1,2): " + ).split(",") + blacks = int(blacks_str) + whites = int(whites_str) + if blacks == NUM_POSITIONS: # Correct guess + print(f"I GOT IT IN {num_moves} MOVES") + computer_score = computer_score + num_moves + print_score() + return # from computer turn + + # computer guessed wrong, deduce which solutions to eliminate. + for i in range(0, POSSIBILITIES): + if all_possibilities[i] == 0: # already ruled out + continue + possible_answer = possibility_to_color_code(i) + comparison = compare_two_positions( + possible_answer, computer_guess + ) + if (blacks != comparison[1]) or (whites != comparison[2]): + all_possibilities[i] = 0 + + if num_moves == 10: + print("I USED UP ALL MY MOVES!") + print("I GUESS MY CPU IS JUST HAVING AN OFF DAY.") + computer_score = computer_score + num_moves + print_score() + return # from computer turn, defeated. + num_moves += 1 + + +def find_first_solution_of(all_possibilities: List[int]) -> int: + """Scan through all_possibilities for first remaining non-zero marker, + starting from some random position and wrapping around if needed. + If not found return -1.""" + start = int(POSSIBILITIES * random.random()) + for i in range(0, POSSIBILITIES): + solution = (i+start) % POSSIBILITIES + if all_possibilities[solution]: + return solution + return -1 + + # 470 def get_invalid_letters(user_command) -> str: """Makes sure player input consists of valid colors for selected game configuration.""" @@ -172,7 +172,6 @@ def print_board(guesses) -> None: print(f"{idx + 1}\t{guess[0]}\t{guess[1]} {guess[2]}") - def possibility_to_color_code(possibility: int) -> str: """Accepts a (decimal) number representing one permutation in the realm of possible secret codes and returns the color code mapped to that permutation.