I am in the process of writing and debugging a Python version of Mastermind. I have to use classes, so I have them all in their own files. I have them called and such. My problem now is that a part of the code likes to repeat itself ad infinitum. Yikes!

Possible colors (enter first letter): [r]ed [g]reen lue [p]urple [o]range [y]ellow
Black pegs: 0
White pegs: 0

That is what is repeated forever until I ctrl+c the program. It only starts repeating after I have entered the four colors for the first guess.

My main code is:

import random
from generateSecretColors import *
from colors import *
from guess import *
from exactMatch import *
from partialMatch import *


	
def main():
	allColors = Colors()
	allColors.colors()
	secret = SecretColors()
	secret.generateSecretColors()
	guess = Guess()
	secretCode = secret.returnSecret()
	exactMatchCount = ExactMatch()
	partialMatchCount = PartialMatch()
	
	#print "Secret Code - for testing - MAKE SURE TO REMOVE!" + str(secretCode)
	exact = 0
	while (exact != 4):
		userGuess = guess.askForGuess()
		exact = exactMatchCount.exactMatch(userGuess,secretCode)
		partial = partialMatchCount.partialMatch(userGuess,secretCode)
		print "Black pegs: " + str(exact)
		print "White pegs: " + str(partial)
		
	if guess == secretCode:
		print "You got it! Good job!"

if __name__=='__main__':
	main()

I don't know if you can fix it from just that. If you need to see the classes, let me know and I will fix them. Thanks for the help!

Recommended Answers

All 16 Replies

Well, I would say it is because you do not wait for user input.

Anyway, this game not so complicated to implement. No need to separate files and so on...
To prove my opinion I made one, in 10 minutes. Most of the time went debugging the match_guess method. It is not the most efficient algo.

import random

class InvalidMove(Exception):pass

class Game:
    def __init__(self): 
        self.colors=('r','g','b','o','p','y')
        self.to_guess=[random.choice(self.colors) for i in range(4)] # the secret to guess

    def match_guess(self,guess):
        if len(guess)!=len(self.to_guess) or [g for g in guess if g not in self.colors]:
            raise InvalidMove()
        ret=[0,0] # first, black peg: good color good place, second white peg: good 
        usedindexes=[] # which place has already given a peg
        for i,g in enumerate(guess):
            if g==self.to_guess[i]:
                ret[0]+=1
                usedindexes.append(i)
        for i,g in enumerate(guess):
            if i in usedindexes: continue
            for j,c in enumerate(self.to_guess):
                if c==g and j not in usedindexes:
                    ret[1]+=1
                    usedindexes.append(j)
        return ret            
    
class UI:
    def make_move(self): 
        guess=raw_input("Guess: ")
        return guess.split()

    def main(self):
        print("The gem begins")
        print("Possible colors (enter first letter): [r]ed [g]reen [b]lue [p]urple [o]range [y]ellow")
        print("Enter your guess like: r g b o")
        g=Game()
        while True:
            guess=self.make_move()
            try:
                bp,wp=g.match_guess(guess)
            except InvalidMove:
                print("Invalid guess, try again")
                continue
            print("Black pegs %s"%bp)
            print("White pegs %s"%wp)
            if bp==4:
                print("You won!")
                break

if __name__=="__main__":
    u=UI()
    u.main()

You want to add a print statement to see what is going on

exact = 0
            while (exact != 4):
               userGuess = guess.askForGuess()

               exact = exactMatchCount.exactMatch(userGuess,secretCode)
               print "exact =", exact
               if exact != 4:
                  print "Another while loop is necessary\n"

               partial = partialMatchCount.partialMatch(userGuess,secretCode)
               print "Black pegs: " + str(exact)
               print "White pegs: " + str(partial)

Also, this code should produce an error since there is no partialMatchCount class declared or imported.

Thanks for all the help, but I figured it out! In class Guess, the count for the loop to ask for a guess four times was not being reset to zero after the first four guesses. Thanks for all the help, though.

Slave - Thanks for writing another Mastermind-like program. However, there are some things in there that I never learned in my computer science class, so I don't know exactly what they are doing.

Also, I have it broken up into so many files because my teacher wants it done this way. We are just learning about classes.

Of course, fix something, something else breaks.

It doesn't seem to be recording matches and partial matches as well as I had hoped (either, it doesn't pick up on them or it keeps the matches stored through the whole game, saying something like there are 2 matches when there should be zero matches.)

The code for the partial match class and exact match class:

'''
partialMatch.py

class
'''

