![]() |
|
![]() |
|
|
Thread Tools | Display Modes |
|
|
#1 |
|
The Oblivious One
Join Date: May 2005
Location: Ontario, Canada
Posts: 644
Rep Power: 4
![]() |
[Python] Simple Hangman game
I'm still learning and experimenting with Python, and in the process, I made a little hangman game.
As I am still new to Python, comments would be welcome. Here is the source:
#!/usr/bin/python
class Hangman:
def __init__(self, word):
self.word = word
self.progress = '_' * len(word)
self.guessed = []
self.attempts = 0
self.chances = 10
def guess_letter(self, letter):
"""Guess a letter
Guess a letter towards the word. Returns True on a good guess, and False
otherwise
"""
self.guessed.sort()
letter = letter.lower()
if len(letter) != 1:
raise ValueError("Input must be a single letter")
if letter in self.guessed:
print "You already guessed the letter \'%s\'" % letter
return False
else:
self.guessed.append(letter) #add the letter to the list of guessed letters
self.attempts += 1 #increment the number of attempts made
if letter in self.word:
#with word: "joe", would create [(j, 0), (o, 1), (e, 2)]
positions = [(l, p) for l, p in zip(self.word, range(len(self.word)))]
#x cycles through the positions of the letters that match the word
for x in [p for l, p in positions if l == letter]:
#add the letter to self.progess
if x == 0:
self.progress = letter + self.progress[1:]
else:
self.progress = self.progress[:x] + letter + self.progress[x + 1:]
return True
else:
self.chances -= 1
return False
def check_winner(self):
"""Check for a winner
Returns True if there is a winner, False otherwise
"""
return self.word == self.progress
def main():
import random
import sys
try:
words = open("words.txt", 'r')
count = 0
for line in words:
count += 1
words.seek(0)
wordlist = words.readlines()
word = wordlist[random.randint(1, count)] #fetch a random word from the word file
word = word[:len(word) - 1] #get rid of space at the end of the word
except IOError, ioe:
print "Necessary file \"words.txt\" not found."
sys.exit()
else:
h = Hangman(word)
while h.chances > -1:
try:
print "\n\n\t%s" % h.progress
if h.check_winner():
print "Excellent! You guessed the secret word in %d attempts!" % h.attempts
sys.exit()
print "\nGuessed letters", h.guessed
print "Bad guesses left: ", h.chances
letter = raw_input("\n\tEnter letter to guess: ")
if h.guess_letter(letter):
print "\nGood guess!"
continue
else:
print "\nBad Guess. Try again."
except ValueError, ve:
print ve
continue
print "\n\nYou ran out of guesses! The word was \"%s\"" % h.word
sys.exit()
if __name__ == "__main__":
main()And the words.txt file which the program gets its words from: http://hakuch.tripod.com/jesse/words.txt
__________________
Dr. Zoidberg: [ecstatic] I'm going to a movie... with FRIENDS! |
|
|
|
|
|
#2 | |
|
Programming Guru
![]() Join Date: Aug 2005
Location: England
Posts: 1,499
Rep Power: 5
![]() |
Quote:
For instance, take the file loading: words = open("words.txt", 'r')
count = 0
for line in words:
count += 1
words.seek(0)
wordlist = words.readlines()
word = wordlist[random.randint(1, count)] #fetch a random word from the word file
word = word[:len(word) - 1] #get rid of space at the end of the wordThe functions random.choice and string.strip can also cut down your workload: random.choice will select an item at random from a list, and str.strip will strip a word of beginning and ending whitespace. With this in mind, your above code can be written: file = open("words.txt")
word = random.choice(file.readlines()).strip()
file.close()self.guessed.append(letter) #add the letter to the list of guessed letters
self.attempts += 1 #increment the number of attempts made
if letter in self.word:
#with word: "joe", would create [(j, 0), (o, 1), (e, 2)]
positions = [(l, p) for l, p in zip(self.word, range(len(self.word)))]
#x cycles through the positions of the letters that match the word
for x in [p for l, p in positions if l == letter]:
#add the letter to self.progess
if x == 0:
self.progress = letter + self.progress[1:]
else:
self.progress = self.progress[:x] + letter + self.progress[x + 1:]
return True
else:
self.chances -= 1
return FalseSecondly, you don't need self.attempts when you have len(self.guessed). And finally, if self.progess was a list, you'd have things a bit easier ![]() self.guessed.append(letter)
if letter in self.word:
for p, l in enumerate(self.word):
if l == letter:
self.progress[p] = letter
return True
else:
self.chances -= 1
return False"".join(self.progress) if len(letter) != 1:
raise ValueError("Input must be a single letter")if letter in self.guessed:
print "You already guessed the letter \'%s\'" % letter
return Falseclass RepeatError(Exception):
"Raised when a user enters a letter twice."![]() |
|
|
|
|
|
|
#3 |
|
The Oblivious One
Join Date: May 2005
Location: Ontario, Canada
Posts: 644
Rep Power: 4
![]() |
Thanks for the great advice Arevos. I'll definitely be using it.
![]()
__________________
Dr. Zoidberg: [ecstatic] I'm going to a movie... with FRIENDS! |
|
|
|
|
|
#4 |
|
The Oblivious One
Join Date: May 2005
Location: Ontario, Canada
Posts: 644
Rep Power: 4
![]() |
I took the advice of Arevos and made a few changes.
Enjoy! (its actually kind-of fun :p) Note: change the directory on line 89 to suit your system, or the program will not work. and the location of words.txt is given at the beginning of this thread.
#!/usr/bin/python
#Copyright 2006 H-K, Jesse E.
#
#This program is free software; you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation; either version 2 of the License, or
#(at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
class Hangman:
"""A game of Hangman
This class provides the means to play a simple
game of hangman. The game is text-based, where properties
such as a list of bad letters, and the number of guesses
are supplied by this class
"""
class RepeatLetterError(ValueError):
""""Raised when a repeat letter is guessed"""
def __init__(self, what):
self.message = str(what)
def __str__(self):
return self.message
def __init__(self, word):
self.word = word
self.progress = ['_' for x in range(len(self.word))]
self.guessed = []
self.chances = 8
def guess_letter(self, letter):
"""Guess a letter
Guess a letter towards the word. Returns True on a good guess, and False
otherwise
Raises:
ValueError --> when input is not a single letter.
Hangman.RepeatLetterError --> when letter has been previously guessed.
"""
letter = letter.lower()
if len(letter) != 1:
raise ValueError("Input must be a single letter.")
if letter in self.guessed:
raise Hangman.RepeatLetterError("You already guessed the letter \'%s\'." % letter)
else: #no problems with input...
self.guessed.append(letter) #add the letter to the list of guessed letters
self.guessed.sort(); #sort the guessed letters alphabetically
if letter in self.word:
for p, l in enumerate(self.word): #word = "apple": [(0, a), (1, p), (2, p), (3, l), (4, e)]
if l == letter:
self.progress[p] = l
return True #good guess
else: #the guessed letter is not in the word...
self.chances -= 1 #decrement the number of bad guesses left
return False #bad guess
def check_winner(self):
"""Check for a winner
Returns True if there is a winner, False otherwise
"""
return self.word == ("".join(self.progress))
def main():
import random
import sys
try:
words = open("/home/jesse/bin/hangman-files/words.txt", 'r')
word = (random.choice(words.readlines())).strip()
except IOError, ioe:
print "Necessary file \"words.txt\" not found."
sys.exit()
else:
h = Hangman(word)
while h.chances > -1:
try:
print "\n\n\t\t\t%s" % " ".join(h.progress)
if h.check_winner():
print "\nExcellent! You guessed the secret word with %d chances left!" % h.chances
sys.exit()
print "\n%20s" % "Bad letters:" , h.guessed
print "\n%20s" % "Bad guesses left:", h.chances
letter = raw_input("\n\tEnter letter to guess: ")
if h.guess_letter(letter):
print "\nGood guess!"
continue
else:
print "\nBad Guess. Try again."
except ValueError, ve:
print ve
continue
except Hangman.RepeatLetterError, rle:
print rle
continue
print "\n\nYou ran out of guesses! The word was \"%s\"" % h.word
sys.exit()
if __name__ == "__main__":
main()
__________________
Dr. Zoidberg: [ecstatic] I'm going to a movie... with FRIENDS! |
|
|
|
|
|
#5 |
|
Programming Guru
![]() Join Date: Aug 2005
Location: England
Posts: 1,499
Rep Power: 5
![]() |
Looking good
- there's not really much more that could be improved on - though there are a few small tricks you could use to reduce the length of your code further.Your RepeatLetterError inherits from ValueError, so it already takes in a message and returns it through "__str__". I believe you can change your RepeatLetterError to just this, and it'll do exactly the same thing (correct me if I'm wrong!): class RepeatLetterError(ValueError): "Raised when a repeat letter is guessed" self.progress = ['_' for x in self.word] except ValueError, ve: print ve continue except Hangman.RepeatLetterError, rle: print rle continue But these are pretty minor things ![]() |
|
|
|
|
|
#6 |
|
The Oblivious One
Join Date: May 2005
Location: Ontario, Canada
Posts: 644
Rep Power: 4
![]() |
A lot of the things you mentioned involve me getting out of the C++ mindset.
for example, to inherit from std::exception:
#include <exception>
#include <string>
class BadLetterError : public std::exception
{
pivate:
std::string message;
public:
BadLetterError(const std::string &str)
: std::exception(), message(str) {}
virtual const char *what() const throw() {return message.c_str();}
~BadLetterError() throw() {}
};I have to remember that Python has no private members, and therefore, everything is inherited. Thanks again. ![]()
__________________
Dr. Zoidberg: [ecstatic] I'm going to a movie... with FRIENDS! Last edited by Jessehk; Jan 24th, 2006 at 8:49 AM. |
|
|
|
![]() |
| Bookmarks |
| Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
| Thread Tools | |
| Display Modes | |
|
|