quarta-feira, 27 de dezembro de 2017

Problema do Rational Tangle Dance - ocorrência de padrões


Compartilhe!

O objetivo deste laboratório é usar funções para manipular strings por meio de um programa que consiga prever o estado final de duas cordas (separadas ou emaranhadas) após a execução de uma sequência de movimentos '+', '-' e '*'.

Descrição dos movimentos:

4----1
ü    ü

ü    ü
3----2
(estado inicial)

Movimento '+' (TROCA POR CIMA): A criança que está na posição 1 troca de lugar com a criança que está na posição 2, passando sua corda por cima da dela.

Movimento '-' (TROCA POR BAIXO): A criança que está na posição 1 troca de lugar com a criança que está na posição 2, passando sua corda por baixo da dela.

Movimento '*' (GIRO): As crianças giram no sentido horário (quem está na posição 1 vai para a 2, quem está na 2 vai para a 3, quem está na 3 vai para 4, e quem está na 4 vai para 1).

Neste laboratório, devemos fazer funções para um programa que, dada sequência de movimentos acima, consiga prever o estado final das cordas. As funções devem estar em um arquivo separado.

Descrição da tarefa:
O nome da brincadeira em inglês é chamada de rational tangle dance, criada pelo matemático Jonh Conway, que nos dá algumas dicas, que justamente serão as tarefas das funções que se seguem:

1 - Você deve processar a sequência de movimentos, removendo subsequências que não afetam o resultado: "+-", "-+", "**".

2 - Se a sequêcia de movimentos começa com um GIRO ('*'), qualquer movimento de TROCA subsequente ('+' ou '-') não vai emaranhar as cordas até que um outro movimento GIRO seja executado. Sendo assim, se a sequência de movimentos começa com '*', você deve remover todos os movimentos da primeira posição da sequência até a segunda aparição de '*', inclusive.

3 - Existem duas subsequências de movimentos que mascaram o resultado. São elas: "+*+" e "-*-". Sempre que uma dessas subsequências aparecer, elas devem ser substituídas por "*-*" e "*+*", respectivamente.

Observação: As alterações 1, 2 e 3 devem ser executadas até que a sequência de movimentos não seja mais alterada. Se a sequência final obtida for vazia ou igual a "*", as cordas terminarão separadas. Caso contrário, as cordas terminarão emaranhadas.

(Obs.: a descrição da atividade e código do arquivo main são de autoria dos PEDs do IC-Unicamp.)

 Arquivo com a função main:
#include <stdio.h>
#include <string.h>

int removePadrao(char str[], char padrao[], char targetStr[]);
int removeBloco(char str[], char c, char targetStr[]);
int substituiPadrao(char str[], char padrao[], char novoPadrao[], char targetStr[]);

#define MAX_MOVES 35

int main(void) {
 
 char seq[MAX_MOVES];
 char auxSeq[MAX_MOVES];

 fgets(seq, 30, stdin);
 int len = strlen(seq);
 seq[len-1] = '\0';

 while (1) {
  int seqAlterada = 0;
  int res = removeBloco(seq, '*', auxSeq);
  if (res) {
   printf("removeBloco(*): %s\n", auxSeq);
   strcpy(seq, auxSeq);
   seqAlterada++;
  }

  res = removePadrao(seq, "**", auxSeq);
  if (res) {
   printf("removePadrao(**): %s\n", auxSeq);
   strcpy(seq, auxSeq);
   seqAlterada++;
  }

  res = removePadrao(seq, "+-", auxSeq);
  if (res) {
   printf("removePadrao(+-): %s\n", auxSeq);
   strcpy(seq, auxSeq);
   seqAlterada++;
  }

  res = removePadrao(seq, "-+", auxSeq);
  if (res) {
   printf("removePadrao(-+): %s\n", auxSeq);
   strcpy(seq, auxSeq);
   seqAlterada++;
  }

  res = substituiPadrao(seq, "+*+", "*-*", auxSeq);
  if (res) {
   printf("substituiPadrao(+*+, *-*): %s\n", auxSeq);
   strcpy(seq, auxSeq);
   seqAlterada++;
  }

  res = substituiPadrao(seq, "-*-", "*+*", auxSeq);
  if (res) {
   printf("substituiPadrao(-*-, *+*): %s\n", auxSeq);
   strcpy(seq, auxSeq);
   seqAlterada++;
  }
  if (!seqAlterada) {
   break;
  }
 }

 len = strlen(seq);

 if ( (len == 0) || ((len == 1) && (strcmp(seq, "*") == 0)) )  {
  printf("Cordas terminam separadas!\n");
 } else {
  printf("Cordas terminam emaranhadas!\n");
 }

 return 0;
}


Arquivo, de minha autoria, com as funções requeridas pela main acima:

#include <stdio.h>
#include <string.h>

/* Funcao: removePadrao
 *
 *    Processa linearmente (e uma unica vez) os carateres de uma dada sequencia de movimentos,
 *    removendo as ocorrencias de um dado padrao.
 * 
 * Parametros:
 *          str: sequencia de movimentos '+', '-', '*'
 *       padrao: subsequencia de movimentos, cujas ocorrencias devem ser removidas
 *    targetStr: sequencia obtida apos a remocao de str de todas as ocorrencias do dado padrao.
 *               
 *  
 * Retorno:
 *
 *    1, se as occorrencias do dado padrao foram removidas
 *    0, caso contrario
 *
 * Exemplo:
 *
 *     char seq1[15] = "+-*+-*-*+++";
 *     char seq2[15];
 *
 *     char padrao1[3] = "+-"
 *     char padrao2[3] = "**"
 * 
 *     removePadrao(seq1, padrao1, seq2); // seq2 sera "**-*+++"
 *     removePadrao(seq1, padrao2, seq2); // seq2 sera "+-*+-*-*+++" 
 * 
 */

int removePadrao(char str[], char padrao[], char targetStr[]) {

 int k = 0, ocRemov = 0, tamPadrao, tamStr, entrou = 0;

 tamPadrao = strlen(padrao);//Guarda tamanhos das strings.
 tamStr = strlen(str);


 for (int i = 0; i < tamStr; i++) {//Do início de str até seu final

  if (str[i] == padrao[0] && tamStr - i > tamPadrao - 1) {//Se o elemento (i) de str for igual ao primeiro elemento do padrão e houver possibilidade de ali conter um padrão:

   for (int j = 1; j < tamPadrao; j++)//Vá até o final do padrao comparando com str.
    if (padrao[j] != str[i + j]) {//Se encontrou algo diferente, registre isso e pare o for.

     entrou = 1;
     break;

    }


   if (entrou == 0) {//Dependendo do resultado ("não entrou"), o i é incrementado de modo que ele "pule" o padrão e isso é registrado.

    i += tamPadrao - 1;
    ocRemov = 1;


   } else {//Caso contrário, não é o padrão, a string targetStr recebe o elemento de str em i.

    targetStr[k] = str[i];
    k++;
    entrou = 0;

   }

  } else {//Caso contrário, não pode ser o padrão, a string targetStr recebe o elemento de str em i.

   targetStr[k] = str[i];
   k++;

  }

 }

 targetStr[k] = '\0';
 return ocRemov;

}


/* Funcao: removeBloco
 *
 *    Processa linearmente (e uma unica vez) os carateres de uma dada sequencia de movimentos,
 *    removendo o bloco inicial delimitado pelo dado caractere.
 * 
 * Parametros:
 *          str: sequencia de movimentos '+', '-', '*'
 *            c: caractere delimitador do bloco a ser removido
 *    targetStr: se str comeca com o caractere do parametro c, targetStr contera a sequencia obtida
 *               apos a remocao (de str) do bloco de movimentos da primeira posicao de str ate a 
 *               segunda ocorrencia (inclusive) do caractere do parametro c. Se nao exsitir uma segunda 
 *               ocorrencia do caracter do parametro c, targetStr deve conter a sequencia vazia.
 *
 * Retorno:
 *
 *    1, se o bloco foi removido
 *    0, caso contrario
 *
 * Exemplo:
 *
 *     char seq1[15] = "+-*+-*-*+++";
 *     char seq2[15] = "*+-*+-*-*+++";
 *     char seq3[15] = "*+-+--+++";
 *     char seq4[15];
 * 
 *     removeBloco(seq1, '*', seq4); // seq4 sera "+-*+-*-*+++"
 *     removeBloco(seq2, '*', seq4); // seq4 sera "+-*-*+++"
 *     removeBloco(seq3, '*', seq4); // seq4 sera "" (sequencia vazia)
 *
 */

int removeBloco(char str[], char c, char targetStr[]) {

 int blcRemov = 0, tamStr, k = 0;

 if (str[0] == c) {//Se o primeiro elemento de str for igual a c:

  blcRemov = 1;//Registra que isso aconteceu.
  tamStr = strlen(str);//Armazena tamanho de str.

  for (int i = 1; i < tamStr; i++)//Do próximo elemento até o final de str, compare para encontrar o próximo c em str.
   if (str[i] == c)//Se encontrou, começa a partir daqui até o final de str a ser elementos de targetStr.
    for (int j = i + 1; j < tamStr + 1; j++) {

     targetStr[k] = str[j];
     k++;
    }

 }
 targetStr[k] = '\0';
 return blcRemov;

}


/* Funcao: substituiPadrao
 *
 *    Processa linearmente (e uma unica vez) os carateres de uma dada sequencia de movimentos,
 *    substituindo as ocorrencias de um dado padrao por um novo padrao.
 *
 * Parametros:
 *           str: sequencia de movimentos '+', '-', '*'
 *        padrao: subsequencia de movimentos, cujas ocorrencias devem ser substituidas pelo novoPadrao
 *    novoPadrao: subsequencia de movimentos que deve substituir cada ocorrencia do dado padrao
 *     targetStr: sequencia obtida apos a substituicao em str de todas as ocorrencias do dado padrao 
 *                pelo novoPadrao
 *
 *     Voce pode assumir que as subsequencias padrao e novoPadrao tem o mesmo tamanho.
 *
 * Retorno:
 *
 *    1, se as ocorrencias do dado padrao foram substituidas pelo novoPadrao
 *    0, caso contrario
 *
 * Exemplo:
 *
 *     char seq1[15] = "+*+-+*+-*++";
 *     char seq2[15];
 *     char seq3[15];
 *
 *     char padrao[5] = "+*+"
 *     char novoPadrao[5] = "*-*"
 * 
 *     substituiPadrao(seq1, padrao, novoPadrao, seq2); // seq2 sera "*-*-*-*-*++"
 *     substituiPadrao(seq2, padrao, novoPadrao, seq3);  // seq3 sera "*-*-*-*-*++" 
 * 
 */

int substituiPadrao(char str[], char padrao[], char novoPadrao[], char targetStr[]) {

 int k = 0, entrou = 0, subPadrao = 0, tamStr, tamPadrao, tamNovoPadrao;

 tamStr = strlen(str);//Armazena os tamanhos destas variáveis.
 tamPadrao = strlen(padrao);
 tamNovoPadrao = strlen(novoPadrao);


 for (int i = 0; i < tamStr; i++) {//Do início até o final de str, faça:

  if (str[i] == padrao[0] && tamStr - i > tamPadrao - 1) {//Se o elemento (i) de str for igual ao primeiro elemento do padrão e houver possibilidade de ali conter um padrão:

   for (int j = 1; j < tamPadrao; j++) {//Verifica se há o padrão. (Mesmo método de removePadrao())

    if (str[i + j] != padrao[j]) {
     entrou = 1;
     break;
    }

   }

   if (entrou == 0) {

    i += tamPadrao - 1;
    subPadrao = 1;
    //A diferença de removePadrao() está aqui. A partir do momento que o padrão foi encontrado, percorre-se a string do novo padrão copiando estes elementos para targetStr.
    for (int j = 0; j < tamNovoPadrao; j++) {

     targetStr[k] = novoPadrao[j];
     k++;
    }


   } else {//Caso contrário, não é o padrão, a string targetStr recebe o elemento de str em i.

    targetStr[k] = str[i];
    k++;
    entrou = 0;

   }

  } else {//Caso contrário, não pode ser o padrão, a string targetStr recebe o elemento de str em i.
   targetStr[k] = str[i];
   k++;

  }


 }

 targetStr[k] = '\0';
 return subPadrao;

}