argcv

codes and stuff

Archive for abril 2012

Strcat() “caseira”

leave a comment »

A função strcat(), cujo protótipo se encontra no arquivo de cabeçalho string.h, recebe str1 e str2 e devolve str1, com o conteúdo de str2 copiado para o seu final. str2 não é alterada. Observe seu protótipo:

char *strcat(char *str1, const char *str2);

Naturalmente, em um programa em que essa função se faz necessária, é melhor que você a use direto. Não há nenhum motivo para reinventar a roda.

Mas como exercício pode ser interessante fazer uma versão própria da função, pra ter uma ideia legal de como ela pode funcionar.

Pensando nisso é que a sua construção é proposta em um exercício da apostila do Curso de Linguagem C, da UFMG (ótima apostila, aliás). E eu resolvi fazer. E se interessar a alguém, cá está, já inserida em um programinha para que ela possa ser testada.

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

char *strcat_caseira(char *str1, const char *str2);

void main() {
    char str1[80] = "isto é";
    char str2[80] = " um teste!";

    strcat_caseira(str1, str2);

    printf("%s\n\n", str1);
}

char *strcat_caseira(char *str1, const char *str2) {
    int i = strlen(str1), j;

    for (j = 0, i; str2[j]; j++, i++) str1[i] = str2[j];
    str1[i] = '\0'; // coloca o terminador nulo em str1

    return str1;
}

Até mais.

Written by rntreis

abril 18, 2012 at 8:26 pm

Publicado em C, dicas, programação

Jogo da velha 2.0 – No Country For Old Men

with one comment

Quando terminei de escrever aquele joguinho da velha, um fato ficou me perturbando: a CPU perdia com muita facilidade.

Daí eu resolvi ensinar a ela uns truques, e criei quatro níveis para o jogo: Noob, Médio, Gladiador e Zeus. Sendo que o último, dizem as más línguas, é impossível de se vencer.

Também adaptei a função mostra_tabuleiro() para apresentar tabuleiros que condizem mais com um de verdade. O “1” do player foi substituído pelo “X” e o “2” da CPU foi substituído pelo “O”.

Percebam também que eu adicionei a função __fpurge() em algumas ocasiões. Isso porque sem ela alguns bugs estavam sendo provocados em situações em que havia interação com o usuário.

Se você usa Windows (tsc tsc) deverá trocar o __fpurge() por fflush().

O código-fonte segue abaixo. E também está no pastebin.

/* ***********************************
   * Autor: Renato R. Leme
   * Data: 14/04/2012 (atualizado em 17/04/2012)
   * e-mail: rntreisleme@gmail.com
   * site: https://argcv.wordpress.com
   ***********************************
   Qualquer dúvida ou sugestão, pode entrar em contato comigo pelo meu e-mail
   ou pelo meu blog.
*/

#include <stdio.h>
#include <stdlib.h>

#define LINHAS  3
#define COLUNAS 3
#define CASAS   9

void init_tabuleiro(void);
void mostra_tabuleiro(void);
void jogada_player(void);
void jogada_cpu(int rodada);
int update_tabuleiro(int x, int y); // retorna 0 se a operação for bem sucedida
int testa_vencedor(void); // 2 - vitória cpu, 1 - vitória player, 0 - nada aconteceu
int velha(void); // conta o número de casas vazias no tabuleiro

enum vez { PLAYER, CPU } jogador;
enum niveis { NOOB, MEDIO, GLADIADOR, ZEUS } nivel;

int tabuleiro[LINHAS][COLUNAS];

