Está en la página 1de 12

Esse o CC50.

O Curso de Harvard, no Brasil 2012/01

Pset 3: O Jogo dos Quinze


a ser entregue at: 19:00, sex 16/03
Tenha certeza de que seu cdigo bem comentado
de forma que a funcionalidade seja aparente apenas pela leitura dos comentrios.

Objetivos.

Introduzir voc a programas maiores, com mltiplos arquivos.


Capacit-lo a criar Makefiles.
Implementar um simptico divertimento.

Leitura recomendada.

Seo 17 de http://informatica.hsw.uol.com.br/programacao-em-c.htm

Esse o CC50.
O Curso de Harvard, no Brasil 2012/01

Honestidade Acadmica.
Todo o trabalho feito no sentido do cumprimento das expectativas deste curso deve ser exclusivamente
seu, a no ser que a colaborao seja expressamente permitida por escrito pelo instrutor do curso. A
colaborao na realizao de Psets no permitida, salvo indicao contrria definida na especificao
do Set.
Ver ou copiar o trabalho de outro indivduo do curso ou retirar material de um livro, site ou outra fonte,
mesmo em parte e apresent-lo como seu prprio constitui desonestidade acadmica, assim como
mostrar ou dar a sua obra, mesmo em parte, a um outro estudante. Da mesma forma desonestidade
acadmica apresentao dupla: voc no poder submeter o mesmo trabalho ou similar a este curso
que voc enviou ou vai enviar para outro. Nem poder fornecer ou tornar as solues disponveis para
os Psets para os indivduos que fazem ou podero fazer este curso no futuro.
Voc est convidado a discutir o material do curso com os outros, a fim de melhor compreend-lo. Voc
pode at discutir sobre os Psets com os colegas, mas voc no pode compartilhar o cdigo. Em outras
palavras, voc poder se comunicar com os colegas em Portugus, mas voc no pode comunicar-se
em, digamos, C. Em caso de dvida quanto adequao de algumas discusses, entre em contato com o
instrutor.
Voc pode e deve recorrer Web para obter referncias na busca de solues para os Psets, mas no
por solues definitivas para os problemas. No entanto, deve-se citar (como comentrios) a origem de
qualquer cdigo ou tcnica que voc descubra fora do curso.
Todas as formas de desonestidade acadmica so tratadas com rigor.
Licena.
Copyright 2011, Gabriel Lima Guimares.
O contedo utilizado pelo CC50 atribudo a David J. Malan e licenciado pela Creative Commons
Atribuio-Uso no-comercial-Compartilhamento pela mesma licena 3.0 Unported License.
Mais informaes no site:
http://cc50.com.br/index.php?nav=license

Esse o CC50.
O Curso de Harvard, no Brasil 2012/01

Notas:
Seu trabalho neste Pset ser avaliado em trs quesitos principais:
Exatido. At que ponto o seu cdigo consistente com as nossas especificaes e livre de bugs?
Design. At que ponto o seu cdigo bem escrito(escrito claramente, funcionando de forma eficiente,
elegante, e / ou lgica)?
Estilo. At que ponto o seu cdigo legvel (comentado e indentado, com nomes de variveis
apropriadas)?

Esse o CC50.
O Curso de Harvard, no Brasil 2012/01

Comeando

Abra o seu Terminal e, como de costume, crie um diretrio dentro de cc50 chamado pset3. Extraia
todos os arquivos de pset3.zip dentro desse diretrio .
Se voc listar o contedo do seu diretrio de trabalho atual (lembra-se como?), voc deve ver algo
como o seguinte.
fifteen/

find/

pset3.pdf

Como voc pode perceber, o seu trabalho para esse Pset ser organizado dentro de dois
subdiretrios.

No se esquea da Lista de Discusses do CC50! Voc pode postar perguntas suas ou procurar
respostas para as perguntas j feitas por outros. E nunca tenha medo de que as suas perguntas
sejam "idiotas".

Find.

Agora vamos mergulhar no primeiro desses subdiretrios. Execute o comando abaixo.


cd ~/cc50/pset3/find/

Listando o contedo desse diretrio, voc deve ver o seguinte


helpers.c

helpers.h

Makefile

find.c

generate.c

Uau, um bocado de arquivos, n? No se preocupe, ns vamos explicar todos.

Em generate.c est implementado um programa que usa rand para gerar um monte de
nmeros pseudoaleatrios, um por linha. (Lembra-se de rand do Pset 1?) V em frente e compile
este programa, executando o comando abaixo.
make generate

Agora, execute o programa que voc acabou de compilar executando o comando abaixo.
./generate

Voc deve ser informado sobre a utilizao adequada do programa:


Utilizao: generate n [s]

Como este resultado sugere, este programa espera um ou dois argumentos de linha de comando.
O primeiro, n, obrigatrio; ele indica quantos nmeros pseudoaleatrios voc gostaria de
gerar. O segundo argumento, s, opcional, como os colchetes implicam; se fornecido, ele
representa o valor que o gerador pseudoaleatrio deve usar como seed. (Lembra-se o que uma

Esse o CC50.
O Curso de Harvard, no Brasil 2012/01

seed do Pset 1?) V em frente e execute este programa novamente, desta vez com um valor de
n de, digamos, 10,como a seguir, voc dever ver uma lista de 10 nmeros pseudoaleatrios.
./generate 10

Execute o programa pela terceira vez usando esse mesmo valor para n, voc dever ver uma lista
de 10 nmeros diferentes dos anteriores. Agora tente executar o programa com um valor para s
(por exemplo 0), como segue.
./generate 10 0

Agora execute esse mesmo comando novamente:


./generate 10 0

Aposto que voc viu a mesma sequncia de dez nmeros pseudoaleatrios de novo? isso que
acontece se voc no variar a semente inicial de um gerador de nmeros pseudoaleatrios.

Agora d uma olhada no generate.c com o Nano. (Lembra-se como?) Os comentrios na parte
superior explicam a funcionalidade geral do programa. Mas parece que ns nos esquecemos de
comentar o cdigo em si. Leia o cdigo com cuidado at que voc entenda cada linha e, em
seguida, comente o nosso cdigo, substituindo cada TODO com uma frase que descreve a
finalidade ou funcionalidade da(s) linha(s) correspondente(s) de cdigo. Perceba que um
comentrio feito com /* e */ pode se estender por vrias linhas enquanto um comentrio
precedido por // s pode se estender at o fim de uma linha, este ltimo um recurso do C99 (a
verso de C que estamos usando) . Se achar que o Pset1 foi h muito tempo atrs, voc pode
querer ler sobre rand e srand novamente nas URLs abaixo.
http://www.cs50.net/resources/cppreference.com/stdother/rand.html
http://www.cs50.net/resources/cppreference.com/stdother/srand.html

Ou voc pode executar os comandos abaixo.


man rand
man srand

Uma vez que voc terminou de comentar generate.c, recompile o programa para ter
certeza que voc no quebrou nada reexecutando o comando abaixo.
make generate

Se gerar no compilar corretamente, d uma olhada no que voc acabou de fazer e conserte o
que voc quebrou!
Agora, lembre-se que make automatiza a compilao do seu cdigo para que voc no tenha que
sempre executar gcc manualmente junto com um monte de switches. Observe, na verdade, como
make acaba de executar um comando muito longo para voc. No entanto, a medida que seus
programas crescem em tamanho, make no ser mais capaz de descobrir a partir do contexto

Esse o CC50.
O Curso de Harvard, no Brasil 2012/01

como compilar seu cdigo, voc vai precisar comear a contar para make como seu programa
deve ser compilado, principalmente quando ele envolve mltiplas fontes (arquivos .c). E por isso
vamos comear a confiar em "Makefiles", arquivos de configurao que dizem a make exatamente
o que fazer.
Como make soube como compilar generate neste caso? Ele usou um arquivo de configurao
que ns escrevemos. Usando Nano, v em frente e olhe o arquivo chamado Makefile que est
no mesmo diretrio que generate.c. Este Makefile , essencialmente, uma lista de regras
que ns escrevemos para voc que diz make como construir generate a partir de generate.c
para voc. As linhas relevantes constam abaixo.
generate: generate.c
gcc -ggdb -std=c99 -Wall -Werror -Wformat=0 -o generate generate.c

A primeira linha diz a make que o "alvo" chamado generate deve ser construdo, invocando a
linha que vem a seguir. Alm disso, essa primeira linha diz ao make que generate dependente
de generate.c. Isso faz com que make reconstrua generate se o arquivo generate.c foi
modificado desde a ltima utilizao de make. Bom truque para economizar o tempo, n? V em
frente e execute o comando abaixo novamente, supondo que voc no tenha modificado
generate.c.
make generate

Voc deve ser informado de que generate j est atualizado. Alis, saiba que o espao em
branco inicial da segunda linha de make no uma sequncia de espaos, mas sim um tab.
Infelizmente, make requer que os comandos sejam precedido por tabs, por isso tome cuidado
para no alter-los para espaos com o Nano (que converte automaticamente tabs para quatro
espaos), se no voc pode encontrar erros estranhos! A flag - Werror, lembre-se, diz ao gcc
para tratar avisos (ruins) como se fossem erros (muito pior) a fim de que voc seja forado (em
uma boa e instrutiva maneira!) a corrigi-los.

Agora d uma olhada em find.c com o Nano. Note que este programa espera um argumento de
linha de comando nico: a "agulha" para pesquisar em um "palheiro" de valores. Uma vez que
voc j tenha olhado todo o cdigo, v em frente e compile o programa executando o comando
abaixo.
make find

Note que make, na verdade, executou o seguinte comando para voc:


gcc -ggdb -std=c99 -Wall -Werror -Wformat=0 -o find find.c helpers.c -lcc50 -lm

Observe, ainda, que voc acaba de compilar um programa que inclui no um, mas dois arquivos
.c: helpers.c e find.c. Como que make sabe o que fazer? Bem, mais uma vez, abra a
Makefile para ver a mgica por trs dos panos. As linhas relevantes aparecem abaixo.
find: find.c helpers.c helpers.h
gcc -ggdb -std=c99 -Wall -Werror -Wformat=0 -o find find.c helpers.c -lcc50 -lm

Esse o CC50.
O Curso de Harvard, no Brasil 2012/01

Como voc pode ver, na primeira linha (aps os dois pontos), qualquer alterao em find.c,
helpers.c,ou helpers.h vai obrigar make a recompilar find na prxima vez que ele for
chamado.
V em frente e execute find.
./find 13

Voc ser solicitado a fornecer algum palheiro (alguns inteiros), uma "palha" de cada vez. Assim
que voc se cansar de fornecer nmeros inteiros, pressione Ctrl-D para enviar o programa um
caractere EOF (End Of File Fim de Arquivo). Esse caractere vai obrigar getInt da Biblioteca do
CC50 a retornar INT_MAX, uma constante que, pelo cdigo de find.c, vai obrigar find a parar
de solicitar palha. O programa ir ento procurar a agulha no palheiro que voc forneceu,
relatando, no fim, se a agulha se encontra nesse palheiro. Em suma, este programa procura um
valor em um array.
Acontece que voc pode automatizar esse processo de fornecimento de feno fornecendo o output
de generate ao input de find. Por exemplo, o comando abaixo passa 1.024 nmeros
pseudoaleatrios para find, que ento procura 13 dentre esses valores.
./generate 1024 | ./find 13

Alternativamente, voc pode "redirecionar" o output de generate para um arquivo com um


comando como o abaixo.
./generate 1024 > numbers.txt

Voc pode ento redirecionar o contedo desse arquivo para o input de find com o comando
abaixo.
./find 13 < numbers.txt

Vamos terminar olhando para aquela Makefile. Observe a linha abaixo.


all: find generate

Esta linha significa que voc pode construir tanto generate quanto find simplesmente
executando o abaixo.
make all

Ainda melhor, voc pode chegar ao mesmo resultado (porque make sem nenhum outro
argumento sempre executa a primeira linha de uma Makefile) utilizando simplesmente:
make

Se voc pudesse deletar todos os arquivos desse Pset com um nico comando! Finalmente,
observe essas ltimas linhas da Makefile:

Esse o CC50.
O Curso de Harvard, no Brasil 2012/01

clean:
rm -f *.o a.out core find generate

Estas linhas permitem que voc apague todos os arquivos terminados em .o ou chamados
a.out, core (tsk, tsk), find ou generate simplesmente atravs do comando abaixo.
make clean

Tenha cuidado para no adicionar, por exemplo, *.c ltima linha da Makefile! (Porqu?)
Alis, qualquer linha que comea com # apenas um comentrio.

E agora comea a diverso! Note que find.c, chama sort uma funo declarada em
helpers.h. Infelizmente ns nos esquecemos de implementar essa funo em helpers.c!
D uma olhada em helpers.c com o Nano, e voc ver que sort retorna imediatamente,
mesmo que a funo main de find passe-a um array real. Nos poderamos ter colocado o
contedo de helpers.h e helpers.c em find.c. Mas as vezes organizar programas em vrios
arquivos muito melhor, especialmente quando algumas funes (por exemplo sort ) sejam de
grande utilidade, que possam ser teis mais tarde para outros programas, bem como as funes
da Biblioteca do CC50.
A propsito, recorde a sintaxe para declarar um array. Voc pode no s especificar o tipo de
array, mas tambm especificar o seu tamanho entre colchetes, tal como fazemos com hay stack
em find.c:
int haystack[HAY_MAX];

Mas ao passar um array para um funo, voc s especifica o seu nome, assim como fazemos ao
passar haystack para sort em find.c:
sort(haystack, size);

(Por que ns tambm passamos o tamanho desse array separadamente?)


Quando voc declarar uma funo que recebe um array unidimensional como um argumento,
porm, voc no precisa especificar o tamanho do array, assim como ns no o fazemos ao
declarar sort em helpers.h (e helpers.c):
void sort(int values[], int n);

Esse o CC50.
O Curso de Harvard, no Brasil 2012/01

Agora v em frente e implemente sort para que a funo realmente organize, do menor para o
maior, o array de nmeros passado a ela, de tal forma que o seu tempo de execuo seja de O(n2),
onde n o tamanho do array. Voc vai provavelmente querer implementar Bubble Sort ou
Selection Sort, porque essas foram discutidas na Semana 3. Basta perceber que no h uma
maneira "certa" para implementar um desses algoritmos, as variaes so muitas. Na verdade,
voc bem-vindo a melhor-las como achar melhor, desde que sua implementao permanea
em O(n2). No entanto, tome cuidado para no alterar a nossa declarao de sort, que deve
permanecer:
void sort(int values[], int n);

Como este tipo de retorno void indica, esta funo no deve retornar um array ordenado, mas
deve ordenar destrutivamente o prprio array que passado, movendo os valores nele. Como
discutiremos na Semana 4, arrays no so passados "por valor", mas "por referncia", o que
significa que sort no ser passada a cpia de um array, mas o verdadeiro array original.
Deixamos para voc a determinao de como testar a implementao de sort . Mas no se
esquea que printf e gdb so seus amigos. E no se esquea que voc pode gerar a mesma
sequncia de nmeros pseudoaleatrios vrias vezes especificando a seed de generate
explicitamente. Antes de enviar, no entanto, no se esquea de remover todas essas utilizaes de
printf. Ns gostamos dos outputs dos nossos programas do jeito que eles so!

Precisa de ajuda? V direto para cc50.com.br/forum !

Agora que sort (supostamente) funciona, hora de desenvolver search, a outra funo que
vive em helpers.c. Repare que a nossa verso implementa uma "busca linear", na qual
search procura por value iterando sobre os inteiros em array linearmente, da esquerda para a
direita. Mande pro espao as linhas que ns escrevemos e reimplemente search utilizando
pesquisa binria, que usa a estratgia dividir e conquistar que foi empregada na Semana 0, a fim
de pesquisar nomes na lista telefnica. 1 Voc est convidado a utilizar uma abordagem iterativa
ou recursiva. Se voc escolher o ltimo, porm, saiba que voc no pode mudar a nossa
declarao de search, mas voc pode escrever uma nova funo recursiva (que talvez usa
parmetros um pouco diferentes) que chamada por search.

No h necessidade de rasgar qualquer coisa pela metade.

Esse o CC50.
O Curso de Harvard, no Brasil 2012/01

O jogo comea.

Agora hora de jogar. O Jogo dos Quinze um quebra-cabea jogado em um tabuleiro


bidimensional quadrado com peas numeradas, que escorregam. O objetivo deste quebra-cabea
organizar todas as peas do tabuleiro, da menor para a maior, da esquerda para a direita, de
cima para baixo, com um espao vazio no canto inferior direito, como mostrado abaixo. 1

Deslizar qualquer pea que est na vizinhana do espao vazio constitui um "movimento". Apesar
de a configurao acima mostrar um jogo j ganho, observe como as peas 12 e 15 podem ser
deslizadas para o espao vazio. Peas no podem ser movidas diagonalmente ou removidas a
fora do tabuleiro.
Embora outras configuraes sejam possveis, ns vamos supor que este jogo comece com as
peas em ordem inversa, da maior para a menor, da esquerda para a direita, de cima para baixo,
com um espao vazio no canto inferior direito. Se e somente se o tabuleiro contm um nmero
mpar de peas (a altura e a largura do tabuleiro so pares), as posies das peas numeradas 1 e
2 devem ser trocadas, como mostrado abaixo. 2 O quebra-cabea solucionvel a partir desta
configurao.

1
2

Figura retirada de http://en.wikipedia.org/wiki/Fifteen_puzzle.


Figura adaptada de http://en.wikipedia.org/wiki/Fifteen_puzzle.

Esse o CC50.
O Curso de Harvard, no Brasil 2012/01

Navegue para ~/cc50/pset3/fifteen/ e d uma olhada em fifteen.c com o Nano. Dentro


deste arquivo est uma base completa para o Jogo dos Quinze. O prximo desafio completar a
implementao deste jogo.
Mas primeiro v em frente e compile a base. (Voc consegue descobrir como?) E, mesmo que
ainda no esteja terminado, v em frente e rode o jogo. (Voc consegue descobrir como?)
Ufa. Parece que o jogo pelo menos parcialmente funcional. Ok, ainda no muito bem um jogo.
Mas a que voc entra!
Leia o cdigo e os comentrios de fifteen.c e depois responda s perguntas abaixo no arquivo
questions.txt.

i. Alm de 4 x 4 (que so as dimenses originais do Jogo dos Quinze), quais outras dimenses so
permitidas?
II. Que tipo de estrutura de dados representa o tabuleiro do jogo?
III. Qual funo chamada para cumprimentar o jogador no incio do jogo?
IV. Que funes voc precisa aparentemente implementar?

Tudo bem, agora faa isso, implemente este jogo. Lembre-se, d "passos de beb". No tente
implementar o jogo inteiro de uma vez. Em vez disso, implemente uma funo de cada vez e
tenha certeza de que ela funciona antes de seguir em frente. Em particular, sugerimos que voc
implemente as funes, nesta ordem: init, draw, move, won. Todas as decises de design
no prescritas aqui explicitamente (por exemplo quanto espao voc deve deixar entre os
nmeros para a impresso do tabuleiro) so intencionalmente deixadas para voc. O tabuleiro,
quando impresso deve se parecer, presumivelmente, com o abaixo, mas deixamos esse design
para voc implementar!
15 14 13 12
11 10

Ainda lembre-se que as posies das peas 1 e 2 s devem comear trocadas se o tabuleiro tem
um nmero mpar de peas (como no caso do tabuleiro 4 x 4 do exemplo acima). Se o tabuleiro
tem um nmero par de peas, essas posies no devem comear trocadas. Por exemplo como no
exemplo 3 x 3 abaixo:
8

Esse o CC50.
O Curso de Harvard, no Brasil 2012/01

Para testar a sua implementao de fifteen, voc certamente pode tentar jog-la. (Saiba que
voc pode forar o seu programa a sair apertando ctrl-c) Tenha certeza que voc (e ns) no pode
travar o seu programa, ao fornecer nmeros falsos de peas.
Sinta-se livre para ajustar o argumento apropriado para usleep para acelerar ou desacelerar a
animao. Na verdade, voc bem-vindo a alterar a esttica do jogo. Para se divertir
(opcionalmente) com "sequncias de escape ANSI", incluindo a cor, d uma olhada em nossa
implementao e confira a URL abaixo para mais truques.
http://isthe.com/chongo/tech/comp/ansi_escapes.html

Voc est livre para escrever suas prprias funes e at mesmo alterar as declaraes das
funes que ns escrevemos. Mas pedimos que voc no altere o fluxo da lgica em main para
que possamos automatizar alguns testes do seu programa, uma vez submetido. Em particular,
main s retorna 0 quando o usurio realmente ganhou o jogo; outros valores diferentes de zero
devem ser retornados em qualquer caso de erro, como implicado no nosso cdigo j pronto. Em
caso de dvida sobre se alguma deciso de design pode contrariar os nossos desejos, mande um
e-mail para ajuda@cc50.com.br.

Esse foi o Pset3.