Discussion:
Tricher au scrabble...
(trop ancien pour répondre)
Dominique
2023-11-28 10:33:38 UTC
Permalink
Bonjour,

Je m'amuse à faire un logiciel de triche au Scrabble. J'ai bien les
presque 350 000 mots en français au format txt.

Voici mes débuts :

-----------------------------------------
f=open('/home/USER/dico.txt')
scrabble=dict()
for i in f:
j=i[:-1]
mot=list(j)
mot.sort()
mot=''.join(mot)
scrabble[str(mot)]=j
del (mot)
del (i)
del(j)
del (f)
print('Création du dictionnaire terminée')

test='xx'
while test=='xx':
lettres=input('Lettres ')
lettres=list(lettres)
lettres.sort()
lettres=''.join(lettres)
if lettres!='xx':
if lettres in scrabble.keys():
print(scrabble[lettres])
else:
test=9
-----------------------------------------

Ce script fonctionne en deux blocs. Le premier, qui ne tourne qu'une
fois, charge le dico.text. Il parcourt chaque mot, le transforme en
liste, met les lettre par ordre alpha croissant, reconstitue un string
avec join. Il affecte comme values à cette keys le mot d'origine j.

Le deuxième bloc me demande mes lettres, les transforme en liste, les
classe par ordre alpha, reconstitue un string et voit si cette variable
existe en keys dans mon dictionnaire scrabble, ce à quoi il me donne la
values correspondant.

Il répond à la question, dès lors que les lettres que je lui soumets
existent à l'identique en clef. J'ai des limites. Par exemple, avec les
lettre aberv, il me proposera brave mais pas baver.

La vraie limite vient de ce que je ne vois pas trop comment faire pour
que l'application cherche un mot avec mes 7 lettres, en cas d'échec,
avec 6 lettres etc. Je pense que je pourrais utiliser les outils
d'itertools (combination ou permutations. mais j'ai peur que les temps
de traitements deviennent très longs...

Comment verriez-vous les choses ?

Merci et belle journée à vous tous,
--
Dominique
Esto quod es
Stefan Ram
2023-11-28 11:30:16 UTC
Permalink
Post by Dominique
del (mot)
Tout d'abord, juste une remarque sur ce qui est cité ci-dessus :

Au lieu de « del(x) », on peut souvent écrire simplement
« del x », sans les parenthèses.

Ensuite, on peut souvent remplacer

x = 1
print( x )
del x

par

def ecris_1():
x = 1
print( x )

ecris_1()

. Cela comprend certes plus de lignes, mais correspond
probablement plus au style habituel. La variable « x »
n'existe plus non plus après cela.
Dominique
2023-11-28 15:39:41 UTC
Permalink
Post by Stefan Ram
Post by Dominique
del (mot)
Au lieu de « del(x) », on peut souvent écrire simplement
« del x », sans les parenthèses.
Ensuite, on peut souvent remplacer
x = 1
print( x )
del x
par
x = 1
print( x )
ecris_1()
. Cela comprend certes plus de lignes, mais correspond
probablement plus au style habituel. La variable « x »
n'existe plus non plus après cela.
Effectivement, par une fonction, les variables disparaissent après
exécution. Merci pour le rappel :)
--
Dominique
Esto quod es
Stefan Ram
2023-11-28 12:22:24 UTC
Permalink
Post by Dominique
Ce script fonctionne en deux blocs.
Il y a encore une remarque à faire sur le style de programmation :
si un script est composé de deux blocs, il sera plus clair
pour le lecteur si chaque bloc est défini comme une fonction
et nommé par sa tâche.
Post by Dominique
La vraie limite vient de ce que je ne vois pas trop comment faire pour
que l'application cherche un mot avec mes 7 lettres, en cas d'échec,
avec 6 lettres etc. Je pense que je pourrais utiliser les outils
d'itertools (combination ou permutations. mais j'ai peur que les temps
de traitements deviennent très longs...
Voici une approche possible (sans essayer d'optimiser quoi que ce
soit) :