class PartialMatch:
	def __init__(self):
		self.count1 = 0
		self.count2 = 0
		self.countPartialMatches = 0
		
	def partialMatch(self,guess,secretCode):
		while (self.count1 < 4):
			while (self.count2 < 4):
				if (guess[self.count1] == secretCode[self.count2]):
					self.countPartialMatches = self.countPartialMatches + 1
					# cross out so doesn't match again
					guess[self.count1] = 'X'
					secretCode[self.count2] = 'Y'
				self.count2 = self.count2 + 1
			self.count1 = self.count1 + 1
		return self.countPartialMatches

'''
exactMatch.py

class
'''

class ExactMatch:
	def __init__(self):
		self.count = 0
		self.countExactMatches = 0
		
	def exactMatch(self,guess,secretCode):
		while (self.count < 4):
			if guess[self.count] == secretCode[self.count]:
				self.countExactMatches = self.countExactMatches + 1
				# cross out so doesn't match again
				guess[self.count] = 'X'
				secretCode[self.count] = 'Y'
			self.count = self.count + 1
		return self.countExactMatches

I am really bad at classes, fyi.

Thanks for the help.

Secret Code - for testing - MAKE SURE TO REMOVE!
Possible colors (enter first letter): [r]ed [g]reen lue [p]urple [o]range [y]ellow
Enter a color: b
Enter a color: b
Enter a color: b
Enter a color: b

Black pegs: 1
White pegs: 0
Possible colors (enter first letter): [r]ed [g]reen lue [p]urple [o]range [y]ellow
Enter a color: g
Enter a color: g
Enter a color: g
Enter a color: g

Black pegs: 1
White pegs: 0
Possible colors (enter first letter): [r]ed [g]reen lue [p]urple [o]range [y]ellow
Enter a color: b
Enter a color: g
Enter a color: o
Enter a color: p

Black pegs: 1
White pegs: 0
Possible colors (enter first letter): [r]ed [g]reen lue [p]urple [o]range [y]ellow
Enter a color:

An example of what is going wrong. Even the correct answer doesn't register. Ahhh. ><

It's just personal preference, but I would use sets for the partial match, and compare two sorted lists for the exact match. Using existing methods that are tested is always a better option. If I understand what you are doing, the following code should illustrate.

def exact_match(guess, secretCode):
   guess_list = [x for x in guess.lower()]
   secret_list = list( secretCode.lower() )
   guess_list.sort()
   secret_list.sort()
   if guess_list == secret_list:
      return True
   return False

def partial_match(guess, secretCode):
   guess_set = set(guess.lower())
   secret_set = set(secretCode.lower())
   intersection_set = guess_set.intersection(secret_set)
   return len(intersection_set)



   
test_list = [ ("ABCDE", "de"),
              ("ABCDE", "ecbad"),
              ("ABCDE", "FGH") ]
for each_test in test_list:
   found = partial_match(each_test[0], each_test[1])
   if found:
      print "partial match for", found, "letters",
   else:
      print "no partial match for", 
   print each_test[0], each_test[1]

   found = exact_match(each_test[0], each_test[1])
   if found:
      print "exact match for", 
   else:
      print "no exact match for", 
   print each_test[0], each_test[1]
   print "---------------------------------------------------"

It's just personal preference, but I would use sets for the partial match, and compare two sorted lists for the exact match. Using existing methods that are tested is always a better option. If I understand what you are doing, the following code should illustrate.

def exact_match(guess, secretCode):
   guess_list = [x for x in guess.lower()]
   secret_list = list( secretCode.lower() )
   guess_list.sort()
   secret_list.sort()
   if guess_list == secret_list:
      return True
   return False
   
def partial_match(guess, secretCode):
   guess_set = set(guess.lower())
   secret_set = set(secretCode.lower())
   intersection_set = guess_set.intersection(secret_set)
   return len(intersection_set)

##   you could also use difference and test
##   for length > 0
##   difference_set = guess_set.difference(secret_set)

   
test_list = [ ("ABCDE", "de"),
              ("ABCDE", "ecbad"),
              ("ABCDE", "FGH") ]
for each_test in test_list:
   found = partial_match(each_test[0], each_test[1])
   if found:
      print "partial match for", 
   else:
      print "no partial match for", 
   print each_test[0], each_test[1]

   found = exact_match(each_test[0], each_test[1])
   if found:
      print "exact match for", 
   else:
      print "no exact match for", 
   print each_test[0], each_test[1]
   print "---------------------------------------------------"

