Baixe o app para aproveitar ainda mais
Prévia do material em texto
Universidade de Brasília Departamento de Ciência da Computação/IE Semestre: 2/2015 Disciplina: Algoritmos e Programação de Computadores Trabalho Prático 1 O objetivo desta primeira etapa é que o aluno aprenda a utilizar estrututas condicionais, laços de repetição, funções, arquivos de texto e variáveis globais em algoritmos e a implementá-los em programas que executem corretamente no computador. Especificação da primeira etapa: Problema: IMPLEMENTAR A ESTRUTURA INICIAL DE UM PROGRAMA QUE (POSTERIORMENTE) DEVERÁ EXECUTAR UMA VERSÃO DO JOGO “EliminaLetras”. Neste trabalho, você deve implementar a estrutura inicial de um programa que (posteriormente) deverá executar uma versão do jogo “EliminaLetras”. Este jogo é basicamente uma matriz de caracteres preenchida com diferentes símbolos, da qual o jogador deve selecionar posições para eliminar um elemento e todos os vizinhos (consecutivos) que contiverem o mesmo símbolo daquele elemento. Quanto maior a vizinhança eliminada de uma vez, maior a pontuação obtida na jogada. Para esta primeira etapa, você deve implementar o menu principal, mensagens iniciais e finais de apresentação e um menu de configurações do jogo. As configurações devem ser persistidas num arquivo de texto. Detalhamento do problema: 1) Hello, world! - Inicialmente, o programa deve mostrar uma mensagem de boas-vindas e solicitar ao usuário que pressione ENTER, como na imagem à seguir: 2) Menu principal - Em seguida, o programa deve entrar em um laço (loop) para o menu principal. Os passos dentro deste laço (loop) devem ser basicamente mostrar a tela do menu, ler a opção do usuário e executar a opção selecionada. A condição de parada para este laço (loop) é que o usuário selecione a última opção do menu: sair. Veja um exemplo de tela: 3) Detalhamento do menu principal 3.1) A primeira opção do menu principal deve ser a opção de jogar a versão implementada do “EliminaLetras”. Esta opção deverá ser posteriormente implementada no Trabalho Prático 2 – parte 1. Coloque apenas um aviso temporário de “em construção”. 3.2) A segunda opção do menu principal deve ser a opção de visualizar o ranking de partidas que já aconteceram, persistido em um arquivo binário. Esta opção deverá ser posteriormente implementada no Trabalho Prático 2 – parte 2. Coloque apenas um aviso temporário de “em construção”. 3.3) A terceira opção do menu principal deve fazer o programa entrar em mais um laço (loop): o menu de configurações do jogo. As quatro primeiras opções deste menu devem servir para alterar respectivamente cada uma das quatro variáveis de configuração do jogo. São elas: – 1. string com os caracteres que serão utilizados para preencher a matriz do jogo, – 2. quantidade de linhas da matriz, – 3. quantidade de colunas da matriz e – 4. a base da pontuação. Após ler o novo valor de alguma configuração, o programa deve perguntar ao usuário se a mudança deve realmente ser efetuada, ou seja, confirmar o alteração. A última opção deve encerrar o laço (loop) do menu de configurações, finalizando a execução da terceira opção do menu principal. Veja alguns exemplos: 3.4) A última opção do menu principal deve encerrar o laço (loop). Ao verificar que o usuário solicitou esta opção, o programa deve sair do laço (loop), mostrar uma mensagem de até logo, solicitar que o usuário pressione ENTER e encerrar o programa, como na seguinte imagem: 4) Variáveis globais - Neste trabalho, você deverá utilizar variáveis globais. Este recurso deve ser utilizado com bastante cuidado, portanto, é sugerido fortemente que você leia o apêndice referente para entender a maneira correta de escolher quais variáveis deverão ser globais ao criar um programa. Neste programa, as variáveis globais deverão ser apenas as seguintes: char caracteres[11]; /* caracteres para preencher a matriz do jogo */ int linhas, colunas; /* dimensoes da matriz */ float base_pontos; /* base para a pontuacao no jogo */ 5) Validação de entrada 5.1) Ao receber uma opção de menu inválida, o programa deve mostrar uma mensagem de erro e solicitar que o usuário entre com a opção novamente. Veja um exemplo: 5.2) Dentro da terceira opção no menu principal, ao ler uma das quatro configurações do jogo, o programa deve realizar as verificações/validações a seguir: 5.2.1) O tamanho da string de caracteres deve ser no mínimo 3 e no máximo 10. ATENÇÃO: Não é necessário realizar nenhum outro teste de consistência com a string. 5.2.2) A quantidade de linhas da matriz deve ser no mínimo 3 e no máximo 9. 5.2.3) A quantidade de colunas da matriz deve ser no mínimo 3 e no máximo 9. 5.2.4) A base da pontuação deve ser maior que 1 e menor ou igual a 2. Este número servirá para calcular o pontos recebidos por jogada. Será utilizado no Trabalho Prático 2. 6) Arquivo de texto – A maneira de armazenar as configurações do jogo será através de um arquivo de texto. Desta forma, o usuário pode alterar as configurações através do programa, ou editando diretamente o arquivo. O formato do arquivo deverá ser o seguinte: - A primeira linha deverá conter a string com as letras que serão utilizadas no jogo; - A segunda linha deverá conter a quantidade de linhas da matriz do jogo; - A terceira linha deverá conter a quantidade de colunas da matriz do jogo; - A quarta e última linha deverá conter a base da pontuação. O nome do arquivo de texto deve ser: EliminaLetras_settings.txt A seguir é dada uma sugestão de funções a serem criadas para implementar o trabalho. Duas delas manipulam diretamente o arquivo de texto. WriteSettings() Esta função deve simplesmente escrever os valores das variáveis globais no arquivo de texto, seguindo o formato especificado. readSettings() Esta função deve ler o arquivo de texto com as configurações do jogo para preencher as variáveis globais. A lógica desta função deve ser a seguinte: - Tentar abrir o arquivo. Se não conseguir, preencher as variáveis globais com valores padrão: string=OZXM, linhas=6, colunas=9, base=1.5; - Se for possível abrir o arquivo, ler os valores do arquivo para preencher as variáveis globais e atribuir valores padrão para as configurações com valores eventualmente inválidos (caso o usuário tenha editado o arquivo de texto diretamente); - Por fim, chamar a função writeSettings() para o caso de não ter sido possível abrir o arquivo, ou para o caso de algum valor do arquivo recém lido ser inválido. A correção do trabalho irá testar a leitura de arquivos com valores inválidos, mas sempre no formato correto. Importante: Sempre que uma das quatro configurações for alterada, através do menu de configurações, todo o arquivo deve ser escrito novamente, ou seja, a função writeSettings() deve ser chamada. 7) Funções sugeridas – Para implementar o trabalho, você deve criar as funções abaixo. Os nomes das funções são apenas sugestões. int main(); /* implementa o loop do menu principal */ void helloworld(); /* mostra a mensagem inicial do programa */ MenuMainOpt menuMain(); /* exibe a tela do menu principal, le uma opcao e retorna o valor escolhido */ void play(); /* sera implementada no trab2 */ void ranking(); /* sera implementada no trab2 */ void settings(); /* implementa o loop do menu de configuracoes */ MenuSettingsOpt menuSettings(); /* exibe a tela do menu de configuracoes, le uma opcao e retorna o valor escolhido */ void changeChars(); /* primeira opcao do menu de configuracoes */ void changeRows(); /* segunda opcao do menu de configuracoes */ void changeCols(); /* terceira opcao do menu de configuracoes */ void changeScoreBase(); /* quarta opcao do menu de configuracoes */ void readSettings(); /* funcao para ler o arquivo de texto */ void writeSettings(); /* funcao para escrever o arquivo de texto */ bool invalidChars(); /* valida a string */ bool invalidRows(); /* valida a quantidade de linhas */ boolinvalidCols(); /* valida a quantidade de colunas */ bool invalidScoreBase(); /* valida a base da pontuacao */ void quit(); /* exibe a tela de ate logo */ Apêndice A) Pré-processador – Os comandos colocados no código-fonte de um programa que se iniciam com o caractere '#' (cerquilha) são comandos para o pré-processador de código. Esta é uma ferramenta que o próprio compilador passa em um código antes de compilá-lo de fato. Instruções deste tipo servem para modificar de alguma maneira o código-fonte que está prestes a ser compilado. Utilizando uma diretiva #include, o pré-processador modifica o código-fonte incluindo, naquela linha onde a diretiva foi colocada, um arquivo inteiro, que é especificado na própria diretiva, exemplo: #include <stdio.h>. Utilizando uma diretiva #define, o pré-processador modifica o código-fonte substituindo toda ocorrência da macro definida pelo respectivo valor de expansão, exemplo: #define VALOR_PADRAO 50. É possível combinar diretivas utilizando diretivas condicionais, exemplo: #ifdef _WIN32 #define CLEAR “cls” #else #define CLEAR “clear” #endif As diretivas acima definem uma macro com um comando utilizado para limpar a tela, de acordo com o sistema operacional em que o código-fonte for compilado. Quando um compilador está sendo executado no Windows, a macro _WIN32 fica definida no ambiente e pode ser utilizada em diretivas do pré-processador. O trecho acima exemplifica uma maneira de escrever código que compila e executa corretamente em diferentes sistemas operacionais. Observação: No Windows, o comando de terminal “cls” de fato limpa todo o terminal. No Linux, o comando equivalente “clear” apenas coloca algumas quebras de linha e sobe a tela. B) typedef enum O comando typedef é utilizado para definir sintaticamente novos tipos, ou seja, algo como int, ou float. Pode ser utilizado em combinação com diversas estruturas da linguagem C para criar novos tipos. Uma estrutura frequentemente utilizada em C é o comando enum. Este comando serve para criar palavras que expandem para valores numéricos durante a compilação. A diferença para um #define, é que uma enumeração é expandida pelo próprio compilador, após a expansão do pré-processador, além de só serem aceitos valores inteiros. Em combinação, estes dois comandos podem ser utilizados para criar tipos integrais de uma maneira extremamente conveniente. Toda variável em C possui tipo e valor. Além disso, para cada diferente tipo existe um conjunto de possíveis valores. Exemplos: int → [ −231 , 231−1 ]; char → símbolos da tabela Ascii. Utilizando a combinação typedef enum, é possível criar novos tipos e novos valores. Um exemplo comum é incluir o cabeçalho da biblioteca padrão do C “stdbool.h”. Este cabeçalho define o tipo bool e seus dois únicos valores: true e false. Este tipo e valores só existem de fato na gramática de C++, mas podem ser implementados em C utilizando a forma sugerida – que não necessariamente é a forma utilizada no cabeçalho “stdbool.h”. Exemplos: typedef enum { false = 0, /* 0 */ true /* 1 */ } bool; typedef enum { MENUMAIN_NA = 0, /* 0 */ MENUMAIN_PLAY, /* 1 */ MENUMAIN_RANKING, /* 2 */ MENUMAIN_SETTINGS, /* 3 */ MENUMAIN_QUIT /* 4 */ } MenuMainOpt; typedef enum { PONTO_CORSA = 200, /* 200 */ PONTO_GOL, /* 201 */ PONTO_CELTA, /* 202 */ PONTO_UNO = 350, /* 350 */ PONTO_PALIO, /* 351 */ PONTO_FIT = 198, /* 198 */ PONTO_CIVIC, /* 199 */ PONTO_CITY, /* 200 */ PONTO_FOCUS, /* 201 */ PONTO_KA /* 202 */ } PontuacaoPorCarro; bool flag = true; if (flag == true) flag = false; PontuacaoPorCarro pts; pts = PONTO_KA; if (pts == PONTO_FIT) /* faca algo */ Apesar de ser possível criar novos tipos e novos valores, estes tipos e valores não estão amarrados. Na verdade, tipos criados com a combinação typedef enum não passam de um apelido para o tipo int. Isto significa que valores definidos dentro de qualquer enumeração podem ser atribuídos a qualquer variável do tipo int, ou de qualquer tipo criado com typedef enum. No entanto, uma boa prática de programação e forma de comunicação entre programadores é respeitar as definições das enumerações, como forma de programação segura. Um programador deixa uma mensagem implícita para outro programador que futuramente coloque as mãos em seu código, dizendo que não é seguro atribuir qualquer valor à uma dada variável inteira que utilize uma enumeração: apenas os valores da enumeração são seguros. Outros valores inteiros não especificados podem causar mal-funcionamento no software. Além disso, utilizar uma enumeração é uma maneira de criar valores mnemônicos, evitando a necessidade de colocar comentários no código para indicar o significado de um valor, o que deixa a programação inclusive mais intuitiva. C) switch – Uma estrutura condicional extremamente recomendada para a implementação de MENUS é a estrutura switch. Esta estrutura é apenas uma maneira simplificada de escrever uma sequência if-else if-else if-...-else if-else, com a diferença de que o teste condicional realizado apenas verifica se um valor inteiro bate com uma das constantes selecionadas. Exemplo para o menu principal do seu jogo: switch (menuMain()) { case MENUMAIN_PLAY: play(); break; case MENUMAIN_RANKING: ranking(); break; case MENUMAIN_SETTINGS: settings(); break; case MENUMAIN_QUIT: quit(); break; default: exit(EXIT_FAILURE); } Uma sequência equivalente seria: MenuMainOpt opt = menuMain(); if (opt == MENUMAIN_PLAY) play(); else if (opt == MENUMAIN_RANKING) ranking(); else if (opt == MENUMAIN_SETTINGS) settings(); else if (opt == MENUMAIN_QUIT) quit(); else exit(EXIT_FAILURE); D) Variáveis globais Neste trabalho, você deve aprender a utilizar variáveis globais de forma correta. Variável global é aquela que é declarada no escopo global, ou seja, no mesmo nível em que funções são criadas. Em outras palavras, para criar uma variável global você deve declarar uma variável fora de todas as funções, no início do arquivo fonte. Ou seja, uma variável global deve possuir o mesmo nível de indentação de declarações de funções: nenhum! A escolha de quais variáveis devem ser globais em um programa deve ser feita com cuidado. Normalmente, variáveis que contêm dados ou apontam para regiões de memória que precisam ser manipuladas por diversas funções do programa são globais. Isto não quer dizer que, por exemplo, variáveis contadoras de laços (loops) devem ser globais. Não é porque diversas funções utilizam contadores de laços, que você deve declarar um inteiro global chamado “i”. Se você tentar utilizar um contador de laço global, você irá notar que o comportamento do programa será totalmente incorreto, se, por exemplo, existir um laço “para” que utiliza esta variável global para realizar a contagem e dentro deste laço há uma chamada à uma função que possui outro laço “para” que utiliza a mesma variável global “i”. Após a função retornar, a variável “i” estará com o último valor do laço desta função, o que muito provavelmente irá provocar o mal funcionamento do laço que chamou a função. Outro exemplo, são variáveis utilizadas para realizar troca de valores entre variáveis. Se você utilizar um inteiro global “aux” para realizar uma troca de valores entre duas variáveis em diversas funções, muito provavelmente o programa irá funcionar de maneira incorreta. Mais um exemplo, são variáveis acumuladoras. Não é porque você precisa somar uma série de valores em diversas funções, que você deve utilizar uma variável global “total” como acumulador. Por outro lado, as configurações de um jogo, por exemplo, precisam estar em apenas um lugar da memória, durante toda a execução de uma partida deste jogo. A pontuação do jogador é um dado cujo valor irá mudar durante a execução da partida, diferentemente dasconfigurações. No entanto, este dado também só precisa estar em um lugar da memória. Por acaso, pontuação é uma variável de acumulação, ou seja, sempre há exceções a considerar. Contudo, perceba que é possível implementar um programa que utiliza apenas variáveis locais, por mais que muitas destas variáveis possam ser globais. A vantagem em declarar variáveis como globais, as que podem ter este privilégio, está em simplificar a interface de diversas funções. Não é necessário, por exemplo, que uma variável de pontuação de um jogador precise ser passada como argumento de uma função, que repassa esta mesma pontuação como argumento de outra função e assim por diante. Todas as variáveis globais são acessíveis dentro de qualquer função. Outra vantagem, do ponto de vista de desempenho, tanto temporal quanto espacial, é que o compilador não precisa gerar instruções de máquina para empilhar argumentos. O computador não perde tempo colocando argumentos na pilha de chamada/registradores e não perde espaço criando cópias e mais cópias de uma mesma variável. Concluindo, chegamos ao seguinte princípio: uma variável deve ser local, até que hajam razões suficientes para que na verdade ela seja global; e não o contrário. Ou seja, não é normal declarar inicialmente uma variável como global e somente mover sua declaração para dentro do corpo de uma função (tornando-a uma variável local) se aparecerem boas razões para isto. A mudança natural do escopo de uma variável é mudar de escopo local para escopo global, quando surgem fortes razões. O contrário pode acontecer, mas não é comum. E) Função system() - Uma sugestão interessante para o trabalho é utilizar a função system() do cabeçalho “stdlib.h” para limpar o terminal, a cada vez que o programa for exibir uma nova tela. Utilizando a dica do item A do apêndice, basta realizar a chamada system(CLEAR); Observações gerais: 1. Incluir cabeçalho como comentário (ou seja, entre /* */), no programa fonte, de acordo com os critérios de avaliação dos trabalhos (Disponível no Moodle). 2. A data de entrega do programa é: 19/10/2015 (2a-feira) até às 23:55 hs, via moodle. 3. O arquivo fonte deve ser compactado em um arquivo .zip ou .rar, seguindo as regras de nomenclatura especificadas na guia do aluno. 4. Sigam as determinações quanto às entradas e saídas do programa. Serão descontados pontos de programas que tiverem de ter a entrada e/ou a saída corrigidas. Se tiverem alguma dúvida quanto à formatação de entrada e de saída, entrem em contato com os monitores com antecedência em relação ao prazo de entrega do trabalho. Universidade de Brasília Departamento de Ciência da Computação/IE Semestre: 2015-2 Disciplina: Algoritmos e Programação de Computadores Trabalho Prático 2 O objetivo desta segunda etapa é que o aluno aprenda a utilizar vetores/strings, matrizes (primeira parte), registros e arquivos binários (segunda parte) em algoritmos e a implementá-los em programas que executem corretamente no computador. Especificação da primeira etapa: Problema: IMPLEMENTAR O JOGO “EliminaLetras”. Neste trabalho, você deve implementar a parte restante do programa proposto na primeira etapa, ou seja, implementar o jogo propriamente dito e um ranking que utiliza arquivo binário para registrar as maiores pontuações obtidas no jogo. O jogo “EliminaLetras” é basicamente uma matriz de caracteres preenchida com diferentes símbolos, da qual o jogador deve selecionar posições para eliminar um elemento e todos os vizinhos laterais que contiverem o mesmo símbolo daquele elemento. Quanto maior a vizinhança eliminada de uma vez, maior a pontuação obtida na jogada. Detalhamento do problema: Parte 1 1) Opção 1 do menu principal – Ao escolher a opção de jogar uma partida, o programa deve perguntar ao usuário um apelido (por exemplo: char nick[21]), que servirá como identificação ao gravar pontuações no ranking. Esta solicitação deve ser repetida até que o usuário digite um apelido com no mínimo 3 e no máximo 20 caracteres. Exemplo: 2) Partida – Ao receber um apelido válido, o programa deve iniciar uma partida. A lógica de uma partida deve ser a seguinte: 2.1) Inicialização – A primeira coisa a ser feita é chamar a função implementada na primeira etapa responsável por ler o arquivo de texto com as configurações do jogo. Em seguida, deve-se inicializar a matriz. Será necessário declarar uma variável global, por exemplo, char matriz[9][10] (veja o item 3 desta especificação), cujas dimensões de fato utilizadas na partida são determinadas pelas variáveis globais de configuração criadas anteriormente (int linhas, colunas). O programa deve passar por cada elemento da parte utilizada da matriz, atribuindo um caractere sorteado a partir da string global de configuração também criada anteriormente: char caracteres[11]. Para sortear um caractere da string, utilize a função rand(), declarada no cabeçalho stdlib.h (leia o item do apêndice sobre geração de números pseudo-aleatórios). É necessário também declarar uma variável global, por exemplo, int pontos, e atribuir a ela o valor zero, para realizar a contagem de pontos da partida. 2.2) Grande laço – Após a inicialização da partida, o programa deve entrar em um laço cuja condição de parada é que todos os elementos da matriz tenham sido eliminados. A sequência de passos que deve ocorrer dentro deste laço é a seguinte: limpar a tela; mostrar a matriz; ler o movimento do jogador até que ele seja válido (linha e coluna de um elemento não-vazio da matriz); marcar o elemento e sua vizinhança com o caractere especial '*' (veja o item 4 desta especificação); limpar a tela; mostrar a matriz; mostrar uma mensagem com a pontuação daquela jogada (veja o item 5 desta especificação); perguntar ao jogador se ele confirma a jogada; caso o jogador confirme, executar a jogada eliminando os elementos marcados com '*' e fazendo com que os elementos restantes caiam sobre as posições vazias; caso o jogador desconfirme a jogada, desmarcar a área marcada com '*', atribuindo o caractere original. Exemplo de uma iteração do laço: 2.3) Finalização da partida – Ao término de uma partida, uma nova tela deve mostrar os pontos obtidos pelo jogador e deve ser feita uma chamada a uma função que realize a atualização do arquivo binário do ranking (esta função será implementada na parte 2 deste trabalho prático). Exemplo: 3) Matriz – A matriz deve possuir o tamanho máximo permitido pelas configurações do jogo: 9 linhas por 9 colunas. No entanto, ela deve ser declarada com uma coluna a mais para armazenar o caractere delimitador '\0'. Isto irá facilitar a impressão da matriz: basta fazer um laço que imprima cada linha como se fosse uma string. Lembre-se também de que a inicialização da partida deve criar a coluna de caracteres '\0' na posição correta, de acordo com as configurações! Ao criar a função de mostrar a matriz, mostre também a pontuação atual e a string bônus (veja o item 6 desta especificação), como nas imagens de exemplos do item 2.2. 4) Marcação da vizinhança – O problema da marcação de vizinhança é naturalmente recursivo. Você deve criar uma função (void markCell(int linha, int coluna)) que implemente a seguinte lógica: verificar se o elemento descrito pelos parâmetros de entrada está nos limites da parte utilizada da matriz; caso esteja fora, a função deve ser retornada (condição de parada da recursão); caso esteja dentro, verificar se o caractere da matriz naquela posição é igual ao caractere do elemento selecionado na jogada do usuário; caso seja diferente, a função deve ser retornada (condição de parada da recursão); caso seja igual, marcar o elemento com '*', incrementar um contador global de elementos na vizinhança (por exemplo: int elementos) e realizar chamadas recursivas à markCell() para os quatro vizinhos laterais (e não diagonais) do elemento de entrada.Lembre-se de que o contador global de elementos na vizinhança sempre deve ser zerado antes da marcação do elemento inicial da jogada escolhida pelo usuário. 5) Pontuação – A pontuação de uma jogada deve ser calculada com a seguinte expressão: (int)ceil(pow(base_pontos, elementos)) Para isto, será necessário incluir o cabeçalho math.h e utilizar a opção -lm na compilação. 6) Pontuação bônus – Sempre que uma jogada for executada, isto é, o jogador confirmar o movimento selecionado, o caractere da jogada deve ser concatenado em uma string global (por exemplo: char bonus[10]). Assim que o tamanho desta string global for igual à quantidade de colunas da matriz utilizadas na partida, o programa deve realizar um laço que percorre cada linha da matriz, verificando se a string é igual a aquela linha. Para cada linha que for igual a string global, o programa deve acumular a seguinte expressão em uma variável temporária: (int)ceil(pow(2*base_pontos, colunas)) Ao final do laço, adicione o valor da variável temporária à pontuação total da partida, imprima uma mensagem avisando ao jogador da pontuação bônus recebida e esvazie a string global. 7) Variáveis globais – A seguir estão as variáveis globais sugeridas nesta primeira parte da especificação. Além delas, você pode incluir outras que achar necessárias para a sua solução, mas lembre-se de que a utilização correta de variáveis globais será avaliada na correção do trabalho! char nick[21]; /* apelido do jogador */ char matriz[9][10]; /* matriz de caracteres do jogo */ int pontos; /* pontuacao da partida */ int elementos; /* contagem de elementos na funcao recursiva */ 8) Funções sugeridas – Para implementar a primeira parte do trabalho, você deve criar as funções abaixo. Os nomes, parâmetros e tipos de retorno utilizados são apenas sugestões, você pode modificar o que achar necessário. void play(); /* funcao para o grande laco de uma partida */ void setupMatch(); /* inicializacao para uma nova partida */ void setupMatrix(); /* inicializacao da matriz em uma nova partida */ void showMatrix(); /* limpa a tela e mostra a matriz */ void readMove(); /* le uma jogada */ void markArea(); /* inicializa a var. elementos e chama markCell() */ void markCell(int linha, int coluna); /* funcao descrita no item 4 */ void unmarkArea(); /* funcao para desmarcar a vizinhanca marcada */ void execMove(); /* implementa a logica de execucao de uma jogada */ void processCol(int coluna); /* “cai” os elementos de uma coluna */ void registerMatch(); /* atualiza o ranking (2a parte) */ void showScore(); /* limpa a tela e mostra o resultado da partida */ void ranking(); /* exibe o ranking (2a parte) */ Parte 2 9) Registro de uma partida no ranking – A função sugerida registerMatch() deve servir para registrar a pontuação obtida durante a partida no arquivo binário com as informações de ranking. Para implementar esta função, será necessário definir o seguinte tipo: typedef struct { char nick[21]; int score; } Player; O nome do arquivo binário de ranking deve ser: Eliminaletras_ranking.bin Utilizando o tipo definido, crie a função registerMatch() com a seguinte lógica: tentar abrir o arquivo para leitura (opção “rb”); caso não seja possível, abrir o arquivo para escrita (opção “wb”) e gravar um vetor de 10 posições do tipo Player, em que o primeiro elemento descreve o resultado da partida que acabou de terminar e os próximos 9 elementos devem possuir a string nick vazia, ou seja, com '\0' na primeira posição, e encerrar a função; caso seja possível, utilizar o arquivo aberto para ler um vetor de 10 posições do tipo Player; após ler o vetor, a função deve procurar uma posição para inserir um registro com o resultado da partida que acabou de terminar; se foi possível inserir o registro, abrir o arquivo para escrita e escrever o novo vetor de 10 posições. Para encontrar a posição do novo registro, basta olhar o valor da pontuação em cada registro. O vetor deve sempre permanecer ordenado da maior pontuação para a menor. Ao encontrar uma posição para o novo registro, o resto do vetor deve ser deslocado para frente e o último elemento deve ser descartado. Caso o laço encontre um registro com pontuação igual a pontuação do novo registro, o novo registro deve ser inserido antes, ou seja, em uma classificação mais próxima do topo do ranking. 10) Opção 2 do menu principal – Ao escolher a opção de visualizar o ranking, o programa deve exibir uma tela com as informações contidas no arquivo binário e perguntar se o usuário deseja remover o arquivo atual com a função int remove ( const char * filename ) declarada no cabeçalho stdio.h. Caso não seja possível abrir o arquivo binário, mostrar uma mensagem dizendo que o ranking está vazio. Exemplos: Apêndice A) Função time() No cabeçalho padrão time.h está declarada a função time_t time (time_t* timer), que retorna o número de segundos que já se passaram desde 00:00, Jan 1, 1970 UTC. O tipo time_t é apenas um apelido para um outro tipo integral nativo. Caso o ponteiro passado como argumento para a função seja diferente de NULL, a função irá gravar o valor que ela retorna no endereço apontado. B) Geração de números pseudo-aleatórios Muitas vezes queremos introduzir coisas aleatórias em nossos programas. A biblioteca padrão do C nos fornece funções para gerar sequências de números pseudo-aleatórias, isto é, a sequência gerada não é de fato aleatória, é previsível, mas é muito próxima de aleatória, pois a dificuldade para prever as sequências é bem grande. As funções estão declaradas no cabeçalho stdlib.h e são as seguintes: void srand (unsigned int seed); Esta função atribui o valor do parâmetro de entrada seed a uma variável global interna da biblioteca. int rand (void); Esta função utiliza o valor atual da variável global interna da biblioteca para calcular o valor retornado e atribui um novo valor à variável global interna. Os valores retornados por esta função costumam ser bem grandes. Calculando o resto da divisão destes valores retornados por algum número N, obtemos números inteiros no intervalo [0, N – 1]. Observe que dada uma semente qualquer, a sequência de números gerada pela função rand() é sempre a mesma. Por isso, uma boa semente para a função srand() são os valores retornados pela função time() (veja o item do apêndice relacionado). Portanto, antes de realizar chamadas à função rand(), faça a seguinte chamada: srand(time(NULL)); Observações gerais: 1. Incluir cabeçalho como comentário (ou seja, entre /* */), no programa fonte, de acordo com os critérios de avaliação dos trabalhos (Disponível no Moodle). 2. A data de entrega do trabalho 2 será dividida em 2 partes com datas diferentes. PARTE 1: entrega dia 03/12/2015 (5a) até às 23:55 hs, via moodle. Enviar o programa completo incluindo o que já foi implementado no Trabalho 1. PARTE 2: entrega dia 10/12/2015 (5a) até às 23:55 hs, via moodle. Enviar o programa completo incluindo o que já foi implementado no Trabalho 1 e na parte 1 do Trabalho 2. 3. O arquivo fonte deve ser compactado em um arquivo .zip ou .rar, seguindo as regras de nomenclatura especificadas na guia do aluno. 4. Sigam as determinações quanto às entradas e saídas do programa. Serão descontados pontos de programas que tiverem de ter a entrada e/ou a saída corrigidas. Se tiverem alguma dúvida quanto à formatação de entrada e de saída, entrem em contato com os monitores com antecedência em relação ao prazo de entrega do trabalho.
Compartilhar