void main() {
    int dificuldade, escolha, status;
    static int rodada;
    rodada = 0;
    jogador = PLAYER; // seta o primeiro jogador para player
    init_tabuleiro(); // inicia o tabuleiro com 0s

    while(1) {
        __fpurge(stdin); // limpa o buffer do teclado
        printf("\nEscolha o nível em que deseja jogar \n(1 - Noob, 2 - Médio, 3 - Gladiador, 4 - Zeus): ");
        scanf("%d", &dificuldade);
        if (dificuldade == 1) { nivel = NOOB; break; }
        else if (dificuldade == 2) { nivel = MEDIO; break; }
        else if (dificuldade == 3) { nivel = GLADIADOR; break; }
        else if (dificuldade == 4) { nivel = ZEUS; break; }
        else printf("\nEscolha invalida!");
    }

    while(1) {
        ++rodada;
        if (jogador == PLAYER) {
            if (!velha()) break;
            mostra_tabuleiro();
            jogada_player();
            status = testa_vencedor();
            if (status) break;
        }
        if (jogador == CPU) {
            if (!velha()) break;
            jogada_cpu(rodada);
            status = testa_vencedor();
            if (status) break;
        }
    }

    mostra_tabuleiro();

    if (status == 1) printf("\nParabéns, você venceu!\n");
    else if (status == 2) printf("\nVocê perdeu! :(\n");
    else printf("\nDeu velha!\n");

    while(1) {
        __fpurge(stdin);
        printf("\nJogar novamente? (1 = sim / 2 = não) : ");
        scanf("%d", &escolha);
        if (escolha == 1) main();
        else if (escolha == 2) exit(0);
        else printf("\nEscolha invalida!");
    }
}

void init_tabuleiro() {
    register int i;
    int *p;
    p = (int *) tabuleiro;

    for (i = 0; i < CASAS; i++ ) *(p+i) = 0;
}

int velha() {
    register int i;
    int *p, zeros = 0;
    p = (int *) tabuleiro;

    for (i = 0; i < CASAS; i++ ) if (*(p+i) == 0) zeros++;

    if (!zeros) return 0;
    else return 1;
}

void mostra_tabuleiro() {
    register int i, j;
    char m_tabuleiro[LINHAS][COLUNAS];

    for (i = 0; i < 3; i++) {
        for (j = 0; j < 3; j++) {
            if (tabuleiro[i][j] == 0) m_tabuleiro[i][j] = ' ';
            if (tabuleiro[i][j] == 1) m_tabuleiro[i][j] = 'X';
            if (tabuleiro[i][j] == 2) m_tabuleiro[i][j] = 'O';
        }
    }

    printf("\n");

    for (i = 0; i < 3; i++) {
        for (j = 0; j < 3; j++) {
            if (i < 2) printf("|_%c_|", m_tabuleiro[i][j]);
            else printf("| %c |", m_tabuleiro[i][j]);
        }
        printf("\n");
    }
}

int update_tabuleiro(int x, int y) {
    if (jogador == PLAYER) {
            tabuleiro[x][y] = 1;
            jogador = CPU;
            return 0;
        }
    if (jogador == CPU) {
        if (tabuleiro[x][y] != 0) return 1;
        else {
            tabuleiro[x][y] = 2;
            jogador = PLAYER;
            return 0;
        }
    }
}

void jogada_player() {
    int x, y;
    __fpurge(stdin);
    printf("\nDigite a linha e a coluna em que deseja jogar: ");
    scanf("%d %d", &x, &y);

    x--, y--;

    if (tabuleiro[x][y] != 0 || x < 0 || x > 2 || y < 0 || y > 2) {
        printf("\nJogada inválida.\n");
        jogada_player();
    }
    else update_tabuleiro(x, y);
}