dictionnaire =[ 'cab', 'ba', 'ccab' ]

def possible( mots, mes_lettres, longueur ):
'''Le mot a-t-il la longueur indiquée et les lettres de
"mes_lettres" sont-elles suffisantes pour former le mot ?'''
if len( mots )!= longueur: return False
for lettre in mots:
if mots.count( lettre )> mes_lettres.count( lettre ):
return False
return True

def mots_possibles( dictionnaire, mes_lettres, longueur ):
'''Tous les mots du dictionnaire qui ont la longueur indiquée et qui
peuvent être formés avec les lettres du « mes_lettres ».'''
résultat = []
for mots in dictionnaire:
if possible( mots, mes_lettres, longueur ):
résultat.append( mots )
return résultat

def mots_de_longueur_maximale( dictionnaire, mes_lettres ):
'''Trouve dans le dictionnaire des mots de longueur maximale qui
peuvent être formés avec les lettres indiquées.'''
for longueur in range( len( mes_lettres ), 1, -1 ):
résultat = mots_possibles( dictionnaire, mes_lettres, longueur )
if résultat: return résultat
return []

for mes_lettres in[ 'abc', 'ab', 'abx', 'axy' ]:
print( mots_de_longueur_maximale( dictionnaire, mes_lettres ))
Dominique
2023-11-28 15:43:50 UTC
Permalink
Post by Stefan Ram
si un script est composé de deux blocs, il sera plus clair
pour le lecteur si chaque bloc est défini comme une fonction
et nommé par sa tâche.
J'y ai bien pensé, mais le premier bloc n'est exécuté qu'une fois, lors
de la création du dictionnaire. Je n'ai pas jugé utile de faire une
fonction...

Je testerai le listing que tu me proposes dès que j'ai un peu de temps.
Merci pour cette approche :)
--
Dominique
Esto quod es
yves
2023-12-01 17:39:07 UTC
Permalink
Post by Dominique
si un script est composé de deux blocs, il sera plus clair pour le
lecteur si chaque bloc est défini comme une fonction et nommé par sa
tâche.
J'y ai bien pensé, mais le premier bloc n'est exécuté qu'une fois, lors
de la création du dictionnaire. Je n'ai pas jugé utile de faire une
fonction...
Je testerai le listing que tu me proposes dès que j'ai un peu de temps.
Merci pour cette approche
J'ai séché aujourd'hui sur un problème wordle qui s'apparentait pour le
coup à un problème de scrabble.

Du coup, j'ai réimplémenté quelques idées de ce fil de façon très brute,
et j'ai été surpris par la rapidité de traitement.

Voici un exemple, avec une séquence de lettres qui a de nombreux
anagrammes :


dictionnaire = "/usr/share/dict/french"
with open(dictionnaire) as f:
b= [mot.rstrip() for mot in f.readlines()]
c= [mot for mot in b if sorted(mot) == sorted("acenrt")]

print(len(b))
print(len(c))
print(c)
print('terminé')


#+RESULTS:
: 346200
: 9
: ['canter', 'cantre', 'carnet', 'centra', 'crante', 'encart', 'nectar',
'tancer', 'tanrec']
: terminé



@+
--
Yves
yves
2023-12-01 17:56:39 UTC
Permalink
Post by yves
Voici un exemple, avec une séquence de lettres qui a de nombreux
Encore plus concis, la ligne va sans doute être coupé par les lecteurs de
news:

dictionnaire = "/usr/share/dict/french"
with open(dictionnaire) as f:
resultat = [mot.rstrip() for mot in f.readlines() if
sorted(mot.rstrip()) == sorted("acenrt")]

print(resultat)
print('terminé')


#+RESULTS:
: ['canter', 'cantre', 'carnet', 'centra', 'crante', 'encart', 'nectar',
'tancer', 'tanrec']
: terminé