Thanks for the help. I am a little confused as to what in that code goes where in my files? I have partial matches and exact matches in separate files. And my main code where I import all these classes is in a completely different file. Could you clarify where I would put your suggestions? Thanks a bunch.

Thanks for all the help, but I figured it out! In class Guess, the count for the loop to ask for a guess four times was not being reset to zero after the first four guesses. Thanks for all the help, though.

Slave - Thanks for writing another Mastermind-like program. However, there are some things in there that I never learned in my computer science class, so I don't know exactly what they are doing.

Also, I have it broken up into so many files because my teacher wants it done this way. We are just learning about classes.

Just caught my typo... That should be Slate. Sorry!

No problem with my name error.

I am sad, my code could not speak for myself, so I try to write down my thoughts on that.

I am in the process of writing and debugging a Python version of Mastermind. I have to use classes, so I have them all in their own files. I have them called and such.

IMHO you do not use classes just to use classes, l'art pour l"art. You use it, because it reflects a way the designer can see the problem.

In my design there are two main objects in this game:
The Game object is responsible:

  • Generation of the thing to guess
  • Give information, if a guess is valid and how much

The UserInterface object is responsible:

  • Game flow
  • Handling the user input
  • Getting a game object and interface with it

This design has the advantage of separating the game's inner logic and the user interface. For example you can write an AI as a user interface, or a graphical one.

So any guessing game, e.g. Memory, MasterMind, Battleship, Hangman etc can be written with practically the same code pattern. Only minor changes are needed.

class Game:
    def __init__(self, params):
        #initialize begin phase
    def make_move(self,params):
        # check if this is a valid guess
        # update status
    def status(self):
        # give back the current status of the game

class UI:
    def __init__(self,params):
        #initialize user interface
    def main(self):
        # the main loop
        # creating game object(s)
        self.g=Game(self.params)
        while True:
            # get user input
            try:
                self.g.make_move(user_input)
            except:
                #handle invalid move
            status=self.g.status()
            # render board according to status
            # check game end, and handle it


if __name__=="__main__":
    # read in settings, or anything systemwide
    u=UI()
    u.main(settings)

If you do not separate concerns in your design, then there is no use of classes or modules. I do not see this separation in your code.

You practically use functions renamed to classes. For example the exactMatchCount and partialMatchCount classes are only once instanciated and have only one method.

I am aware, that you did not ask for helping in the design, but IMHO this is a more important issue, than to get a counting loop working.

No problem with my name error.

I am sad, my code could not speak for myself, so I try to write down my thoughts on that.


IMHO you do not use classes just to use classes, l'art pour l"art. You use it, because it reflects a way the designer can see the problem.

In my design there are two main objects in this game:
The Game object is responsible:

  • Generation of the thing to guess
  • Give information, if a guess is valid and how much

The UserInterface object is responsible:

  • Game flow
  • Handling the user input
  • Getting a game object and interface with it

This design has the advantage of separating the game's inner logic and the user interface. For example you can write an AI as a user interface, or a graphical one.

So any guessing game, e.g. Memory, MasterMind, Battleship, Hangman etc can be written with practically the same code pattern. Only minor changes are needed.

class Game:
    def __init__(self, params):
        #initialize begin phase
    def make_move(self,params):
        # check if this is a valid guess
        # update status
    def status(self):
        # give back the current status of the game

class UI:
    def __init__(self,params):
        #initialize user interface
    def main(self):
        # the main loop
        # creating game object(s)
        self.g=Game(self.params)
        while True:
            # get user input
            try:
                self.g.make_move(user_input)
            except:
                #handle invalid move
            status=self.g.status()
            # render board according to status
            # check game end, and handle it


if __name__=="__main__":
    # read in settings, or anything systemwide
    u=UI()
    u.main(settings)

If you do not separate concerns in your design, then there is no use of classes or modules. I do not see this separation in your code.

You practically use functions renamed to classes. For example the exactMatchCount and partialMatchCount classes are only once instanciated and have only one method.

I am aware, that you did not ask for helping in the design, but IMHO this is a more important issue, than to get a counting loop working.

Thanks for your post. What I meant was there were pieces of Python code which I had never seen before.

As for classes, I suck at them to be honest. But we are required to use them for the class I am in.

Also, I guess I would understand your code better/it would be more helpful if I knew certain pieces of code that you used. Like I have no idea what self.params is.

Well, I figured out what self.params is. But like try/except, I have never used. Also, enumerate.

