Jump to content
joao_o grande

Duvida - cronometro e um ciclo repeat...until

Recommended Posts

joao_o grande

Ola,

como é que eu devo fazer para colocar um cronometro num ciclo repeat, ou seja, quando o cronometro chegar a 30. o ciclo repeat pára de funcionar.

Nota: eu sei fazer o cronómetro.

Edited by joao_o grande

Share this post


Link to post
Share on other sites
pwseo

joao_o grande,

Podes ter uma variável «de controlo» que é verificada no ciclo para saber se este será ou não executado. Basta que essa variável seja alterada ao fim de 30 segundos (presumo que sejam segundos).

O que tens feito para já?

Share this post


Link to post
Share on other sites
joao_o grande

sim, são 30 segundos.

a estrutura é algo deste género:

repeat
 Case ch of
Case ch of
  //comandos
  for
  end;
  end;
end;
until ...

os dois primeiros cases movem um cursor.

o until do repeat pode estar relacionado com essa variavel de controlo.

Aqui fica o que tenho:

  repeat
ch:=readkey;
Case ch of
  #0:begin
	ch:=readkey;
	Case ch of
	  #75:begin  //left
		Escrever(Jogador.X,Jogador.Y,' ',False);
		Jogador.X:=Jogador.X-1;
	  end;
	  #77:begin  //right
		Escrever(Jogador.X,Jogador.Y,' ',False);
		Jogador.X:=Jogador.X+1;
	  end;

	  #80:begin  //up
		Escrever(Jogador.X,Jogador.Y,' ',False);
		Jogador.Y:=Jogador.Y+1;
	  end;
	  #72:begin  //down
		Escrever(Jogador.X,Jogador.Y,' ',False);
		Jogador.Y:=Jogador.Y-1;
	  end;
	end;
	If Jogador.X>MaxX then Jogador.X:=MaxX else If Jogador.X<MinX then Jogador.X:=MinX;
	If Jogador.Y>MaxY then Jogador.Y:=MaxY else If Jogador.Y<MinY then Jogador.Y:=MinY;

	textcolor(15);
	Desenha_Jogador(Jogador.X,Jogador.Y);

	for n_com:=1 to n_comida do
	If (Jogador.X=coordenadasx[n_com]) and (Jogador.Y=coordenadasy[n_com]) then
	Begin
	  Pontos:=Pontos+1;
	  Textcolor(13);
	  gotoxy(2,24);
	  Writeln('Pontos: ',Pontos);
	end;
  end;
  #27:Exit;
end;
 until ch=#27;

o meu objetivo é: quando passarem 30 segundos este ciclo repeat termina. Tbm queria que o tempo aparecesse na posiçao 60,24.

Não sei se te interessa perceber o ciclo for, mas os vetores coordenadasx[n_com] e coordenadasy[n_com] criam um carater (#178) aleatorio na tela. Há n_comida destes caracteres aleatorios e o qie o ciclo for faz é aumentar o numero de pontos se as posiçoes Jogador.X e Jogador.Y (ou seja o caracter branco que o usuario move pela tela) coincidirem com um caracter aleatorio.

Edited by joao_o grande

Share this post


Link to post
Share on other sites
pwseo

Onde está o código que controla a passagem do tempo? Referiste que também sabias fazer isso.

Share this post


Link to post
Share on other sites
joao_o grande

Já o apaguei e decidi deixar estar como estava dantes. Já o faço. Fi.lo numa function, ok??

5 minutos depois........

Cá está ele, é um funçao do tipo boolean se quiseres eu mudo. para integer, ou assim. O meu problema neste caso é como ligar esta function ao repeat... Sempre que eu chamo esta function não posso mover o caracter pela tela uma vez que ele primeiro conta 30 segundos e depois disso fecha o programa porque é o fim do repeat

Program Cronometro ;

function Cronos (tmp_desejado:integer) : boolean;
var i:byte;
Begin
Cronos:=False;
for i:= 1 to tmp_desejado do
begin
If i<10 then
begin
delay(1000);
Gotoxy(60,24);
Writeln('Tempo: 00:0',i);
end
else
begin
delay(1000);
Gotoxy(60,24);
Writeln('Tempo: 00:',i);
end;
end;
Cronos:=True;
end;

Begin
Cronos(30);

End.[/Code]

Edited by joao_o grande

Share this post


Link to post
Share on other sites
pwseo

joao_o grande,

Da forma que estás a fazer vais ter dificuldade em resolver o teu problema. O ideal é fazeres a contagem do tempo dentro do repeat .. until, recorrendo a valores do tipo TDateTime e à função SecondsBetween.

Vais ter que determinar quantas vezes queres que o ciclo seja executado por segundo para determinar o que fornecer como parâmetro à delay, pois o valor de 1000 implica uma paragem durante 1 segundo inteiro no teu programa (e pode não ser isso que queres).

De uma forma mais geral, é o seguinte: o teu programa (ou jogo, o que for) deveria ter todo o seu funcionamento *dentro* desse ciclo repeat .. until, pois assim todos os componentes passariam a ter acesso a uma forma de medir o tempo. A tua função Cronos só deveria ser responsável por escrever o tempo actual, e não contá-lo também.

Atenção: isto são coisas algo complicadas, não te sintas mal se não perceberes tudo.

  • Vote 1

Share this post


Link to post
Share on other sites
joao_o grande

percebi mais ou menos o que disseste, exceto:

joao_o grande,

De uma forma mais geral, é o seguinte: o teu programa (ou jogo, o que for) deveria ter todo o seu funcionamento *dentro* desse ciclo repeat .. until, pois assim todos os componentes passariam a ter acesso a uma forma de medir o tempo. A tua função Cronos só deveria ser responsável por escrever o tempo actual, e não contá-lo também.

Podes dar-me uma sugestão de erstrutura?? Estou um bocado à nora.

como é que é suposto eu escrever o tempo sem o contar??

Edited by joao_o grande

Share this post


Link to post
Share on other sites
pwseo

Repara no seguinte pseudo-código:

inicio ← time()
repeat
 agora ← time()
 tempo-passado ← seconds-between(inicio, agora)

 # Aqui estaria a parte principal do teu programa

 # Esta função desenharia o tempo passado
 desenhar-cronometro(tempo-passado)

 # Este valor de 50 é arbitrário... terias que decidir o melhor para ti
 delay(50)
until quit = True

  • Vote 1

Share this post


Link to post
Share on other sites
thoga31

como é que é suposto eu escrever o tempo sem o contar??

Se eu te pedir para veres quando passou 1 hora, tu vais pegar no relógio e contar segundo a segundo sem tirares os olhos do relógio, ou vais vendo de tempos a tempos e calcular de cabeça se já passou 1 hora?

O pensamento aqui é o mesmo. Neste momento tu estás a pedir ao programa que conte segundo a segundo - o que nem sequer é exacto, e já explico porquê - usando o Delay. Isto traz um problema grave: durante esse segundo, o programa está parado uma vez que essa é a função do Delay. Nota bem isto: durante o Delay, o programa não faz mais nada a não ser esperar os milissegundos que indicaste!

Aquilo que tu deves pedir ao programa é calcular quanto tempo já passou desde o início do ciclo repeat.

Existe um tipo de dados, o TDateTime, que permite manipular datas de forma simples. Este tipo de dados e respectivas funções e procedimentos estão disponíveis nas units sysutils e dateutils.

NOTA: porque é que usar o Delay não é exacto?

Qualquer comando gasta um determinado tempo a ser processador na CPU. Mesmo sendo normalmente na ordem de milésimas de segundo, é um tempo com o qual devemos contar. Este tempo é maior quantos mais comandos dermos à CPU para processar (isto dito de uma forma muito simplista).

Ora, tu pedes 30 vezes um Delay de 1000 milissegundos - tempo este no qual o programa pára e não faz mais nada -, mas entre cada Delay tu dás uma série de comandos à CPU para processar. Imaginemos que esta série de comandos demoram 10 milissegundos de cada vez. Ao fim dos 30 Delays, passaram 30*(1000+10) = 30300 milissegundos, ou seja, 30,3 segundos - passaram 300 milissegundos a mais daquilo que pretendias.

Este é o motivo pelo qual jamais se faz um cronómetro usando o Delay! Ele é minimamente exacto em contagens de tempo pequenas, mas rapidamente começa a ganhar um erro enorme, contando menos tempo do que aquele que realmente passou.

Cumprimentos.

Edited by thoga31
  • Vote 1

Knowledge is free!

Share this post


Link to post
Share on other sites
joao_o grande

contando menos tempo do que aquele que realmente passou.

Cumprimentos.

Muito obrigado pela explicação:

so um pequeno pormenor: menos = mais.

Edited by joao_o grande

Share this post


Link to post
Share on other sites
thoga31

so um pequeno pormenor: menos = mais.

Menos.

Olha o exemplo que te dei dos 30 segundos com comandos a demorar 10 milissegundos de cada vez que eram executados.

No final contaste 30 segundos mas passaram 30,3 segundos.

Vamos passar a 1 hora: são contados 3600 segundos mas passaram 3636 segundos.

Então, contou mais ou menos do que o tempo real que passou?

Edited by thoga31

Knowledge is free!

Share this post


Link to post
Share on other sites
KTachyon

Este problema é relativamente fácil de resolver utilizando event-driven programming (ou programação orientada a eventos, em português). Carregar numa tecla é um evento. E podes agendar um evento para acontecer no futuro (e, dependendo de quão low level a linguagem conseguir chegar, a precisão que se pretende).

Basicamente, a ideia é teres um handler que está à escuta por eventos do teclado e que é configurado com uma estratégia. E depois tens qualquer coisa que agenda um evento que muda a estratégia do handler passado X tempo.

Não te vou dizer como é que isto se faz em Pascal, mas tendo em conta que praticamente todas as linguagens modernas têm mecanismos para fazer isto, não deve ser difícil de descobrir como é que isto se faz.


“There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.”

-- Tony Hoare

Share this post


Link to post
Share on other sites
joao_o grande

Este problema é relativamente fácil de resolver utilizando event-driven programming (ou programação orientada a eventos, em português). Carregar numa tecla é um evento. E podes agendar um evento para acontecer no futuro (e, dependendo de quão low level a linguagem conseguir chegar, a precisão que se pretende).

Basicamente, a ideia é teres um handler que está à escuta por eventos do teclado e que é configurado com uma estratégia. E depois tens qualquer coisa que agenda um evento que muda a estratégia do handler passado X tempo.

Não te vou dizer como é que isto se faz em Pascal, mas tendo em conta que praticamente todas as linguagens modernas têm mecanismos para fazer isto, não deve ser difícil de descobrir como é que isto se faz.

No percebi o que quiseste dizer com isso. Mas está relacionado com o tempo??

Share this post


Link to post
Share on other sites
joao_o grande

Bem, já consegui fazer um relogio. Agora so tenho de fazer algumas alterações para parecer um cronometro.

Aceitam-se sugestões.

Aqui fica o codigo:

program relogio_sistema;
uses crt;

type TDataHora = record
 relogio : record
hora : word;
minuto : word;
segundo : word;
centesimo_segundo : word;
 end;
 data : record
ano : word;
mes : word;
dia : word;
dia_semana : word;
 end;
end;

procedure GuardaDataHora(var datahora : TDataHora);
begin
 with datahora.relogio do begin
gettime(hora, minuto, segundo, centesimo_segundo);
// determina a hora do sistema
 end;
 with datahora.data do begin
getdate(ano, mes, dia, dia_semana);
// determina a data do sistema
 end;
end;

var
tempo_registo : TDataHora;
begin
cursoroff;
 repeat
GuardaDataHora(tempo_registo);
with tempo_registo.relogio, tempo_registo.data do
begin
  Gotoxy(1,1);
  writeln(hora,':',minuto,':',segundo);
end;
 until keypressed;
end.

Edited by joao_o grande

Share this post


Link to post
Share on other sites
nunopicado

No percebi o que quiseste dizer com isso. Mas está relacionado com o tempo??

O que o @KTachyon está a falar, é extremamente válido em Object Pascal, ou qualquer linguagem OO/ED (Object Oriented/Event Driven).

No teu caso concreto, creio que é melhor manteres-te para já com as sugestões do @pwseo e do @Thoga31.

Mas guarda a dica dele na memória. Um dia, quando tiveres os conceitos de programação básicos bem definidos, e começares a mexer em conceitos OO/ED, será interessante refazeres este projecto com recursos como objectos e eventos.

Bem, já consegui fazer um relogio. Agora so tenho de fazer algumas alterações para parecer um cronometro.

Aceitam-se sugestões.

Aqui fica o codigo:

O teu relógio tem dois problemas:

1. Está permanentemente a escrever a data e hora no ecrã, sem controlo.

Visto que todo aquele ciclo repeat demora menos de um segundo a ser executado pelo processador, quer dizer que a mesma data/hora será escrita várias vezes no ecrã, quando o que devia acontecer era escrever apenas se a data/hora for diferente da que lá está.

Deverias ter uma segunda variável do tipo TDataHora, que guarde a última hora que foi escrita, e só escrever novamente se elas forem diferentes:

var
tempo_registo, lastDateTime : TDataHora;

begin
 cursoroff;

 lastDateTime.Data.ano := 0; // Inicializa a variável lastDateTime. Basta neste caso inicializar o campo Ano, porque é certo que tempo_registo nunca terá o ano 0, pelo que a primeira comparação irá ser True (são diferentes) e portanto, irá ser escrita a data e hora inicial sem problemas.

 repeat
   GuardaDataHora(tempo_registo);

   if tempo_registo <> lastDateTime
     then with tempo_registo.relogio, tempo_registo.data do
            begin
              lastDateTime := tempo_registo;
              Gotoxy(1,1);
              writeln(hora,':',minuto,':',segundo);               
            end;
 until keypressed;
end.

No entanto, para isto funcionar, tens de te livrar o campo centesimo_segundo, que não usas para nada, mas iria fazer com que a data e hora fosse escrita no ecrã bastando que mudasse o centésimo, ou seja, 100x mais do que o necessário (em teoria).

Como não o usas, seria caso de removeres o campo:

type 
 TDataHora = record
   relogio : record
     hora : word;
     minuto : word;
     segundo : word;
   end;
   data : record
     ano : word;
     mes : word;
     dia : word;
     dia_semana : word;
   end;
end;

procedure GuardaDataHora(var datahora : TDataHora);
var
 centesimo_segundo: Word; // A variável centesimo_segundo passa a ser local, e não será usada para mais nada
begin
 with datahora.relogio do begin
   gettime(hora, minuto, segundo, centesimo_segundo); // determina a hora do sistema
 end;
 with datahora.data do begin
   getdate(ano, mes, dia, dia_semana); // determina a data do sistema
 end;
end;

Isto vai reduzir imenso o número de vezes que a data e hora é escrita.

Na verdade, vai escrever apenas uma vez por segundo, muito mais optimizado do que escrever sempre que o ciclo completa uma volta.

Se só mostrasses minutos e segundos, bastava-te tirar o campo segundos do record, e a data/hora já só seria escrita uma vez por minutos.

O teu segundo problema leva-te de encontro ao conceito de threads.

Um programa em Pascal tem, nativamente, uma thread, ou seja, um fluxo único em que os comandos são executados. Um comando só é executado depois de terminada a execução do seu antecessor.

Na prática isto quer dizer que este teu código de relógio só funciona correctamente em duas situações:

1. Se o programa só fizer isto

2. Se o programa fizer mais coisas, mas essas outras coisas, no total, demorarem menos do que a unidade mais pequena que escreves no ecrã, neste caso, se demorarem menos de 1 segundo. Isto engloba tudo, desde as instruções que dás ao tempo que o utilizador tem para reagir - é impraticável.

"E se essas coisas demorarem mais", perguntas tu...

Pois, não morre ninguém neste caso. Simplesmente o relógio vai saltar numeros. Imagina que o que fazes demora 10 segundos. O relógio só será actualizado a cada 10 segundos. Se demorar 10 minutos, o relógio só é actualizado a cada 10 minutos, e assim sucessivamente.

Isto porque o ciclo repeat só será concluído depois de tudo feito.

Se esta limitação servir para o teu programa, tudo bem, podes avançar. Lembra-te só que tudo o que estiver controlado pelo relógio deve estar dentro daquele repeat...until.

Se não funcionar, como resolver? Como ter um relógio a trabalhar tranquilamente sem que isso interfira no resto do programa?

A única solução é fazeres com que a execução do relógio não esteja dependente da execução do resto do código, ou seja, estarem em fluxos de execução diferentes.

Para fazer isso, ou vamos de volta ao que disse o @KTachyon, com um sistema de eventos, em que o programa está permanentemente a verificar determinadas condições (mudança de hora) e caso se verifique, escreve a nova hora (de forma independente do restante código), ou então tens de criar uma nova thread, um novo fluxo de execução, e colocar o relógio nesse fluxo à parte.

Nesta fase em que estás a aprender ainda os conceitos básicos, threads não me parecem de todo um passo que devas dar já. Enquanto não tiveres bem definidos os conceitos básicos na tua forma de programar, avançares para threads é dar um tiro no pé, pois o risco de bugs é enorme, especialmente porque no caso do teu jogo vai haver momentos em que as threads têm de ser sincronizadas, pois terão de aceder aos mesmos recursos (variáveis, por exemplo).

Edited by nunopicado

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
thoga31

Todos estes tópicos levam-me a comentar algo que tenho vindo a concluir.

@joao_o grande, gosto bastante de ver este teu entusiasmo em programar em Pascal, mas tenho visto que ainda há vários conceitos com os quais não estás bem familiarizado, e falo em conceitos fundamentais do Pascal.

É minha opinião que, nesta fase, estás a dar o passo maior que a perna. Sem dúvida que este programa te está a levar a aprender imensas coisas, mas a verdade é que estás a aprender demasiadas coisas para um programa só na tua fase de aprendizagem.

Digo isto porque estás a lidar com diversos conceitos que deviam ficar bem cimentados e, com o desenvolver deste programa, aquilo que tenho visto é que, assim que consegues resolver um problema que tens em mãos, consideras o assunto resolvido mesmo que os conceitos inerentes não ficassem, de todo, cimentados.

Isto é problemático uma vez que, quando quiseres avançar para outros programas, vais ter dificuldade em implementar de novo estes conceitos. Vais ver os teus programas anteriores mas não vais conseguir possivelmente adaptar boa parte do código uma vez que em cada programa há uma implementação específica do conceito que pode não se ajustar ao que precisas no próximo programa.

Friso novamente que considero muito bom quereres aprender mais acerca desta linguagem. Apenas quero deixar a minha opinião acerca deste assunto em particular: não é vantajoso aplicares conceitos sem antes os dominares.

Uma sugestão (que é aquilo que eu ainda hoje faço quando preciso de aprender conceitos novos): podes ir fazendo este programa, mas quando te deparas com um novo conceito, treina-o à parte, e só quando o dominares minimamente é que o tentas implementar no programa.

Cumprimentos.

  • Vote 1

Knowledge is free!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.