0.4 secondes sur mon ordi, quand même.


@+
--
Yves
Michel
2023-12-02 02:48:09 UTC
Permalink
Post by yves
dictionnaire = "/usr/share/dict/french"
resultat = [mot.rstrip() for mot in f.readlines() if
sorted(mot.rstrip()) == sorted("acenrt")]
Pour les anagrammes c'est bon mais pour le scrabble on peut avoir des
lettres qui ne seraient pas utilisées et donc cet algo ne marcherait pas.
Post by yves
0.4 secondes sur mon ordi, quand même.
Sur le mien 0.280. Mais en reprenant le même principe que pour le
scrabble, en chargeant le fichier dans une array par longueur de mot,
j'obtiens 0.170.

fichier = "/usr/share/dict/french"

# on suppose que les mots font 27 caractères maxi
# (désinstitutionnalisassions)
MAX = 27

def charge_dico():
dictionnaire = [[] for i in range(MAX + 1)]
with open(fichier, 'r') as fp:
while line := fp.readline().rstrip():
dictionnaire[len(line)].append(line)
return dictionnaire

def recherche(lettres, dictionnaire):
resultat = [mot for mot in dictionnaire[len(lettres)]
if sorted(mot) == sorted(lettres)]
return resultat

tests = ['acenrt']

dictionnaire = charge_dico()

for lettres in tests:
print(lettres, ':', recherche(lettres, dictionnaire))
yves
2023-12-06 22:22:12 UTC
Permalink
Post by Michel
Post by yves
0.4 secondes sur mon ordi, quand même.
Sur le mien 0.280. Mais en reprenant le même principe que pour le
scrabble, en chargeant le fichier dans une array par longueur de mot,
j'obtiens 0.170.
Oui, c'est clair que c'est mieux.
Tiens, je ne connaissais pas cet opérateur ( := ) ni cette syntaxe.

En espérant qu'il ne va pas se transformer en émoji.

@+
--
Yves
Michel
2023-12-07 09:28:11 UTC
Permalink
Post by yves
Tiens, je ne connaissais pas cet opérateur ( := ) ni cette syntaxe.
Je viens du Perl où il est dit qu'il y a plusieurs façons de faire un
truc :) Mais en python je ne suis pas sûr d'utiliser une syntaxe
mainstream...
Thierry Pinelli
2023-12-07 15:35:16 UTC
Permalink
Post by yves
Post by Michel
Post by yves
0.4 secondes sur mon ordi, quand même.
Sur le mien 0.280. Mais en reprenant le même principe que pour le
scrabble, en chargeant le fichier dans une array par longueur de mot,
j'obtiens 0.170.
Oui, c'est clair que c'est mieux.
Tiens, je ne connaissais pas cet opérateur ( := ) ni cette syntaxe.
du bon vieux Pascal

PPP Perl Python Pascal
Stefan Ram
2023-12-07 15:57:34 UTC
Permalink
Post by yves
Tiens, je ne connaissais pas cet opérateur ( := ) ni cette syntaxe.
La syntaxe de l'instruction while est la suivante :

|while_stmt ::= "while" assignment_expression ":" suite

. Elle ne dit donc rien directement sur les opérateurs.

Mais ":=" apparaît directement dans la syntaxe de l'instruction
d'expression !

|assignment_expression ::= [identifier ":="] expression

Il y a aussi un exemple :

|while chunk := file.read(9000):
| process(chunk)

("The Python Language Reference", Release 3.13.0a0).
Thierry Pinelli
2023-12-07 16:52:48 UTC
Permalink
Post by Stefan Ram
| process(chunk)
("The Python Language Reference", Release 3.13.0a0).
oui, 6.12. Assignment expressions
yves
2023-12-07 18:00:30 UTC
Permalink
Post by Stefan Ram
("The Python Language Reference", Release 3.13.0a0).
Oui, j'en profite pour rappeler l'aide intégrée au prompt python :


Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
Post by Stefan Ram
help("while")
The "while" statement
*********************

