consistait à construire une équipe de Pokémon avec la force de frappe maximale, capable de couvrir tous les types et toutes les techniques.
On va voir dès maintenant vos équipes ! Pour les lots, ce sera à la fin de l'article.
, sur toutes les plateformes, avec des techniques variées qui ont semble-t-il toutes ou presque abouties au résultat optimal.
qui nous présente son équipe de 10 Pokémons. Pensant qu'il faut du piquant pour avancer, il en confie la gestion à
. Cette équipe bien échelonnée totalise
. Ces ajouts de poids lui permettent d'atteindre
. Et ça marche, avec maintenant
. Croyant en l'intelligence collective il nomme non pas un mais quatre chefs pour conduire son équipe au combat, et comme tout bon dresseur Pokémon il choisit bien évidemment des chefs de types tous différents :
. Une stratégie qui rapporte
en ce qui le concerne croit aux vertus de l'égalité. Dans l'équipe à coloration végétale qu'il t'a constituée, aucun Pokémon n'a plus de pouvoir qu'un autre. Et effectivement ces nobles intentions lui permettent de s'inscrire au classement avec
, bien évidemment de types différents. Il récolte ainsi
, là encore deux chefs de types différents. Un duo visiblement gagnant puisque cela lui permet de se hisser à
, que des types différents. Ces grandes compétences en dessage de Pokémon lui permettent ainsi de monter à
. La disparition des échelons intermédiaires est visiblement payante avec
. Parmi le reste des troufions, il nous fait évoluer
. Des ajustements lui permetant d'atteindre
. Chez les simples soldats nous avons donc cette fois-ci à la fois
.
parmi les autres. On progresse à
. Mais cette fois-ci, il nous accompagne les autres d'un
.
. Les autres sont également moins bien dotés avec 1,08% de puissance chacun.
Amiga68000 a écrit :Bravo pour vos algos de code génétique, c'est vraiment très intéressant, va falloir que je creuse cette technique. Bravo et merci pour le concours j'ai appris beaucoup. A la fois sur python, les algos
(que je tenterai de creuser à tête reposée).
Voici ma méthode, un peu plus classique ou du moins à l'ancienne. J'ai essayé plein de trucs, dans différentes voies. J'ai essayé de vous les synthétiser par étapes.
•
Brute force :
J'ai commencé par du
bruteforce en tirant aléatoirement des lots de 10 individus et des priorités aléatoires. Score 46 pas plus !
•
Jauger les Pokémon :
Un peu moins bourrin, j'ai jaugé chaque Pokémon un à un. Ça m'a permis de les classer.
•
Comprendre l'algo :
Comprendre le code et l'algo, j'en ai fait
un Excel.
Dans la colonne
Y, vous rentrez votre priorité d'attaque en face du Pokémon choisi. En cellule
Y2 vous récupérez le score.
/!\ le score est l'ancienne évaluation.
Pour l'excel, il a fallu que je cartographgie mes Pokémons. C'est là que j'ai compris que :
- la somme des priorités devait être inférieure à 187
- l'enregistrement d'une priorité devait se faire de la plus petite valeur vers la plus grande sinon par enchantement des individus disparaissaient de votre lot de Pokémon.
def cartographie():
priorite=9
global pkcarto
pkcarto=[]
lgn=["ID", "Points", "nb de X", "valeurs"]
#pkcarto.append(lgn)
for i in range(1,95):
pk(i,priorite)
# pkcarto.append(pkt)
t=""
for j in range(len(pkt)):
t+=str(int(pkt[j]))
if j!=len(pkt)-1:
t+=","
print("signature.append(["+str(t)+"]) #"+str(i))
pk(i,0)
return
•
Classer les Pokémon, méthode 2 :
J'ai vu que 2 Pokémons 63 et 72 sortaient du lot. J'ai alors fait 100.000 tirages de 10 Pokémons avec les 63 et 72 avec priorité 1. A chaque fois je relevais le score pour l'ajouter à la moyenne de chaque Pokémon contenu dans le tirage. But : les classer.
•
Varier les priorités :
En prenant les Pokémons avec les meilleurs résultats, j'ai fait varier toutes les priorités. Je suis arrivé à un très bon résultat
(3. je crois), malheureusement la combinaison était déjà prise, il a fallu que je réduise mes priorités pour arriver sur une combinaison et un score vierge. C'est là où j'ai eu mon classement
•
Combinaisons :
En prenant les meilleurs Pokémons de l'étape 4,
(30 environ, je ne me souviens plus du chiffre exact), j'ai fait toutes les combinaisons possibles par récursivité. Ça n'a pas amélioré mon score. Autre chose, je ne me suis pas penché sur la cas de la correction de score routine setst pour mieux comprendre les différences de score.
Voilà mes étapes à peu près dans l'ordre, j'ai fait plein de petites routines pour celà Voici ci-dessous mon code global, n'hésitez pas à me faire vos remarques, je suis pas un pro de la prog. Bonne lecture ;-)
Si vous avez besoin de plus d'explication je suis dispo.
#cas
import time
signature=[]
signature.append([1,1,1,1,0,0,0,1,1,1,0,1,0,1,0,1,1,0,1,0,0]) #1
signature.append([1,0,0,1,1,1,0,1,1,1,0,1,1,0,1,0,0,1,0,0,0]) #2
signature.append([0,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,0,1]) #3
signature.append([1,1,0,1,0,1,1,0,1,1,0,1,1,0,0,0,0,0,0,1,0]) #4
signature.append([0,1,0,1,1,0,1,1,0,0,1,0,0,1,1,1,1,1,1,1,1]) #5
signature.append([1,0,0,0,1,1,1,0,1,1,1,1,0,1,1,0,1,0,0,1,0]) #6
signature.append([0,0,0,1,0,0,1,1,1,1,0,0,0,1,0,1,1,1,0,0,1]) #7
signature.append([0,1,0,1,1,0,0,0,1,0,0,0,0,0,1,1,1,0,1,0,0]) #8
signature.append([0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,1,0]) #9
signature.append([1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0,1,0,1]) #10
signature.append([1,1,1,0,0,0,0,0,0,1,1,0,0,1,1,1,1,0,0,0,0]) #11
signature.append([0,1,0,1,1,0,0,0,1,1,0,1,0,0,0,0,1,0,1,0,0]) #12
signature.append([0,1,1,0,1,0,0,0,1,1,0,0,1,0,0,0,0,0,1,1,1]) #13
signature.append([1,0,0,0,0,1,1,0,1,0,1,0,0,1,1,1,0,0,0,1,0]) #14
signature.append([1,0,1,1,1,1,0,1,1,0,0,0,1,0,0,0,1,1,0,1,0]) #15
signature.append([1,1,0,0,0,1,1,1,1,0,1,1,1,0,1,0,1,0,0,1,1]) #16
signature.append([1,1,0,1,0,0,1,1,0,0,1,1,0,1,0,1,0,1,0,0,0]) #17
signature.append([1,0,0,0,0,0,0,0,1,1,1,0,1,0,0,0,0,1,1,1,1]) #18
signature.append([0,1,0,1,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1]) #19
signature.append([1,1,1,0,0,0,1,1,1,1,0,0,1,0,0,1,1,1,1,0,0]) #20
signature.append([0,1,1,0,0,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,1]) #21
signature.append([0,1,0,1,1,1,1,0,1,1,0,0,0,1,0,1,0,1,1,0,1]) #22
signature.append([0,1,0,1,1,1,1,1,0,1,1,1,0,0,1,1,0,1,1,0,0]) #23
signature.append([1,1,0,0,1,0,0,1,0,1,0,0,0,0,1,0,0,1,1,1,1]) #24
signature.append([1,0,0,1,0,0,1,0,0,0,1,0,0,1,1,1,0,0,1,0,0]) #25
signature.append([1,0,0,0,1,0,0,1,1,1,0,1,1,1,0,0,1,1,1,0,0]) #26
signature.append([0,0,0,1,1,1,0,0,1,0,1,0,0,1,1,1,1,1,1,0,1]) #27
signature.append([0,1,0,0,0,1,1,0,0,0,1,1,1,0,0,1,1,0,0,1,0]) #28
signature.append([0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,1,1,0,0,1,1]) #29
signature.append([1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,1,0,0,0]) #30
signature.append([1,0,1,0,1,0,0,0,0,0,0,0,0,1,0,1,1,1,0,0,1]) #31
signature.append([0,0,1,1,1,0,1,1,1,0,0,0,0,0,0,1,0,1,0,0,1]) #32
signature.append([1,1,1,1,1,0,1,0,0,1,0,0,1,0,1,0,1,1,0,1,0]) #33
signature.append([0,1,0,0,1,1,0,0,0,0,1,1,1,0,0,0,1,0,1,1,0]) #34
signature.append([1,1,1,0,1,1,0,1,0,1,0,0,1,0,1,0,0,1,1,0,1]) #35
signature.append([0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,1]) #36
signature.append([1,0,0,0,0,0,1,0,0,0,0,1,1,1,1,0,0,1,1,0,1]) #37
signature.append([1,1,1,0,0,1,1,1,1,0,1,0,0,1,0,0,1,0,0,0,1]) #38
signature.append([0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,1,1,1,1,1,0]) #39
signature.append([1,0,0,0,0,1,0,1,1,1,1,1,1,1,0,1,0,0,1,0,1]) #40
signature.append([0,1,0,1,0,0,0,0,0,1,0,1,1,1,0,1,0,0,0,0,1]) #41
signature.append([1,0,0,1,0,1,1,0,0,1,1,0,1,1,0,1,1,0,1,0,0]) #42
signature.append([1,1,1,1,0,1,1,1,0,0,1,1,1,1,1,0,0,1,0,1,0]) #43
signature.append([0,1,1,1,1,1,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1]) #44
signature.append([0,1,1,1,0,1,1,1,1,0,0,0,0,1,1,0,0,1,0,0,0]) #45
signature.append([0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,0,1,1,0,1,0]) #46
signature.append([0,0,1,0,1,1,1,0,0,1,1,1,1,1,1,0,1,1,1,1,0]) #47
signature.append([0,0,0,0,0,1,1,0,0,1,0,1,1,1,0,1,0,1,1,0,1]) #48
signature.append([1,0,1,0,0,0,1,1,0,0,0,0,1,0,1,1,1,0,1,0,0]) #49
signature.append([1,1,0,1,1,1,1,0,1,0,0,1,0,0,0,0,1,0,1,1,0]) #50
signature.append([1,1,1,1,1,1,0,0,1,0,1,0,1,1,0,0,1,0,0,1,0]) #51
signature.append([1,0,0,0,1,1,0,1,1,0,1,1,1,1,1,0,0,1,1,0,0]) #52
signature.append([1,1,0,1,1,1,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0]) #53
signature.append([0,0,0,1,1,0,1,0,1,0,1,0,1,0,0,1,0,0,0,1,0]) #54
signature.append([0,1,0,1,1,0,0,1,0,0,0,1,0,0,0,1,0,0,1,0,0]) #55
signature.append([1,0,1,1,0,1,0,1,1,1,1,0,1,1,1,0,0,0,1,0,0]) #56
signature.append([1,0,1,1,0,1,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0]) #57
signature.append([1,1,0,1,0,1,1,1,0,1,0,1,1,0,0,0,1,1,1,0,0]) #58
signature.append([0,0,0,1,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1]) #59
signature.append([1,0,0,1,0,0,0,0,1,1,1,1,1,1,0,0,0,1,0,1,0]) #60
signature.append([0,1,0,1,1,1,0,0,1,0,0,1,0,1,0,1,0,0,0,0,1]) #61
signature.append([1,1,1,0,0,1,0,1,1,0,0,1,0,1,1,1,1,0,1,0,1]) #62
signature.append([0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1]) #63
signature.append([0,0,1,0,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1]) #64
signature.append([0,1,1,0,0,1,1,0,0,0,0,0,0,1,1,0,0,1,1,1,1]) #65
signature.append([0,1,1,0,1,0,0,1,1,0,0,1,1,1,1,1,0,1,0,1,0]) #66
signature.append([1,1,0,0,0,1,1,1,0,0,0,0,1,1,0,1,1,1,1,0,0]) #67
signature.append([1,1,1,1,0,0,1,0,0,0,1,1,1,1,0,0,0,1,1,1,0]) #68
signature.append([0,1,1,0,1,1,1,0,1,1,0,1,1,0,0,1,1,0,0,1,1]) #69
signature.append([0,1,0,1,1,1,0,1,0,0,1,0,0,1,0,0,1,0,1,1,0]) #70
signature.append([1,1,0,0,0,1,0,1,1,1,1,1,0,1,1,1,1,0,1,0,0]) #71
signature.append([1,0,0,1,1,1,1,0,1,0,1,1,1,1,1,0,1,1,1,0,1]) #72
signature.append([1,0,0,1,0,0,1,1,0,0,1,1,1,1,0,1,0,1,1,1,0]) #73
signature.append([1,0,0,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,1,1,0]) #74
signature.append([1,1,1,1,0,1,1,0,0,1,0,1,1,0,1,0,0,0,1,1,1]) #75
signature.append([1,0,1,1,0,1,0,1,0,1,1,1,0,0,1,0,1,0,1,0,1]) #76
signature.append([1,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0]) #77
signature.append([1,0,0,1,0,0,0,1,0,1,0,0,1,0,0,1,0,1,1,0,0]) #78
signature.append([1,1,0,0,1,1,1,1,0,0,0,0,0,1,0,1,0,1,1,1,1]) #79
signature.append([0,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,0,0,0,1,1]) #80
signature.append([0,1,0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,1,1,0,1]) #81
signature.append([0,1,0,0,0,1,1,1,0,0,1,0,0,0,0,0,0,1,1,1,0]) #82
signature.append([0,1,1,1,0,1,0,0,1,0,1,1,1,1,0,1,0,1,0,0,0]) #83
signature.append([1,1,1,0,1,0,0,0,1,1,0,0,1,0,1,0,0,1,0,0,1]) #84
signature.append([1,0,0,1,1,1,1,1,1,1,1,0,0,1,1,1,0,0,1,1,0]) #85
signature.append([0,0,0,1,1,1,0,1,1,1,1,0,0,1,1,0,1,0,0,0,1]) #86
signature.append([1,1,1,0,0,0,0,1,1,0,1,1,1,0,0,1,0,0,0,0,1]) #87
signature.append([1,1,1,0,1,0,0,0,1,1,1,0,0,1,1,1,1,0,1,0,1]) #88
signature.append([0,1,0,1,0,1,0,0,1,1,1,0,1,0,1,1,0,1,1,0,1]) #89
signature.append([1,0,0,0,0,0,1,1,1,0,0,1,1,0,1,1,1,1,1,0,0]) #90
signature.append([0,1,0,1,0,0,0,0,0,1,0,0,0,0,1,0,1,0,0,1,0]) #91
signature.append([0,0,0,1,1,0,1,0,1,1,1,0,1,0,1,1,0,1,1,1,0]) #92
signature.append([1,0,0,1,0,1,0,0,1,1,0,0,0,0,0,0,0,0,1,1,1]) #93
signature.append([0,0,0,1,1,0,1,0,1,0,1,1,1,0,0,1,1,1,0,0,1]) #94
"""
Tente d'être le meilleur le meilleur de tous les dresseurs
en relevant notre défi.
Ton but est simple, tu dois te constituer la main Pokémon
la plus puissante possible sachant que bien évidemment les Pokémons
ont des compétences différentes, et ce sous les seules règles suivantes :
seuls les Pokémon
n°1 à 94 sont autorisés
ta main ne peut contenir qu'un maximum de 10 Pokémons
tous les Pokémons
dans ta main doivent être différents
Pour cela, un script Python va offrir à ta calculatrice
la fonction pk(n,p) pour ajouter un Pokémon à ta main, avec :
n, le numéro de Pokémon
de 1 à 94
p, la priorité d'attaque que tu souhaites donner au Pokémon
en question (1 par défaut)
Cas particuliers; si le Pokémon est déjà dans ta main sa priorité d'attaque sera mise à jour;
et p=0 retire le Pokémon de ta main.
"""
from math import *
def mmod(a,b):
#modulo a b
return a%b
#0 Nspire MicroPython
#1 NumWorks Python
#2 G90/35+E2 Python
#3 G75/85/95 CasioPython
#4 83PCE Python/PyAdapt
#5 HP Prime CAS
#6 G90/35+E2 KhiCAS
def getplatform():
k=-1
try:
if chr(256)==chr(0):
k=[6,5][("HP" in version())>0]
except:
pass
if k>=0:
return k
try:
import sys
try:
if sys.platform=="nspire":
k=0
elif sys.platform.startswith("TI-Python"):
k=4
except:
k=3
except:
try:
import kandinsky
k=1
except:
k=2
return k
def getlinechars(o=False):
# c,k=2**31-1,getplatform()
c=2**31-1
k=getplatform() #=-1 sur PC
if k>=0:
c=[53,o and 99 or 29,o and 509 or 21,31,32,c,c][k]
return c
lnm=["Bulbizarre","Herbizarre","Florizarre","Salameche","Reptincel","Dracaufeu","Carapuce","Carabaffe","Tortank","Chenipan","Chrysacier","Papilusion","Aspicot","Coconfort","Dardargnan","Roucool","Roucoups","Roucarnage","Rattata","Rattatac","Piafabec"]
lnm.extend(["Rapasdepic","Abo","Arbok","Pikachu","Raichu","Sabelette","Sablaireau","Nidoran F","Nidorina","Nidoqueen","Nidoran M","Nidorino","Nidoking","Melofee","Melodelfe","Goupix","Feunard","Rondoudou","Grodoudou","Nosferapti","Nosferalto"])
lnm.extend(["Mystherbe","Ortide","Rafflesia","Paras","Parasect","Mimitoss","Aeromite","Taupiqueur","Triopikeur","Miaouss","Persian","Psykokwak","Akwakwak","Ferosinge","Colossinge","Caninos","Arcanin","Ptitard","Tetarte","Tartard","Abra","Kadabra"])
lnm.extend(["Alakazam","Machoc","Machopeur","Mackogneur","Chetiflor","Boustiflor","Empiflor","Tentacool","Tentacruel","Racaillou","Gravalanch","Grolem","Ponyta","Galopa","Ramoloss","Flagadoss","Magneti","Magneton","Canarticho","Doduo","Dodrio","Otaria"])
lnm.extend(["Lamantine","Tadmorv","Grotadmorv","Kokiyas","Crustabri","Fantominus","Spectrum","Ectoplasma"])
#na,pkl=21,[]
na=21
pkl=[]
#mrandmax,mrand,mfmax,nn,mp=2**31-1,0,93,getlinechars(True)-na,na//2
mrandmax=2**31-1
mrand=0
mfmax=93
nn=getlinechars(True)-na
mp=na//2 #quotien de la division entière, 21//2 = 10
def mround(f):
#renvoie l'entier le plus proche
# 0.5 --> 1
# 0.4 --> 0
#-0.4 --> 0
#-1.4 --> -1
#-1.5 --> -2
#-1.6 --> -2
d=mmod(abs(f),1) #resultat = 0.xxxxx
return (mfloor(abs(f))+(d>=.5))*(1-2*(f<0))
def mfloor(f):
#Arrondi -mfloor(-5.2)=-5
return round(f)-(round(f)>f)
def mceil(f):
#arrondi SUP
return round(f)+(round(f)<f)
def mseed(s):
global mrand
mrand=mmod(s,mrandmax)
def mrandom():
mseed(mrand*16807)
return float(mrand/mrandmax)
def muniform(mini,maxi):
return mrandom()*(maxi-mini)+mini
def mrandint(mini,maxi):
return mround(muniform(mceil(mini),mfloor(maxi)))
def mf2f(n):
return float(n/mfmax) #mfmax=93
def mbit(a,b):
return mmod((a//(2**b)),2)
def getattack(p,pts):
#p=numéro de l'individu
#pts=l[2]/somme(l[2])
global pkt
# mseed(42) #mrand=42
# print(str(pts))
# for k in range(p+1):
# mrandom() #génère p+1 fois mrand
# a,pka=mrandint(1,mrandmax),""
# a=mrandint(1,mrandmax)
pka=""
npka=0
# print("p="+str(p))
# print(signature[p])
for j in range(na): #na=21
# if mbit(a,j)!=0:
if signature[p][j]==1:
pka+="X"
npka+=1
pkt[j]+=pts
else:
pka+=" -"[getplatform()>=5]
# print("pka="+pka)
return pka
def i2c(k):
return chr(k+33)
def c2i(c):
return ord(c)-33
def f2mf(f):
return mround(float(f*mfmax)) #mfmax=93
def clean():
#recalcule tous les l[2] des individus
global pkl #données des individus
# t,s=0,0
s=0
t=0
for l in pkl:
#t=somme(priorités)
t+=l[1] #t=t+l[1]
# print("t="+str(t))
for l in pkl:
l[2]=f2mf(l[1]/(t or 1)) #t or 1 pour eviter la division par 0
# l[2]=mround(float(l[1]/(t or 1)*93)) #mround = entier le plus proche
s+=l[2] #s=s+l[2] --> s= sommes des l[2]
if(l[2]<=0):
# print("-----")
# print("t="+str(t))
# print("remove "+str(l))
pkl.remove(l) #on enlève l'individu
return clean() #on reitère
return s #on renvoie
def pk_ORIGINE(n,p=1,d=2):
global pkt, pkl
n-=1
if n>=0 and n<len(lnm):
new=True
for k in range(len(pkl)):
if pkl[k][0]==n:
new,pkl[k][1]=False,max(p,0)
if new and len(pkl)<mp:
pkl.append([n,max(p,0),0])
ptt,pkt,t,st=clean(),[0 for k in range(na)],0,""
for l in pkl:
s=getattack(l[0],l[2]/ptt)
if d:
sn=" "+lnm[l[0]]
if len(sn)>nn:
sn=sn[:nn]
print(s+sn+" #"+str(l[0]+1)+" (f="+str(l[1])+")")
st=i2c(l[0])+st+i2c(l[2])
for k in pkt:
if(k):
t+=log(e+k*len(pkl))
if(d):
if(d>=2):
print("Bon score ? Si oui envoie code suivant a info@tiplanet.org :")
print(""+st)
return float(t)
def pk(n,p=1,d=2):
global pkt,pkl
global sign
#on décrémente de 1, la liste commence à 0
n-=1
if n>=0 and n<len(lnm):
#le n° correspond à un individu
new=True
for k in range(len(pkl)):
if pkl[k][0]==n:
#individu existant, on remplace sa priorité
new=False
pkl[k][1]=max(p,0) #nouvelle priorité
if new and len(pkl)<mp:
#nouvel individu et poignée de 10 non pleine
pkl.append([n,max(p,0),0]) #ajout de l'individu
#calcul des attaques
# ptt,pkt,t,st=clean(),[0 for k in range(na)],0,""
ptt=clean() #recalcule les l[2] et renvoie la somme des l[2]
pkt=[0 for k in range(na)] # [0 0 ... 0 0 0]
t=0
st=""
for l in pkl:
s=getattack(l[0],l[2]/ptt) #maximiser l[2]/ppt
if d:
sn=" "+lnm[l[0]]
if len(sn)>nn:
sn=sn[:nn]
st=i2c(l[0])+st+i2c(l[2])
for k in pkt:
if(k): #k<>0
t+=log(e+k*len(pkl)) #LN log neperien
# print(t,e,k,len(pkl))
# if(d):
# print(""+st)
sign=""+st
return float(t)
def setst(st):
s,pkl[:],n=0,[],len(st)//2
for k in range(n):
s=pk_ORIGINE(c2i(st[n-1-k])+1,c2i(st[n+k+len(st)%2]),k+1>=n)
return s
#print("pk(n,p) pour rajouter le Pokemon n a ta main avec p points d'attaque.")
# print(s+sn+" #"+str(l[0]+1)+" (f="+str(l[1])+")")
#
#
#
# ICI CODE PERSO
#
#
#
import csv
from random import randint
"""
if len(pkl)>9:
numPkARetirer=pkl[randint(0,len(pkl)-1)]][0]
pk(numPkARetirer,0)
"""
#ALGO de recherche
def affListe(listepkl):
for k in listepkl:
print(lnm[k[0]]+" #"+str(k[0]+1)+" (f="+str(k[1])+")" )
def brutforce():
global pkl
global pklMAX
global scoreMax
global score
#construction d'une liste de 10
for i in range(20):
numPk=randint(1,94)
force=randint(1,10)
score=pk(numPk,force)
scoreMax=score
print("===== Liste depart")
pkl=[[25, 1, 0], [81,1, 0], [46,1, 0], [19, 1, 0], [49, 1, 0], [50, 1, 0], [66, 1, 0], [34, 1, 0], [71, 35, 0], [62, 143, 0]]
pklMAX=list(pkl)
affListe(pklMAX)
#bouclage pour trouver meilleur score
for i in range(20000):
# print("\n************** Iterration="+str(i))
if i%1000==0:print(i)
# n=len(pkl)-1
# print(n)
# if n==9:
n=7 #onnenleve pas 72 ni 63
numPkARetirer=pkl[randint(0,n)][0]+1
# print("----- supprimer "+str(numPkARetirer))
pk(numPkARetirer,0)
# affListe(pkl)
while len(pkl)<10:
numPk=72
while numPk==72 or numPk==63:
numPk=randint(1,94)
# force=randint(1,10)
force=1
# print("+++++ Ajouter "+str(numPk))
score=pk(numPk,force)
# print("SCORE="+str(score))
# affListe(pkl)
if score>scoreMax:
scoreMax=score
pklMAX=list(pkl)
print("\nMAX --------------------------------")
print("score ="+str(scoreMax))
affListe(pklMAX)
else:
pkl=list(pklMAX)
return
def ScanStatPKi(numPkFixe):
global pkl
#on fixe 72 et 63
#pour chaque PKi de 1 à 93
# on tire n combinaisons aléatoires,
# on note le score pour le PKi
#à la fin on classe les PKi selon leur score
print(numPkFixe)
score=0
scoreMax=0
ctr=0
#on remplace l'élément
pkl[7][0]=numPkFixe-1
#teste si numPkFixe est dans la plage pkl de 0 à 6
for i in range(0,7):
if pkl[i][0]+1==numPkFixe:
boucler=True
while boucler:
n=randint(1,94)
if n!=numPkFixe and n!=72 and n!=63:
pkl[i][0]=n
boucler=False
boucle=2000
#bouclage pour trouver meilleur score
for i in range(boucle):
#if i%1000==0:print(i)
n=6 #onnenleve pas 72 ni 63, ni numPkFixe
del pkl[0]
boucler=True
while boucler: #len(pkl)<10:
numPk=randint(1,94)
boucler=False
for k in pkl:
if k[0]+1==numPk:
#il y a déjà un num
boucler=True
#on a un numéro
pkunit=[]
pkunit.append(numPk-1)
pkunit.append(1)
pkunit.append(1)
pkl.insert(6,pkunit)
score=pk(pkl[0][0]+1,1)
scoreMax+=score
score=scoreMax/boucle
data=[]
data.append(numPkFixe)
data.append(score) #Score
return data
def scanstat():
global pkl
start_time=time.time()
pkl=[[25,1,0],[81,1,0],[46,1,0],[19,1,0],[49,1,0],[50,1,0],[66,1,0],[34,1,0],[71,35,0],[62,143,0]]
d=[]
for i in range(1,95): #range(1,95) --> 1 à 94
if i!=72 and i!=63:
d.append(ScanStatPKi(i))
print("Temps d execution : "+str( (time.time() - start_time)))
sauve(d)
return d
#scanstat()
def init():
pk(3)
pk(43)
pk(85)
pk(75)
pk(71)
pk(62)
pk(16)
pk(6)
pk(72,35)
pk(63,143)
return
def scoreprio(pk1,pk2):
#max 186
data=[]
data.append(0)
data.append(pk1)
data.append(0)
data.append(pk2)
data.append(0)
smax=0
for p1 in range(1,177): #K186-8-1
for p2 in range(1,178-p1):
if p1<p2:
pk(pk1,p1)
s=pk(pk2,p2)
else:
pk(pk2,p2)
s=pk(pk1,p1)
if s>smax:
smax=s
data[0]=smax
data[2]=p1
data[4]=p2
# pkl=[]
pk(pk1,0)
pk(pk2,0)
return data
def duelprio():
start_time=time.time()
d=[]
for i in range(1,94):
for j in range(i,95):
d.append(scoreprio(i,j))
print("pk "+str(i)+" vs pk "+str(j))
print("Temps d execution : "+str( (time.time() - start_time)))
sauve(d)
# Affichage du temps d execution
return d
def duel(numpk,r):
#crée une ligne se scores du numpk vs chaque element dans r
priorite=1
pkduel=[]
pk(numpk,priorite)
for i in r:
if numpk!=i:
pkduel.append(pk(i,priorite))
pk(i,0)
else:
pkduel.append(0)
pk(numpk,0)
return pkduel
def matriceduel():
p=[]
r=range(1,95)
r=[16,23,62,69,71,75,88]
r=range(1,4)
r=range(1,95)
for i in r:
p.append(duel(i,r))
return p
def cartographie():
priorite=9
global pkcarto
pkcarto=[]
lgn=["ID","Points","nb de X","valeurs"]
#pkcarto.append(lgn)
for i in range(1,95):
pk(i,priorite)
# pkcarto.append(pkt)
t=""
for j in range(len(pkt)):
t+=str(int(pkt[j]))
if j!=len(pkt)-1:
t+=","
print("signature.append(["+str(t)+"]) #"+str(i))
pk(i,0)
return
def sauve(p):
#sauvegarde une matrice p en csv
#with open("L:/_Datas/11 - Arnaud/Python - DEFI/table.csv", "w") as f_write:
with open("d:/table.csv", "w") as f_write:
writer = csv.writer(f_write,delimiter=";")
writer.writerows(p)
return
def seek(id):
smax=0
for i in range(1,130):
s=pk(id,i)
if s>smax:
smax=s
priorite=i
score=pk(id,priorite)
print("pk("+str(id)+","+str(priorite)+")=" +str(score) )
return score
def valeur(priorite=1):
#renoie une liste de chaque score ID seul
l=[]
for i in range(1,95):
s=pk(i,priorite)
l.append(s)
pk(i,0)
print(s)
return
def estdanspkl(ID):
r=False
for p in pkl:
if p[0]+1==ID:
r=True
break
return r
def meilleurID(IDaexclure=0):
#renvoie le meilleur ID
priorite=1
IDmax=0
smax=0
if len(pkl)==10:
IDmax=0
else:
for ID in range(1,95):
if ID!=IDaexclure:
if not(estdanspkl(ID)):
#l'ID n'est pas dans la liste pkl
s=pk(ID,priorite)
if s>smax:
#score meilleur
smax=s
IDmax=ID
pk(ID,0)
return IDmax
def creermeilleurID():
ID=meilleurID()
if ID!=0:
pk(ID)
seekall()
return
def seekall():
for p in pkl:
score=seek(p[0]+1)
return score
def scan():
#max 186
#186-8 = 178
smax=0
f63=94
f72=0
f03=0
pk(63,f63)
for i in range(1,178-1):
for j in range(1,178-i):
# for k in range(1,178-i-j):
pk(3,i)
s=pk(72,j)
st=setst(""+sign)
if st>smax:
smax=st
# f63=i
f72=i
f03=j
print("MAX setst="+str(smax))
print("S="+str(s))
# print("Signature="+sign+" "+str(st))
print(" P72="+str(i)+" P03="+str(j))
pk(63,f63)
pk(72,f72)
pk(3,f03)
return
def combi(level,levelmax,pkpossibles,smax=0,pkdeb=0):
#on incrémente le niveau d'arbo
l=level+1
# print("l="+str(l))
if l>levelmax:
#on est arrivé en bas de l'arbo souhaitée
#on peut faire les calculs
s=seekall()
print("s="+str(s))
if s>smax:
smax=s
print("smax=",str(smax))
else:
# for i in pkpossibles:
for ii in range(pkdeb,len(pkpossibles)):
i=pkpossibles[ii]
#☺ if not(estdanspkl(i)):
#l'ID n'est pas déjà dans la liste
# print("id="+str(i))
#on ajoute l'individu
pk(i)
#on descend en arbo
smax=combi(l,levelmax,pkpossibles,smax,ii+1)
pk(i,0) #on eneleve l'individu
return smax
def trouvecombi():
pkpossibles=[16,62,71,23,69,75,88]
pkpossibles=[16,62,71,23,69]
smax=combi(0,3,pkpossibles)
return smax
#
#
# RESULTATS
#
#
def initamiga():
#amiga68000
#record à battre = 49,31730
#49.31975298152274
pk(3,1)
pk(62,1)
pk(71,1)
pk(16,1)
pk(43,1)
pk(85,1)
pk(47,1)
pk(51,1)
pk(72,32)
pk(63,128)
#la somme des priorités <=186
return
"""
transmis le 14/10/19 via amiga68000
XXXXX XXXXXXXXX X Florizarre
XXX X XX X XXXX X X Tartard
XX X XXXXX XXXX X Empiflor
XX XXXX XXX X X XX Roucool
XXXX XXX XXXXX X X Mystherbe
X XXXXXXXX XXX XX Dodrio
X XXX XXXXXX XXXX Parasect
XXXXXX X X XX X X Triopikeur
X XXXX X XXXXX XXX X Tentacool
XXXX XXXXXXXXXX XXXX Abra
Bon score ? Si oui
envoie code suivant
a info@tiplanet.org :
_hSOuK0g^#""""""""3h
49.31975298152274
"""
def initk():
#record à battre = 49,31730
#49.32078546995182
pk(3,1)
pk(62,1)
pk(71,1)
pk(16,1)
pk(43,1)
pk(85,1)
pk(47,1)
pk(51,1)
pk(72,35)
pk(63,143)
#la somme des priorités <=186
return
"""
XXXXX XXXXXXXXX X Florizarre
XXX X XX X XXXX X X Tartard
XX X XXXXX XXXX X Empiflor
XX XXXX XXX X X XX Roucool
XXXX XXX XXXXX X X Mystherbe
X XXXXXXXX XXX XX Dodrio
X XXX XXXXXX XXXX Parasect
XXXXXX X X XX X X Triopikeur
X XXXX X XXXXX XXX X Tentacool
XXXX XXXXXXXXXX XXXX Abra
Bon score ? Si oui
envoie code suivant
a info@tiplanet.org :
_hSOuK0g^#""""""""3i
49.32078546995182
"""
def init5():
#record à battre = 49,31730
#49.28269871690558
pk(16,1)
pk(51,1)
pk(58,1)
pk(62,1)
pk(71,1)
pk(76,1)
pk(5,1)
pk(6,1)
pk(72,35)
pk(63,143)
return
def init4():
#record à battre = 49,31730
#49.138894711933105
pk(85,1)
pk(89,1)
pk(69,1)
pk(73,1)
pk(90,1)
pk(86,1)
pk(88,1)
pk(87,1)
pk(72,35)
pk(63,143)
return
def init3():
#record à battre = 49,31730
#
pk(63,64)
pk(3,1)
pk(72,16)
pk(62,1)
pk(71,1)
pk(16,1)
pk(43,1)
#13 X
pk(23,1)
pk(16,1)
pk(75,1)
return
def init21():
#record à battre = 49,31730
#49.274636982498805
pk(63,61) #56
pk(3,1)
pk(72,16)
pk(5,1)
pk(43,1)
pk(47,1)
pk(85,1)
#17.0724019377368
pk(16,1)
# pk(62,1)
# pk(71,1)
pk(23,1)
# pk(69,1)
# pk(75,1)
pk(88,1)
# seek(63)
# seek(72)
return
def init1():
#record à battre = 49,31730
#49.28984977976379
pk(63,61) #56
pk(3,1)
pk(72,16)
pk(5,1)
pk(43,1)
pk(47,1)
pk(85,1)
#17.0724019377368
# pk(16,1)
# pk(62,1)
pk(71,1)
# pk(23,1)
pk(69,1)
pk(75,1)
# pk(88,1)
# seek(63)
# seek(72)
return
def init2():
#record à battre = 49,31730
#49.31571202586076
pk(3,1)
pk(5,1)
pk(43,1)
pk(47,1)
pk(85,1)
#17.0724019377368
pk(16,1)
pk(62,1)
pk(71,1)
# pk(23,1)
# pk(69,1)
# pk(75,1)
# pk(88,1)
pk(72,35)
pk(63,143) #56
return
#algo()
"""
print("\nMAX --------------------------------")
print(scoreMax)
affListe(pklMAX)
"""
print("--------------------------------")
print("pk(n,p) pour rajouter\nle Pokemon n a ta main\navec p points d'attaque.")
#end
un peu plus puissant. Le reste c'est le commun des mortels avec plus que 1.05% chacun. Score
un peu moins effacé. Et surtout, plus que 1,04% chacun pour le reste qui n'est plus que de la chair à canon. Score
mais nous en fait une version poids-lourd en nous remontant les autres à 1,05% et leur ajoutant un
. Tout ça pour
cent20 et Golden man a écrit :
•
PREMIERS TESTS "À LA MAIN"
Comme beaucoup de joueurs, nous avons commencé par générer des mains au petit bonheur la chance, gratifié d’un score maximum de 44,2 nous sommes vite passé à une autre méthode. Nous aurions bien voulu commencer les recherches sur la NumWorks, mais les problèmes de mémoire dont elle souffre rendent ces recherches impossibles. Ajouter quelques lignes de codes au script de 3.7 Ko aurait fait planter la calculatrice. Nous sommes donc passé sur
Thonny et y avons exécuté nos scripts Python.
•
ATTAQUE N°1 : 10^N TIRAGES ALÉATOIRES
Après avoir neutralisé les fonctions
print, les affichages des scripts du concours, et rajouté quelques variables globales, une boucle de tirage aléatoire fut codée.
Le résultat n’est pas optimal, on ne tire aléatoirement que les Pokémons en pensant naïvement que les forces sont forcement des entiers entre 1 et 10. Mais on arrive à fabriquer des scores aux alentours de 46,2. En une nuit, on arrive péniblement à réaliser entre 4 et 7 millions de tirages. A ce stade de la recherche, compte tenu de nos hypothèses, on cherche une solution optimale parmi 3 x 10^19 possibilité. Toute force brute est impossible.
import random
score, scoremax = 0.0, 0.0
code, codemax = 0.0, 0.0
tentative = 0
def tiragemain():
for i in range(1,11,1):
pokemonaleatoire = random.randint(1,94)
score=pk(pokemonaleatoire,i)
return score,code
while score<49.3:
# Les trois lignes ci-dessous réinitialisent le script, qui tourne sans s'arrêter
na,pkl=21,[]
lnm =["Bulbizarre","Herbizarre","Florizarre","Salameche","Reptincel","Dracaufeu",
"Carapuce","Carabaffe","Tortank","Chenipan","Chrysacier","Papilusion","Aspicot",
"Coconfort","Dardargnan","Roucool","Roucoups","Roucarnage","Rattata","Rattatac",
"Piafabec","Rapasdepic","Abo","Arbok","Pikachu","Raichu","Sabelette","Sablaireau",
"Nidoran F","Nidorina","Nidoqueen","Nidoran M","Nidorino","Nidoking","Melofee",
"Melodelfe","Goupix","Feunard","Rondoudou","Grodoudou","Nosferapti","Nosferalto",
"Mystherbe","Ortide","Rafflesia","Paras","Parasect","Mimitoss","Aeromite","Taupiqueur",
"Triopikeur","Miaouss","Persian","Psykokwak","Akwakwak","Ferosinge","Colossinge","Caninos",
"Arcanin","Ptitard","Tetarte","Tartard","Abra","Kadabra","Alakazam","Machoc","Machopeur",
"Mackogneur","Chetiflor","Boustiflor","Empiflor","Tentacool","Tentacruel","Racaillou",
"Gravalanch","Grolem","Ponyta","Galopa","Ramoloss","Flagadoss","Magneti","Magneton",
"Canarticho","Doduo","Dodrio","Otaria","Lamantine","Tadmorv","Grotadmorv","Kokiyas",
"Crustabri","Fantominus","Spectrum","Ectoplasma"]
mrandmax,mrand,mfmax,nn,mp=2**31-1,0,93,getlinechars(True)-na,na//2
tentative = tentative+1
score,code = tiragemain()
if score>scoremax:
scoremax = score
codemax = code
print("################# tirage n°",tentative,"score =", scoremax,"avec le code", codemax,"#################", round(score,8))
•
ATTAQUE N°2 : RECHERCHE DES POKÉMON FORTS !
On décide de faire tourner le script précédent et de mémoriser les compositions des mains supérieures à 46. On va donc réaliser quelques millions de tirages, et dénombrer les Pokémons qui ont permis de faire une main supérieure à 46.
Le lendemain, nous avons un histogramme qui nous donne des Pokémons performants :
[0, 54, 25, 143, 11, 99, 39, 23, 5, 10, 6, 11, 9, 17, 10, 31, 70, 13, 12, 10, 48, 15, 38, 51, 18, 6, 21, 33, 13, 19, 5, 8, 13, 48, 13, 33, 35, 5, 31, 24, 31, 9, 33, 94, 28, 13, 5, 106, 16, 8, 34, 51, 27, 6, 13, 3, 36, 33, 42, 4, 17, 9, 72, 311, 3, 16, 30, 25, 32, 74, 13, 60, 172, 40, 12, 62, 44, 1, 8, 38, 6, 32, 15, 22, 21, 101, 25, 24, 73, 19, 26, 5, 33, 4, 18]
Le Pokémon 63
(Abra) est sorti 311 fois dans la nuit dans des mains valant plus de 46 points. Le 64 lui était très mauvais, et il n’était que dans 3 mains valant plus de 46 points.
if score>46:
for i in listeobtenu:
benchmark46(i,score)
•
ATTAQUE N°3 : TIRAGES ALÉATOIRES SUR LISTE OPTIMALE
Le deuxième jour, nous avons poursuivi les tirages aléatoires mais sur des listes optimales générées à l’aide de l’histogramme de la veille.
• Liste large :
[ 3,5,16,33,43,47,51,58,62,63,69,71,72,73,75,76,85,88 ]
• Liste
short :
[ 3,5,16,47,62,63,69,72,75,85,88]
Au lieu de tirer au hasard un Pokémon parmi 94, on le tirait dans une liste prédéfinie à diverses positions
(nous pensions que la force était la position, donc un entier entre 1 et 11).
5 000 000 de tirages plus tard, pas de grandes améliorations, 47.6 est notre meilleur score, mais c’est déjà un joli résultat.
def tiragemain():
listeobtenu =[]
top = [ 3 , 5 , 16 , 47, 62, 63, 69, 72, 75, 85, 88]
random.shuffle(top)
for i in range(1,11,1):
pokemonaleatoire = top[i-1]
listeobtenu.append(pokemonaleatoire)
score=pk(pokemonaleatoire,i)
for i in listeobtenu:
benchmark(i,score)
if score>47:
for i in listeobtenu:
benchmark47(i,score)
return score,code,listeobtenu
•
ATTAQUE N°4 : VALEUR MOYENNE DES MAINS
Toutes les tentatives pour optimiser la valeur moyenne des mains ont échouées. Le calcul lui même de cette moyenne n’étant pas concluant.
•
ATTAQUE N°5 : RECHERCHE DES POKÉMON FORTS SUR ℝ
En lisant le forum associée à ce défi
sur Planète Casio, on croit comprendre qu’il n’y a pas un nombre fini de combinaisons, donc si on tire
n dans les entiers entre 1 et 94,
p lui serait un réel. On relance les scripts précédents et miracle on passe au dessus de 47.8.
def tiragemain():
listeobtenu =[]
for i in range(1,11,1):
pokemonaleatoire = random.randint(1,94)
listeobtenu.append(pokemonaleatoire)
score=pk(pokemonaleatoire,uniform(0,2**31-1))
if score>46:
for i in listeobtenu:
benchmark47(i,score)
return score,code,listeobtenu
Après avoir affiné la liste des Pokémons optimaux, on relance les autres scripts qui mélangent, permutent, tirent d’après la liste optimale, et on obtient nos premiers score à 48.
0^uxOeh%_##>(6#))*&#
48.12835164090643
^heO0xku#_D#’’#%$*.0
48.14694065075124
•
ATTAQUE N°6 : ÉLIMINATION DES PLUS FAIBLES
N’étant pas certains d’avoir la main optimale, on essaye de remplacer un Pokémon par tous les autres pour voir si le score s’améliore. Cette méthode ne permet pas de progresser.
•
ATTAQUE N°7 : TENTATIVE DE SPÉCIFICATIONS DES FONCTIONS
On décide alors de documenter le code, de le décrypter, d’essayer de voir si on ne peut pas faire le problème à l’envers, c’est une attaque par spécification du code.
def mmod(a, b):
# retourne la partie décimale de a a vérifier
# si a est un entier, retourne 0
# ?? intérêt , a % b économise de la mémoire
return a % b
def getplatform():
# retourne la valeur 1
# ?? intérêt ?
return 1
def getlinechars(o=False):
# Cette fonction est une bague. Elle est appelée une unique fois avec true
# et alors getlinechars(True) retourne 99
c = 2 ** 31 - 1
k = getplatform()
# ?? k = 1 ;-)
if k >= 0:
# k = 1 donc est exécuté dans 100% des cas ...
c = [53, o and 99 or 29, o and 509 or 21, 31, 32, c, c][k]
return c # c= 99 ... sauf astuce et codage plus bas ^^
# pas défaut, en l'absence de True getlinechars() retourne 29
Les premières fonctions sont faciles à spécifier. Les variables sont rigolotes.
na = 21
# 42/2 ? :-) Utilisé 2 fois, jamais modifié
mrandmax = 2 ** 31 - 1
# valeur max d'un entier positif signé codé en 32 bits... spé NSI inside
mrand = 0
# rien d'aléatoire ici, sera modifié par la fonction mseed()
mfmax = 93
# 94-1
nn = getlinechars(True) - na
# nn = 99-21 = 78 (sans calculatrice !)
mp = na // 2
# mp = 10, jamais modifié, utilisé une seule fois f° pk
Mais nous sommes restés coincés sur les fonctions
getattack(),
clean() et surtout
pk(). Par contre la fonction
setst() nous a passionnés !
Ci-contre le script original à peine modifié, commenté et codé en pep8.
# cas
import math
# pas utilisé ?
"""Ajouts"""
# sera utilisé dans nos scripts
code = 0.0
"""Fin des ajouts"""
def mmod(a, b):
# retourne la partie décimale de a a vérifier
# si a est un entier, retourne 0
# ?? intérêt , a % b économise de la mémoire
return a % b
def getplatform():
# retourne la valeur 1
# ?? intérêt ?
""" modifié, 1 avant 2 maintenant"""
return 2
def getlinechars(o=False):
# Cette fonction est une bague. Elle est appelée une unique fois avec true
# et alors getlinechars(True) retourne 99
c = 2 ** 31 - 1
k = getplatform()
# ?? k = 1 ;-)
if k >= 0:
# k = 1 donc est éxécuté dans 100% des cas ...
c = [53, o and 99 or 29, o and 509 or 21, 31, 32, c, c][k]
return c # c= 99 ... sauf astuce et codage plus bas ^^
# pas défaut, en l'absence de True getlinechars() retourne 29
na = 21
# 42/2 ? :-) Utilisé 2 fois, jamais modifié
pkl = []
# une liste
lnm = ["Bulbizarre", "Herbizarre", "Florizarre", "Salameche", "Reptincel", "Dracaufeu", "Carapuce",
"Carabaffe", "Tortank", "Chenipan", "Chrysacier", "Papilusion", "Aspicot", "Coconfort",
"Dardargnan", "Roucool", "Roucoups", "Roucarnage", "Rattata", "Rattatac", "Piafabec",
"Rapasdepic", "Abo", "Arbok", "Pikachu", "Raichu", "Sabelette", "Sablaireau", "Nidoran F",
"Nidorina", "Nidoqueen", "Nidoran M", "Nidorino", "Nidoking", "Melofee", "Melodelfe", "Goupix",
"Feunard", "Rondoudou", "Grodoudou", "Nosferapti", "Nosferalto", "Mystherbe", "Ortide",
"Rafflesia", "Paras", "Parasect", "Mimitoss", "Aeromite", "Taupiqueur", "Triopikeur", "Miaouss",
"Persian", "Psykokwak", "Akwakwak", "Ferosinge", "Colossinge", "Caninos", "Arcanin", "Ptitard",
"Tetarte", "Tartard", "Abra", "Kadabra", "Alakazam", "Machoc", "Machopeur", "Mackogneur",
"Chetiflor", "Boustiflor", "Empiflor", "Tentacool", "Tentacruel", "Racaillou", "Gravalanch",
"Grolem", "Ponyta", "Galopa", "Ramoloss", "Flagadoss", "Magneti", "Magneton", "Canarticho",
"Doduo", "Dodrio", "Otaria", "Lamantine", "Tadmorv", "Grotadmorv", "Kokiyas", "Crustabri",
"Fantominus", "Spectrum", "Ectoplasma"]
# la liste des pokemons
mrandmax = 2 ** 31 - 1
# valeur max d'un entier positif signé codé en 32 bits... spé NSI inside
mrand = 0
# rien d'aléatoire ici, sera modifié par la fonction mseed()
mfmax = 93
# 94-1
nn = getlinechars(True) - na
# nn = 99-21 = 78 (sans calculatrice !) si k =1
# nn = 509-21 = 488 (sans calculatrice !) si k =2
mp = na // 2
# mp = 10, jamais modifié, utilisé une seule fois f° pk (mp = p maximum je pense)
def mround(f):
# arrondi f à l'entier le plus proche
d = mmod(abs(f), 1)
return (mfloor(abs(f)) + (d >= .5)) * (1 - 2 * (f < 0))
def mfloor(f):
# arrondi f à l'entier naturel inférieur
return round(f) - (round(f) > f)
def mceil(f):
# arrondi f à l'entier naturel supérieur
return round(f) + (round(f) < f)
def mseed(s):
global mrand
# modifie une variable globale sans retourner aucune valeur
# mrand est le reste dans la division euclidienne de s par mrandmax
# suprenant car mrandmax est l'entier maximum, filouterie ?
# Si s=mrandmax alors elle réaffecte 0 à mrand,
# Si s!=mrandmax elle affecte s à mrand
mrand = mmod(s, mrandmax)
def mrandom():
# un générateur aléatoire sans fonction random ...
# je dois creuser - à tester en profondeur
mseed(mrand * 16807)
return float(mrand / mrandmax)
def muniform(mini, maxi):
# à coup sur une loi uniforme sur un intervalle
# je dois creuser - à tester en profondeur
return mrandom() * (maxi - mini) + mini
def mrandint(mini, maxi):
# à coup sur un tirage d'entier naturel
# je dois creuser - à tester en profondeur
return mround(muniform(mceil(mini), mfloor(maxi)))
def f2mf(f):
# Multiple f par 93 car c'est le dernier pokemon en commençant à 0
# en retourne l'arrondi de ce nombre
# ?? a quoi ça sert ??
return mround(float(f * mfmax))
def mf2f(n):
# retourne un réel obtenu en divisant n par 93
return float(n / mfmax)
def mbit(a, b):
# a tester
# renvoie le reste de la division euclidienne de a // 2**b et 2
# autrement dit, permet de savoir si le quotient de la division euclidienne
# de a par 2**b est impair (retourne 1) ou pair (retourne 0)
return mmod((a // (2 ** b)), 2)
def getattack(p, pts):
# Permet d'obtenir l'attaque du pokemon en fonction de son indice n (ici p)
# Et d'un parametre (l[2] dans le code) qui change avec le clean()
global pkt
mseed(42)
for k in range(p + 1):
mrandom()
a = mrandint(1, mrandmax)
pka = ""
for j in range(na):
if mbit(a, j) != 0:
# Si a / 2^j est impair, la condition est valide et on gagne des points
pka += "X"
pkt[j] += pts
else:
# Else parfaitement inutile
pka += " -"[getplatform() >= 5]
return pka
def i2c(k):
# Transforme un nombre décimal en chaine de caractère
return chr(k + 33)
def c2i(c):
# transforme une chaine de caractère ayant un élèment en valeur décimale
# cette valeur décimale sera comprise entre 32 (a) et 89 (z)
return ord(c) - 33
def clean():
global pkl
t = 0
s = 0
for l in pkl:
t += l[1]
# On rajoute 1 a tout les p des pokemons de la liste
for l in pkl:
# Affecte l[1] / t (si t est superieur a 1) / 93 (fonction f2mf) a l[2]
l[2] = f2mf(l[1] / (t or 1))
# Ajoute le resultat obtenu a s
s += l[2]
if (l[2] <= 0):
# Si l[2] <= 0: le pokemon est supprime et la fonction engage une recursion
pkl.remove(l)
return clean()
return s
def pk(n, p=1, d=2):
global pkt, pkl, code # @@ AJOUT code @@
n -= 1 # Permet de selectionner le bon indice du pokemon
if n >= 0 and n < len(lnm):
# Verifie la validite de l'indice n
# Ajoute un pokemon a la liste s'il est nouveau
# Sinon, actualise la position si p >= 0 et met 0 sinon
new = True
for k in range(len(pkl)):
# C'est cette boucle qui verifie si le pokemon est deja selectionne
# RAPPEL : pkl[k][0] est l'indice du pokemon k qui sont a nous
# PEU IMPORTE p
if pkl[k][0] == n:
new = False
pkl[k][1] = max(p,0)
# S'il est nouveau, on le dompte et il est a nous !
if new and len(pkl) < mp:
pkl.append([n, max(p, 0), 0])
# Initialisation des variables
ptt = clean() # Fonction la plus obscure, les recursions, c'est chiant
# J'ai du mal a bien discerner son fonctionnement
# du coup on ne sait pas combien vaut ptt embétant ça
pkt = [0 for k in range(na)] # Une liste avec 21 zeros dedans
t = 0
# initialise le score à 0
st = ""
# initilise la chaine de caractère donnant le code
for l in pkl:
# Chaque l est de la forme
# l = [n ; max(0;p) ; 0 (pour l'instant...) ]
# Et represente un pokemon de notre main, peu importe sa position
s = getattack(l[0], l[2] / ptt)
# j'ai l'impression que le pokemon n affronte le n+2
if d:
sn = " " + lnm[l[0]]
if len(sn) > nn:
sn = sn[:nn]
"""" print(s + sn)""" # desactivé
st = i2c(l[0]) + st + i2c(l[2])
# Fabrique le code en ajoutant par la gauche le pokemon et par la droite sa force
for k in pkt:
if (k):
t += math.log(math.exp(1) + k * len(pkl))
if (d):
"""if (d >= 2):
print("Bon score ? Si oui\nenvoie code suivant\na info@tiplanet.org :") """ # neutralisé
"""print("" + st)""" # neutralisé
code = ""+st # @@ AJOUT @@
return float(t)
def setst(st):
s = 0
# Initialise le score à 0
pkl[:] = []
# vide la liste pkl de son contenu
n = len(st) // 2
# Mesure la longueur de la chaine de caractère (20) et divise par 2
# utilisé pour la boucle for ci-dessous
# la partie gauche c'est le codage des pokemon en ACSII décimal
# la partie gauche c'est les forces en ACSII décimal
# L'ordre des poid et des pokemons n'a aucune importance tant que le pokemon conserve sa force
# En changeant l'ordre de 2 pokemons ou de 2 poid aucune différence (merci kevin)
for k in range(n):
s = pk(c2i(st[n - 1 - k]) + 1, c2i(st[n + k + len(st) % 2]), k + 1 >= n)
return s
•
ATTAQUE N°8 : MANIPULATIONS AUTOUR DU CODE RÉPONSE
La liste des Pokémons est codée en dur dans le script, mais il n’en est rien de leurs qualités ni de la valeur de force optimale pour chaque Pokémon. Cette dernière est donc calculée. Or la chaîne de caractère du code réponse semble peu varier lorsque de l’on tire depuis une liste fixé de Pokémons. Nous comprenons que :
• La partie gauche code la liste des Pokémons.
• La partie droite leur force.
• Pour un code
"ABCDEFGHIJ0987654321", on a 10 couples (un exemple est ici en gras) qui définissent les 10 Pokémons et leurs points d’attaque.
• On peut faire de jolies permutations sans changer le score obtenu car les couples n’influent pas les uns sur les autres.
• Il n’y a aucune clé de vérification, on peut tester des codes au hasard sans avoir la moindre idée de la main des Pokémons et de leur forces.
• Les forces sont codés par des caractères ASCII, on n’est plus du tout dans ℝ mais de retour dans ℕ.
def i2c(k):
# Transforme un nombre décimal en chaine de caractère
return chr(k + 33)
def c2i(c):
# transforme une chaine de caractère ayant un élèment en valeur décimale
# cette valeur décimale sera comprise entre 32 (a) et 89 (z)
return ord(c) - 33
En modifiant à la main la partie droite de la chaîne de caractères, on arrive à modifier très substantiellement le score, à la hausse comme à la baisse. Notre premier 49 est obtenu en bricolant à la main la chaîne de caractère.
C’était assez jouissif il faut dire, en changeant un caractère parmi les 10 derniers, notre score pouvait plonger OU augmenter bien plus vite que tous nos algorithme de tirages aléatoires qui tournaient des nuits complètes... Ce code réponse ne contient que 20 caractères, on connaît déjà les 10 premiers (du moins on le pense) il ne nous reste plus qu’à utiliser la force brute sur les dix derniers caractères.
u^KhxO_%#l"""l"""%%%
Out[17] : 49.031613189324844
• ATTAQUE N°9 : FORCE BRUTE POUR TRAITER LES CHAINES DE CARACTÈRE DU CODE RÉPONSE
Grace à notre compréhension obtenue avec l’attaque n°08, on a supposé que pour une liste de 10 pokémons, il existe une suite de forces optimales.
On a ainsi écrit un petit script permettant de trouver cette suite optimale.
lc = [None,"!","!","!","!","!","!","!","!","!","!"]
carac = '!"#$%&'+"'"+'()*+^;_,-./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
def cons(liste):
# Construit le code
global cc
cc = ""
for j in range(len(liste)):
cc = cc+liste[j]
def turboBoost(pokemons,vitesse=1,style=1):
# Prend une main de pokemon et lui donne des steroides
# la chaine finale sous forme de liste pour modifier les caracteres
lc[0]=pokemons
# les caracteres a tester (tout ce qui est possible)
for l in range(vitesse+3):
# creation du code par test (5x ou 4x pour trouver "l'etat stable" a tout les coups)
# vitesse 1 = rapide, vitesse 2 = lent mais plus sur
for i in range(1,11):
# On initialise tout
global cc
cons(lc)
scores = [0]
for k in range(len(carac)):
# On recense le score pour chaque caractere
lc[i] = carac[k]
cons(lc)
score = setst(cc)
scores.append(score)
# On prend le gagnant, c'est notre partie de cle
lc[i] = carac[scores.index(max(scores))-1]
# on cree le code final
cons(lc)
# style pour la fonction (purement cosmetique)
if style:
print(int((l+1)*100/(vitesse+3)),"%")
if vitesse == 1:
print("====="*(l+1)*2+"....."*(8-(l+1)*2))
if vitesse == 2:
print("===="*(l+1)*2+"...."*(10-(l+1)*2))
score = setst(cc)
print(cc+" :",score,": "+code)
On remarque qu’il est nécessaire d’avoir une liste pour modifier les éléments de la chaîne de caractères un par un à une position donnée.
Rien qu’avec ce script, on a pu augmenter considérablement le score des meilleurs Pokémons issus des attaques précédentes.Avant :
u^KhxO_%#l"""l"""%%%
Out[17] : 49.031613189324844
Après :
turboBoost("u^KhxO_%#l")
25 %
==========..............................
50 %
====================....................
75 %
==============================..........
100 %
========================================
u^KhxO_%#l"""^""1""" : 49.29025926955508 : u^KhxO_%#l"""d""3"""
Cette optimisation du membre de droite nous permettait de faire la même chose pour le membre de gauche.
On a alors écrit une fonction utilisant notre fonction d’optimisation pour tester chaque caractères pour le membre de gauche en optimisant le score à chaque fois pour trouver le code parfait.
def chasseAuxPokemons(vitesse=1):
# La vitesse 1 m'a prise 9h mais a bien fonctionnée
lcp = ["!","!","!","!","!","!","!","!","!","!"]
for l in range(vitesse+3):
for i in range(10):
# On initialise tout
global cc
cons(lcp)
scores = [0]
for k in range(len(carac)):
# On cree la liste de Pokemons avec les caractères
lcp[i] = carac[k]
cons(lcp)
# On applique le booster de performances dessus (rapide et sans les barres de %)
turboBoost(cc,1,0)
# On recense les scores
score = setst(cc)
scores.append(score)
# On prend le gagnant, c'est notre nouveau Pokemon
lcp[i] = carac[scores.index(max(scores))-1]
Grace à cette fonction, on a pu obtenir le TOP résultat, le fameux 49,31730.
On a donc envoyé des scores légèrement en dessous pour pouvoir nous qualifier dans les premiers.
_h#^g0KuOS""""""""7u
49.31730339247606
• ATTAQUE N°10 : FORCE BRUTE OUI MAIS...
Nous avions exclu quelques caractères de cette force brute, on a donc réussi uniquement à obtenir le premier TOP résultat qui était déjà pris, le fameux 49,31730 mais pas au delà. Quand les premiers 49.319 et 49.32 ont commencé à sortir, nous avons compris que nous avions trop traîné, et qu’en intégrant d’autres caractères de la table ASCII nous aurions pu finir premier. Nous pensions que 49,31730 était le résultat optimal, nous n’avons pas testé davantage alors qu’on avait à priori la bonne méthode. Mais notre échec relatif nous à gonflé à bloc pour le défi historique, que nous avons fait en Python cela va de soit et sur PC car la mémoire de la NumWorks à la date d’octobre 2019 ... enfin vous voyez de quoi on veut parler, sinon il suffit de lire ceci : Script qui refuse de s’exécuter sur la Numworks N0100 pour comprendre de quoi il en retourne.
• CONCLUSIONS
Nombres de tirages réalisés au total : entre 20 000 000 et 30 000 000 maximum. 3 scripts tournaient au maximum en même temps, sur deux ordinateurs distincts. L’année prochaine, il faudra compter sur nous ! Et cette fois-ci on commencera le défi le jour J et pas en retard. Pavel va devoir ... heu non rien du tout, Pavel est très au dessus du niveau moyen. ;-)
Nous remercions les organisateurs des sites TI-Planet et Planète Casio pour ce bon moment, cette recherche était passionnante, nous a réveillé la nuit (histoire de vérifier que les scripts tournaient bien) et maintenant les listes en Python n’ont plus de secret pour nous. Sur un autre sujet, nous supplions l’équipe de NumWorks d’augmenter la mémoire allouée aux scripts Python sur la N0100 et la N0110. Ne pas pouvoir écrire un script de plus de 4Ko est une absurdité ! . Particularité ici, les bidasses ont la compagnie de leur meilleur ami
. Résultat
nous remet le couvert avec la même équipe, avec juste un petit peu plus de puissance pour
. Ce qui fait toute la différence avec
points.
qui lui permet bién évidemment de passer la dernière décimale du score précédent au chiffre 3 supérieur :
points.
un peu plus puissant, au détriment de ses esclaves qui tombent à 1,04% de puissance chacun. Et il a visiblement raison de procéder ainsi, puisque le score monte à
et réduits à 1,03% de puissance chacun. Résultat
pour nous apporter la touche finale. Même équipé, mais en réduisant les Pokémons annexes à seulement 1,02% de puissance chacun, il donne la toute puissance à
Sur ce problème, on voit très clairement se dégager une tendance, avec tout le monde qui converge vers les mêmes équipes. Bravo ! Combien d'entre vous pariraient que c'est l'optimal sur tout l'espace ?
On va passer à l'attribution des lots. Comme la dernière fois, ça se passe dans les commentaires, ici et sur TI-Planet.
est le premier à partir. Quand votre tour arrive, postez votre choix sur l'un des deux forums !
Citer : Posté le 22/11/2019 13:52 | #
J'ai mangé; je commence à vérifier maintenant.
Ajouté le 22/11/2019 à 14:19 :
Voilà.
Le porte-documents ce n'est pas possible, celui que tu as précisé est épuisé. Peux-tu en choisir un autre stp ?
Les épuisés sont mentionnés en rouge dans la liste des goodies TI communs :
https://tiplanet.org/forum/viewtopic.php?t=23140&p=248362#p248362
La balle anti-stress ça aurait été avec plaisir mais elle n'est pas incluse dans ce lot, et je n'en aurai pas en rab. Je n'ai pas pu en réapprovisionner le stock, puisque cela fait déjà plusieurs événements que TI n'en distribue plus.
Tout le reste c'est bon, un très beau lot, félicitations !
Citer : Posté le 22/11/2019 16:18 | #
Pour la balle ce n'est pas grave, je voulais juste savoir. Sinon pour le porte doc il n'y en a que 2 possibles normalement.. ben si celui que j'ai choisis n'est plus dispo je prends l'autre
-Planétarium 2
Citer : Posté le 22/11/2019 17:44 | #
Il y en avait de 5 types, il en reste 3 :
Tu oublies les 2 en haut à gauche.
Lequel prends-tu ?
Citer : Posté le 22/11/2019 18:49 | #
Celui en bas a gauche Merci Critor !
Ajouté le 22/11/2019 à 18:52 :
Ah et pour savoir, quand penses tu expédier ces lots ?
-Planétarium 2
Citer : Posté le 22/11/2019 18:56 | #
Si tout va bien, courant décembre.
Ajouté le 24/11/2019 à 16:44 :
@CaptainLuigi, c'est à ton tour, et il nous reste de jolies choses pour toi :
https://tiplanet.org/forum/viewtopic.php?f=49&t=23140&p=248494#p248494
Citer : Posté le 26/11/2019 09:30 | #
Désolé pour mon retard , voilà voilà , je choisis :
Une clé USB hp 16 go , avec le lot Radon et le cahier d'activité eyrolles 2de générale
Ajouté le 26/11/2019 à 09:42 :
Est ce qu'il reste une calculatrice ?
Passé ici il y a peu. ಥ‿ಥ
Jouez à Mario sans arrêt sur votre Casio !
City Heroes
Piano Casio
Micro GIMP
Citer : Posté le 26/11/2019 10:07 | #
Merci.
Par contre la clé USB HP ne fait pas partie du lot TI/Radon, et cette clé-là nous n'en aurons pas en rab.
Si tu veux cette clé, il faut prendre un lot HP.
Tu confirmes le choix de ton lot TI/Radon ?
Si oui, il te reste plein de trucs à choisir avant que l'on puisse continuer :
- la décalcomanie
- le sac au choix
- la clé USB TI
- le poster
- le porte-documents
- ...
Clique bien sur l'onglet 'TI' dans la sections des goodies communs en bas de post pour en consulter le choix :
https://tiplanet.org/forum/viewtopic.php?f=49&t=23140&start=160#p248494
Merci.
Ajouté le 26/11/2019 à 10:09 :
Est ce qu'il reste une calculatrice ?
Non, edgar13 a raflé la dernière juste avant toi.
Citer : Posté le 26/11/2019 10:32 | #
Je confirme le lot Ti/Radon du coup ...
Je prends donc avec :
-1 clé USB grande TI-83 Premium CE avec chaînette - 4 Go
-1 stylo TI
-1 autocollant TI
-1 compte premium TI-Planet ( ça consiste en quoi ? )
-et pour les choix du lot Radon :
_TI-SmartView 83 Premium CE pour Windows/Mac
-le cahier Eyrolles TI-83 Premium CE Python 2nde Générale & Technique
-T-shirt T3IC 2019 taille M
et tout le reste dedans ...
Merci d'avance
Passé ici il y a peu. ಥ‿ಥ
Jouez à Mario sans arrêt sur votre Casio !
City Heroes
Piano Casio
Micro GIMP
Citer : Posté le 26/11/2019 12:20 | #
Est ce qu'il reste une calculatrice ?
Non, edgar13 a raflé la dernière juste avant toi.
Citer : Posté le 26/11/2019 16:23 | #
Merci toi, mais on n'en a toujours pas fini. Je te renvoie au post en question, il manque encore plein de choses.
Quelle décalcomanie ?
Quel sac ?
Dessous de verre, lunettes ou collier de serrage ?
Quel sticker TI ?
Quel stylo ?
Quel porte-documents ?
Quel poster ?
Quel sticker TI-Planet ?
Merci à toi.
Citer : Posté le 26/11/2019 17:27 | #
Aaaiie désolé :
-Le dessous de verre .
- L'autocollant Math Buddy
- Le deuxième stylo en partant du bas
- Le sticker ti planet VIP
-Le sac ti , noir
-Le porte documents : rouge
-Le poster mode examen ti
Et je récapitule le choix pour le lot Radon :
Je prends donc avec :
-1 clé USB grande TI-83 Premium CE avec chaînette - 4 Go
-1 compte premium TI-Planet ( ça consiste en quoi ? )
_TI-SmartView 83 Premium CE pour Windows/Mac
-le cahier Eyrolles TI-83 Premium CE Python 2nde Générale & Technique
-T-shirt T3IC 2019 taille M
Merci d'avance
Passé ici il y a peu. ಥ‿ಥ
Jouez à Mario sans arrêt sur votre Casio !
City Heroes
Piano Casio
Micro GIMP
Citer : Posté le 26/11/2019 18:24 | #
Merci. Désolé si je t'embête, mais sauf erreur de ma part nous n'y sommes toujours pas :
- Il y a 2 porte-documents rouges. Lequel ?
- Comme indiqué ( https://tiplanet.org/forum/viewtopic.php?f=49&t=23140&start=160#p248494 ), le poster mode examen est épuisé. Il faudrait donc en choisir un autre.
- Et quelle décalcomanie ?
Merci à toi.
Citer : Posté le 26/11/2019 18:28 | #
Tu y arriveras Critor!
Citer : Posté le 26/11/2019 18:39 | #
Merci. Désolé si je t'embête, mais sauf erreur de ma part nous n'y sommes toujours pas :
- Il y a 2 porte-documents rouges. Lequel ?
- Comme indiqué ( https://tiplanet.org/forum/viewtopic.php?f=49&t=23140&start=160#p248494 ), le poster mode examen est épuisé. Il faudrait donc en choisir un autre.
- Et quelle décalcomanie ?
Merci à toi.
La décalcomanie dorée ( n'importe laquelle sinon ) :
Le porte document à droite :
Je laisse donc le poster au 16ème
Passé ici il y a peu. ಥ‿ಥ
Jouez à Mario sans arrêt sur votre Casio !
City Heroes
Piano Casio
Micro GIMP
Citer : Posté le 26/11/2019 18:46 | #
Ok, merci à toi.
Ajouté le 26/11/2019 à 19:00 :
@Filoji, c'est donc à ton tour :
https://tiplanet.org/forum/viewtopic.php?f=49&t=23140&p=248548#p248548
Citer : Posté le 27/11/2019 12:17 | #
Filoji????
Citer : Posté le 27/11/2019 12:23 | #
Je sais, c'est bien embêtant.
Le choix du 1er défi a été expédié en 3 jours...
Le choix du 2ème défi va maintenant sur les 2 semaines, et bloque tout le reste (résultats 3ème défi, choix 3ème défi, envoi des lots). Nous n'avons jamais eu un tel délai en 4 ans de concours de rentrée.
J'ai l'impression que le problème en bonne partie vient des 2 candidats ayant généreusement renoncé à leur lot pour ce défi. On tombe donc maintenant sur des candidats qui avaient compris qu'ils avaient perdu, et qui soit ne suivent donc plus le concours, soit ne se pressent absolument pas maintenant qu'il ne reste plus que des lots avec goodies qui les intéresse possiblement moins malgré la rareté...
Nous en discuterons collectivement, mais il est possible que le règlement soit donc moins sympa l'année prochaine. Pas forcément un délai réduit à moins de 48h (cela me semble être le minimum si ça tombe un week-end), mais genre que si un candidat renonce à son lot, nous mettons bien évidemment le stock à la disposition des suivants, mais sans désigner de nouveau gagnant au-delà de la dernière place prévue. Ce serait dommage, mais visiblement nécessaire...
Ne pas hésiter à faire des suggestions, et toutes mes excuses.
Citer : Posté le 27/11/2019 13:05 | #
48h pour faire 1choix ? Je pense que c raisonnable
-Planétarium 2
Citer : Posté le 28/11/2019 01:07 | #
Merci.
Donc @Renaud42 ?
Citer : Posté le 28/11/2019 07:11 | #
Renaud42 n'est pas là depuis le 24 Novembre
Citer : Posté le 28/11/2019 07:17 | #
On va quand même pas attendre comme ça après chacun plusieurs jours !?
-Planétarium 2