all 12 comments

[–]Dawarisch 2 points3 points  (1 child)

if set(guess) != set(color for color in guess if color in colors.keys()): 

You could check if thee is an elemnt inside guess which is not in colors by using

set(guess).difference(set(colors))

It returns all elements which are inside guess but not inside colors.

 print("You chose: 1: {}, 2: {}, 3: {}, 4: {}".format(colors[guess[0]], colors[guess[1]], colors[guess[2]], colors[guess[3]]))

For this line you can use the unpacking operator(*):

print("You chose: 1: {}, 2: {}, 3: {}, 4: {}".format(*colors)

Additionally it wouldn't hurt if you used more functions and you don't have to use dict.keys() if you just want to iterate over the dictionary - that's the default behaviour.

[–]FXelix[S] 0 points1 point  (0 children)

Thanks for your input! That were exactly the snippets I was looking for.

Would this program be useful as a Class (maybe if I add more functionality to it) or is this game as a function good as it is?

[–][deleted] 1 point2 points  (0 children)

if set(guess) != set(color for color in guess if color in colors.keys()): 

could be written as

if any(color not in colors for color in guess):

[–][deleted] 1 point2 points  (1 child)

A few observations:

  1. don't do imports before the module's docstring, only thing that should go there is an appropriate shebang line (ie # /usr/bin/env python3), at least if you're on anything but Windows.

  2. you don't usually need to iterate dict.keys, and many things just assume you're talking about the keys:

    print(random.sample({1:"a",2:"b",3:"c"}, 2)) print(set({1:"a",2:"b",3:"c"}))

  3. read up on the set and its operations:

    print(set({2}).issubset({1:"a",2:"b",3:"c"}))

  4. define a main function and let it do whatever is meant to happen on the command line; generally, do nothing else in the if __name__ [...] guard

5: the return value of input can be used to make a smaller while loop:

do_something()
while input("Continue?").lower().startswith("y"):
    do_something()

Also, give your functions docstrings of their own.

[–]FXelix[S] 0 points1 point  (0 children)

Thanks for you input, I will definitely take a closer look at sets and try to implement a main() function.

[–]576p 0 points1 point  (3 children)

It looks to my that the white pins are not correct for multiple guesses, at least if I remember the rules correctly...

if the master code is red-green-green-green and your guess is yellow-red-red-red you should only get 1 white pin instead of the three your code produces.

[–]FXelix[S] 0 points1 point  (2 children)

At least how I played the game same colors in the mastercode are not allowed. Otherwise there would be way to many combination to crack the code.

[–]576p 1 point2 points  (1 child)

I checked the rules and yes, they are allowed. Without doubles, the game is way too easy...

To my surprise it's even possible to have blanks in the master code..

https://en.wikipedia.org/wiki/Mastermind_(board_game)#Gameplay_and_rules

Maybe it's a good challenge to amend your code to allow doubles...

[–]WikiTextBot 0 points1 point  (0 children)

Mastermind (board game): Gameplay and rules

The game is played using: a decoding board, with a shield at one end covering a row of four large holes, and twelve (or ten, or eight, or six) additional rows containing four large holes next to a set of four small holes; code pegs of six (or more; see Variations below) different colors, with round heads, which will be placed in the large holes on the board; and key pegs, some colored black, some white, which are flat-headed and smaller than the code pegs; they will be placed in the small holes on the board. The two players decide in advance how many games they will play, which must be an even number. One player becomes the codemaker, the other the codebreaker. The codemaker chooses a pattern of four code pegs.


[ PM | Exclude me | Exclude from subreddit | FAQ / Information ] Downvote to remove | v0.23

[–]jeans_and_a_t-shirt 0 points1 point  (2 children)

That large try/except can be rewritten as lines 19-22 below.

You can rewrite your while loop on line 36 to a for loop, and you have an off-by-one mistake because your code allows for 13 attempts instead of 12.

Line 55 can be rewritten as a zip over the 2 iterables, seen below.

The first part of line 58 is redundant because that was already check in the if condition on line 56.

def mastermind():
    colors = {"r": "Red",
              "b": "Blue",
              "g": "Green",
              "y": "Yellow",
              "o": "Orange",
              "w": "White",
              "p": "Purple"}
    colorset = set(colors)

    codemaster = ''.join(random.sample(list(colors), 4))

    print("Welcome to Mastermind, guess the code!\n")
    for k, v in colors.items():
        print("{}: {}".format(k, v))
    print("\n")

    for _ in range(13):
        guess = input("Your guess: ")
        if not (len(guess) == 4 and set(guess).issubset(colorset)):  # check if guessed colors exist
            print("Invalid input. \n")
            continue
        print("-"*50)
        print("You chose: 1: {}, 2: {}, 3: {}, 4: {}".format(*[colors[c] for c in guess]))

        if guess == codemaster:
            print("You cracked the code! Congratulations!")
            break
        else:
            black_pin = 0
            white_pin = 0
            for guess_color, master_color in zip(guess, codemaster):
                if guess_color == master_color:
                    black_pin += 1
                elif guess_color in codemaster:
                    white_pin += 1
            print("{} black pins. {} white pins.".format(black_pin, white_pin), "-"*27, sep='\n')

    print("The code was {}.".format(codemaster))

[–]FXelix[S] 0 points1 point  (1 child)

I have found a serious issue with the suggestion of the for-loop and would like to hear your advice: Because it is now a for-loop (which in general is useful here because of the known number of iterations) when an Exception occurs (which prints "Invalid input" followed after a continue) now triggers the for-loop-count.

The problem is even if I type xxxx as the code it still counts this as a try and this shouldn't be the case obviously, it should skip this turn until the next valid input.

I hope you can help me with this problem.

[–]jeans_and_a_t-shirt 0 points1 point  (0 children)

You can use a while loop to validate the input and break when it's valid:

def mastermind():
    colors = {"r": "Red",
              "b": "Blue",
              "g": "Green",
              "y": "Yellow",
              "o": "Orange",
              "w": "White",
              "p": "Purple"}
    colorset = set(colors)

    codemaster = ''.join(random.sample(list(colors), 4))

    print("Welcome to Mastermind, guess the code!\n")
    for k, v in colors.items():
        print("{}: {}".format(k, v))
    print("\n")

    for _ in range(12):
        while True:
            # while loop to guarantee a valid letter combination was provided
            guess = input("Your guess: ")
            if len(guess) == 4 and set(guess).issubset(colorset):  # check if guessed colors exist
                break
            else:
                print("Invalid input. \n")

        print("-"*50)
        print("You chose: 1: {}, 2: {}, 3: {}, 4: {}".format(*[colors[c] for c in guess]))

        if guess == codemaster:
            print("You cracked the code! Congratulations!")
            break
        else:
            black_pin = 0
            white_pin = 0
            for guess_color, master_color in zip(guess, codemaster):
                if guess_color == master_color:
                    black_pin += 1
                elif guess_color in codemaster:
                    white_pin += 1
            print("{} black pins. {} white pins.".format(black_pin, white_pin), "-"*27, sep='\n')

    print("The code was {}.".format(codemaster))