void jogada_cpu(int rodada) {
    register int i;
    int x, y;
    static int diagonais; // será alterada caso o player comece a jogar pelas diagonais
    int ad; // ataque e defesa. Quando 'ad' vale 2, a cpu testa a possibilidade de ataque.
            // se valer 1, ela testa por defesa.

    diagonais = (rodada == 1) ? 0 : diagonais; // será zerada toda vez que o jogo reinicia

    // série de testes que verificará se há possibilidade de o jogador ou
    // a CPU vencer o jogo em alguma linha, alguma coluna ou alguma diagonal.
    // serão executados de acordo com o nivel do jogo.

    if (nivel == MEDIO || nivel == GLADIADOR || nivel == ZEUS) {
        for (ad = 2; ad >= 1; ad--) {
            for (i = 0; i < 3; i++) { // para todas as linhas e colunas
                if (tabuleiro[i][1] == ad && tabuleiro[i][2] == ad) if (!update_tabuleiro(i, 0)) return;
                if (tabuleiro[i][0] == ad && tabuleiro[i][2] == ad) if (!update_tabuleiro(i, 1)) return;
                if (tabuleiro[i][0] == ad && tabuleiro[i][1] == ad) if (!update_tabuleiro(i, 2)) return;
                if (tabuleiro[1][i] == ad && tabuleiro[2][i] == ad) if (!update_tabuleiro(0, i)) return;
                if (tabuleiro[0][i] == ad && tabuleiro[2][i] == ad) if (!update_tabuleiro(1, i)) return;
                if (tabuleiro[0][i] == ad && tabuleiro[1][i] == ad) if (!update_tabuleiro(2, i)) return;
            }

            // para as diagonais
            if (tabuleiro[0][0] == ad && tabuleiro[2][2] == ad) if (!update_tabuleiro(1, 1)) return;
            if (tabuleiro[0][2] == ad && tabuleiro[2][0] == ad) if (!update_tabuleiro(1, 1)) return;
            if (tabuleiro[0][2] == ad && tabuleiro[1][1] == ad) if (!update_tabuleiro(2, 0)) return;
            if (tabuleiro[2][2] == ad && tabuleiro[1][1] == ad) if (!update_tabuleiro(0, 0)) return;
            if (tabuleiro[2][0] == ad && tabuleiro[1][1] == ad) if (!update_tabuleiro(0, 2)) return;
            if (tabuleiro[0][0] == ad && tabuleiro[1][1] == ad) if (!update_tabuleiro(2, 2)) return;
        }

        if (nivel == GLADIADOR || nivel == ZEUS) { // testes que evitam jogadas do player
            if (rodada == 1 && tabuleiro[1][1] == 1) if (!update_tabuleiro(2, 0)) return;
            if ((!diagonais && rodada == 2) && tabuleiro[0][2] == 1) if (!update_tabuleiro(0, 0)) return;

            if (tabuleiro[0][1] == 1  || tabuleiro[1][0] == 1 || tabuleiro[1][2] == 1 || tabuleiro[2][1] == 1) {
                if (rodada == 1 && !update_tabuleiro(1, 1)) return;
                if (rodada == 2) {
                    if (tabuleiro[1][2] == 1 || tabuleiro[1][0] == 1) {
                        if (tabuleiro[2][0] == 1 && !update_tabuleiro(2, 2)) return;
                        if (tabuleiro[0][0] == 1 && !update_tabuleiro(0, 2)) return;
                        if (tabuleiro[0][1] == 1 && !update_tabuleiro(0, 0)) return;
                        if (tabuleiro[2][1] == 1 && !update_tabuleiro(2, 0)) return;
                    }
                    if (tabuleiro[0][1] == 1 || tabuleiro[2][1] == 1) {
                        if ((tabuleiro[2][0] == 1 || tabuleiro[0][0] == 1) && !update_tabuleiro(1, 2)) return;
                        if ((tabuleiro[2][2] == 1 || tabuleiro[0][2] == 1) && !update_tabuleiro(1, 0)) return;
                    }
                }
            }
        }

        if (nivel == ZEUS) { // ultimo nivel de testes para evitar jogadas
            if (tabuleiro[0][0] == 1 || tabuleiro[0][2] == 1 || tabuleiro[2][0] == 1 || tabuleiro[2][2] == 1) {
                diagonais++;
                if (!update_tabuleiro(1, 1)) return;
                if (rodada == 2) if (!update_tabuleiro(1, 0)) return;
            }
        }
    }

    // caso nenhum dos testes acima sejam satisfeitos, a cpu joga aleatoriamente

    srand(time(NULL));

    while(1) {
        x = rand() % 3;
        y = rand() % 3;
        if (!update_tabuleiro(x,y)) return;
    }

}

