Animando o Console - Efeito Matrix

Saída da 1ª animação em modo texto. Bem vindo à Matrix! :)
Depois de ver o vídeo Armaggedon na postagem anterior, fiquei nostálgico e resolvi brincar um pouco com o console na tentativa de gerar algo graficamente interessante.

E por que alguém faria isto em pleno ano de 2014 com tantas opções e tantas tecnologias capazes de renderizar imagens super realistas em hardwares comuns e baratos? - eu até posso ouvir você perguntando.

Pelo prazer e pelo desafio, ora bolas! Sinceramente, precisa de mais que isto?

Então vamos começar nos familiarizando com o console dentro do Windows. Segundo a documentação da Microsoft

Consoles gerenciam inputs e outpus (I/O) para aplicações em modo caractere (aplicações que não providenciam sua própria interface gráfica).

Então, como primeira e mais importante restrição, temos a impossibilidade de acessar qualquer tipo de modo gráfico. Nossa tela é capaz de exibir 25 linhas de 80 caracteres em possui uma paleta de 16 cores.
Um... caracteres coloridos na tela? Que tal simular um efeito "matrix" pra começar nossa exploração?

Vamos lá.

Crie uma nova aplicação console em seu ambiente (Delphi ou Lazarus), salve-a com o nome de matrix e você vai ter um código parecido com este :

program matrix;
{$APPTYPE CONSOLE}
uses
  SysUtils;
begin
  try
    { TODO -oUser -cConsole Main : Insert code here }
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Apague todo o código entre o begin e o end e substitua SysUtils na clásula uses pela unit Windows (vamos utilizar somente a API do windows neste primeiro programa). Feito isto, escreva o código conforme a listagem abaixo, execute e veja o efeito funcionando.

program matrix;
{$APPTYPE CONSOLE}
uses
  windows;
const
  SCREEN_W = 80;       //largura da tela
  SCREEN_H = 25;       //altura da tela
var
  stdOut   : THANDLE;  //handler da janela do console
  position : COORD;    //posição em que vamos imprimir o caracter (x, y)
  attribute: Word;     //atributos de saída de texto no console
begin
  randomize;
  stdOut := GetStdHandle(STD_OUTPUT_HANDLE);
  while hi(GetKeyState(VK_ESCAPE)) = 0 do
  begin
    position.X:= short(random(SCREEN_W-1));
    position.Y:= short(random(SCREEN_H-1));
    if (Random(10) mod 2 = 0) then
       attribute:= FOREGROUND_GREEN
    else
       attribute:= 10;
    SetConsoleTextAttribute(stdOut, attribute);
    SetConsoleCursorPosition(stdOut, position);
    if (Random(10) mod 2 = 0) then
       write('0')
    else
       write('1');
  end;
end.

Ok, há espaços para várias melhorias, mas é um bom ponto de partida. Vamos entender o código.

O algoritmo aqui é o mais básico possível, simplesmente vamos imprimir zeros e uns em dois tons de verde em posições aleatórias na tela em um loop que só termina quando pressionamos a tecla ESC.

Começamos definindo as constantes que representam os limites da tela (SCREEN_H e SCREEN_W) e as variáveis que serão necessárias para as chamadas da API que utilizaremos. Depois inicializamos o gerador de números pseudo-aleatórios chamando a função randomize e terminamos nossas inicializações recuperando o handler de saída da nossa janela com a função GetStdHandle.

Em seguida, iniciamos nosso loop verificando, através da função GetKeyState, se a tecla ESC não foi pressionada e, a cada iteração escolhemos uma posição aleatória (linhas 17 e 18), um tom de verde entre as duas opções que temos na paleta (linhas 19 até 22), preparamos o terminal com as opções escolhidas (linhas 23 e 24) usando as funções SetConsoleTextAttribute e SetConsoleCursorPosition e, finalmente, escrevemos 0 ou 1 na tela.

Seguindo os links para a documentação das funções da API do windows, o código deve ser bem fácil de acompanhar para quem possui um conhecimento básico na linguagem. Talvez a única parte que mereça um comentário adicional é a função hi usada no teste do loop. Esta função serve para verificar o valor do byte mais significativo do valor inteiro retornado por GetKeyState.

Sempre que estivermos interagindo com a API do windows e com  boa parte das APIs escritas em C, vamos utilizar operações de manipulação de bits. Aqui você encontra um texto explicando o que é o bit mais significativo de um valor e aqui está documentação da função hi no free pascal. A leitura vale muito à pena.

Enfim, código compilado rodando e totalmente compreendido, vamos ao próximo post onde iremos realizar algumas melhorias no efeito.

Links