Curso de ScriptVox Intermediário - Aula 9 - Prof. Oswaldo Vernet - iNCE/UFRJ

Vamos retomar, nesta aula, a implementação do jogo de cartas, iniciada na Aula 8.
Transcrevemos, a seguir, o programa principal, acrescido de algumas linhas que 
informam o resultado final do jogo:

* início do programa principal
vez := 1
pontos_computador := 0
pontos_usuário := 0
embaralha_monte ()
enquanto monte_tem_cartas ()
	carta := compra_carta ()
	pontos := pontua_carta ()
	se vez = 0 
		pontos_computador := pontos_computador + pontos
		escreve "Seu adversário comprou a carta " carta " e acumulou " pontos_computador " pontos até agora"
	senão
		pontos_usuário := pontos_usuário + pontos
		escreve "Você comprou a carta " carta " e acumulou " pontos_usuário " pontos até agora"
	fim se
	vez := 1 - vez
fim enquanto

se pontos_usuário > pontos_computador
	escreve "Parabéns, o placar foi " pontos_usuário " x " pontos_computador ", você venceu. Batata frita."
senão
se pontos_usuário = pontos_computador
	escreve "O placar foi " pontos_usuário " x " pontos_computador ", houve empate."
senão
	escreve "Lamento, o placar foi " pontos_usuário " x " pontos_computador ", você perdeu. Hahahaha."
fim se
* fim do programa principal

Se você cumpriu direitinho sua tarefa, providenciou a função embaralha_monte,
que, conforme combinado, deve produzir uma lista de cartas embaralhadas e guardá-la na
variável global "monte".

Pois bem. É sobre esta lista guardada na variável "monte" que as duas operações seguintes
(monte_tem_cartas e compra_carta) vão agir. Verificar se o monte ainda tem cartas corresponde 
a verificar se a lista é diferente da lista vazia. Portanto, a função monte_tem_cartas deverá 
apenas retornar o resultado deste teste. Assim:

função monte_tem_cartas ()
    retorna monte <> []
fim função

O operador menor maior "<>" faz exatamente o contrário do operador de igualdade. Ele produz
o resultado VERDADEIRO se os operandos são diferentes e o resultado FALSO se os operandos
não são diferentes, ou seja, se eles são iguais. Portanto, se a variável "monte" armazenar 
uma lista vazia, a função retornará zero (que significa FALSO); caso contrário, a função
retornará um (que significa VERDADEIRO). O valor de retorno da função é usado no comando
ENQUANTO.

Você deverá estar se perguntando por que fazer uma função que se resume a uma só linha de
código.  Não seria mais prático escrever diretamente este teste no comando ENQUANTO, em vez
de ter que chamar uma função e usar o valor que ela retorna?  Se este questionamento ainda
persiste, releia a aula anterior; lá explicamos por que construir uma função para cada
operação.

Comprar uma carta é uma operação igualmente fácil de implementar: todas as cartas disponíveis
são elementos de uma lista que está armazenada na variável "monte". Comprar uma delas significa
retirar uma carta desta lista, diminuindo em uma unidade a sua cardinalidade. A questão é:
qual carta retirar?

Em princípio, qualquer uma, não faz diferença. Mas vamos pensar um pouco em como se retira
uma carta da lista. A função do ScriptVox que faz isso chama-se RETIRA e espera dois parâmetros:
o primeiro é a lista que estamos tratando; o segundo é uma posição válida na lista. O que
significa uma posição válida?  Significa um número inteiro que deve estar entre zero
(a primeira posição da lista) e o tamanho da lista menos um (que é a última posição da
lista). Qualuer outro inteiro é inválido, pois não corresponde a uma posição em que haja
elemento da lista.

Poderíamos pensar em sortear um número inteiro entre zero e o tamanho da lista menos um.
A função RAND faria isto para nós. Entretanto, se pensarmos um pouquinho mais, verificaremos
o seguinte: a lista certamente está povoada no momento em que um dos jogadores vai comprar
uma carta. Por que podemos garantir isso?  Porque, quando a lista fica vazia, o teste do
comando ENQUANTO vai falhar e o bloco não será executado. Portanto, temos certeza de que
a lista tem, pelo menos, um elemento. Isto significa que ela tem pelo menos a posição 
zero ocupada. Podemos, então, retirar sempre o elemento da posição zero, já que sabemos, 
com certeza, que ele existe. Além disso, este elemento é desconhecido, pois o monte de
cartas está embaralhado.

Nossa função compra_carta ficará assim:

função compra_carta ()
    retorna retira (monte, 0)
fim função

Novamente uma função de uma única linha. E o que essa linha faz?  Ela simplesmente produz
como valor de retorno da função o valor da expressão que se segue ao comando RETORNA.
E que expressão é essa? Ela consiste em chamar a função RETIRA, passando como parâmetros
a lista (que está na variável global "monte") e a posição (no caso, zero). Então esta
única linha funcionará assim: ela vai fazer com que o valor de retorno da função compra_carta
seja exatamente o que a chamada da função RETIRA retornar. E o que a função RETIRA retorna?
Ela retorna o elemento que estava na lista na posição indicada pelo segundo parâmetro.
Por que dissemos estava? Porque não está mais: ele foi retirado da lista. Portanto, usar
o valor de retorno da função RETIRA é importante quando desejamos saber quem saiu da lista.

