diff --git a/60 Mastermind/python/mastermind.py b/60 Mastermind/python/mastermind.py index adb62be3..ccc2ae9c 100644 --- a/60 Mastermind/python/mastermind.py +++ b/60 Mastermind/python/mastermind.py @@ -1,67 +1,66 @@ -import random +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 + human_score = 0 computer_score = 0 # get user inputs for game conditions print("Mastermind") print('Creative Computing Morristown, New Jersey') - while (num_colors > 8): + while num_colors > 8: num_colors = int(input("Number of colors (max 8): ")) # C9 in BASIC - # TODO no more than 8 colors 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)) - #TODO tab fixed formatting - print('Color Letter') - print('===== ======') + print('Color\tLetter') + print('=====\t======') for element in range(0, num_colors): - print("{} {}".format(colors[element], colors[element][0])) - - num_moves = 1 - guesses = [] + 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()) # A in BASIC - numeric_answer = init_possibility() + answer = int(possibilities * random.random()) + numeric_answer = [-1] * num_positions for i in range(0, answer): numeric_answer = get_possibility(numeric_answer) - #debug - print("{} - {}".format(numeric_answer, make_human_readable(numeric_answer))) #human_readable_answer = make_human_readable(numeric_answer) - while (num_moves < 10 and not(turn_over)): + 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": - quit_game(numeric_answer) #2500 + 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 > ""): + 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 + 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 @@ -70,150 +69,148 @@ def main(): 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 + 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 - all_possibilities = [1] * possibilities - num_moves = 1 - print ("NOW I GUESS. THINK OF A COMBINATION.") - player_ready = input("HIT RETURN WHEN READY: ") - while (num_moves < 10 and not(turn_over)): - foundGuess = False - computer_guess = int(possibilities * random.random()) - if (all_possibilities[computer_guess] == 1): # random guess is possible, use it - foundGuess = True - guess = computer_guess - else: - for i in range (computer_guess, possibilities): - if (all_possibilities[i] == 1): - foundGuess = True - guess = i - break - if (not(foundGuess)): - for i in range (0, computer_guess): - if (all_possibilities[i] == 1): - foundGuess = True + 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(foundGuess)): # inconsistent info from user - print('YOU HAVE GIVEN ME INCONSISTENT INFORMATION.') - print('TRY AGAIN, AND THIS TIME PLEASE BE MORE CAREFUL.') - quit() - # TODO start computer turn over - else: - numeric_guess = init_possibility() - 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)) + 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 - computer_score = computer_score + num_moves - print_score() + inconsistent_information = True else: - num_moves += 1 - for i in range (0, possibilities): - if(all_possibilities[i] == 0): #already ruled out - continue - numeric_possibility = init_possibility() - 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("{} {} {}".format(human_readable_guess, human_readable_possibility, 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() + 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): - #TODO - return "" - -def validate_human_guess(user_input): - - guess_results = compare_two_positions(user_input, answer) - return guess_results + """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 Guess Black White") - for guess in guesses: - print('{} {} {} {}'.format(guess[0], guess[0], guess[1], guess[2])) - -#2500 -def quit_game(numeric_answer): - human_readable_answer = make_human_readable(numeric_answer) - print('QUITTER! MY COMBINATION WAS: {}'.format(human_readable_answer)) - print('GOOD BYE') - -#3000 -def init_possibility(): - possibility = [0] * num_positions - return possibility + 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): - if possibility[0] > 0: #3530 - current_position = 0 # Python arrays are zero-indexed - while (True): - if possibility[current_position] < num_colors: - possibility[current_position] += 1 - return possibility - else: - possibility[current_position] = 1 - current_position += 1 + #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 = [1] * num_positions + possibility = [0] * num_positions return possibility -#4000 -def convert_q_to_a(): - return map_num_to_vals(q, color_letters) - #4500 def compare_two_positions(guess, answer): - f = 0 - b = 0 - w = 0 + """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]): + 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 - w = w + 1 - answer = answer[:pos2] + chr(f) + answer[pos2+1:] - guess = guess[:pos] + chr(f+1) + guess[pos+1:] - f = f + 2 + 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 - b = b + 1 + blacks = blacks + 1 # THIS IS DEVIOUSLY CLEVER - guess = guess[:pos] + chr(f+1) + guess[pos+1:] - answer = answer[:pos] + chr(f) + answer[pos+1:] - f = f + 2 - return [initial_guess, b, w] - - - - - -#5000 + 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): - if (is_final_score): + """Prints score after each turn ends, including final score at end of game.""" + if is_final_score: print("GAME OVER") print("FINAL SCORE:") else: @@ -221,33 +218,13 @@ def print_score(is_final_score=False): print(" COMPUTER {}".format(computer_score)) print(" HUMAN {}".format(human_score)) -#5500 -def convert_q_to_g(): - return map_num_to_vals(q, g) - -#6000 -def convert_q_to_h(): - return map_num_to_vals(q, h) - -#6500 -def copy_g_to_h(): - g = h - +#4000, 5500, 6000 subroutines are all identical def make_human_readable(num): + """Make the numeric representation of a position human readable.""" retval = '' - for z in range(0, len(num)): - retval = retval + color_letters[int(num[z])] - return retval - - -def map_num_to_vals(num, v): - retval = '' - print(len(num)) - for z in range(0, len(num)): - print(num[z]) - print(v[int(num[z])]) - retval = retval + v[int(num[z])] + for i in range(0, len(num)): + retval = retval + color_letters[int(num[i])] return retval if __name__ == "__main__": - main() \ No newline at end of file + main()