The "while" statement is used for repeated execution as long as an
expression is true:

while_stmt ::= "while" assignment_expression ":" suite
["else" ":" suite]

This repeatedly tests the expression and, if it is true, executes the
first suite; if the expression is false (which may be the first time
it is tested) the suite of the "else" clause, if present, is executed
and the loop terminates.

A "break" statement executed in the first suite terminates the loop
without executing the "else" clause’s suite. A "continue" statement
executed in the first suite skips the rest of the suite and goes back
to testing the expression.

Related help topics: break, continue, if, TRUTHVALUE

@+
--
Yves
yves
2023-12-07 18:07:49 UTC
Permalink
Post by Stefan Ram
|while_stmt ::= "while" assignment_expression ":" suite
On peut noter que cette syntaxe, (:=, également appelé "opérateur
d'affectation de l'expression") a été introduite dans la version 3.8
de python, sortie fin 2019*.

* juste un peu avant l'épidémie de Covid. Coïncidence ? JE POSE LA
QUESTION.

@+
--
Yves
Michel
2023-11-28 18:23:19 UTC
Permalink
Post by Stefan Ram
Voici une approche possible (sans essayer d'optimiser quoi que ce
J'ai optimisé ton algo en passant par un dictionnaire pour chaque
longueur de mot. Ca permet de limiter les parcours sur le sous-ensemble.
Ca accélère nettement quand on a plein de lettres qui ne collent pas, et
donc qu'on doit chercher plusieurs longueurs.


# apt install wfrench
fichier = '/usr/share/dict/french'

# on suppose que les mots font 27 caractères maxi
# (désinstitutionnalisassions)
MAX = 27

tests = [ 'emmop', 'aefirs', 'aegnor', 'aberv',
'axy', 'emmo', 'emmop', 'emmoepat', 'emoepat',
'aefirs', 'aegnor', 'aegnorer' ]

def possible( mot, mes_lettres ):
'''
les lettres de mes_lettres sont-elles suffisantes pour former le mot ?
'''
for lettre in mot:
if mot.count(lettre) > mes_lettres.count(lettre):
return False
return True

def mots_possibles( dictionnaire, mes_lettres ):
'''
Tous les mots du dictionnaire et qui
peuvent être formés avec les lettres du « mes_lettres ».
'''
résultat = []
for mot in dictionnaire:
if possible(mot, mes_lettres):
résultat.append(mot)
return résultat

def mots_de_longueur_maximale( dictionnaire, mes_lettres ):
'''
Trouve dans le dictionnaire des mots de longueur maximale qui
peuvent être formés avec les lettres indiquées.
'''
for longueur in range( len( mes_lettres ), 1, -1 ):
résultat = mots_possibles( dictionnaire[longueur], mes_lettres )
if résultat: return résultat
return []

def charge_dico():
'''
on passe par une array par longueur de mot
pour lors de la recherche ne parcourir que N mots
au lieu de N x longueurs recherchées
ça augmente le temps de chargement mais
ça divise le temps de recherche par 10
'''
dictionnaire = [[] for i in range(MAX + 1)]
with open(fichier, 'r') as fp:
while line := fp.readline().rstrip():
dictionnaire[len(line)].append(line)
return dictionnaire

dictionnaire = charge_dico()

for lettres in tests:
print(f'{lettres:8s}:', mots_de_longueur_maximale(dictionnaire, lettres))
yves
2023-11-28 16:05:20 UTC
Permalink
Post by Dominique
Bonjour,
Je m'amuse à faire un logiciel de triche au Scrabble. J'ai bien les
presque 350 000 mots en français au format txt.
-----------------------------------------
f=open('/home/USER/dico.txt')
scrabble=dict()
j=i[:-1]
mot=list(j)
mot.sort() mot=''.join(mot) scrabble[str(mot)]=j
del (mot)
del (i)
del(j)
del (f)
print('Création du dictionnaire terminée')
Comment verriez-vous les choses ?
J'aime bien cogiter sur du concret limpide.

Un petit exemple minimal pour la partie "création de dictionnaire" :

En entrée :
dico.txt :

pomme
fraise
orange

En sortie :
le dictionnaire scrabble :

{'emmop': 'pomme', 'aefirs': 'fraise', 'aegnor': 'orange'}

@+
--
Yves
yves
2023-11-28 16:17:30 UTC
Permalink
Encore mieux:

En entrée :
dico.txt :

pomme
fraise
orange
brave
baver

En sortie :
le dictionnaire scrabble :

{'emmop': 'pomme', 'aefirs': 'fraise', 'aegnor': 'orange', 'aberv':
'baver'}

@+
--
Yves
Dominique
2023-11-29 04:55:02 UTC
Permalink
Post by yves
pomme
fraise
orange
brave
baver
'baver'}
@+
C'est exactement ce que j'ai fait:)
--
Dominique
Esto quod es
Stefan Ram
2023-11-28 16:19:20 UTC
Permalink
Post by yves
pomme
fraise
orange
{'emmop': 'pomme', 'aefirs': 'fraise', 'aegnor': 'orange'}
C'est vraiment très clair ! (Cependant, les exemples ne rendent
pas toujours les tâches claires. Ainsi, "Entrée 2, Sortie 4",
pourrait être compris comme x+x, x*2, x+2, x*x, ou autre.)