int testa_vencedor() {
    register int i;
    int a = (jogador == CPU) ? 1 : 2; // 'a' varia de acordo com quem o chama (player ou cpu)

    for (i = 0; i < 3; i++) if (tabuleiro[i][0] == a && tabuleiro[i][1] == a && tabuleiro[i][2] == a) return a;
    for (i = 0; i < 3; i++) if (tabuleiro[0][i] == a && tabuleiro[1][i] == a && tabuleiro[2][i] == a) return a;
    if (tabuleiro[2][0] == a && tabuleiro[1][1] == a && tabuleiro[0][2] == a) return a; 
    if (tabuleiro[0][0] == a && tabuleiro[1][1] == a && tabuleiro[2][2] == a) return a;

    return 0; // se não houver retorno até aqui, é sinal de que ninguém venceu. Portanto, a função retorna 0
}

Written by rntreis

abril 18, 2012 at 8:00 am

Publicado em C, jogos, programação

Scanf e o buffer do teclado

with one comment

As vezes quando nosso programa se propõe interativo, enfrentamos alguns problemas quanto às possibilidades de erros de entrada.

Observe a função abaixo:

void main() {
    int x, y;

    printf("Digite um numero: ");
    scanf("%d", &x);
    printf("Digite outro numero: ");
    scanf("%d", &y);

    printf("\n\n%d %d\n\n", x, y);
}

Se aqui o usuário digitar “1 2” para o primeiro scanf(), o segundo scanf() atribuiria automaticamente à y o número 2. Isso porque o programa não limpa o seu buffer automaticamente.

Para evitar dores de cabeça, um dos métodos para quem programa no Linux seria utilizar a função __fpurge().

O exemplo acima ficaria assim:

void main() {
    int x, y;

    printf("Digite um numero: ");
    scanf("%d", &x);
    __fpurge(stdin); // limpa o buffer do teclado
    printf("Digite outro numero: ");
    scanf("%d", &y);

    printf("\n\n%d %d\n\n", x, y);
}

O parâmetro stdin informa à __fpurge() que o stream do buffer que deverá ser limpado é o do teclado.

Se você usa Windows, a alternativa seria usar o fflush(). Assim:

void main() {
    int x, y;

    printf("Digite um numero: ");
    scanf("%d", &x);
    fflush(stdin); // também limpa o buffer do teclado
    printf("Digite outro numero: ");
    scanf("%d", &y);

    printf("\n\n%d %d\n\n", x, y);
}

Até mais.

Written by rntreis

abril 17, 2012 at 8:54 pm

Publicado em C, dicas

Jogo da velha em C

with one comment

(Confira também a versão melhorada deste código).

Eu tinha feito há algum tempo atrás o mesmo jogo em Java, mas não tinha gostado muito do meu código-fonte. Era ineficiente e longo demais. Daí que resolvi refazê-lo, agora em C. Demorei cerca de três horas para terminar.

A inteligência da CPU é bem simples. Ela primeiro testa se existe a possibilidade de ganhar o jogo no ato, e, se houver, ela joga para ganhar. Se não tiver como vencer, ela tenta se defender, testando se existe alguma forma de o jogador ganhar na próxima jogada dele. Se existir, ela se defende.

Pra jogar em uma posição vazia (representada por 0) é só digitar a linha e a coluna, que variam de 1 a 3, separadas por um espaço.

/* ***********************************
   * Autor: Renato R. Leme
   * Data: 14/04/2012 (atualizado em 16/04/2012)
   * e-mail: rntreisleme@gmail.com
   * site: https://argcv.wordpress.com
   ***********************************
    Qualquer dúvida ou sugestão, pode entrar em contato comigo pelo meu e-mail
    ou pelo meu blog.
*/

#include <stdio.h>
#include <stdlib.h>

#define LINHAS  3
#define COLUNAS 3
#define CASAS   9

void init_tabuleiro(void);
void mostra_tabuleiro(void);
void jogada_player(void);
void jogada_cpu(void);
int update_tabuleiro(int x, int y); // retorna 0 se a operação for bem sucedida
int testa_vencedor(void); // 2 - vitória cpu, 1 - vitória player, 0 - nada aconteceu
int velha(void); // conta o número de casas vazias no tabuleiro

enum vez { PLAYER, CPU } jogador;

