Page 7 sur 57

Re: Concours de rentrée 2020 - défi Python du Léviathan

Message non luPosté: 07 Nov 2020, 16:27
de critor
Nouvelle participation du n°2. Evaluation en direct dès maintenant, voyons si il arrive à se dépasser : ;)
https://tiplanet.org/concours_rentree20 ... &invalid=1

Re: Concours de rentrée 2020 - défi Python du Léviathan

Message non luPosté: 07 Nov 2020, 17:38
de critor
Non pas une, mais 2 nouvelles IA par le participant n°10.

Evaluation retransmise en direct dès maintenant : ;)
https://tiplanet.org/concours_rentree20 ... &invalid=1

Re: Concours de rentrée 2020 - défi Python du Léviathan

Message non luPosté: 07 Nov 2020, 18:46
de Fime
Image
(la porte est à la même corniche que le leviathan)
Il m'est arrivé plusieurs situations de ce type, est-ce prévu ?

Re: Concours de rentrée 2020 - défi Python du Léviathan

Message non luPosté: 07 Nov 2020, 18:49
de critor
Oui, puisque tu peux tuer le Léviathan.
Quand tu es à côté, c'est return corniche_leviathan, 1 pour tirer ton unique flèche.

Si tu as bien visé son affichage passe en gris, et tu peux alors aller sur sa corniche sans danger. :)

Re: Concours de rentrée 2020 - défi Python du Léviathan

Message non luPosté: 07 Nov 2020, 18:56
de Fime
critor a écrit:Oui, puisque tu peux tuer le Léviathan.
Quand tu es à côté, c'est return corniche_leviathan, 1 pour tirer ton unique flèche.

Si tu as bien visé son affichage passe en gris, et tu peux alors aller sur sa corniche sans danger. :)

Attends, on est d'accord que corniche_leviathan est une variable que l'on créer pour se faciliter la vie, qui n'est pas fournie par defaut ?

Re: Concours de rentrée 2020 - défi Python du Léviathan

Message non luPosté: 07 Nov 2020, 18:58
de critor
C'est le numéro de sa corniche, bien évidemment inconnu.

A toi de le deviner/déduire, si bien sûr tu en as besoin.

Il n'y a aucun bonus de score pour avoir tué le Léviathan.

Re: Concours de rentrée 2020 - défi Python du Léviathan

Message non luPosté: 07 Nov 2020, 19:01
de Afyu
cent20 a écrit:J'ai envoyé ma première participation mais je suis à l'aveugle complet faute de réussir à boucler l'évaluation.
Afyu tu nous fais un tuto sur linux, en expliquant pas à pas comment tu fais pour tester un script 1000 fois ?

C'est parti !

Les deux scripts proposés juste après sont simplement ceux proposés par critor dans les commentaires. Merci à lui ! :)

On crée un fichier web.py qui contient :
Code: Tout sélectionner
#cas
from math import pi, cos, sin, floor
import sys

#---
# RNG

rnd_seed = int(sys.argv[1]) if len(sys.argv) > 1 else 0xc0ffee
web_dim = int(sys.argv[2]) if len(sys.argv) > 2 else 36
web_density = float(sys.argv[3]) if len(sys.argv) > 3 else .05
pits_density = float(sys.argv[4]) if len(sys.argv) > 4 else .1
bats_density = float(sys.argv[5]) if len(sys.argv) > 5 else .15

