Le « Plus / Moins » le plus horrible du siècle !

L'auteur de ce sujet a trouvé une solution à son problème.
Staff
Auteur du sujet

Salut les agrumes !

Voici un petit atelier de programmation, très simple. Il s'agit d'écrire un jeu, et la règle de l'atelier tient dans les deux points suivants :

  1. Le jeu est un « Plus / Moins » : l'ordinateur tire un nombre entre 0 et 99 compris. L'utilisateur rentre un nombre, et l'ordinateur indique si le nombre à deviner est inférieur, supérieur ou s'il a été trouvé – le but est de le trouver en le moins d'étapes possibles.
  2. Le code de votre jeu doit être un abus total des dégeulasseries que permet le langage utilisé.

Attention, il ne s'agit pas de faire un bête one-liner, c'est beaucoup trop facile.

Le formattage ne compte pas, l'idée est plutôt de faire un code intelligent, mais horriblement mal conçu et/ou inutilement compliqué. Et surtout, expliquez ce qui est mal fait et pourquoi.

L'un des buts derrière cet atelier, c'est aussi et surtout de réfléchir aux différentes « bonnes pratiques », possibilités et limitations des langages ; et de leurs raisons d'être.

Vous pourrez enfin vous venger de toutes les horreurs que vous avez vues dans votre vie de développeur !

Pour montrer l'exemple, je vous prépare (il n'est pas encore fini) une version en Java qui utilise les patrons de conception si chers à certains…


Liste des participations

Je rajouterai les one-liners quand ils auront été formatés « correctement », sinon c'est trop simple. Idem pour les codes sans explications.

Édité par SpaceFox

Staff

Salut !

J'ai un peu triché, je n'ai pas fait le vrai jeu du Plus ou Moins, mais un solveur de Plus ou Moins sur [0;99]. Le programme ne se comporte bien que si on lui fournie des réponses cohérentes, j'ai pas réfléchi à ce qu'il ferait le reste du temps.

J'ai voulu parodier un type de code-source qu'on croise parfois (souvent chez les débutants qui n'ont pas compris l'intérêt de la programmation).
Comme le code est trop long pour ZdS je le met ici : http://pastebin.com/3R9YZuna

+1 -1

C'est une bonne idée ^^ Ça va être rigolo …

Aux limites du C (Extensions GNU + quelques avertissements), je propose :

1
2
3
4
5
6
7
8
9
#include <stdlib.h>
#include <stdio.h>
int _,__,$=0x3C;
main(){<%
??=include<time.h>
srand(time(0));__=(rand()%0x64);
    gr: (_-__)?scanf("%d",&_),
    printf("%c?\n",$+((_>=__)?(_==__)?(int<::>)??<1??><:0:>:2:0)):1;
    if(__-_) goto gr;??>}

Au dernier moment j'ai décidé de rajouter 2-3 absurdités que j'ai vu récemment … Comme par exemple, l'include en plein milieu du code ou encore la ternaire sur des fonctions.

@Bouli1515 : C'est pour tromper la coloration syntaxique :3

Édité par ache

+6 -0

Re,

Voila peut-être pas très original comme idée mais c'est le genre de code qu'on trouve sur les forums posté par des gens pratiquant un C++ préhistorique.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <stdlib.h>   //Comment ça on n'inclut pas comme ça en C++
#include <time.h>

using namespace std;    //Un using namespace pour bien commencer...

int chiffreADeviner;    //Des variables globales !
int chiffreUtilisateur;
char reponseUtilisateur[50];     //Comme en C...
char rejouer;

