dimanche 30 mars 2008

Jeu de Nim en Fortran

Vous connaissez peut-être le jeu de Nim sous le nom de "jeu des allumettes"... Version Frotran 90 "sale" ("Goto statement considered harmfull", non respect des normes actuelles...) en mode console. Au passage, je teste l'outil de conversion html de (g)Vim (f*ck it ça passe pas...)... Got a problem homie ? Just tell me (give my money and buy my medecine...). Version collée chez Pastie pour plus de lisibilité (lignes trop longues, etc...) : goto ICI.

Ze code :
(compilé avec GFortran (fortran 90) sous Windows)


Program Jeu_de_Nim
IMPLICIT NONE

integer :: nbre_allum, ordre, choix, scorePC, scoreHumain, nb_parties, i, aleas
logical :: PC_first, tournoi


!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! BIENVENUE
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

print*, "!!!!!!!!!!!!!!!"
print*, "JEU DE NIM v2.0"
print*, "!!!!!!!!!!!!!!!"
print*, "Bienvenue dans ce petit jeu de Nim."

print*, "A chaque tour, le joueur ou le PC doivent prendre 1, 2 ou 3 allumettes."
print*, "Celui qui prend la derniere allumette a perdu. Bonne chance."
print*, " "

scorePC = 0
scoreHumain = 0
tournoi = .FALSE.

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! MENU

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
111 print*, "Entrez votre choix :"
print*, "[0] : Nouvelle Partie"
print*, "[1] : Nouveau Tournoi"
print*, "[2] : Afficher Scores"

print*, "[3] : Reinitialiser les Scores"
print*, "[4] : Quitter"
read*, choix

SELECT CASE (choix)
CASE (0)
GOTO 222
CASE (1)
tournoi = .TRUE.
scorePC = 0
scoreHumain = 0
11 print*, "Tournoi en combien de parties ?"

read*, nb_parties
if (nb_parties < 1) then
print*, "Tssss... Trop drole..."
GOTO 11
end if

aleas = 2*rand()

PC_first = .TRUE.
IF (aleas == 1) PC_first = .FALSE.
i = 0
10 i = i+1
print*, "PARTIE numero : ", i
call Afficher_scores(scorePC, scoreHumain)
IF (i > nb_parties) then
call Afficher_scores(scorePC, scoreHumain)
if (scorePC > scoreHumain) print*, "LE PC GAGNE LE TOURNOI !!!"

if (scorePC == scoreHumain) print*, "EGALITE !!! INCROYABLE !!!"
if (scorePC < scoreHumain) print*, "VOUS GAGNEZ LE TOURNOI !!! BRAVO !!!"
GOTO 111
END IF


nbre_allum = 50*rand() + 1
PC_first = .NOT.PC_first
GOTO 223

CASE (2)
call Afficher_scores(scorePC, scoreHumain)
GOTO 111

CASE (3)
scorePC = 0
scoreHumain = 0
GOTO 111

CASE (4)
GOTO 666

CASE DEFAULT

print*, "Mauvais choooooooooooix......"
GOTO 111

END SELECT

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! PARTIE
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

222 print*, "Combien d'allumettes pour la partie ? (0 pour un nombre au hasard)"
read*, nbre_allum
if (nbre_allum == 0) nbre_allum = 50*rand()+1
if (nbre_allum < 0) then
print*, "Hummmmmm... Pas beaucoup ca..."
GOTO 222
end if
print*, "Qui joue en premier ?"

print*, "[1] : PC"
print*, "[2] : Humain"
print*, "[3] : Au hasard"
read*, ordre

SELECT CASE (ordre)
CASE (1)
PC_first = .TRUE.
print*, "Le PC commence."

CASE (2)
PC_first = .FALSE.
print*, "L'humain commence."
CASE (3)
aleas = 2*rand()
PC_first = .TRUE.
IF (aleas > 1) PC_first = .FALSE.
CASE DEFAULT

PC_first = .TRUE.
print*, "Apprends a taper cono !"
print*, "Du coup le PC commence..."
END SELECT

223 IF (PC_first) THEN

do while (nbre_allum > 0)
call Tour_de_jeu_PC(nbre_allum,scoreHumain)
IF (nbre_allum > 0) call Tour_de_jeu_Humain(nbre_allum,scorePC)
end do
ELSE
do while (nbre_allum > 0)
call Tour_de_jeu_Humain(nbre_allum,scorePC)
IF (nbre_allum > 0) call Tour_de_jeu_PC(nbre_allum,scoreHumain)
end do
END IF

IF (tournoi) GOTO 10

GOTO 111

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

! EXIT
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
666 print*, "MERCI, A BIENTOT !"