def rnd():
  global rnd_seed
  rnd_max = 0x7fff
  rnd_seed = (rnd_seed * 214013 + 2531011) % 4294967296
  return ((rnd_seed // (2*rnd_max + 1)) & rnd_max)

def random():
  return rnd() / 0x7fff

def randint(a,b):
  return rnd() % (b-a+1) + a

def choice(l):
  return l[randint(0, len(l)-1)]

#---


screen_h = 240
m_p, m_l, m_k, m_b, m_d, m_a, m_m = 1, 4, 16, 64, 256, 1024, 4096

def insertinto(l1, l2):
  for v in l1:
    if v not in l2:
      l2.append(v)
  return l2

def removefrom(l1, l2):
  for v in l1:
    try:
      l2.remove(v)
    except:
      pass
  return l2

def connectPlatforms(s1, s2):
  global web
  web[s1][s2], web[s2][s1] = 1, 1

def get_reachable_platforms_from_platforms(l, safe):
  lv = []
  for s in l:
    for i in range(dimweb):
      if web[s][i]:
        if i not in lv and (not(safe) or not (platforms[i] & m_p)):
          lv.append(i)
  return lv

def cango(s1, s2, safe):
  lvo1, lvi1, lvo2, lvi2, t_inter, k = [], [s1], [], [s2], 0, 0
  while not (t_inter) and len(lvi1) and len(lvi2):
    lvo1, lvo2 = insertinto(lvo1, lvi1), insertinto(lvo2, lvi2)
    for v in lvo1:
      if v in lvo2:
        return k
    lvi1, lvi2 = get_reachable_platforms_from_platforms(lvo1, safe), get_reachable_platforms_from_platforms(lvo2, safe)
    lvi1, lvi2 = removefrom(lvo1, lvi1), removefrom(lvo2, lvi2)
    k += 1
  return 0

def my_bitor(a, b):
  return ~(~a & ~b)

def init_web(d, p_p, p_b):
  global web, platforms, mwspr, mhspr, zoom, mwc, screen_w, screen_h
  yweb, l0 = screen_h / 2, list(range(dimweb))
  l0.remove(0)
  web, platforms, conn, dconn, i_k = [], [0 for k in range(dimweb)], [0], list(range(1, dimweb)), choice(l0)
  for j in range(dimweb):
    web.append([0 for k in range(dimweb)])
  while len(dconn):
    s = dconn[randint(0, len(dconn) - 1)]
    connectPlatforms(conn[randint(0, len(conn) - 1)], s)
    dconn.remove(s)
    conn.append(s)
  for j in range(dimweb-1):
    for i in range(j + 1, dimweb):
      if floor(d + random()):
        connectPlatforms(i, j)
  i_d = choice(l0)
  platforms[i_d] = my_bitor(platforms[i_d], m_d)
  l1 = list(l0)
  for v in get_reachable_platforms_from_platforms([0], 0):
    l1.remove(v)
  if not(len(l1)):
    l1 = l0
  l2 = list(l1)
  for v in get_reachable_platforms_from_platforms(get_reachable_platforms_from_platforms([0], 0), 0):
    try:
      l2.remove(v)
    except:
      pass
  if not(len(l2)):
    l2 = l1
  i_l = choice(l2)
  platforms[i_l] = my_bitor(platforms[i_l], m_l)
  platforms[i_k] = my_bitor(platforms[i_k], m_k)
  for i in l1:
    if i != i_k and i != i_d and floor(p_p*dimweb/len(l1) + random()):
      if cango(0, i_k, 1) and cango(0, i_d, 1):
        platforms[i] = my_bitor(platforms[i], m_p)
    if floor(p_b*dimweb/len(l1) + random()):
      platforms[i] = my_bitor(platforms[i], m_b)

def parcourir_selon(ia):
  global dimweb, platforms, web_dim, web_density, pits_density, bats_density
  dimweb = web_dim
  maxcoups = dimweb**2 * 2
  init_web(web_density, pits_density, bats_density)

  s0, s1, s2, s3, s4, s5, s6, s7 = 0, 0, m_a, 0, 1, -1, 0, 0
  pfs0, pfs5 = platforms[s0], 0
  while s4 > 0  and (not (s2 & (2 * m_k)) or not (pfs0 & m_d)):
    if s5 < 0:
      s5 = 0
    else:
      try:
        k, k2 = ia(s0, voisines, dimweb, s1, s2)
        if pfs5 & (2 * m_b):
          while s0 == s5:
            s0 = randint(0, dimweb - 1)
          pfs0, pfs5 = my_bitor(platforms[s0], m_b), pfs5 & ~(3 * m_b) & ~m_m
        else:
          if k2:
            if s2 & m_a:
              v = platforms[k]
              if v & m_l:
                v, s2 = v & ~m_l, my_bitor(s2, 2 * m_l)
                platforms[k] = my_bitor(v, 2 * m_l)
              s2 = s2 & ~m_a
              s2 = my_bitor(s2, 2 * m_a)
          else:
            if k in voisines:
              s0 = k
              if pfs5 & m_b:
                pfs5 = my_bitor(pfs5, 2 * m_b)
              pfs0, pfs5 = platforms[s0], pfs5 & ~m_m
          s3 += 1
          if s3 >= maxcoups:
            s4 = 0
        if pfs0 & m_k:
          pfs0 = pfs0 & ~m_k
          s2 = my_bitor(s2, 2 * m_k)
        if pfs0 & my_bitor(m_p, m_l):
          s4 = 0
          pfs0 = my_bitor(pfs0, 2 * m_m)
        platforms[s5] = pfs5
      except Exception as t_excpt:
        s4 = -1
        print(t_excpt)
    pfs0 = my_bitor(pfs0, m_m)
    s1, voisines = pfs0, get_reachable_platforms_from_platforms([s0], 0)
    platforms[s0] = pfs0
    for v in voisines:
      t = my_bitor(m_p, m_l)
      t = platforms[v] & my_bitor(t, m_k)
      s1 = my_bitor(s1, t)
    for v in get_reachable_platforms_from_platforms(voisines, 0):
      t = platforms[v] & m_l
      s1 = my_bitor(s1, t)
    s, sold = my_bitor(s1, s2), my_bitor(s6, s7)
    s5, s6, s7, pfs5 = s0, s1, s2, pfs0
  r = s4 > 0 and s3 < maxcoups
  if r:
    print("win " + str(s3), file=sys.stderr)
  else:
    if pfs0 & m_l:
      print("leviathan", file=sys.stderr)
    elif pfs0 & m_p:
      print("well", file=sys.stderr)
    elif s3 >= maxcoups:
      print("lowpower", file=sys.stderr)
    elif s4 < 0:
      print("exception", file=sys.stderr)
  return r, s2, s3

Globalement c'est le script web.py fourni pour le concours mais dont on a retiré tout ce qui est affichage graphique. Attention au respect de l'indentation quand on fait son copier-coller.

Ensuite, on crée un fichier bash que l'on nomme comme on veut (disons eval.sh) et qui contient :
Code: Tout sélectionner
#! /bin/bash

if [[ ! "$2" ]]; then
  echo "usage: $0 <nb of simulations> <l?.py file num>" >&2
  exit 1
fi

base_file="l""$2"
script_file="$base_file"".py"
infos_file="$base_file"".txt"
timestamp="$(date +"%s")"
# First seed for the tests
base_seed="$2"
# Web size for the tests
web_size=36
# Web density for the tests
web_density=.05
# Pits density for the tests
pits_density=.1
# Bats density for the tests
bats_density=.15
# Number of tests
test_count="$1"
# Total time spent escaping
escape_time=0
# Number of successful escapes, falls in wells, leviathan mishaps, exceptions
escapes=0
well=0
leviathan=0
exception=0
lowpower=0

RANDOM=$base_seed

# Upper bound is the number of tests
for i in $(seq 1 $test_count); do
  percent=$(echo "scale=2; $i*100/$test_count" | bc)
#  echo -ne "\rseed: $i/$test_count ($percent%)..." >&2
  result=$(python3 "$script_file" $RANDOM $web_size $web_density $pits_density $bats_density 2>&1 >/dev/null)

  case "$result" in
    "win "*)
      escape_time=$(($escape_time+${result#* }))
      escapes=$(($escapes+1));;
    "well")
      well=$(($well+1));;
    "leviathan")
      leviathan=$(($leviathan+1));;
    "lowpower")
      lowpower=$(($lowpower+1));;
    "exception")
      exception=$(($exception+1));;
  esac
  cat <<EOF > "$infos_file"
  $timestamp #date
  $test_count #total simulations
  $i #simulations
  $base_seed #seed
  $web_size #web size
  $web_density #web density
  $pits_density #pits density
  $bats_density #bats density
  $escapes #successes
  $escape_time #successes total steps
  $leviathan #fails by Leviathan
  $well #fails by well
  $lowpower #fails by low power
  $exception #fails by exception
