• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

Dkid

[Perl] Jogo básico - Adivinha o numero

Na tua opinião foi útil?   9 membros votaram

  1. 1. Na tua opinião foi útil?

    • sim
      8
    • não
      1

Please inicie sessão ou registe-se para votar.

12 mensagens neste tópico

Já à algum tempo que não dizia nada, decidi refazer um jogo que tinha realizado no tempo de pascal e em tempo de aulas, mas desta vez em perl.

Quero agradecer ao user falco, pois basei-me no código que me mostrou na aplicação "Converter Celsius para Fahrenheits"

Acrescentei uma poll, porque queria saber se acham útil continuar a falar sobre perl, visto que poucos se manifestam textualmente.

Obrigado e boa programação

#!/usr/bin/perl

#
# Jogo básico para acertar num numero
# usando o random e subroutines
#

use strict;

my $entrada; #dado inserido pelo utilizador
my $range = 101; # 0 a 100

sub entrada{
print "\n>";
$entrada = <STDIN>;
chomp $entrada;
($entrada) =~ /(\d+)/; #obrigado falco por me ensinares esta  (e as subroutines)

return $entrada;
}

sub adivinha{

my $random = int(rand($range)); # random 0 a 100
my $contagem = 5;

while ($entrada ne $random && $contagem ne 0){ #enquanto o valor de entrada "nao for igual" (ne) ao random e(&&) a contagem "nao for igual"(ne) a zero
$entrada = entrada;
	if ($entrada < $random){
	print "o seu numero é menor";
	$contagem -= 1;
	}
	if ($entrada > $random){
	print "o seu numero é maior";
	$contagem -= 1;
	}
	if ($entrada eq $random){
	print "Parabens, acertou!\n";
	}
	if ($contagem == 0){
	print "\nDesculpe, mas acabaram as oportunidades!\n";
	print "O numero era :$random \n";
	}

} # fim do while
}

while (1){ #enquanto for verdadeiro
print "Escolha a sua opcao:\n\t 1- Jogar\n\t 2- Sair\n";

$entrada = entrada; #vai buscar o valor retornado do "sub entrada"

if ($entrada == 2){ # se o valor for 2 vai sair da aplicacao
	print "A fechar...\n";
	last;
}
if ($entrada == 1){ # se o valor for 1 vai chamar o "sub adivinha"
	print "Adivinhe o numero de 0 a 100:\nNão se esqueça que tem 5 tentativas";
	$entrada => adivinha;
}
} #fim do while

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Boas,

Eu acho que sim, se deve continuar a falar de Perl o fórum.... Neste momento não tenho disponibilidade para aprender Perl, mas num futuro próximo irei tentar aprender....

cumps  :P

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

@zecapistolas

Obrigado!!!

Agora, queria fazer uma pequena observação, que há bocado fiz o código um pouco à pressa e esqueci-me de limitar os valores de 0 a 100! E já corrigi!

Agora tenho de ver como limitar o input apenas a números  :hmm:

Usei uma expressão regular para contornar esta situação! (Vou tentar explicar num próximo tópico)

#!/usr/bin/perl

#
# Jogo básico para acertar num numero
# usando o random
#

use strict;

my $entrada; #dado inserido pelo utilizador
my $range = 101; # 0 a 100

sub entrada{
print "\n>";
$entrada = <STDIN>;
chomp $entrada;
($entrada) =~ /(\d+)/; #obrigado falco por me ensinares esta  e as subroutines

return $entrada;
}

sub adivinha{

my $random = int(rand($range)); # random 0 a 100
my $contagem = 5;

while ($entrada ne $random && $contagem ne 0){ #enquanto o valor de entrada "nao for igual" (ne) ao random e(&&) a contagem "nao for igual"(ne) a zero
$entrada = entrada;

if ($entrada =~ /^[0-9]+$/){ #se a entrada for númerica prossegue, Expressão regular (vou tentar falar dela numa próxima vez)

	if ($entrada <0){
	 print "Deveria inserir um valor superior a 0!\nEsta jogada não foi contabilizada!";
	}
	elsif ($entrada < $random){
	print "o seu numero é menor";
	$contagem -= 1;
	}

	if ($entrada >100){
	 print "Deveria inserir um valor inferior a 100!\nEsta jogada não foi contabilizada!";
	}		
	elsif ($entrada > $random){
	print "o seu numero é maior";
	$contagem -= 1;
	}

	if ($entrada eq $random){
	print "\tParabens, acertou!\n";
	}
	if ($contagem == 0){
	print "\nDesculpe, mas acabaram as oportunidades!\n";
	print "O numero era :$random \n";
	}
} #fim da validacao
else { print "Apenas números!";} #aviso se inserir caracteres que não sejam numéricos
  } # fim do while
}

