import random, sys def main(): global colors, color_letters, num_positions, num_colors, human_score, computer_score colors = ["BLACK", "WHITE", "RED", "GREEN", "ORANGE", "YELLOW", "PURPLE", "TAN"] color_letters = "BWRGOYPT" num_colors = 100 human_score = 0 computer_score = 0 # get user inputs for game conditions print("Mastermind") print('Creative Computing Morristown, New Jersey') while num_colors > 8: num_colors = int(input("Number of colors (max 8): ")) # C9 in BASIC num_positions = int(input("Number of positions: ")) # P9 in BASIC num_rounds = int(input("Number of rounds: ")) # R9 in BASIC possibilities = num_colors**num_positions all_possibilities = [1] * possibilities print("Number of possibilities {}".format(possibilities)) print('Color\tLetter') print('=====\t======') for element in range(0, num_colors): print("{}\t{}".format(colors[element], colors[element][0])) current_round = 1 while current_round <= num_rounds: print('Round number {}'.format(current_round)) num_moves = 1 guesses = [] turn_over = False print('Guess my combination ...') answer = int(possibilities * random.random()) numeric_answer = [-1] * num_positions for i in range(0, answer): numeric_answer = get_possibility(numeric_answer) #human_readable_answer = make_human_readable(numeric_answer) while (num_moves < 10 and not turn_over ): print('Move # {} Guess : '.format(num_moves)) user_command = input('Guess ') if user_command == "BOARD": print_board(guesses) #2000 elif user_command == "QUIT": #2500 human_readable_answer = make_human_readable(numeric_answer) print('QUITTER! MY COMBINATION WAS: {}'.format(human_readable_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("INVALID GUESS: {}".format(invalid_letters)) else: guess_results = compare_two_positions(user_command, make_human_readable(numeric_answer)) print("Results: {}".format(guess_results)) if guess_results[1] == num_positions: # correct guess turn_over = True print("You guessed it in {} moves!".format(num_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("THE ACTUAL COMBINATION WAS: {}".format(make_human_readable(numeric_answer))) human_score = human_score + num_moves print_score() # COMPUTER TURN guesses = [] 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.") player_ready = input("HIT RETURN WHEN READY: ") while (num_moves < 10 and not turn_over and not inconsistent_information): found_guess = False computer_guess = int(possibilities * random.random()) if all_possibilities[computer_guess] == 1: # random guess is possible, use it found_guess = True guess = computer_guess else: for i in range (computer_guess, possibilities): if all_possibilities[i] == 1: found_guess = True guess = i break if not found_guess: for i in range (0, computer_guess): if all_possibilities[i] == 1: found_guess = True 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: numeric_guess = [-1] * num_positions for i in range(0, guess): numeric_guess = get_possibility(numeric_guess) human_readable_guess = make_human_readable(numeric_guess) print('My guess is: {}'.format(human_readable_guess)) blacks, whites = input("ENTER BLACKS, WHITES (e.g. 1,2): ").split(",") blacks = int(blacks) whites = int(whites) if blacks == num_positions: #Correct guess print('I GOT IT IN {} MOVES'.format(num_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 numeric_possibility = [-1] * num_positions for j in range (0, i): numeric_possibility = get_possibility(numeric_possibility) human_readable_possibility = make_human_readable(numeric_possibility) #4000 comparison = compare_two_positions(human_readable_possibility, human_readable_guess) print(comparison) 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() current_round += 1 print_score(is_final_score=True) sys.exit() #470 def get_invalid_letters(user_command): """Makes sure player input consists of valid colors for selected game configuration.""" valid_colors = color_letters[:num_colors] invalid_letters = "" for letter in user_command: if letter not in valid_colors: invalid_letters = invalid_letters + letter return invalid_letters #2000 def print_board(guesses): """Prints previous guesses within the round.""" print("Board") print("Move\tGuess\tBlack White") for idx, guess in enumerate(guesses): print('{}\t{}\t{} {}'.format(idx+1, guess[0], guess[1], guess[2])) #3500 # Easily the place for most optimization, since they generate every possibility # every time when checking for potential solutions # From the original article: # "We did try a version that kept an actual list of all possible combinations # (as a string array), which was significantly faster than this versionn but # which ate tremendous amounts of memory." def get_possibility(possibility): #print(possibility) if possibility[0] > -1: #3530 current_position = 0 # Python arrays are zero-indexed while True: if possibility[current_position] < num_colors-1: # zero-index again possibility[current_position] += 1 return possibility else: possibility[current_position] = 0 current_position += 1 else: #3524 possibility = [0] * num_positions return possibility #4500 def compare_two_positions(guess, answer): """Returns blacks (correct color and position) and whites (correct color only) for candidate position (guess) versus reference position (answer).""" increment = 0 blacks = 0 whites = 0 initial_guess = guess for pos in range(0, num_positions): if guess[pos] != answer[pos]: for pos2 in range(0, num_positions): if not(guess[pos] != answer[pos2] or guess[pos2] == answer[pos2]): # correct color but not correct place whites = whites + 1 answer = answer[:pos2] + chr(increment) + answer[pos2+1:] guess = guess[:pos] + chr(increment+1) + guess[pos+1:] increment = increment + 2 else: #correct color and placement blacks = blacks + 1 # THIS IS DEVIOUSLY CLEVER guess = guess[:pos] + chr(increment+1) + guess[pos+1:] answer = answer[:pos] + chr(increment) + answer[pos+1:] increment = increment + 2 return [initial_guess, blacks, whites] #5000 + logic from 1160 def print_score(is_final_score=False): """Prints score after each turn ends, including final score at end of game.""" if is_final_score: print("GAME OVER") print("FINAL SCORE:") else: print("SCORE:") print(" COMPUTER {}".format(computer_score)) print(" HUMAN {}".format(human_score)) #4000, 5500, 6000 subroutines are all identical def make_human_readable(num): """Make the numeric representation of a position human readable.""" retval = '' for i in range(0, len(num)): retval = retval + color_letters[int(num[i])] return retval if __name__ == "__main__": main()