EOF
done

printf "\r%40s\r" "" >&2

Ce script eval.sh doit être exécutable. Pour ça, on peut taper la ligne de commande suivante :
Code: Tout sélectionner
$ sudo chmod a+x eval.sh


Ensuite, on met son IA dans un fichier webtest.py (comme c'est demandé pour le concours, on peut par exemple reprendre le webtest.py prévu pour l'IDE Omega, dans les ressources NumWorks).

On renomme le fichier webtest.py en l1.py et pour tester cette IA 1000 fois, on tape, dans une console Linux ouverte dans le dossier qui contient nos 3 scripts :
Code: Tout sélectionner
$ ./eval.sh 1000 1
ou bien
Code: Tout sélectionner
$ source ./eval.sh 1000 1

(On peut également ouvrir une console, puis se déplacer jusqu'au dossier qui contient les 3 scripts avec
Code: Tout sélectionner
$ cd /chemin/vers/mon/dossier
juste avant de lancer la commande qui lance les 1000 tests)

Dans cette ligne de commande, eval.sh est le script de test, 1000 est le nombre de tests que l'on souhaite effectuer et 1 est transformé en l1.py lors de l'exécution de eval.sh et ça va exécuter le script l1.py 1000 fois. En remplaçant 1 par 2 on exécute le script l2.py 1000 fois et en remplaçant 1000 par 100000 on lance l'exécution 100 000 fois plutôt que 1000 fois.