Ok, I decided to rewrite everything. I based it off the previous code I had written for the simple version of Mastermind called Bagels (numbers, not colors).

The error I get now (the only changes I made were switching from numbers to letters and changing variable names. However, I switched to letters before variable name changes and it worked fine. This error only popped up after I changed variable names) is:

Traceback (most recent call last):
File "mastermind.py", line 58, in <module>
main()
File "mastermind.py", line 45, in main
print "Pegs: " + hint.hints()
File "/Users/huntermartin/Desktop/Mastermind/hints.py", line 18, in hints
for i in range(len(self.guess)):
AttributeError: Colors instance has no attribute '__len__'

from createSecretCode import *
from hints import *
from colors import *

def main():
	print "-------------------------------------------------------------------"
	print "Welcome to a game of Mastermind!"
	print "You will guess the secret code made up of 5 colors."
	print "Color choices: [b]lue [o]range [y]ellow [r]ed [p]urple [g]reen"
	print "Enter your guess like this: byrgp"
	print "That will guess blue, yellow, red, green, purple in that order."
	print "Your goal is to match the colors and positions of the colors to"
	print "that of the secret code"
	print "Your hints are black or white pegs"
	print "Black pegs mean that a color is in the correct position"
	print "White pegs mean that a color is correct, but in the wrong position"
	print "You have 10 guesses"
	print "Good luck!"
	print "-------------------------------------------------------------------"
	while True:
		game = CreateSecretCode()
		game.createSecretCode(5)
		colorCode = game.secretCode
		colors = Colors()
		numGuesses = 1
		maxGuesses = 10

		while numGuesses <= maxGuesses:
			guess = ""
			while len(guess) != game.createSecretCode(5) or not colors.checkColors(guess):
				print "Guess #" + str(numGuesses) + ":"
				guess = raw_input()
			numGuesses = numGuesses + 1
			hint = Hints(guess, colors)
			#problem when secret code has two of a color in it and user only guesses that color once. game will report multiple white/black for that
			print "Pegs: " + hint.hints()
			if guess == colors:
				break
			if numGuesses > maxGuesses:
				print "You ran out of guesses. The secret color code was " + str(number) + "."
				break
				
		continueGame = raw_input("Want to play again? (yes or no) ")
		if continueGame == "no" or continueGame == "NO" or continueGame == "No":
			print "Thanks for playing!"
			break
	
if __name__=='__main__':
	main()

The classes:

class Colors:
		def __init__(self):
			self.checkReturn = ""
			self.colorRange = "r g b p o y"
			
		def checkColors(self, guess):
				# Returns True if num is a string made up only of correct color choices. Otherwise returns False.
			if guess == self.checkReturn:
				return False
			for i in guess:
				if i not in self.colorRange.split():
					return False

			return True

import random

class CreateSecretCode:
	def __init__(self):
		self.secretCode = ""

	def createSecretCode(self, numColors):
		for i in range(numColors):
			self.secretCode = self.secretCode + random.choice("r g b p o y".split())
		return numColors
		
	def secretCode(self):
		return self.secretCode

class Hints:
	def __init__(self, secretCode, guess):
		self.guess = guess
		self.secretCode = secretCode

	def hints(self):
		if self.guess == self.secretCode:
			return "Wow, you got it! Well done!"
		#Creates a list for the hints
		hint = []
		for i in range(len(self.guess)):
			if self.guess[i] == self.secretCode[i]:
				hint.append("Black")
			elif self.guess[i] in self.secretCode:
				hint.append("White")
		#If no numbers in the guess are correct, there are no hints given
		if len(hint) == 0:
			return ""
		hint.sort()
		return " ".join(hint)

I can't figured out why it doesn't like len just because I changed variable names...

you make a Hints instance with the call:
hint = Hints(guess, colors)

So hint.guess will be colors and hint.secretCode will be guess.
Obviously you mean the other way around.

When you try to get the Color instance's len, then python tries to find a __len__ method of the of the object.

Yeah, I figured that out. Thanks though. I also got a couple variables mixed up. But it pretty much works now. Just one little problem with "white" pegs, but I am in the process of working it out.

Thanks for all the help. I can post my finished game here if anyone wants to see it.

I was wondering if there is a way to clear the terminal window using Python on a Mac. I am going to be setting up a player vs. player option where one person enters the secret code and the other guesses it. Is there a way to clear the terminal so the guessing player can't see the secret code?

I think this should work

import os

# windows
os.system("cls")

# bash ( mac, linux )
os.system("clear")
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.