Sö para recordar, os elementos da lista são os nomes das cartas e, portanto, são cadeias de
caracteres. Logo, a função compra_cartas retornará sempre uma cadeia com o nome da
carta que foi comprada. E qual variável global recebe o valor retornado pela função
compra_carta?  A variável "carta". 

A última operação, chamada pontua_carta. Sua missão é descobrir o valor da carta cujo
nome está guardado na variável global "carta". Lembra-se de como construímos os nomes das
cartas na aula passada? Era exatamente o valor da carta, expresso em caracteres numéricos,
seguido da preposição "de", seguida do nome do naipe. Portanto, nossa resposta está na
primeira parte do nome da carta. Lembra-se da função PALAVRA? Ela espera dois parâmetros:
um número inteiro e uma cadeia; ela busca na cadeia e retorna a palavra cuja ordem é dada pelo
primeiro parâmetro. Assim, se passarmos o inteiro um como parâmetro, ela buscará a primeira
palavra; se passarmos dois, ela buscará a segunda; e assim por diante. Em qual palavra
está o valor da carta que queremos?  Exatamente na primeira.

Logo, a nossa função pontua_carta será também uma função de uma linha apenas:

função pontua_carta ()
    retorna palavra (1, carta)
fim função

Se você cumpriu a tarefa da aula passada, que era completar a função embaralha_monte,
que tal, agora, você colocar este script em execução no interpretador?

Há um último ponto que precisamos melhorar neste script. É pouco educado chamar uma 
Dama de doze. Mas, com a opção feita na aula passada para representação dos nomes das cartas, 
isto está acontecendo. No exercício prático, você encontrará o conserto provisório para isto. 
O conserto definitivo será tema da próxima aula.  


EXERCÍCIOS

Perguntas de Estudo Dirigido

1.  Na função compra_carta, optamos por retirar sempre o elemento da posição zero da lista.
    Isto significa que o ScriptVox deverá mover todos os elementos seguintes de uma posição para
	trás, para que não fique um buraco com elemento faltando na primeira posição da lista.
	Nós não percebemos este esforço, mas o ScriptVox certamente fica cansado tendo que empurrar
	todos os elementos para trás cada vez que se compra uma carta. Já que é indiferente remover 
	o elemento da posição zero ou qualquer outro, pois as cartas estão embaralhadas, qual elemento
	você removeria a fim de não cansar o ScriptVox?

2.  Funções como compra_carta e pontua_carta possuem uma característica especial: elas simplesmente
    chamam uma outra função e retornam o que esta outra função retorna (uma espécie de terceirização
	de serviços). No caso de compra_carta, ela vai retornar o que a função RETIRA retornar; no caso de 
	pontua_carta, ela vai retornar o que a função PALAVRA retornar. Faça uma analogia entre 
	este comportamento e algum fato da vida diária.
	
3.  Você deve ter percebido que este jogo não tem graça nenhuma. O usuário fica teclando ênter a
    vida toda e o computador vai dizendo quantos pontos cada um tem. Ninguém jogará este jogo mais
	de uma vez na vida. O que falta é alguma interatividade em que o usuário participe do jogo,
    não ficando totalmente passivo. Analise em que ponto do jogo alguma interatividade poderia ser
    introduzida.

4.  A vantagem de encapsular as operações essenciais do jogo em funções é a independência que se
    consegue entre o funcionamento do jogo e a maneira como os dados estão representados. Na próxima
    aula, por exemplo, iremos mudar a maneira de representar as cartas e veremos que o programa
    principal vai continuar o mesmo. Ou seja, o programa principal captura exatamente o que há
	de essencial no jogo e as funções que implementam as operações é que lidam com detalhes de
	representação. Pense sobre isso.	

5.  Por que o script, quando parabeniza o usuário pela vitória, escreve Batata Frita?


Exercício de avaliação (enviar para scriptvox@gmail.com até o meio-dia de 16 de fevereiro de 2012)

A tarefa agora é corrigir os nomes das cartas. Para isto, acrescentamos uma lista, que é inicializada
no início do script. As posições nesta lista correspondem a valores de cartas e os elementos destas
posições, aos nomes. Assim:

nomes := [ "", "Ás", "Dois", "Três", "Quatro", "Cinco", "Seis", "Sete", "Oito", "Nove", "Dez", "Valete", "Dama", "Rei" ]

A ideia é a seguinte: o valor da carta nós sabemos. Ele foi extraído da primeira palavra do nome da carta.
Para obter o nome correto, basta indexar a lista "nomes" usando o valor da carta. Repare que o elemento 
da posição zero (ou seja, o primeiro da lista) pode ser qualquer coisa, pois nunca será usado, 
já que nenhuma carta tem valor zero. 

Escreva a função nome_da_carta, que recebe como parâmetro uma cadeia com o nome antigo da carta 
e retorna outra cadeia, contendo o nome correto da carta. Assim, se o parâmetro contiver a cadeia

"11 de copas"

a função deverá retornar

"Valete de copas"

Use a sua função nos dois comandos ESCREVE do script, para escrever o nome correto da carta.

Bom estudo!

Oswaldo Vernet