int tabuleiro[LINHAS][COLUNAS];

void main() {
    int escolha, status;
    jogador = PLAYER; // seta o primeiro jogador para player
    init_tabuleiro(); // inicia o tabuleiro com 0s

    while(1) {
        if (jogador == PLAYER) {
            if (!velha()) break;
            mostra_tabuleiro();
            jogada_player();
            status = testa_vencedor();
            if (status) break;
        }
        if (jogador == CPU) {
            if (!velha()) break;
            jogada_cpu();
            status = testa_vencedor();
            if (status) break;
        }
    }

    mostra_tabuleiro();

    if (status == 1) printf("\nParabéns, você venceu!\n");
    else if (status == 2) printf("\nVocê perdeu! :(\n");
    else printf("\nDeu velha!\n");

    while(1) {
        printf("\nJogar novamente? (1 = sim / 2 = não) : ");
        scanf("%d", &escolha);
        if (escolha == 1) main();
        else if (escolha == 2) exit(0);
        else printf("\nEscolha invalida!");
    }
}

void init_tabuleiro() {
    register int i;
    int *p;
    p = (int *) tabuleiro;

    for (i = 0; i < CASAS; i++ ) *(p+i) = 0;
}

int velha() {
    register int i;
    int *p, zeros = 0;
    p = (int *) tabuleiro;

    for (i = 0; i < CASAS; i++ ) if (*(p+i) == 0) zeros++;

    if (!zeros) return 0;
    else return 1;
}

void mostra_tabuleiro() {
    register int i, j;
    printf("\n");

    for (i = 0; i < 3; i++) {
        for (j = 0; j < 3; j++) {
            printf("%d", tabuleiro[i][j]);
        }
        printf("\n");
    }
}

int update_tabuleiro(int x, int y) {
    if (jogador == PLAYER) {
            tabuleiro[x][y] = 1;
            jogador = CPU;
            return 0;
        }
    if (jogador == CPU) {
        if (tabuleiro[x][y] != 0) return 1;
        else {
            tabuleiro[x][y] = 2;
            jogador = PLAYER;
            return 0;
        }
    }
}

void jogada_player() {
    int x, y;
    printf("\nDigite a linha e a coluna em que deseja jogar: ");
    scanf("%d %d", &x, &y);

    x--, y--;

    if (tabuleiro[x][y] != 0 || x < 0 || x > 2 || y < 0 || y > 2) {
        printf("\nJogada inválida.\n");
        jogada_player();
    }
    else update_tabuleiro(x, y);
}

void jogada_cpu() {
    register int i, j, x, y;
    int ad; // ataque e defesa. Quando 'ad' vale 2, a cpu testa a possibilidade de ataque.
            // se valer 1, ela testa por defesa.

    // série de testes que verificará se há possibilidade de o jogador ou a CPU vencer
    // o jogo em alguma linha, alguma coluna ou alguma diagonal.

    for (ad = 2; ad >= 1; ad--) {
        for (i = 0; i < 3; i++) { // para todas as linhas e colunas
            if (tabuleiro[i][1] == ad && tabuleiro[i][2] == ad) 
                if (!update_tabuleiro(i, 0)) return;
            if (tabuleiro[i][0] == ad && tabuleiro[i][2] == ad) 
                if (!update_tabuleiro(i, 1)) return;
            if (tabuleiro[i][0] == ad && tabuleiro[i][1] == ad) 
                if (!update_tabuleiro(i, 2)) return;
            if (tabuleiro[1][i] == ad && tabuleiro[2][i] == ad) 
                if (!update_tabuleiro(0, i)) return;
            if (tabuleiro[0][i] == ad && tabuleiro[2][i] == ad) 
                if (!update_tabuleiro(1, i)) return;
            if (tabuleiro[0][i] == ad && tabuleiro[1][i] == ad) 
                if (!update_tabuleiro(2, i)) return;
        }

        // para as diagonais
        if (tabuleiro[0][0] == ad && tabuleiro[2][2] == ad) 
            if (!update_tabuleiro(1, 1)) return;
        if (tabuleiro[0][2] == ad && tabuleiro[2][0] == ad) 
            if (!update_tabuleiro(1, 1)) return;
        if (tabuleiro[0][2] == ad && tabuleiro[1][1] == ad) 
            if (!update_tabuleiro(2, 0)) return;
        if (tabuleiro[2][2] == ad && tabuleiro[1][1] == ad) 
            if (!update_tabuleiro(0, 0)) return;
        if (tabuleiro[2][0] == ad && tabuleiro[1][1] == ad) 
            if (!update_tabuleiro(0, 2)) return;
        if (tabuleiro[0][0] == ad && tabuleiro[1][1] == ad) 
            if (!update_tabuleiro(2, 2)) return;
    }

    // caso nenhum dos testes acima sejam satisfeitos, a cpu joga aleatoriamente

    srand(time(NULL));

    while(1) {
        x = rand() % 3;
        y = rand() % 3;
        if (!update_tabuleiro(x,y)) return;
    }
}