End Program Jeu_de_Nim

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! AFFICHAGE DES SCORES
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Subroutine Afficher_scores(scPC, scHuman)
integer, intent(in) :: scPC, scHuman

print*, "!!!!! SCORE !!!!!"

print*, "PC : ", scPC
print*, "Humain : ", scHuman

End Subroutine Afficher_scores

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! TOUR PC
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Subroutine Tour_de_jeu_PC(nb_al, scHuman)
integer, intent(inout) :: nb_al, scHuman
integer :: reste, prise

print*, "Il y a ", nb_al, "allumettes."

reste = MOD(nb_al,4)

if (nb_al == 1) then
prise = 1
else
SELECT CASE (reste)
CASE (1)
prise = 3*rand()+1
CASE (2)
prise = 1
CASE (3)
prise = 2
CASE (0)
prise = 3
CASE DEFAULT

print*, "Erreur : module inconsistant"
print*, "Module = ", reste
STOP
END SELECT
end if

nb_al = nb_al - prise
print*, "Le PC prend ", prise, " allumettes."

print*, "Il reste : ", nb_al, "allumettes."

if (nb_al == 0) then
print*, "VOUS AVEZ GAGNE !!!!! FELICITATIONS !!!!"
scHuman = scHuman + 1
end if

End Subroutine Tour_de_jeu_PC


!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! TOUR HUMAIN
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Subroutine Tour_de_jeu_Humain(nb_al, scPC)
integer, intent(inout) :: nb_al, scPC
integer :: prise

print*, "Il y a ", nb_al, "allumettes."

print*, "Combien souhaitez vous en prendre ? (1, 2 ou 3)"
999 read*, prise

if ((prise > 3).OR.(prise < 1).OR.(prise > nb_al)) then
print*, "Nombre saisi incorrect : veuillez recommencer."
GOTO 999
end if

nb_al = nb_al - prise
print*, "Vous avez pris ", prise, " allumettes."

print*, "Il reste : ", nb_al, "allumettes."

if (nb_al == 0) then
print*, "VOUS AVEZ PERDU !!!!!"
scPC = scPC + 1
end if

End Subroutine Tour_de_jeu_Humain

mercredi 26 mars 2008

Google Summer Of Code

Cette année encore, Google organise le Summer of Code (abrégé en SoC) : il s'agit d'une opération permettant à des étudiants du monde entier de s'investir dans un grand projet libre le temps de l'été. Pour en savoir plus :

Plutôt que de plagier cette dernière news très bien faite, je vais juste apporter quelques remarques.

Beaucoup de projets impliqués ; Un grand absent ?

De mes petits yeux fatigués, j'ai compté 177 projets libres participant au programme. C'est énorme. Chacun profitant de l'engagement de plusieurs étudiants, à temps plein, pendant 2 mois complets, il faut se figurer le bénéfice que cela génère pour le monde du libre ! Sans compter les 4500 $ pour l'étudiant et 500 $ pour le projet en cas de succès...