int main()      //On va tout faire dans la fonction main pourquoi s'embêter à découper son |programme ?
{
srand(time(0));    //l'indentaquoi ?
do
{
chiffreADeviner = rand() % 100;      //Ah bon c'est déprécié depuis le C++11 ? Il y a d'autres |fonctions ?
do
{
cout << "Deviner le chiffre" << endl;
cin >> reponseUtilisateur;                       //Ca veut dire quoi "sécuriser la saisie" ?
chiffreUtilisateur = atoi(reponseUtilisateur);   //"atoi()" aussi c'est déprécié ?
if(chiffreUtilisateur < chiffreADeviner)
   cout << "c'est plus" << endl;
else if(chiffreUtilisateur > chiffreADeviner)
   cout << "c'est moins" << endl;
else if(chiffreUtilisateur == chiffreADeviner)
   cout << "c'est bon" << endl;
}
while(chiffreUtilisateur != chiffreADeviner);
cout << "Le chiffre etait " << chiffreADeviner << endl;
cout << "rejouer o/n ?"<< endl;
cin >> rejouer;
}
while(rejouer == 'o');
return 0;
}

Edit: orthographe

Édité par Bouli1515

+3 -0

Je m'étais déjà amusé à écrire un code de plus ou moins immonde. L'idée était que les identifiants devaient uniquement être composés de 1, de I et de l. Des defines permettent de rendre le tout illisible, et le code en lui même est bourré de gotos. Je ne sais pas si ça rentre exactement dans l'idée du topic (Le formattage ne compte pas), mais comme ça me fait rire, je le poste quand même.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define IlI1I1lIll printf
#define lllII1IIl1 scanf
#define IlIlI11llI rand
#define lIIIll11ll srand
#define I11l1111lI time
#define llI1llI1ll if
#define IIlIllIIlI goto
#define l1111lll11 -
#define IIllIllIIl =
#define lIIIIIlllI "Entrez un nombre : "
#define IIlllllIIl "Gagne ! \n"
#define llIIlII1I1 "Plus petit \n"
#define l11IIlIl11 "Plus grand \n"
#define lIlIlI1111 %
#define l111IIIlll "%d"
#define IIIIIllII1 <
#define lllIIIlll1 >
#define l1111I1111 0
#define Il11lIlIIl 100
#define lIIIlII111 ==
#define IIIl111lII return
#define lllllllI1I int

int main(void){ 
  lllllllI1I llIIIl1ll1, l111IllIll IIllIllIIl l1111lll11 1;
  IIlIllIIlI I1lIIl11ll; 
 IIl1l1l1ll:
  llI1llI1ll(l111IllIll l1111lll11 llIIIl1ll1 IIIIIllII1 l1111I1111)
    IIlIllIIlI llllII1I1l;
 I1ll11lIll:
  IlI1I1lIll(lIIIIIlllI);
  lllII1IIl1(l111IIIlll, &l111IllIll);
  IIlIllIIlI IlII11lII1;
 llIIIII111:
  IIlIllIIlI I1ll11lIll;
 IlII11lII1:
  llI1llI1ll(l111IllIll lIIIlII111 llIIIl1ll1)
    IIlIllIIlI I1l1lIIllI;
  IIlIllIIlI I1IlIl11lI;
 I1l1lIIllI:
  IlI1I1lIll(IIlllllIIl); 
  IIlIllIIlI llllI11III;
 lI111lIlII:
  IlI1I1lIll(llIIlII1I1); 
  IIlIllIIlI llIIIII111;
 I1IlIl11lI:
  llI1llI1ll(l111IllIll l1111lll11 llIIIl1ll1 lllIIIlll1 l1111I1111)
    IIlIllIIlI lI111lIlII;
  IIlIllIIlI IIl1l1l1ll;
 l111lIIl11:
  llIIIl1ll1 IIllIllIIl IlIlI11llI() lIlIlI1111 Il11lIlIIl;
  IIlIllIIlI llIIIII111;
 llllII1I1l:
  IlI1I1lIll(l11IIlIl11);
  IIlIllIIlI llIIIII111;
 llllI11III:
  IIIl111lII l1111I1111;
 I1lIIl11ll:
  lIIIll11ll(I11l1111lI(l1111I1111));
  IIlIllIIlI l111lIIl11;
}

Édité par Bibibye

Voilà voilà, ma petite contribution en Haskell…

1
2
import System.Random
main=getStdGen>>=(\->let(,_)=randomR (0,99) ::(Int,StdGen)in  >>=(\_-> ))where{ =getLine>>=(\->let =read  in if <0||>99 then  >>=(\_-> )else if == then   else if < then  >>=(\_-> )else  >>=(\_-> )); =case  of{[]->return ();(:)->putChar >>=(\_-> )};="Woh le boloss, il a pas pigé qu'il fallait taper entre 0 et 99 ! Bouffon… ";="C'est gagné mon bogoss, tape m'en 5, wesh !\n";="Rho p'tite bite, faut taper plus haut ! ";="Aïe beliiiv aï kèn flaaaaï ! Redescends sur Terre, mec… ";="Wesh ! Devine c'est quoi le nombre ? ";}

Au programme :

  • des monades sans utilisation de la notation do ;
  • des imbrications de if plutôt que des gardes ;
  • pourquoi se faire chier avec l'indentation alors qu'on peut utiliser la notation avec accolades ;
  • des clauses where dans le désordre par rapport à leur utilisation dans la fonction ;
  • une utilisation adéquate des capacités de GHC à piger l'Unicode ;
  • une redéfinition de putStr, parce que la bibliothèque standard, c'est pour les faibles ;
  • un espacement millimétré.

Amusez-vous bien ! :D (Et dites-vous que je vous ai épargné les String exprimé comme des listes de Char:P )

Édité par Dominus Carnufex

#JeSuisGrimur #OnVautMieuxQueÇa

+4 -0

Lu'!

J'aime bien le concept aussi.

Vous avez dit paramétrique ?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <random>
#include <stdexcept>
#include <iostream>

using namespace std;

template<class T, class In, In& in, class Out, Out& out> 
T read(auto&& message){
  out<<message<<": ";
  T var{};
  while(! (in >> var) ){
    if(in.eof()) throw runtime_error{"closed input"};
    else { 
      in.clear(); 
      in.ignore(numeric_limits<streamsize>::max(), '\n');
    }
  } 
  return var;
}

template<class T, class In, In& in, class Out, Out& out>
bool new_try(T const& value){
  auto v = read<T, decltype(cin), cin, decltype(cout), cout>("Trouvez");

  if(v > value)      out<<"moins\n";
  else if(v < value) out<<"plus\n";

  return (v == value);
}

template<class ValueType, ValueType min, ValueType max, class In, In& in, class Out, Out& out>
void play_set(auto&& generator){
  ValueType value = min + generator()% (max-min);
  while(! new_try<ValueType, decltype(cin), cin, decltype(cout), cout>(value));
}

int main(){
  do{
    play_set<unsigned, 0, 100, decltype(cin), cin, decltype(cout), cout>(random_device{});
  } while(read<bool, decltype(cin), cin, decltype(cout), cout>("Recommencer"));
}

EDIT, explication :

Ce code est la version sur-générique de ceci :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <random>
#include <stdexcept>
#include <iostream>
#include <string>

