patternpythonMajor
The right way to hang a man
Viewed 0 times
thehangwaymanright
Problem
Problem:
I have seen a few questions around hangman. Usually this is done in a very hackish way, which usually can not be generalized any further. My thought or question is about the creation of the gown, which is a central part in any hangman game.
I want to create the ASCII image below
Where one "limb" of the stick figure should appear each time the function is prompted.
I also wanted to be able to specify both the height and width of the gown. However I was not able to scale the stick figure relative to the gown size. The code is provided below and I have two simple questions about it =)
Question:
Code:
```
from math import ceil
def create_gown(width, height):
gown = []
gown.append('{:>{}s}'.format('_'*int(width/2), 10))
gown.append('{:>{}s} {:>{}s}'.format(
'|', 11 - int(width/2), '|', int(width/2)-2))
for i in range(height-3):
gown.append('{:^{}s}'.format('|', 20))
gown.append('{:^{}s}'.format('-'*width, 20))
return gown
def wrong_answer(gown, attempt=0):
height, width = len(gown), len(gown[-1].strip())
offset1 = int((-width+23)*0.5)+1
offset3 = int(ceil(0.5*(width-7)))
if attempt == 0:
return gown
elif attempt == 1:
new_line = '{:>{}s} {:>{}s}'.format('O', offset1-1, '|', offset3+1)
row = 2
elif attempt == 2:
new_line = '{:>{}s} {:>{}s}'.format('|', offset1-1, '|', offset3+1)
row = 3
elif attempt == 3:
new_line = '{:>{}s} {:>{}s}'.format('/| ', offset1, '|', offset3)
row = 3
elif attempt == 4:
new_line = '{:>{}s} {:>{}s}'.format('/|\\', offset1, '|', offset3)
row = 3
elif at
I have seen a few questions around hangman. Usually this is done in a very hackish way, which usually can not be generalized any further. My thought or question is about the creation of the gown, which is a central part in any hangman game.
I want to create the ASCII image below
_____
| |
O |
/|\ |
/ \ |
|
----------Where one "limb" of the stick figure should appear each time the function is prompted.
I also wanted to be able to specify both the height and width of the gown. However I was not able to scale the stick figure relative to the gown size. The code is provided below and I have two simple questions about it =)
Question:
- Is there a cleaner way to hang the man? I feel my method is very barbaric (I used some black voodo graduate mathematics to get it to look correct).
- Is there a way to make the stick figure scale with the size of the gown? (essentially providing more guesses).
Code:
```
from math import ceil
def create_gown(width, height):
gown = []
gown.append('{:>{}s}'.format('_'*int(width/2), 10))
gown.append('{:>{}s} {:>{}s}'.format(
'|', 11 - int(width/2), '|', int(width/2)-2))
for i in range(height-3):
gown.append('{:^{}s}'.format('|', 20))
gown.append('{:^{}s}'.format('-'*width, 20))
return gown
def wrong_answer(gown, attempt=0):
height, width = len(gown), len(gown[-1].strip())
offset1 = int((-width+23)*0.5)+1
offset3 = int(ceil(0.5*(width-7)))
if attempt == 0:
return gown
elif attempt == 1:
new_line = '{:>{}s} {:>{}s}'.format('O', offset1-1, '|', offset3+1)
row = 2
elif attempt == 2:
new_line = '{:>{}s} {:>{}s}'.format('|', offset1-1, '|', offset3+1)
row = 3
elif attempt == 3:
new_line = '{:>{}s} {:>{}s}'.format('/| ', offset1, '|', offset3)
row = 3
elif attempt == 4:
new_line = '{:>{}s} {:>{}s}'.format('/|\\', offset1, '|', offset3)
row = 3
elif at
Solution
Repetitions
All your
Calculus
You do not need
Creation vs modifications
You're aware that you can create sequences of repeating elements using
On modifying in place and returning a value
and a bit about printing too
Your
but
So there is really no point in duplicating references to the same object. Instead, what you can do is preprocess the printing job, so you don't really need the
Usage:
Exceptions
Raising a generic purpose
However, there is something odd in the way you use your
```
def wrong_answer(width, height):
gown = create_gown(width, height)
offset1 = (23 - width) // 2 + 1
offset3 = (width - 6) // 2
yield '\n'.join(gown)
for attempt in range(1, 7):
pattern, row = _GOWN_MODIFIER[attempt]
All your
elif share the same structure. You just need to ensure that the inserted pattern is always 3 wide and you can drop your offsets adjustments. The logical next step is to use a dictionary to store the pattern and the row for each attempt:_GOWN_MODIFIER = {
1: (' O ', 2),
2: (' | ', 3),
3: ('/| ', 3),
4: ('/|\\', 3),
5: ('/ ', 4),
6: ('/ \\', 4),
}
def wrong_answer(gown, attempt=0):
height, width = len(gown), len(gown[-1].strip())
offset1 = int((-width+23)*0.5)+1
offset3 = int(ceil(0.5*(width-7)))
if not attempt:
return gown
try:
pattern, row = _GOWN_MODIFIER[attempt]
except KeyError:
raise Exception("Ops! The number of attempts must be an integer from 0 to 6.")
else:
gown[row] = '{:>{}s} {:>{}s}'.format(pattern, offset1, '|', offset3)
return gownCalculus
You do not need
ceil nor int to perform an integer division by 2. In Python 2, / is already the operator you need. You can also use // which is also the same operation. The advantage of the latter over the former is that the behaviour is the same in Python 3, whereas / will perform a decimal division in Python 3._GOWN_MODIFIER = {
1: (' O ', 2),
2: (' | ', 3),
3: ('/| ', 3),
4: ('/|\\', 3),
5: ('/ ', 4),
6: ('/ \\', 4),
}
def wrong_answer(gown, attempt=0):
if not attempt:
return gown
height, width = len(gown), len(gown[-1].strip())
offset1 = (23 - width) // 2 + 1
offset3 = (width - 6) // 2
try:
pattern, row = _GOWN_MODIFIER[attempt]
except KeyError:
raise Exception("Ops! The number of attempts must be an integer from 0 to 6.")
else:
gown[row] = '{:>{}s} {:>{}s}'.format(pattern, offset1, '|', offset3)
return gown
def create_gown(width, height):
half_width = width // 2
gown = []
gown.append('{:>{}s}'.format('_'*half_width, 10))
gown.append('{:>{}s} {:>{}s}'.format(
'|', 11 - half_width, '|', half_width-2))
for i in range(height-3):
gown.append('{:^{}s}'.format('|', 20))
gown.append('{:^{}s}'.format('-'*width, 20))
return gownCreation vs modifications
You're aware that you can create sequences of repeating elements using
seq(element) * count. You can use that to create your gown variable by repeating the pole and then modifying the top two rows and the last one; it should be more memory friendly as the space needed to store the list will be allocated in one go:def create_gown(width, height):
half_width = width // 2
gown = ['{:^{}s}'.format('|', 20)] * height
gown[0] = '{:>{}s}'.format('_' * half_width, 10)
gown[1] = '{:>{}s} {:>{}s}'.format(
'|', 11 - half_width, '|', half_width-2)
gown[-1] = '{:^{}s}'.format('-' * width, 20)
return gownOn modifying in place and returning a value
and a bit about printing too
Your
wrong_answer both modifies the gown in place and return the modified value. This is unnecessary as the caller still should hold a reference to the value (which was modified). When calling the code, you can have:gown2 = wrong_answer(gown, attempt=3)but
>>> gown2 is gown
TrueSo there is really no point in duplicating references to the same object. Instead, what you can do is preprocess the printing job, so you don't really need the
print_gown function:def wrong_answer(gown, attempt=0):
height, width = len(gown), len(gown[-1].strip())
offset1 = (23 - width) // 2 + 1
offset3 = (width - 6) // 2
if attempt:
try:
pattern, row = _GOWN_MODIFIER[attempt]
except KeyError:
raise Exception("Ops! The number of attempts must be an integer from 0 to 6.")
else:
gown[row] = '{:>{}s} {:>{}s}'.format(pattern, offset1, '|', offset3)
return '\n'.join(gown)Usage:
print(wrong_answer(gown, attempt=3))Exceptions
Raising a generic purpose
Exception is bad practice, as it makes the except clauses trying to handle your code being able to catch more than they should. You should at least use a more generic exception (such as ValueError) or define your own:class AlreadyHangedError(ValueError):
pass
def wrong_answer(gown, attempt):
...
raise AlreadyHangedError('...')
...However, there is something odd in the way you use your
wrong_answer: the caller is expected to call this function using increasing attempts. This is the job for a generator. By turning wrong_answer into a generator, you can call it to create a generator instance and then call next on this instance (or let a for loop do it for you) to get the next gown to print:```
def wrong_answer(width, height):
gown = create_gown(width, height)
offset1 = (23 - width) // 2 + 1
offset3 = (width - 6) // 2
yield '\n'.join(gown)
for attempt in range(1, 7):
pattern, row = _GOWN_MODIFIER[attempt]
Code Snippets
_GOWN_MODIFIER = {
1: (' O ', 2),
2: (' | ', 3),
3: ('/| ', 3),
4: ('/|\\', 3),
5: ('/ ', 4),
6: ('/ \\', 4),
}
def wrong_answer(gown, attempt=0):
height, width = len(gown), len(gown[-1].strip())
offset1 = int((-width+23)*0.5)+1
offset3 = int(ceil(0.5*(width-7)))
if not attempt:
return gown
try:
pattern, row = _GOWN_MODIFIER[attempt]
except KeyError:
raise Exception("Ops! The number of attempts must be an integer from 0 to 6.")
else:
gown[row] = '{:>{}s} {:>{}s}'.format(pattern, offset1, '|', offset3)
return gown_GOWN_MODIFIER = {
1: (' O ', 2),
2: (' | ', 3),
3: ('/| ', 3),
4: ('/|\\', 3),
5: ('/ ', 4),
6: ('/ \\', 4),
}
def wrong_answer(gown, attempt=0):
if not attempt:
return gown
height, width = len(gown), len(gown[-1].strip())
offset1 = (23 - width) // 2 + 1
offset3 = (width - 6) // 2
try:
pattern, row = _GOWN_MODIFIER[attempt]
except KeyError:
raise Exception("Ops! The number of attempts must be an integer from 0 to 6.")
else:
gown[row] = '{:>{}s} {:>{}s}'.format(pattern, offset1, '|', offset3)
return gown
def create_gown(width, height):
half_width = width // 2
gown = []
gown.append('{:>{}s}'.format('_'*half_width, 10))
gown.append('{:>{}s} {:>{}s}'.format(
'|', 11 - half_width, '|', half_width-2))
for i in range(height-3):
gown.append('{:^{}s}'.format('|', 20))
gown.append('{:^{}s}'.format('-'*width, 20))
return gowndef create_gown(width, height):
half_width = width // 2
gown = ['{:^{}s}'.format('|', 20)] * height
gown[0] = '{:>{}s}'.format('_' * half_width, 10)
gown[1] = '{:>{}s} {:>{}s}'.format(
'|', 11 - half_width, '|', half_width-2)
gown[-1] = '{:^{}s}'.format('-' * width, 20)
return gowngown2 = wrong_answer(gown, attempt=3)>>> gown2 is gown
TrueContext
StackExchange Code Review Q#131314, answer score: 35
Revisions (0)
No revisions yet.