int testa_vencedor() {
    register int i;
    int a = (jogador == CPU) ? 1 : 2; // 'a' varia de acordo com quem o chama

    for (i = 0; i < 3; i++)
        if (tabuleiro[i][0] == a && tabuleiro[i][1] == a && tabuleiro[i][2] == a) return a;
    for (i = 0; i < 3; i++)
        if (tabuleiro[0][i] == a && tabuleiro[1][i] == a && tabuleiro[2][i] == a) return a;

    if (tabuleiro[2][0] == a && tabuleiro[1][1] == a && tabuleiro[0][2] == a) return a;
    if (tabuleiro[0][0] == a && tabuleiro[1][1] == a && tabuleiro[2][2] == a) return a;

    return 0;
}

Written by rntreis

abril 14, 2012 at 11:18 am

Publicado em C, jogos, programação

Criptografia simples

leave a comment »

Este código-fonte é de um programa que fiz que lê um arquivo de texto do computador e inverte os bits de cada caractere encontrado. Para usá-lo, na chamada do programa o usuário deve especificar o nome e a extensão do arquivo (ou o endereço completo, caso ele não esteja na mesma pasta do programa). Por exemplo:

$ ./cripto texto.txt

Com isso, o programa armazenará a string no banco de strings e o argumento char *argv[] de main() terá um ponteiro apontando para o nome do arquivo.

Depois de o programa ter criptografado o arquivo, para reaver o conteúdo original basta utilizar o programa novamente, agora sobre o arquivo criptografado. Assim, os bits de cada caractere serão novamente invertidos, voltando para a sua condição inicial.

/*********************************
   Autor: Renato R. Leme
   e-mail: rntreisleme@gmail.com
***********************************/

#include <stdio.h>
#include <stdlib.h>

inline void encrypt(char *ch); // função responsável pela inversão dos bits

int main (int argc, char *argv[]) {
    FILE *text, *temp;
    char ch;

    if (argc != 2) {
        printf("Digite o nome do arquivo.");
        exit(1);
    }

    if (!(text = fopen(argv[1], "r+"))) { // 'text' aponta para o arquivo que será lido
        printf("Erro na abertura do arquivo.\n");
        exit(1);
    }

    if (!(temp = tmpfile())) { // cria arquivo para armazenar as alterações temporariamente
        printf("Erro na criação do arquivo.\n");
        exit(1);
    }

    do {
        ch = getc(text);
        encrypt(&ch); // envia o endereço de cada caractere lido para encrypt
        if ((!feof(text))) putc(ch, temp); // coloca o caractere invertido em temp
    } while (!feof(text));

    rewind(text); // aponta o cursor de 'text' para o início do arquivo
    rewind(temp); // aponta o cursor de 'temp' para o início do arquivo

    do {
        ch = getc(temp);
        if ((!feof(temp))) putc(ch, text);
    } while (!feof(temp));


    fclose(text);
    fclose(temp);

    return 0;
}

inline void encrypt(char *ch) {
    *ch = ~(*ch);
}

Written by rntreis

abril 13, 2012 at 5:20 pm

Publicado em C, programação