while (1){ #enquanto for verdadeiro
print "Escolha a sua opcao:\n\t 1- Jogar\n\t 2- Sair\n";

$entrada = entrada; #vai buscar o valor retornado do "sub entrada"

if ($entrada == 2){ # se o valor for 2 vai sair da aplicacao
	print "A fechar...\n";
	last;
}
if ($entrada == 1){ # se o valor for 1 vai chamar o "sub adivinha"
	print "Adivinhe o numero de 0 a 100:\nNão se esqueça que tem 7 tentativas";
	$entrada => adivinha;
}
} #fim do while

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Um comentário rápido, quanto à questão de continuares a falar de Perl:

Continua!

Uma recomendação aos outros:

O Perl facilmente vos arranjará um emprego fixe. Há falta de programadores de Perl em Portugal e algumas das empresas mais fixes do mundo usam bastante Perl.

Mas aprendam Perl da forma certa. Ou seja, não se metam em guias manhosos. Leiam documentação oficial, leiam a documentação recomendada pela comunidade de Perl, envolvam-se com os Perl Mongers da vossa cidade (se não houverem na vossa cidade considerem a ideia de criar depois de saber se há gente suficiente para valer a pena) e leiam os muitos bons livros escritos pelos gurus de Perl e publicados pela O'Reilly.

Quanto ao código do post, já o vou ver para comentar...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Um comentário rápido, quanto à questão de continuares a falar de Perl:

Continua!

Uma recomendação aos outros:

O Perl facilmente vos arranjará um emprego fixe. Há falta de programadores de Perl em Portugal e algumas das empresas mais fixes do mundo usam bastante Perl.

Obrigado pelo apoio,

Quanto ao emprego tens razão, também te devo agradecer por isso, uma vez que vou mudar de estágio :D para programar em Perl.

Cumprimentos,

Dkid

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Começando a comentar o código...

Falta usares -w, ou o "use warnings". Deves utilizar sempre isso.

Recomendo também que no Futuro uses também outra coisa, que é o taint mode (-T).

O taint mode vai fazer com que não possam haver coisas como input directo do utilizador sem validação por expressões regulares e outras medidas de segurança para o programa.

Podes ler mais sobre isso em: http://perldoc.perl.org/perlsec.html e no Mastering Perl do Brian d Foy.

Se não entenderes, ou se tiveres dificuldade em entender, ou em resolver problemas com isto, deixa para outra altura (isto já exige algum à vontade em Perl, que só existe com alguma experiência). Mas valida sempre o input do utilizador com regexp antes de o utilizares internamente. E por utilizador entende tudo o que não seja gerado internamente no teu código.

Não faças isto:

$entrada = entrada; #vai buscar o valor retornado do "sub entrada"

Torna o teu código difícil de ler!

entrada é uma função da tua autoria, e por tanto deves chama-la sempre com:

entrada();

Só não se utiliza parêntesis para keywords para as funções builtin que sejam para todos os efeitos keywords (por exemplo chomp, print, etc...) e que são por isso chamadas pelo Damian Conway como builtins honorários.

A excepção relativamente aos builtins honorários, ou seja, utiliza-se parêntesis, apenas quando se quer fazer valer alguma precedência. Por exemplo:

print "\nah e tal: ", substr($string, 0, 5), "!\n";

Algumas funções importadas (não façam parte do core do Perl), mas que façam parte da distribuição core do Perl, também são considerados builtins honorários (por exemplo carp, croak do módulo Carp).

As vantagens disto, são:

* código mais limpo;

* rápida distinção entre chamadas a builtins e chamadas a funções;

A respeito desta questão de boa prática de código, recomendo-te a leitura do Perl Best Practices do Damian Conway (leitura obrigatória para todos os programadores de Perl).

Criei um jogo baseado no teu...

Mas com as seguintes diferenças:

* Retirei o menu. Ele não tinha utilidade.

Um menu pode ser útil para diversas coisas, mas a utilização que se estava a dar ao menu neste caso, era apenas forçar o utilizador a fazer mais um passo para executar duas funções que poderiam ser feitas com apenas um passo em vez de dois (iniciar o jogo e terminar o jogo).

Se quiserem adicionar mais funcionalidades como por exemplo uma configuração do jogo em "run-time", então pode-se adicionar facilmente o menu, porque o programa faz está estruturado com funções de forma a permitir isso com facilidade;

* tem uma funcionalidade de configuração do jogo, que neste momento permite definir um parâmetro via variável de ambiente, mas que pode ser facilmente expandida, para obter mais parâmetros e/ou para receber parâmetros via ficheiro de configuração, sendo que também é uma boa ideia receber parâmetros via argumentos. Os utilizadores não devem ter que saber fazer código para configurar os programas e como estava o jogo exigia que se tivesse que alterar no código o valor de uma variável para configurar o jogo.

Não adicionei algumas dessas opções de configuração porque davam mais trabalho do que as que utilizei e o objectivo era apenas dar um exemplo e depois abordar o tema;

* não gosto de construções if...elsif... de grandes dimensões, por isso mais uma vez utilizei uma hash com referências para funções que neste caso por serem tão pequenas preferi que fossem anónimas.

* sim o meu programa está maior, mas as suas funcionalidades estão mais auto-contidas e funções de pequena dimensão e simples de entender e modificar e o mesmo se aplica à estrutura geral do programa, ao ter uma função run, que basicamente é o equivalente a uma main de um programa em C. Também está maior para eu fazer coisas que não são estritamente  necessárias para este jogo, mas que são boas práticas e ideias úteis em geral, principalmente se falarmos de aplicações mais complexas e de maior dimensão. E assim pude falar dessas coisas.

#!/usr/bin/perl


use strict;
use warnings;
use Data::Dumper;
use Term::ReadKey;



my $game_results = {
    0   =>  sub {   print("ÉS UM FALHADO!!!\n"); },
    1   =>  sub {
                    print("PARABÉNS!!!\nGANHOU!!!\n");
                    return 0;
                },
    2   =>  sub {   print(
                        "Vamos começar a jogar!!\nSe tiveres coragem...\n");
                },
    3   =>  sub {   print "INPUT INVÁLIDO!!!\n";
                    return 0;
                },
};



run();



sub run {
    my ($range) = usr_i();
    game_loop($range);

    print "\n";
    exit;
}



sub usr_i {
    #validates user inputed configuration
    my $range = $ENV{'GUESS_RANGE'} // 101;
    ($range) = $range =~ /(\d*)/;

    return ($range);
}



sub game_loop {
    #the game logic
    my ($range) = @_;
    my $acertou = 2;

    while (1) {
        unless ($acertou) {
            $acertou = 0;
        }

        feedback($acertou);
        my $alvo = int(rand($range));
        sleep(1);

        my $escolhido = prompt_key(
            "\n\n\nDesiste como um falhado (prime a letra 'q'), ou\n".
            "escolhe um número de 0 a ".($range-1).": ",
            "([\\d || q])"
            );
        
        $acertou = check_result($alvo, $escolhido);
    }
}



sub feedback {
    #feedbacks the user what happened
    my ($acertou) = @_;

    if(exists $game_results->{$acertou}) {
        &{$game_results->{$acertou}}; 
    }
    else {
        &{$game_results->{3}};
        return undef;
    }
}



sub prompt_key {
    my ($prompt, $regexp) = @_;

    print $prompt;
    my $key = ReadLine();

    if (($key) = $key =~ /$regexp/) {
       return $key;
    }
    else {
        return undef;
    }
}



sub check_result {
    my ($alvo, $escolhido) = @_;

    if(!$escolhido) {
        #invalid input
        &{$game_results->{3}};
        return undef;
    }
    elsif($escolhido eq 'q') {
        #leave the program
        exit;
    }

    print 
        "\n\nEscolheste: '$escolhido'\t e o número sorteado foi: '$alvo'\n";
        
    if($alvo == $escolhido) {
        return 1;
    }
    else {
        return 0;
    }
}

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites
Só não se utiliza parêntesis para keywords para as funções builtin que sejam para todos os efeitos keywords