template<class T> 
T read(std::string const& message){
  std::cout<<message<<": ";
  T var{};
  while(! (std::cin >> var) ){
    if(std::cin.eof()) throw std::runtime_error{"closed input"};
    else { 
      std::cin.clear(); 
      std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
  } 
  return var;
}

bool new_try(unsigned const& value){
  auto v = read<unsigned>("Trouvez");
  
  if(v > value)      std::cout<<"moins\n";
  else if(v < value) std::cout<<"plus\n";
  
  return (v == value);
}

void play_set(auto& generator, unsigned min, unsigned max){
  unsigned value = min + generator()% (max-min);
  while(! new_try(value));
}

int main(){
  std::random_device rd{};
  do{
    play_set(rd, 0, 100);
  } while(read<bool>("Recommencer"));
}

Mais que s'est il passé ensuite ?

Ensuite, on s'est dit qu'après tout :

  • Le type du nombre recherché n'avait pas d'importance dans les fonctions appelées, on veut juste qu'il puisse être saisi, affiché et comparé. Allez hop ! Template + auto.
  • Comme on ne demande pas à l'utilisateur sa valeur max, il fallait bien que ce soit paramétré quelque part, or, quitte à paramétrer ça statiquement dans le code, autant y aller à fond, hop re-template.
  • Le message affiché pour la saisie est actuellement nécessairement une std::string, pourquoi être si restrictif ? Après tout, tant que c'est affichable, ça passe. Hop ! Déduction de type et on verra (à noter : j'aurais également pu prendre statiquement le message puisque ce n'est pas une valeur dynamique).
  • Pourquoi se borner aux flux standards ? Si le développeur en veut d'autres, il faut le laisser faire. Allez hop ! Les types des inputs et output en template aussi.
  • Mais en fait, les inputs et outputs ne vont pas changer pendant l'exécution, allez, template aussi.

Au final, le code est à peine plus long. Il est juste sur-générique à mort.

A la limite une version générique moins WTF aurait donné ceci :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <random>
#include <stdexcept>
#include <iostream>
#include <string>

template<class T> 
T read(auto const& message, auto& in, auto& out){
  out<<message<<": ";
  T var{};
  while(! (in >> var) ){
    if(in.eof()) throw std::runtime_error{"closed input"};
    else { 
      in.clear(); 
      in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
  } 
  return var;
}

bool new_try(auto value){
  auto v = read<decltype(value)>("Trouvez", std::cin, std::cout);
  
  if(v > value)      std::cout<<"moins\n";
  else if(v < value) std::cout<<"plus\n";
  
  return (v == value);
}

template<class ValueType, ValueType min, ValueType max>
void play_set(auto& generator){
  ValueType value = min + generator()% (max-min);
  while(! new_try(value));
}

int main(){
  std::random_device rd{};
  do{
    play_set<unsigned, 0, 100>(rd);
  } while(read<bool>("Recommencer", std::cin, std::cout));
}

Édité par Ksass`Peuk

First : Always RTFM - "Tout devrait être rendu aussi simple que possible, mais pas plus." A.Einstein

+7 -0
Staff

Puisque le style ne compte pas, autant vérifier que tous nos maux sont dans le dictionnaire :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import sys
def plus():
    print("plus")

def moins():
    print("moins")

def gagne():
    print("gagné")
    exit()

mustRead = True
nb = -1
while mustRead:
    try:
        nb = int(sys.stdin.read().trim())
        mustRead = False
    except TypeError:
        mustRead = True

result = {str(i):plus for i in range(nb)}
result.update({str(i):gagne})
result.update({str(i):moins for i in range(nb, 99)})

while True:
    try:
        result[sys.stdin.read().trim()]()
    except KeyError as e:
        if e.message.isdigit() and int(e.message) > 99:
            print("It's over 99")
        else:
            print("Un nombre positif est un NOMBRE supérieur à 0.")

Édité par artragis

+0 -0

Pas sûr de comprendre comment un code intelligent peut être horriblement mal conçu, mais je propose mon humble participation en python :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/home/rozo_a/../../usr/local/../bin/python3.4                                                                                                                                                                                                                                  

def decore(f):
    def ff(a, b):
        return f(b, a)
    return ff

@decore
def test(to_find, this):
    if not int(this.__class__.__name__) != to_find:
        ret = 'gagné'
    if int(this.__class__.__name__) < to_find:
        ret = 'plus grand'
    if not int(this.__class__.__name__) <= to_find:
        ret = 'plus petit'
    return ret

pikachu = set()
for tmp in range(201 - 1):
    tmp = str(tmp)
    tmp = [tmp]
    tmp.append(())
    tmp.append({'tset':test})
    tmp = type(*tmp)
    pikachu.add(tmp)
from random import *
user = randrange(50) + randrange(50)

s = input('Entré un nombre entre 0 et 99')
while s != 0:
    raichu = set()
    while pikachu:
        pichu = pikachu.pop()
        if pichu.__name__ == s:
            s = pichu().tset(user)
            if s == 'gagné':
                print('gagné')
                exit()
            print(s)
        raichu.add(pichu)
    while raichu:
        salameche = raichu.pop()
        pikachu.add(salameche)
    s = input()

Maintenant, les explications

  • ligne 1: généralement, on ne met pas le chemin absolu vers python dans le shebang, mais on réfrence le programme env. On n'indique pas souvent non plus la version mineure.
  • ligne 3: un décorateur est un objet python permettant d'altérer le fonctionnement d'une fonction. Ici, on inverse l'ordre des deux paramètres à l'entrée.
  • ligne 9: les paramètres sont inversés, donc, et de plus l'habituel self se nomme ici this.
  • lignes 10, 13 et 15: On teste chaque condition alors que le résultat peut être connu dès la première. Les conditions sont inutilement compliquées (inversées), de plus, on utilise plutôt type(x) que x.__class__. Et le test sur le __name__ (nom de la classe) n'est pas la meilleure manière de procéder pour un plus ou moins.
  • lignes 18 à 25: On génère 200 classes, ayant comme nom les nombres de 0 à 199, et possédant une méthode tset (la fonction test définie au-dessus). Différentes "erreurs" sont mêlées à cela : variables au nom incongru, réutilisation d'une variable pour stocker quelque chose qui n'a rien à voir, le set n'est pas non plus très adapté pour ça.
  • ligne 26: le import * n'est généralement pas recommandé, car encombre l'espace de nom courant.
  • ligne 27: aléatoire foireux, les nombres du milieu de l'intervalle (50) ont plus de chance d'être tirés que les extrêmes.
  • lignes 29 à la fin: la boucle est inadaptée, avec l'instruction input répétée. Pour caque nombre entré, on vide complètement le set pour en remplir un deuxième, et on fait ensuite l'opération inverse. Puis, pour chaque classe de l'ensemble, on instancie un objet afin de réaliser le test. La chaîne 'gagné' est répétée deux fois, le exit n'est pas idéal.

Édité par entwanne

Apparemment, on dit qu'un code Perl est horriblement laid à regarder…

1
loop (my $tentatives = 0; $tentatives++) { state $nombreAleatoire = round 100.rand; given prompt('Choisissez un nombre entre 1 et 100 : ') { when * < $nombreAleatoire { say "C'est plus ! $tentatives tentatives"; } when * > $nombreAleatoire { say "C'est moins ! $tentatives tentatives"; } when * {say "Victoire !, $tentatives tentatives"; last; } } }

PS : Entwanne, j'espère que le

Entré un nombre entre 0 et 99

est volontaire !

+0 -0

Salut,

Je me suis bien éclaté à faire un code en objective-c ;)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#import <CloudKit/CloudKit.h>
typedef void(^gagner)();
typedef void(^perdu)();
typedef void(^plusGrand)();
typedef void(^plusPetit)();
typedef NSString *(^entrerUnChiffre)();
typedef int(^choisirUnChifre)();


@interface LeJeuDuPlusMoin : NSObject
- (instancetype)JouerACeJeuTtropbienYoupie;
- (void)go:(gagner)o go:(perdu)o1 p:(plusGrand)pg p2:(plusPetit)pp p45:(entrerUnChiffre)ppp pop:(choisirUnChifre)popopop;
@end
@implementation LeJeuDuPlusMoin
- (instancetype)JouerACeJeuTtropbienYoupie{
    return [super init];
}
- (void)go:(gagner)o go:(perdu)o1 p:(plusGrand)pg p2:(plusPetit)pp p45:(entrerUnChiffre)ppp pop:(choisirUnChifre)popopop{
    BOOL gn = false; //Goupe Nominal
    int numberChoice = popopop();



    while (!gn){
        NSString *s = ppp(); int number = [s intValue];



        if (numberChoice > number){


            pp();
        }
        else if (numberChoice < number){

            pg();
        }
        else if (number == numberChoice){
            gn = true;
            o();
        }
        else{

            o1();
        }


    }
}


@end

















int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        [[[LeJeuDuPlusMoin alloc] JouerACeJeuTtropbienYoupie] go:^{NSLog(@"Vous avez gagné !!! ");} go:^{NSLog(@"On ne pas paerde dans ce jeu");
        } p:^{





            NSLog(@"Le nombre est plus grand que le nombre choisie par l'ordinateur \n Cordialement");

        } p2:^{
            NSLog(@"Nombre trop petit");

        } p45:^NSString *{
            float ppopoppopoppopppoppopoppopo = (8.0 * -15.75+126);
            float ppooppooopooppopooppoopppfpppp = (32*2.96875 + 4);
            NSLog(@"Choisissez un nombre entre %f et %f : \n", ppopoppopoppopppoppopoppopo, ppooppooopooppopooppoopppfpppp);
            char cstringlll[40];
            scanf("%s", cstringlll);
            return [NSString stringWithCString:cstringlll encoding:1];
        }pop:^int{
            srand(time(NULL));
            int numberChoice = 0;
            do {
                numberChoice = rand() % 99;
            }while (numberChoice == ((2 * -23) + 46));
            return numberChoice;
        }];
    }
    return 2*-4+8 ;
}

Le code est assez mac conçu car il importe le frameworks CloudKit et Cocoa qui n'a rien voire là dedans. J'utilise une class dont la fonction d'initialisation est totalement contraire au convention. Il y a 6 blocks, ce qui veut dire qu'on mélange le main avec la class. Certaines variables sont explicites et d'autres pas du tout. Certains chiffres sont écrits par des calculs comme 2*-4+8 = 0. Tout est dans le même fichier, l'implémentation comme l'interface.

Édité par Cirdo

+0 -0

J'ai essayé de faire à la fois mal conçu et intelligent :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import random

class Int(int):
    def __new__(cls, *args, **kwargs):
        try:
            return super().__new__(cls, *args, **kwargs)
        except ValueError:
            return print('veuillez entrer un nombre svp')
    def __gt__(self, other):
        return super().__gt__(other) and (print("c'est plus") or True)
    def __lt__(self, other):
        return super().__lt__(other) and (print("c'est moins") or True)
    def __eq__(self, other):
        return super().__eq__(other) and (print("bravo, vous avez gagné") or True)
    def __ne__(self, other):
        return super().__ne__(other) and (print("désolé, vous avez perdu") or True)

def range(n):
    i = 0
    while i < n:
        v = yield i
        i = i + 1 if v is None else v

x = Int(random.randint(0, 99))

r = range(10)
for i in r:
    y = Int(input('%d. entrez un nombre: ' % (i + 1)))
    if y is None:
        r.send(i - 1)
    elif x > y or x < y:
        continue
    elif x == y:
        break
else:
    assert x != y

EDIT: quelques explications

L'idée principale est de déporter l'affichage et une partie de la logique du jeu à une classe héritant de int qui n'est pas du tout censée être en rapport, à travers le constructeur et la surcharge des opérateurs de comparaison.

La classe Int prend en charge les erreurs de saisie (constructeur), les comparaisons du jeu du plus ou moins et l'affichage des messages relatifs (qui ne s'affichent que si la comparaison renvoie True), et la fin du jeu en détournant la méthode __ne__ de son usage normal.

Édité par yoch

+0 -0

Attention: Ce code risque, sous certaines circonstances (pas d'optimisation du compilateur), de fonctionner. Vous avez été prévenus.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
a, b, c, d;
unsigned long p;

s()
{
    p--;

x:

    if (*(char*)(++p) == 00)
    {
        goto y;
    }

    write(sizeof (char), p, sizeof (char));
    goto x;

y:

    00;
}

l()
{
    write(sizeof (char), "\x0A", sizeof (char));
}

g()
{
    read((int)read & 0, &c, sizeof (char));

    if (*(char*)&c != '\n')
    {
        (d = d + d + d + d + d + d + d + d + d + d + *(char*)&c - 060), g();
    }
}

main()
{
    b = time(00) % 0x64;

x:

    (p = "entre 1 nombre"), s(), l(), d = d ^ d, g(), a = d;

    if (a == b)
    {
        goto y;
    }

    else if (a < b)
    {
        goto f;
    }

    else
    {
        goto z;
    }

r:

    goto x;

y:

    (p = "u win bro") & s() & l();
    goto k;

z:

    (p = "Lez bro") & s() & l();
    goto r;

f:

    (p = "more bro") & s() & l();
    goto r;

k:

    00;
}

Pour les horreurs qui sont présentes dans ce code:

  • globales avec le type par défaut (int), qui est utilisé dès que possible

  • pointeurs remplacés par des unsigned long

  • i-- / while (++i != '\0') {code}

  • sizeof (char)

  • fonctions standard non utilisées

  • mélange aléatoire d'octal, de décimal et hexadécimal

  • pas de paramètres pour les fonctions, type de retour = int, mais pas de return

  • x * 10 = x + x + x + x + x + x + x + x + x + x

  • passage de int* pour lire un seul byte avec read

  • ASCII only ("\x0A" = "\n", 060 = '0')

  • random = temps % 100

  • goto utilisé n'importe comment

  • mise à 0 de d: d ^= d

  • utilisation de & pour faire une suite d'instructions

  • includes implicites

  • noms pas explicites

  • franglais dégueu. Parce que sinon ce n'est pas marrant. :D

J'ai honte.

Édité par superlama

+0 -0

Hello,

Version minimaliste mais fonctionnelle en C :)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    main(
    r,c,d
){srand(time(
0));r=rand();
do{scanf("%d"
    ,&c);
    d=c-r

;puts(d>0?"-"
:d<0?"+":"=="
);}while(d);}

EDIT:petit effort de presentation, le formattage ne compte pas mais c'est aussi une maniere de se compliquer la vie inutilement ;)

Variables declarees sans specifier de type, c'est mal :) mais en C le type par defaut est int ce qui tombe bien ici.

Pareil pour les fonctions dont le prototype n'est pas fourni (pas d'include), par defaut le type de retour est int, par chance la fonction rand dont le code retour nous importe retourne un int.

Main non standard egalement. Les prototypes attendus, suivant si on souhaite recuperer les arguemnts de la ligne de commande ou pas, sont

int main(int argc, char *argv[]);

int main(void);

Ici il aurait fallu utiliser le 2eme prototype et declarer des variables locales au main. Mais cela ne m'arrange pas pour le formattage, qui est quand meme plus important que le respect de la norme C :D

Édité par fromvega

+2 -0

Ce que j'aime bien dans le Haskell, c'est son petit côté impératif :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import System.IO.Unsafe
import Data.IORef
import System.Random

-- éléments manquant du haskell
get a () = readIORef a

infixr 0 #
a # b = a (const b)

infixr 0 =:
a =: b = writeIORef a b

while pred action = do {
    cond <- pred();
    if(cond) then do {
      action();
      while(pred) # do {
        action();
      };
    } else do {
      return();
    };
  }
-- fin des éléments manquant du haskell

{-# NOINLINE randNbr #-}
randNbr = unsafePerformIO (newIORef (0 :: Int))
{-# NOINLINE userInput #-}
userInput = unsafePerformIO (newIORef 0)
{-# NOINLINE continue #-}
continue = unsafePerformIO (newIORef True)

main = do {
    initGame();
    readUser();
    analyseInput();
    while(get continue) # do {
      readUser();
      analyseInput();
    };
  }

initGame() = do {
    nbr <- randomRIO (0, 100);
    randNbr =: nbr;
  }

readUser() = do {
    putStrLn("Choisir un nombre entre 0 et 100");
    nbr <- readLn;
    userInput =: nbr;
  }

analyseInput() = do {
    uNbr <- readIORef userInput;
    nbr <- readIORef randNbr;
    if(nbr > uNbr) then do {
      putStrLn("Trop petit!");
    } else if(nbr < uNbr) then do {
      putStrLn("Trop grand!");
    } else do {
      putStrLn("Parfait!");
      continue =: False;
    }
  }

Pour ceux qui ne sont pas familier avec le Haskell, il faut savoir que c'est un langage fonctionnel pure, c'est à dire sans effet de bord. Il est donc de mise de limiter au strict minimum l'utilisation de données "mutables" (elles ne le sont pas réellement, c'est surtout la syntaxe qui donne cette impression).

Le but de mon code est d'avoir quelque chose le plus proche possible d'un langage impératif (donc basé sur la mutation des données), ce qui est totalement contraire à la philosophie du Haskell.

J'ai redéfini quelques éléments au début du code pour faciliter la syntaxe.

Voici une petite liste des aberrations utilisés :

  • L'absence de déclaration de type. Il est courant (et même très fortement conseillé) d'indiquer les types de toutes les définitions globales.
  • L'utilisation d'un paramètre void () pour donner l'impression d'avoir un appel de fonction.
  • Coller les paramètres entre parenthèse au nom de la fonction. Habituellement, on sépare les paramètres et le nom de la fonction par des espaces, mais la syntaxe autorise l'absence d'espace s'il y a des parenthèses f a peut s'écrire f(a).
  • La création de l'opérateur =: (:= n'est malheureusement pas disponible) pour donner un peu plus l'impression que l'on a affaire à un langage impératif.
  • La définition d'une "boucle" while. La notion de boucle ne peut exister que s'il y a des changement d'état, ce qui est absent des langages fonctionnel pure. Encore une fois, c'est quelque chose de parfaitement contraire à la philosophie du langage.
  • L'utilisation des crochets et des points virgule à la place de l'indentation :
1
2
3
4
main = do {
    initGame();
    readUser();
  }

est équivalent à

1
2
3
main = do
  initGame ()
  readUser ()
  • L'étron d'or est décerné à l'utilisation des unsafePerformIO avec les IORef. C'est typiquement la pire chose que l'on peut faire en Haskell. Au passage, pour que cela fonctionne, il est nécessaire d'ajouter un NOINLINE puisque la sémantique du code change si la déclaration de ces variables est inliné (ce qui n'est sensé jamais arriver). Inutile de vous signaler qu'utiliser unsafePerformIO (qui permet de cacher un effet de bord dans un code pure) ne doit se faire qu'en cas d'extrême nécessité et qu'il faut bien avoir conscience que ça peut poser de sérieux problème (son nom est assez clair d'ailleurs).
  • La cerise sur le gâteau : quasiment tout mon code est dans la monade IO (qui est sensé contenir les effets de bords) alors que les seuls éléments qui devraient y être sont : les entrées/sorties et l'initialisation du générateur de nombre aléatoire.
  • Ah, le compilateur me signale encore deux problèmes :
    • J'utilise une variable pred qui cache la fonction du même nom inclue par défaut par la librairie standard.
    • Toujours dans while, action() peut retourner un résultat qui est implicitement ignoré. Il faudrait écrire _ <- action () plutôt que action() pour signaler que l'on ignore volontairement le résultat.

Édité par Berdes

+6 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte