Obrigado a Julio Marchi pelo espaço cedido na MSX All
 

Jogo 21


  21 é um famoso jogo de cartas, onde dois jogadores compram cartas de uma pilha e o objetivo é somar o maior número de pontos que o adversário, desde que no máximo até 21 pontos. Caso a soma dos valores das cartas de qualquer jogador ultrapasse o valor 21, ele perde a partida.
  Qualquer jogador pode terminar a partida, quando, ao final da rodada, achar que tem mais pontos que o adversário. Então, todos mostram as mãos e verifica-se o vencedor.

Pegue aqui:
Código fonte e binário


  1. Código fonte
var carta : array[1..12,1..4] of boolean;
    mao : array[1..10,1..2] of integer;
    car_mao : array[1..2] of integer;

procedure limpa;
var f,g : integer;
begin
  { Limpa Cartas }
  for f:=1 to 12 do
  begin
    for g:=1 to 4 do
      carta[f,g]:=false;
  end;
  { Limpa Mao }
  for f:=1 to 10 do
  begin
    mao[f,1]:=0;
    mao[f,2]:=0;
  end;
  { Limpa Cartas da Mao }
  car_mao[1]:=1;
  car_mao[2]:=1;
end;

function sorteia_carta:integer;
var f, g : integer;
    flag : boolean;
begin
  Randomize;
  flag:=false;
  while (not flag) do
  begin
    f:=Random(12)+1;
    g:=Random(4)+1;
    if (carta[f,g]=false) then
    begin
      carta[f,g]:=true;
      flag:=true;
    end;
  end;
  sorteia_carta:=f;
end;

procedure vencedor;
var f,k,l : integer;
begin
  k:=0;
  l:=0;
  for f:=1 to 10 do
  begin
    k:=k+mao[f,1];
    l:=l+mao[f,2];
  end;
  writeln;
  if ((k>l) or (l>21)) and (k<22) then
    writeln('Voce ganhou!');
  if ((k21)) and (l<22) then
    writeln('Eu ganhei!');
  if (k=l) or ((k>21) and (l>21)) then
    writeln('Deu empate!');
  write('Sua mao: ');
  for f:=1 to car_mao[1]-1 do
    write(mao[f,1],' ');
  writeln('- ',k);
  write('Minha mao: ');
  for f:=1 to car_mao[2]-1 do
    write(mao[f,2],' ');
  writeln('- ',l);
end;

function analisa_mao(j : integer):boolean;
var f, g : integer;
begin
  g:=0;
  analisa_mao:=false;
  for f:=1 to 10 do
  begin
    g:=g+mao[f,j];
  end;
  if g>21 then
    analisa_mao:=true;
end;

function computador:boolean;
var f, g : integer;
    r : real;
    c : char;
    flag : boolean;
begin
  mao[car_mao[2],2]:=sorteia_carta;
  car_mao[2]:=car_mao[2]+1;
  Randomize;
  flag:=analisa_mao(2);
  computador:=flag;
  if (not flag) then
  begin
    g:=0;
    for f:=1 to car_mao[2]-1 do
      g:=g+mao[f][2];
    r:=random(3)*0.1+(g/21)*0.7;
    c:='p';
    if (r>0.7) then
      c:='t';
    if (c='t') then
    begin
      computador:=true;
      writeln('O computador arrisca ...');
    end;
  end
  else
    writeln('Ai! Estorei a mao.');
end;

function jogador:boolean;
var f : integer;
    c : char;
    flag : boolean;
begin
  mao[car_mao[1],1]:=sorteia_carta;
  write('Sua mao: ');
  for f:=1 to car_mao[1] do
    write(mao[f,1],' ');
  writeln;
  car_mao[1]:=car_mao[1]+1;
  flag:=analisa_mao(1);
  jogador:=flag;
  if (not flag) then
  begin
    repeat
      writeln('Escolha: (p) - prosseguir ou (t) - terminar');
      read(kbd,c);
    until (c='p') or (c='t');
    if (c='t') then
    begin
      jogador:=true;
      writeln('O jogador arrisca ...');
    end;
  end
  else
    writeln('Xiiii. Voce estourou a mao!');
end;

procedure jogo;
var j,c : boolean;
begin
 clrscr;
 limpa;
 repeat
   j:=jogador;
   c:=computador;
 until (j=true) or (c=true);
 vencedor;
end;

begin
  jogo;
end.


  2. Explicação do código
  O fluxo do programa é simples: começa na rotina principal e segue-se o caminho das chamadas das funções ou procedures. Ele começa chamando a procedure "jogo". Dentro de jogo, as funções jogador (que chama outras), computador (que chama outras) e vencedor são chamadas. É assim o caminho.
  O código é grande. Podemos dividi-lo em partes, para o estudo detalhado de cada parte:

Função / Procedure Descrição
limpa  Limpa todas as variáveis do sistema.
sorteia_carta  Sorteia uma carta para os jogadores.
vencedor  Descobre quem venceu o jogo.
analisa_mao  Verifica estouro de mão (total > 21).
computador  Rotina de jogo do computador.
jogador  Rotina de jogo do jogador.
jogo  Rotina de controle do jogo
principal  Inicia o jogo


  Variáveis Globais

  Primeiro, vamos entender as variáveis globais do jogo:
var carta : array[1..12,1..4] of boolean;
    mao : array[1..10,1..2] of integer;
    car_mao : array[1..2] of integer;
  Carta, diz respeito ao baralho. Representa as cartas A, 2, 3, 4, 5, 6, 7, 8, 9, J, Q, K e os naipes de ouros, paus, valete e damas. Os valores de 1 a 12 representam os valores das cartas e de 1 a 4, o naipe. O tipo de variável é booleano, porque desejamos marcar com true as cartas que estão sendo retiradas da pilha.
  Mao, é um vetor contendo o valor das cartas que cada jogador tirou. O naipe não interessa aqui.
  Car_mao indica a posição no vetor mao que será inserida a próxima carta. Logo, temos que total de cartas de um jogador é car_mao[x]-1.
  Confira na seção limpa, como são e como são iniciadas estas tabelas.


 Limpa

  Esta função tem como por objetivo ajustar as tabelas com os valores inicias, ou seja, limpar as tabelas. Ela somente atribui valores às tabelas, não sendo necessários quaisquer comentários.
procedure limpa;
var f,g : integer;
begin
  { Limpa Cartas }
  for f:=1 to 12 do
  begin
    for g:=1 to 4 do
    carta[f,g]:=false;
  end;
  { Limpa Mao }
  for f:=1 to 10 do
    begin
    mao[f,1]:=0;
    mao[f,2]:=0;
  end;
  { Limpa Cartas da Mao }
  car_mao[1]:=1;
  car_mao[2]:=1;
end; 
  Depois de rodarem, as tabelas ficam:

cartas
  1 2 3 4
1 false false false false
2 false false false false
3 false false false false
4 false false false false
5 false false false false
6 false false false false
7 false false false false
8 false false false false
9 false false false false
10 false false false false
11 false false false false
12 false false false false


mao
  1 2 3 4 5 6 7 8 9 10
1 0 0 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0 0


car_mao
1 2
1 1


  Sorteia_carta

  Esta função sorteia uma carta no baralho, isto é, simula uma pilha de cartas. Ela escolhe o valor da carta em f e o naipe da carta em g. Então, verifica se esta carta já foi comprada, consultando a tabela. Se ela já tiver sido comprada, um novo sorteio é realizado, até a carta sorteada não tiver sido sorteada.
  Quando consegue-se sortear uma carta, a tabela cartas é atualizada, setando-se true na carta retirada. Se a carta retirada for (10,2), teremos:

cartas
  1 2 3 4
1 false false false false
2 false false false false
3 false false false false
4 false false false false
5 false false false false
6 false false false false
7 false false false false
8 false false false false
9 false false false false
10 false true false false
11 false false false false
12 false false false false
function sorteia_carta:integer;
var f, g : integer;
flag : boolean;
begin
  // Função que gera números aleatórios.
  Randomize;
  // Flag que identifica se carta está disponível.
  flag:=false;
  // Enquanto não achar carta livre ...
  while (not flag) do
  begin
    // Gera um número aleatório entre 1 e 12.
    f:=Random(12)+1;
    // Gera um número entre 1 e 4.
    g:=Random(4)+1;
    // Se a carta estiver disponível ...
    if (carta[f,g]=false) then
    begin
      // Marca na tabela cartas a carta sorteada.
      carta[f,g]:=true;
      // Indica ao flag que a carta livre já foi encontrada.
      flag:=true;
    end;
  end;
  // Retorna o valor f da carta sorteada. O naipe não importa!
  sorteia_carta:=f;
end;

  Vencedor

  Esta procedure verifica quem venceu o jogo.
procedure vencedor;
var f,k,l : integer;
begin
  k:=0;
  Zera o contador do jogador.
  l:=0;
  Zera o contador do computador.
  for f:=1 to 10 do
  Varre toda a tabela mao.
  begin
    // Conta os valores armazenados na mão do jogador.
    k:=k+mao[f,1];
    // Conta os valores armazenados na mão do computador.
    l:=l+mao[f,2];
  end;
  writeln;
  if ((k>l) or (l>21)) and (k<22) then
    writeln('Voce ganhou!');
  if ((k<l) or (k>21)) and (l<22) then
    writeln('Eu ganhei!');
  if (k=l) or ((k>21) and (l>21)) then
    writeln('Deu empate!');
  write('Sua mao: ');
  for f:=1 to car_mao[1]-1 do
    write(mao[f,1],' ');
  writeln('- ',k);
  write('Minha mao: ');
  for f:=1 to car_mao[2]-1 do
    write(mao[f,2],' ');
  writeln('- ',l);
end;

  Analisa_mao

  Esta função realiza algo semelhante a anterior: conta os valores armazenados em uma determinada mão. Retorna true, se mão menor que 21 e false se mão menor ou igual a 21. A linha da tabela (jogador ou computador) é passado pelo parâmetro j.
function analisa_mao(j : integer):boolean;
var f, g : integer;
begin
  g:=0;
  analisa_mao:=false;
  for f:=1 to 10 do
  begin
    g:=g+mao[f,j];
  end;
  if g>21 then
    analisa_mao:=true;
end; 

  Computador

  Esta função controla o jogo do computador.
function computador:boolean;
var f, g : integer;
      r : real;
      c : char;
      flag : boolean;
begin
  // Pega uma carta. Utiliza a função sorteia carta para
  // preencher a tabela mao[x,2] com um valor de 1 a 12.
  mao[car_mao[2],2]:=sorteia_carta;
  // Passa o ponteiro para a próxima posição na tabela mao.
  car_mao[2]:=car_mao[2]+1;
  Randomize;
  flag:=analisa_mao(2);
  computador:=flag;
  if (not flag) then
  begin
    g:=0;
    for f:=1 to car_mao[2]-1 do
      g:=g+mao[f][2];
    // Utiliza-se de um cálculo probabilístico, onde leva-se em
    // conta o total de pontos acumulados na mão para terminar.
    r:=random(3)*0.1+(g/21)*0.7;
    c:='p';
    // Se o cálculo for maior ou igual a 0.7, o computador
    // termina o jogo, arriscando.
    if (r>=0.7) then
      c:='t';
    if (c='t') then
    begin
      computador:=true;
      writeln('O computador arrisca ...');
    end;
  end
  else
    writeln('Ai! Estorei a mao.');
end; 

  Jogador

  Esta função controla o jogador.
function jogador:boolean;
var f : integer;
      c : char;
      flag : boolean;
begin
  // Pega uma carta.
  mao[car_mao[1],1]:=sorteia_carta;
  write('Sua mao: ');
  for f:=1 to car_mao[1] do
    write(mao[f,1],' ');
  writeln;
  car_mao[1]:=car_mao[1]+1;
  // Verifica se estourou.
  flag:=analisa_mao(1);
  jogador:=flag;
  if (not flag) then
  begin
    repeat
      writeln('Escolha: (p) - prosseguir ou (t) - terminar');
      read(kbd,c);
    until (c='p') or (c='t');
    if (c='t') then
    begin
      jogador:=true;
      writeln('O jogador arrisca ...');
    end;
  end
  else
    writeln('Xiiii. Voce estourou a mao!');
end; 

  Jogo

  Esta função gerencia o jogo todo.
procedure jogo;
var j,c : boolean;
begin
  // Limpa a tela.
  clrscr;  
  // Limpa as variáveis. 
  limpa;
  repeat
    // Jogador recebe uma carta e testes são feitos.
    j:=jogador;
    // Computador recebe uma carta e testes são feitos.
    c:=computador;
  until (j=true) or (c=true);
  // Quando um terminar, verifica-se o vencedor.
  vencedor;
end;

  Rotina principal

  Nesta rotina, podemos controlar quantos jogos serão realizados. Neste exemplo, somente um jogo é realizado.
begin
  jogo;
end. 

  Dicas:

  A margem do parágrafo indica inicio e fim de algum for, while, if, etc. A medida que o parágrafo se afasta, as linhas abaixo pertencem a esta estrutura, até que o parágrafo retorne a mesma coluna, com um END. Veja mais detalhes em analisando estruturas, no curso de Pascal.
  Quando usamos o ELSE, a linha imediatamente anterior não tem ponto e vírgula (;).


Marcelo Teixeira Silveira
Engenheiro de Sistemas e Computação - UERJ
Mestre em Engenharia de Computação - UERJ

© MarMSX 1999-2017