É boa práctica mas não é obrigatório.

Também é boa práctica correr os programas com o Perl::Critic.

Na linha de comandos perlcritic prog.pl  ou  http://perlcritic.com/

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites
É boa práctica mas não é obrigatório.

Sim, eu estava a falar de boas práticas.

O Perl::Critic, analisa o código para fazer observações a respeito das boas práticas.

Muitas das criticas que ele faz, são retiradas em sugestões feitas por Damian Conway no livro Perl Best Practices, cuja leitura eu sugeri e qualquer bom programador de Perl o fará. Por isso é também é boa ideia utilizar o Perl::Critic, pois ajuda a procurar falhas que em programas maiores podemos ter cometido.

Mas também é preciso ter em conta que nisto das boas práticas não há verdades absolutas e situações que não possam ter excepções.

Por exemplo e passarem o meu código pelo Perl::Critic no nível mais exigente, vão encontrar bastantes avisos. Contudo o propósito do meu código faz com que alguns deles possam não ser tidos em conta. O código que aqui coloquei tinha o objectivo de introduzir ao dkid mais algumas boas práticas, e não todas de uma só vez.

Como vim falar de boas práticas acho que também devo algumas explicações sobre o meu código.

Para além disso como disse há situações de excepção. Por exemplo:

* achei melhor usar na linha 62, em que usei parêntesis para tornava mais óbvio que se estava a fazer;

* fiz a interpolação de duas strings literais, para evitar uma linha demasiado longa;

* a critica feita à utilização de chaves numéricas acho que não é particularmente importante neste programa e alias, a utilização dos números foi feita para fazer com que o programa fosse de utilização mais parecida com o programa do qual foi derivado (um dos objectivos a que me propus);

Não fiz várias outras coisas porque não achei que faria sentido neste "programinha de treta", que só tinha o objectivo de introduzir algumas boas práticas (outras podem esperar para outras oportunidades). E até tive alguns lapsos como a utilização de parêntesis em chamadas a print.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Lembrei-me hoje de manhã porque é que estão aí alguns prints que usam parêntesis.

Eu estive a testar um módulo que estava no cpan, para programas de consola. Esse módulo permitia fazer coisas com piada, como por exemplo imprimir para a consola de forma a simular a velocidade de escrita de um ser humano (e também coisas úteis), mas o módulo tem um problema a lidar com unicode e por isso decidi não o utilizar. Ao descontinuar a utilização do módulo fiz uma substituição de algumas chamadas a funções desse módulo com uma expressão regular, que alterou de facto o nome da função para print, mas esqueci-me dos parêntesis (mais uma prova que o Perl::Critic é útil para rever o código).

Ainda bem que há quem esteja a gostar.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Boas,

Bem me parecia que tinha uma pergunta.

Em perl o código é lido de baixo para cima?

Estou a perguntar isto porque tu fazes :

run ();

sub run{

}

E também porque em Apache as instruções que ficam por baixo têm precedência sobre as que estão por cima.

Ou é totalmente indiferente a ordem?

Cumprimentos e bons códigos

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites
Ou é totalmente indiferente a ordem?

A ordem em Perl  de forma geral a ordem de definição é indiferente. Mas existem blocos de código que podem ser utilizados para executar determinadas operações antes do programa ser compilado, antes de se iniciar o run-time, antes de o programa ser executado e/ou no fim da execução.

O Perl tem 5 blocos especiais que têm uma ordem de execução específica BEGIN, UNITCHECK, CHECK, INIT e END. Por exemplo:

* O BEGIN é sempre aquilo que é executado primeiro (pode ser útil para poder exemplo adicionar coisas a @INC;

* O END é o oposto do BEGIN, e pode ser útil para termos a certeza que as coisas terminam de forma segura, executa SEMPRE depois do programa excepto se o programa terminar morrer num eval;

Para mais detalhes: http://perldoc.perl.org/perlmod.html#BEGIN,-UNITCHECK,-CHECK,-INIT-and-END

E tirando estes blocos, o resto do código não tem restrições de layout como em C. O que interessa é as variáveis e funções estarem definidas e os módulos carregados. E tirando a necessidade de indicar os módulos antes de utilizar os seus recursos, não me recordo de mais nenhuma restrição.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora