I'm pretty new to python and have created a simple hangman. Since doing so I realise this appears to be a fairy common theme. It uses /usr/share/dict/words on the local system, resulting in some tricky words to guess. I guess I should look at maybe curl'ing a URL to get random words or an API. I've also not defined a main(), not sure if that's bad practice. Feedback appreciated.
#!/usr/local/bin/python3
import os
import random
import subprocess
def draw_man_and_show_word():
global word
showword=''.join(word)
os.system('clear')
for y in range(0,7):
for x in range(0,7):
print (chr(array[y][x]),end='')
print()
print ()
print (showword)
print ()
def add_limb_to_man():
global size
y=man[size][0]
x=man[size][1]
c=man[size][2]
array[x][y]=c
size=size+1
def find_random_word():
global theword
lines=int(subprocess.getoutput("wc -l /usr/share/dict/words | awk \'{print 1ドル}\'"))
line=random.randint(1,lines)
theword=subprocess.getoutput("head -"+str(line)+" /usr/share/dict/words |tail -1")
theword=str.lower(theword)
# 2D array of character (ASCII) values as a matrix for the hangman pic
array=[[124,45,45,45,45,32,32,32],[124,32,32,32,124,32,32,32],[124,32,32,32,32,32,32,32],[124,32,32,32,32,32,32,32],[124,32,32,32,32,32,32,32],[124,32,32,32,32,32,32,32],[124,32,32,32,32,32,32,32]]
# 2D array with the (single) character position to change (x,y,newvalue) for each additional limb
man=[[4,2,79],[4,3,43],[3,3,47],[5,3,92],[4,4,124],[3,5,47],[5,5,92]]
size=tries=0
limit=6
letters_tried=""
find_random_word()
# Array to represent word shown on screen (using array as string immutable)
word=['-' for x in range(0,len(theword))]
while ((tries <= limit)):
draw_man_and_show_word()
letter=""
while (len(letter) != 1 or not letter.islower() or letters_tried.find(letter) >= 0):
letter=input("Enter your choice of (single lowercase) letter:")
letters_tried=letters_tried+letter
pos=theword.find(letter)
if (pos >= 0):
tmpword=theword
while (pos >= 0):
word[pos]=letter
tmpword=tmpword.replace(letter,'#',1)
pos=tmpword.find(letter)
else:
add_limb_to_man()
tries=tries+1
if (''.join(word) == theword):
draw_man_and_show_word()
print()
print("you got it!")
exit()
draw_man_and_show_word()
print ("you lost. It was "+theword)
-
\$\begingroup\$ Edit: corrected the handling of counting lines in /usr/share/dict/words to allow it to not be mac specific, (slightly different wc output to fedora) and added correct handling of letters already tried using letters_tried variable \$\endgroup\$23vc– 23vc2018年09月18日 11:56:17 +00:00Commented Sep 18, 2018 at 11:56
1 Answer 1
Avoid working in the global namespace
This makes maintenance of code a pain. Instead create a
main
orhangman
functionDon't use
global
instead make these variables parameters of you functiondef draw_man_and_show_word(): global word
Could be
def draw_man_and_show_word(word):
Don't make lines to long, they become really hard to read
array=[[124,45,45,45,45,32,32,32],[124,32,32,32,124,32,32,32],[124,32,32,32,32,32,32,32],[124,32,32,32,32,32,32,32],[124,32,32,32,32,32,32,32],[124,32,32,32,32,32,32,32],[124,32,32,32,32,32,32,32]]
See how you need to scroll all the way to the right, this would be much more easy on the eyes
HANGMAN = [ ['|', '-', '-', '-', '-', ' ', ' ', ' '], ['|', ' ', ' ', ' ', '|', ' ', ' ', ' '], ['|', ' ', ' ', ' ', ' ', ' ', ' ', ' '], ['|', ' ', ' ', ' ', ' ', ' ', ' ', ' '], ['|', ' ', ' ', ' ', ' ', ' ', ' ', ' '], ['|', ' ', ' ', ' ', ' ', ' ', ' ', ' '], ['|', ' ', ' ', ' ', ' ', ' ', ' ', ' '] ]
I also changed it to their character form which makes it even more easy to read
And made the variable name
ALL_CAPS
since this is a constantUse unpacking // Optional namedtuple
man=[[4,2,79],[4,3,43],[3,3,47],[5,3,92],[4,4,124],[3,5,47],[5,5,92]] y=man[size][0] x=man[size][1] c=man[size][2] array[x][y]=c size=size+1
First I cleaned it up a bit
HANGMAN_STAGES = [ [4, 2, 'O'], [4, 3, '+'], [3, 3, '/'], [5, 3, '\\'], [4, 4, '|'], [3, 5, '/'], [5, 5, '\\'] ]
And we can unpack the values from the list of lists with this
x, y, value = HANGMAN_STAGES[size]
Alternatively you can use the
namedtuple
module to store information more visuallyThe it would become
from collections import namedtuple Stage = namedtuple('Stage', 'x, y, value') HANGMAN_STAGES = [ Stage(4, 2, 'O'), Stage(4, 3, '+'), Stage(3, 3, '/'), Stage(5, 3, '\\'), Stage(4, 4, '|'), Stage(3, 5, '/'), Stage(5, 5, '\\') ]
And you can call them like this
stage = HANGMAN_STAGES[size] HANGMAN[stage.y][stage.x] = stage.value
Make the
guessed_letters
aset()
For fast lookup O(0)
join()
instead of manually appending charsfor y in range(0,7): for x in range(0,7): print (chr(array[y][x]),end='') print()
Could be rewritten as
print('\n'.join(''.join(row) for row in draw_list))
If we have changed the HANGMAN to contain
chr
instead of the ordinal valuesInstead of the
find
andreplace
methods you could have usedenumerate
pos=theword.find(letter) if (pos >= 0): tmpword=theword while (pos >= 0): word[pos]=letter tmpword=tmpword.replace(letter,'#',1) pos=tmpword.find(letter)
Can be rewritten in the form of
for idx, char in enumerate(theword): if char == letter: guessed_word[idx] = letter
But
strings
can't be assigned... so you should set the data type of guessed_word to alist
Lastly top it off with a
if __name__ == '__main__'
Putting it all together
import random
HANGMAN = [
['|', '-', '-', '-', '-', ' ', ' ', ' '],
['|', ' ', ' ', ' ', '|', ' ', ' ', ' '],
['|', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
['|', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
['|', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
['|', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
['|', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
]
HANGMAN_STAGES = [
[4, 2, 'O'],
[4, 3, '+'],
[3, 3, '/'],
[5, 3, '\\'],
[4, 4, '|'],
[3, 5, '/'],
[5, 5, '\\']
]
SPACE = '_'
def print_man_and_word(draw_list, guessed_word):
print('\n'.join(''.join(row) for row in draw_list))
print()
print(''.join(guessed_word))
def get_random_word():
# Your logic here (I don't have linux atm ;)
word_list = ['cheese', 'pepper', 'sausage']
return random.choice(word_list)
def get_letter(guessed_letters):
letter = ''
while len(letter) != 1 or not letter.islower() or letter in guessed_letters:
letter = input("Enter your choice of (single lowercase) letter:")
return letter
def hangman(word=None, draw_list=HANGMAN):
if word is None:
word = get_random_word()
guessed_word = [SPACE] * len(word)
guessed_letters = set()
tries = 0
while True:
print_man_and_word(draw_list, guessed_word)
letter = get_letter(guessed_letters)
guessed_letters.add(letter)
if letter in word:
for idx, char in enumerate(word):
if char == letter:
guessed_word[idx] = letter
else:
x, y, value = HANGMAN_STAGES[tries]
draw_list[y][x] = value
tries += 1
if all(char != SPACE for char in guessed_word):
print_man_and_word(draw_list, guessed_word)
print('\nYou got it!')
exit()
elif tries == len(HANGMAN_STAGES):
print_man_and_word(draw_list, guessed_word)
print('You lost! it is {}'.format(word))
exit()
if __name__ == '__main__':
hangman()