179 lines
6.1 KiB
Python
179 lines
6.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Hangman example
|
|
"""
|
|
|
|
|
|
from random import choice
|
|
import unicodedata
|
|
import re
|
|
from os import unlink
|
|
from os.path import isfile
|
|
from tiramisu import RegexpOption, OptionDescription, Config, IntOption, UnicodeOption, BoolOption, ParamOption, Params
|
|
from tiramisu.storage import storage_type
|
|
from tiramisu.storage.sqlite3.storage import SETTING
|
|
from tiramisu_parser import TiramisuParser
|
|
|
|
|
|
LANG = 'fr_FR'
|
|
DICT_FILE = '/usr/share/myspell/{}.dic'.format(LANG)
|
|
WORD_REGEXP = re.compile(r'^[a-z]{7,12}$')
|
|
PROPOSALS_LEN = 27
|
|
NB_PROPOSALS = 6
|
|
|
|
|
|
def remove_accent(word):
|
|
"""remove all accent"""
|
|
word = unicodedata.normalize('NFD', word)
|
|
return word.encode('ascii', 'ignore').decode()
|
|
|
|
|
|
def get_random_word():
|
|
"""get line randomly in myspell file
|
|
"""
|
|
with open(DICT_FILE, 'r') as file_content:
|
|
word = choice(file_content.readlines()).strip()
|
|
if word.endswith('/S.'):
|
|
word = word[:-3]
|
|
if word.endswith('/X.'):
|
|
word = word[:-3]
|
|
if word.endswith('/F.'):
|
|
word = word[:-3]
|
|
if word.endswith('/a0p+') or word.endswith('/d0p+') or word.endswith('/a3p+'):
|
|
word = word[:-5]
|
|
if not WORD_REGEXP.search(remove_accent(word)):
|
|
return get_random_word()
|
|
return word
|
|
|
|
|
|
def display_uncomplete_word(word, *proposals):
|
|
"""display response with proposals
|
|
"""
|
|
if display_proposals_left(display_misses(word, *proposals)) == 0:
|
|
return word
|
|
display = ['-'] * len(word)
|
|
for idx, char in enumerate(remove_accent(word)):
|
|
if char in proposals:
|
|
display[idx] = word[idx]
|
|
return ''.join(display)
|
|
|
|
|
|
def display_misses(word, *proposals):
|
|
"""display all proposals
|
|
"""
|
|
ret = list(set(proposals) - set(list(remove_accent(word))))
|
|
if None in ret:
|
|
ret.remove(None)
|
|
ret.sort()
|
|
return ' '.join(ret)
|
|
|
|
|
|
def validate_misses(misses):
|
|
if display_proposals_left(misses) == 0:
|
|
raise ValueError('No more guest possible')
|
|
|
|
|
|
def display_proposals_left(misses):
|
|
if not misses:
|
|
return NB_PROPOSALS
|
|
return max(NB_PROPOSALS - len(misses.split(' ')), 0)
|
|
|
|
|
|
def display_proposal(word, *proposals):
|
|
if display_uncomplete_word(word, *proposals) == word:
|
|
return False
|
|
return display_proposals_left(display_misses(word, *proposals)) != 0
|
|
|
|
|
|
class ProposalOption(RegexpOption):
|
|
__slots__ = tuple()
|
|
_regexp = re.compile(r'^[a-z]$')
|
|
_display_name = 'proposal'
|
|
|
|
|
|
def main():
|
|
options = []
|
|
proposal = None
|
|
word = UnicodeOption('word',
|
|
'Word',
|
|
properties=('hidden', 'force_store_value'),
|
|
callback=get_random_word)
|
|
proposals = [ParamOption(word)]
|
|
for idx in range(PROPOSALS_LEN):
|
|
requires = [{'option': 'self',
|
|
'expected': None,
|
|
'action': 'hidden',
|
|
'inverse': True}]
|
|
if proposal is not None:
|
|
display = BoolOption('display{}'.format(idx),
|
|
'Display {}'.format(idx),
|
|
properties=('hidden',),
|
|
callback=display_proposal,
|
|
callback_params=Params(tuple(proposals)))
|
|
options.append(display)
|
|
requires.append({'option': proposal,
|
|
'expected': None,
|
|
'action': 'disabled'})
|
|
requires.append({'option': display,
|
|
'expected': False,
|
|
'action': 'disabled'})
|
|
|
|
proposal = ProposalOption('guess{}'.format(idx),
|
|
'Guess {}'.format(idx),
|
|
requires=requires,
|
|
properties=('positional',))
|
|
#FIXME maximum recursion ...
|
|
#if proposals:
|
|
# proposal.impl_add_consistency('not_equal', proposals[0])
|
|
|
|
proposals.append(ParamOption(proposal, True))
|
|
options.append(proposal)
|
|
#
|
|
proposal_word = UnicodeOption('proposal_word',
|
|
'Word',
|
|
properties=('frozen',),
|
|
callback=display_uncomplete_word,
|
|
callback_params=Params(tuple(proposals)))
|
|
misses = UnicodeOption('misses',
|
|
'Misses',
|
|
properties=('frozen',),
|
|
callback=display_misses,
|
|
callback_params=Params(tuple(proposals)),
|
|
validator=validate_misses)
|
|
proposals_left = IntOption('proposals_left',
|
|
'Proposals left',
|
|
properties=('frozen',),
|
|
callback=display_proposals_left,
|
|
callback_params=Params(ParamOption(misses)))
|
|
#descr = OptionDescription('proposals',
|
|
# 'Suggesting letters',
|
|
# options)
|
|
storage_type.set('sqlite3')
|
|
config = Config(OptionDescription('root', 'root', [word, proposal_word, misses, proposals_left] + options), persistent=True, session_id='hangman')
|
|
parser = TiramisuParser()
|
|
parser.add_arguments(config)
|
|
try:
|
|
parser.parse_args()
|
|
except ValueError:
|
|
pass
|
|
config = parser.get_config()
|
|
filename = '{}/tiramisu.db'.format(SETTING.dir_database)
|
|
lost = False
|
|
for name in ['proposal_word', 'misses', 'proposals_left']:
|
|
option = config.option(name)
|
|
try:
|
|
value = option.value.get()
|
|
print('{}: {}'.format(option.option.doc(), value))
|
|
except ValueError as err:
|
|
lost = True
|
|
err.prefix = ''
|
|
print(option.option.doc(), str(err))
|
|
if isfile(filename):
|
|
unlink(filename)
|
|
if not lost and \
|
|
config.option('proposal_word').value.get() == config.forcepermissive.option('word').value.get():
|
|
print('You win')
|
|
if isfile(filename):
|
|
unlink(filename)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|