On retrouve beaucoup de noms très connus du monde libre : Apache, The GIMP, Wikimedia, Linux, Mozilla... ( Mais aussi certains un peu moins célèbres qui m'ont fait plaisir comme Battle for Wesnoth).

Mais il en manque certains, dont un qui saute aux yeux : OpenOffice.org. Pourquoi ? Vraiment là, je donne ma langue au chat... (Mauvaises relations avec Google ?).
J'ai aussi noté l'absence totale de logiciel de P2P (Azureus et Bittorrent sont eux aussi libres). Là en revanche, ça peut s'expliquer plus simplement vu l'utilisation souvent approximativement légale de ce type d'application. Quoique. A ce compte là, on pourrait exclure tous les projets de langage de prog' : on peut potentiellement en faire mauvais usage...

Les langages ? (Ruby ?)

Question essentielle s'il en est... On en retrouve un nombre considérable : C, C++ et Java bien sûr. Mais pas seulement. La plupart des langages de script sont là : PHP, Perl, Python, Ruby, Javascript, Groovy, Scheme... Et aussi du fonctionnel pour les durs : LISP (CommonLISP et Scheme notamment) et Haskell au moins. Et puis des langages de grognards : Fortran et Ada notamment sur GCC.
Au passage, je trouve vraiment exceptionnel de pouvoir participer, en tant qu'étudiant, au développement d'un "grand" langage via ce programme : LISP, PHP, Perl... Geeky !

Et le Ruby ? On le retrouve sur différents projets, et pas forcément ceux auxquels on pouvait s'attendre...
Certes, il y a Ruby Central dont les propositions de sujet sont très axées sur Merb et sur la collaboration avec d'autres langages (Python et Java notamment) ou d'autres interpréteurs (JRuby et Rubinius).
On retrouve ensuite Ruby dans les projets Codehaus (JRuby) et... NESCent : le "National Evolutionary Synthesis Center" sur le projet Bio* (Bio_ : Perl, Python, Ruby, SQL, Java...) dans le domaine de la bio-informatique. Ruby en science ?

Triple-win ? (Winwinwin...)

Vous l'aurez compris, je suis très favorable à ce type d'opération qui semble profitable aux trois protagonistes :
  • Pour les étudiants : de l'expérience (très valorisable) et potentiellement une rétribution non négligeable.
  • Pour les projets : 2 mois de travail par étudiant (+ 500 $ ?), un peu de pub, et potentiellement un nouveau contributeur régulier avec de l'expérience.
  • Pour Google : de la pub ciblant plus particulièrement le monde du libre. Mais aussi une grosse manœuvre RH de pré-recrutements éventuels.
Pourquoi je pose cette question ?

Parce qu'il est toujours bon de savoir pourquoi l'on reçoit de l'aide ou de l'argent. J'apprécie Google pour différentes raisons et différents produits. Mais si les pratiques de cette entreprise me paraissent bien plus correctes que d'autres (là je pense très fort à la petite boîte de Bill le portail, mais aussi au principal concurrent de Google pour la recherche en ligne, et malheureusement parfois à l'autre pomme...), elle n'en reste pas moins une entreprise comme une autre (peut être plus riche, certes...).
S'il serait dommage de tomber dans la paranoïa libriste extrémiste malheureusement trop courante, il faut néanmoins que le monde du libre sache clairement garder son indépendance et ses valeurs, tout en s'ouvrant davantage sur le monde de l'entreprise. Cela semble être le cas ici.

En bref...

Si contrairement à votre serviteur vous êtes (encore) étudiant et vous avez un poil de talent (l'acharnement marche aussi parfois...) : allez (re-)jeter un coup d'œil sur la page du SoC 2008...


jeudi 20 mars 2008

Matrices en Ruby : 3 - Linalg !

Les matrices sont assez mal gérées de base en Ruby (cf articles précédents : [1] et [2]). Heureusement, il existe une bonne bibliothèque basée sur LAPACK (Linear Algebra PACKage, bien connue en Fortran) :

Comme indiqué dans sa doc, cette lib permet pas mal de choses : gestion simple et fiable des matrices, opérations classiques (déterminant, trace, valeurs propres...), décompositions diverses (LU, Cholesky...) et différentes autres méthodes.

L'installation est simple. Ensuite un simple require 'linalg' suivi de include Linalg, et c'est parti ! La doc contient un bon petit tuto (recopié dans le README) d'introduction pour faire tour avec irb.

Deux petits exemples d'utilisations :

Problème N-corps :

N corps interagissent gravitationnellement selon la loi de Newton. Dans cet exemple, j'utilise Linalg uniquement pour la structure de mes vecteurs position et vitesse. Code crade et valeurs mal choisies...


Convection-diffusion :

Ici, il s'agit de la résolution 1D par la méthode des différences finies de l'équation d'advection-diffusion d'un scalaire passif (exemples : polluant en rivière, transfert de chaleur...). La méthode mathématique a été écrite rapidement... j'ai essayé de faire en finesse : la partie diffusive est centrée, et la partie advective décentrée. Mais du coup, j'ai pas pris le temps de faire de calculs de conditionnement et de condition CFL, ni de faire un schéma en temps moins brutal que du pur explicite... Je me suis cependant autorisé la possibilité de faire varier le champs de vitesse au cours du temps (utile par exemple pour coupler avec un modèle de calcul de champs de vitesses : Saint Venant pour de l'hydraulique ?). Par contre, ici aussi, les valeurs proposées sont assez mauvaises (pas de diffusion notamment !) : n'hésitez pas à tester avec d'autres !


Toute question, remarque ou commentaire est le bienvenu !

mercredi 19 mars 2008

Quelle que soit la casse

Une petite fonction récursive pour générer toute les casses possibles d'un mot. A toutes fins utiles...



def evocate str
if str.size == 1 then
return [str.downcase, str.upcase]
else
a = []
evocate(str[1...str.size]).each do |tail|
a.push(str[0].chr.downcase + tail, \\
str[0].chr.upcase + tail)
end
return a
end
end

test = evocate("toto")
p test
#=>["toto", "Toto", "tOto", "TOto", "toTo", "ToTo",
# "tOTo", "TOTo", "totO", "TotO", "tOtO", "TOtO",
# "toTO", "ToTO", "tOTO", "TOTO"]