#!/bin/bash
# cannon.sh: Approximation de PI en tirant des balles de canon.

# C'est une très simple instance de la simulation "Monte Carlo" : un modèle
#+ mathématique d'un événement réel, en utilisant des nombres pseudo-aléatoires
#+ pour émuler la chance.

#  Considérez un terrain parfaitement carré, de 10000 unités par côté.
#  Ce terrain comprend un lac parfaitement circulaire en son centre d'un
#+ diamètre de 10000 unités.
#  Ce terrain ne comprend pratiquement que de l'eau mais aussi un peu de
#+ terre dans ses quatre coins.
#  (pensez-y comme un carré comprenant un cercle.)
#
#  Nous tirons des balles de canon à partir d'un vieux canon situé sur un des côtés
#+ du terrain.
#  Tous les tirs créent des impacts quelque part sur le carré, soit dans le
#+ lac soit dans un des coins secs.
#  Comme le lac prend la majorité de l'espace disponible, la
#+ plupart des tirs va tomber dans l'eau.
#  Seuls quelques tirs tomberont sur un sol rigide compris dans les quatre coins
#+ du carré.
#
#  Si nous prenons assez de tirs non visés et au hasard,
#+ alors le ratio des coups dans l'eau par rapport au nombre total sera
#+ approximativement de PI/4.
#
#  La raison de ceci est que le canon ne tire réellement que dans la partie
#+ haute à droite du carré, premier quadrant des coordonnées cartésiennes.
#  (La précédente explication était une simplification.)
#
#  Théoriquement, plus de tirs sont réalisés, plus cela correspondra.
#  Néanmoins, un script shell, contrairement à un langage compilé avec un
#+ support des calculs à virgule flottante, nécessite quelques compromis.
#  Ceci tend à rendre la simulation moins précise.


DIMENSION=10000  # Longueur de chaque côté.
                 # Initialise aussi le nombre d'entiers générés au hasard.

NB_TIRS_MAX=1000 # Tire ce nombre de fois.
                 # 10000 ou plus serait mieux mais prendrait bien plus de temps.
PMULTIPLIEUR=4.0 # Facteur d'échelle pour l'approximation de PI.

M_PI=3.141592654 # Valeur réelle de PI, dans un but de comparaison.

au_hasard ()
{
RECHERCHE=$(head -n 1 /dev/urandom | od -N 1 | awk '{ print $2 }')
HASARD=$RECHERCHE                    # Du script d'exemple "seeding-random.sh"
let "rnum = $HASARD % $DIMENSION"    # Echelle plus petite que 10000.
echo $rnum
}

distance=        # Déclaration de la variable globale.
hypotenuse ()    # Calcule de l'hypoténuse d'un triangle à angle droit.
{                # A partir de l'exemple "alt-bc.sh".
distance=$(bc -l << EOF
scale = 0
sqrt ( $1 * $1 + $2 * $2 )
EOF
)
#  Initiale "scale" à zéro fait que le résultat sera une valeur entière, un
#+ compris nécessaire dans ce script.
#  Ceci diminue l'exactitude de la simulation.
}


# main() {

# Initialisation des variables.
tirs=0
dans_l_eau=0
sur_terre=0
Pi=0
error=0

while [ "$tirs" -lt  "$NB_TIRS_MAX" ]        # Boucle principale.
do

  xCoord=$(au_hasard)                        # Obtenir les coordonnées X et Y au
                                             # hasard.
  yCoord=$(au_hasard)
  hypotenuse $xCoord $yCoord                 #  Hypoténuse du triangle rectangle =
                                             #+ distance.
  ((tirs++))

  printf "#%4d   " $tirs
  printf "Xc = %4d  " $xCoord
  printf "Yc = %4d  " $yCoord
  printf "Distance = %5d  " $distance         #  Distance à partir du centre
                                              #+ du lac --
                                              #  l'"origine" --
                                              #+ coordonnées (0,0).

  if [ "$distance" -le "$DIMENSION" ]
  then
    echo -n "Dans l'eau !  "
    ((dans_l_eau++))
  else
    echo -n "Sur terre !   "
    ((sur_terre++))
  fi

  Pi=$(echo "scale=9; $PMULTIPLIEUR*$dans_l_eau/$tirs" | bc)
  # Multipliez le ratio par 4.0.
  echo -n "PI ~ $Pi"
  echo

done

echo
echo "Après $tirs tirs, PI ressemble approximativement à $Pi"
# Tend à être supérieur.
# Probablement dû aux erreurs d'arrondi et au hasard perfectible de $RANDOM.
error=$(echo "scale=9; $Pi - $M_PI" | bc)
echo "Deviation de la valeur mathématique de PI =      $error"
echo

# }

exit 0

#  On peut se demander si un script shell est approprié pour une application
#+ aussi complexe et aussi intensive en calcul.
#
#  Il existe au moins deux justifications.
#  1) La preuve du concept: pour montrer que cela est possible.
#  2) Pour réaliser un prototype et tester les algorithmes avant de le réécrire
#+    dans un langage compilé de haut niveau.