Le résultat des 1000 simulations est stocké dans le fichier l1.txt qui est alors créé dans le même dossier.

Remarque 1 :
Si on veut utiliser autre chose que "python3" comme lanceur python, alors dans le script eval.sh il faut remplacer "python3" par ce que l'on souhaite.

Remarque 2 :
Le script polycal3.py n'est pas nécessaire puisqu'il n'y a pas d'affichage graphique.

Remarque 3 :
Si on veut un affichage comme dans les scripts du concours, alors il vaut mieux se tourner vers le script proposé par LeGmask dans les commentaires, mais en oubliant l'idée de lancer une simulation de 1000 exécutions (et en prenant garde au fait qu'il demande 2 arguments dans "parcourir_selon(ia,N)").

Remarque 4 :
Dans le fichier eval.sh ce sont les lignes
Code: Tout sélectionner
base_file="l""$2"
script_file="$base_file"".py"
infos_file="$base_file"".txt"
qui créent une variable base_file qui contient "l1" si on a mis "1" comme dernier argument dans la ligne de commande lançant les 1000 tests, une variable script_file qui contient "l1.py" et une variable infos_file qui contient "l1.txt". On peut adapter ces trois lignes si l'on veut par exemple mettre comme argument (dans la ligne de commande qui lance les 1000 simulations) son nom de fichier complet et pas uniquement un "1" pour "l1.py", un "2" pour "l2.py" et ainsi de suite... On pourrait alors remplacer ces 3 lignes par :
Code: Tout sélectionner
script_file="$2"
infos_file="$2"".txt"
Mais attention, la graine qui initialise le générateur de nombres aléatoires est précisément le 2ème argument passé dans la ligne de commande. Il faut alors passer un nombre en 3ème argument et remplacer la ligne
Code: Tout sélectionner
base_seed="$2"
par
Code: Tout sélectionner
base_seed="$3"
pour être sûr d'initialiser le générateur de nombres aléatoires sans avoir d'erreur.
La commande pour lancer une simulation de 3000 exécutions de son script webtest.py avec le nombre 42 pour initialiser le générateur de nombres aléatoires, devient alors :
Code: Tout sélectionner
$ ./eval.sh 3000 ./webtest.py 42
ou bien
Code: Tout sélectionner
$ source ./eval.sh 3000 ./webtest.py 42
et le résultat des 3000 simulations est alors stocké dans le fichier webtest.py.txt créé automatiquement.

J'ai mis les fichiers web.py et eval.sh (il s'appelle eval.txt et il faut le renommer) en pièces jointes.

J'espère n'avoir rien oublié. Si un passage n'est pas clair ou pas suffisamment développé, dites-le moi ! :D

Bonne chance !

Re: Concours de rentrée 2020 - défi Python du Léviathan

Message non luPosté: 07 Nov 2020, 19:49
de critor
Merci @Afyu, c'est sympa de ta part. :)

Re: Concours de rentrée 2020 - défi Python du Léviathan

Message non luPosté: 07 Nov 2020, 19:53
de Afyu
critor a écrit:Merci @Afyu, c'est sympa de ta part. :)

J'espère que ça pourra aider. J'espère surtout n'avoir rien oublié et ne pas m'être trompé...
Dites-moi si ça fonctionne ! :)

Re: Concours de rentrée 2020 - défi Python du Léviathan

Message non luPosté: 07 Nov 2020, 19:54
de critor
Nouvel essai du n°3, à suivre en direct :
https://tiplanet.org/concours_rentree20 ... &invalid=1