Pour aller de l'entrée à la sortie, on pourrait par exemple
utiliser quelque chose comme dans le petit script suivant.

dico = 'pomme', 'fraise', 'orange'
scrd ={ ''.join( sorted( mot )) : mot for mot in dico }
print( scrd )
yves
2023-11-28 20:16:49 UTC
Permalink
Post by Stefan Ram
dico = 'pomme', 'fraise', 'orange'
scrd ={ ''.join( sorted( mot )) : mot for mot in dico }
print( scrd )
ah oui, ça c'est du concis.

Sympathique :

print(sorted('citron')

Résultat :
['c', 'i', 'n', 'o', 'r', 't']

Mais pas facile à décrypter avant d'avoir bien étudié cette syntaxe.

avec la version étendue, on constate aussi la limite de l'approche par ce
dictionnaire :

dico = 'pomme', 'fraise', 'orange', 'baver', 'brave'
scrd ={ ''.join( sorted( mot )) : mot for mot in dico }
print( scrd )


{'emmop': 'pomme', 'aefirs': 'fraise', 'aegnor': 'orange', 'aberv':
'brave'}

Comme ça :
srcd1 = {mot : ''.join( sorted( mot )) for mot in dico}
print(srcd1)

Résultat:

{'pomme': 'emmop', 'fraise': 'aefirs', 'orange': 'aegnor', 'baver':
'aberv', 'brave': 'aberv'}

Ensuite, il reste à écrire le code pour faire une inversion key/value qui
traite le cas des anagrammes.

@+
--
Yves
Michel
2023-11-28 16:52:42 UTC
Permalink
Post by Dominique
print(scrabble[lettres])
De passer par un dict ça impose que tu n'auras toujours qu'un seul mot
fourni pour un ensemble de lettres. Je ne pense pas que ce soit le mieux
pour jouer. Et de plus si tu as des lettres supplémentaires qui ne
rentrent pas dans un mot tu n'obtiendras aucun résultat.
Dominique
2023-11-29 04:57:18 UTC
Permalink
Post by Michel
Post by Dominique
print(scrabble[lettres])
De passer par un dict ça impose que tu n'auras toujours qu'un seul mot
fourni pour un ensemble de lettres. Je ne pense pas que ce soit le mieux
pour jouer. Et de plus si tu as des lettres supplémentaires qui ne
rentrent pas dans un mot tu n'obtiendras aucun résultat.
Je sais, et c'est bien là ma limite :)
--
Dominique
Esto quod es
Loading...