192 lines
4.6 KiB
C

/* A common security method used for online banking is to ask the user for three random characters from a passcode.
* For example, if the passcode was 531278, they may ask for the 2nd, 3rd, and 5th characters; the expected reply would be: 317.
*
* The text file, keylog.txt, contains fifty successful login attempts.
*
* Given that the three characters are always asked for in order, analyse the file so as to determine the shortest possible
* secret passcode of unknown length.*/
#define _POSIX_C_SOURCE 199309L
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "projecteuler.h"
int compare(void *a, void *b);
int check_passcode(int **passcode, int len, int **logins, int n);
int main(int argc, char **argv)
{
int i, j, keylog, len = 4, found = 0, digits[10] = {0}, passcode_digits[10] = {0}, **passcode, **logins;
char line[5];
FILE *fp;
double elapsed;
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
if((fp = fopen("keylog.txt", "r")) == NULL)
{
fprintf(stderr, "Error while opening file %s\n", "keylog.txt");
return 1;
}
if((logins = (int **)malloc(50*sizeof(int*))) == NULL)
{
fprintf(stderr, "Error while allocating memory\n");
return 1;
}
for(i = 0; i < 50; i++)
{
if((logins[i] = (int *)malloc(3*sizeof(int))) == NULL)
{
fprintf(stderr, "Error while allocating memory\n");
return 1;
}
}
i = 0;
while(fscanf(fp, "%s", line) != EOF)
{
j = 2;
keylog = atoi(line);
while(keylog > 0)
{
logins[i][j--] = keylog % 10;
/* Check which digits are present in the login attempts.*/
digits[keylog%10]++;
keylog /= 10;
}
i++;
}
fclose(fp);
j = 0;
for(i = 0; i < 10; i++)
{
/* To generate the passcode, only use the digits present in the
* login attempts.*/
if(digits[i] > 0)
{
passcode_digits[j++] = i;
}
}
while(!found)
{
if((passcode = (int **)malloc(len*sizeof(int *))) == NULL)
{
fprintf(stderr, "Error while allocating memory\n");
return 1;
}
for(i = 0; i < len; i++)
{
if((passcode[i] = (int *)malloc(sizeof(int))) == NULL)
{
fprintf(stderr, "Error while allocating memory\n");
return 1;
}
/* For the current length, generate the first passcode with the
* digits in order.*/
*passcode[i] = passcode_digits[i];
}
/* Check if the passcode is compatible with the login attempts.*/
if(check_passcode(passcode, len, logins, 50))
{
found = 1;
break;
}
/* For the given length, check every permutation until the correct
* passcode has been found, or all the permutations have been tried.*/
while(next_permutation((void **)passcode, len, compare) != -1)
{
if(check_passcode(passcode, len, logins, 50))
{
printf("Project Euler, Problem 79\n");
printf("Answer: ");
for(i = 0; i < len; i++)
printf("%d", *passcode[i]);
printf("\n");
found = 1;
break;
}
}
for(i = 0; i < len; i++)
{
free(passcode[i]);
}
free(passcode);
/* If the passcode has not yet been found, try a longer passcode.*/
len++;
}
for(i = 0; i < 50; i++)
{
free(logins[i]);
}
free(logins);
clock_gettime(CLOCK_MONOTONIC, &end);
elapsed = (end.tv_sec - start.tv_sec) + (double)(end.tv_nsec - start.tv_nsec) / 1000000000;
printf("Elapsed time: %.9lf seconds\n", elapsed);
return 0;
}
int compare(void *a, void *b)
{
int *n1, *n2;
n1 = (int *)a;
n2 = (int *)b;
return *n1 - *n2;
}
int check_passcode(int **passcode, int len, int **logins, int n)
{
int i, j, k;
/* For every login attempt, check if all the digits appear in the
* passcode in the correct order. Return 0 if a login attempt
* incompatible with the current passcode is found.*/
for(i = 0; i < n; i++)
{
k = 0;
for(j = 0; j < len; j++)
{
if(*passcode[j] == logins[i][k])
{
k++;
if(k == 3)
{
break;
}
}
}
if(k < 3)
{
return 0;
}
}
return 1;
}