/* -------------------------------------------------------------
* Project Euler, Problem #22 (less functions)
*
* Using names.txt (http://p...content-available-to-author-only...r.net/project/names.txt), a 46K text file
* containing over five-thousand first names, begin by sorting it into alphabetical
* order. Then working out the alphabetical value for each name, multiply this value
* by its alphabetical position in the list to obtain a name score.
*
* For example, when the list is sorted into alphabetical order, COLIN, which is
* worth 3 + 15 + 12 + 9 + 14 = 53, is the 938th name in the list. So, COLIN would
* obtain a score of 938 ? 53 = 49714.
*
* What is the total of all the name scores in the file?
*
* Solution by: migf1
* File contents are split and loaded into an ascending-ordered singly linked list
* in one pass. A second pass traverses the list to calculate the total score.
* -------------------------------------------------------------
*/
#include <stdio.h>
#include <string.h> // for strncpy(), for strncmp()
#include <stdlib.h> // for calloc(), free()
#define MAX_WLEN 25+1 // max length for any word + '\0'
#define LETTVAL(c) ((c)-'A'+1) // letter value
typedef enum { FALSE=0, TRUE } Bool; // our own boolean type
typedef struct node {
char word[ MAX_WLEN ]; // the word
struct node *next; // pointer to next node
} Node;
typedef struct {
Node *head, *tail; // pointers to 1st & last nodes
} List;
// ------------------------------------------------------------------------------------
// insert data into list in ascending order
//
Bool list_insert( List *list, char *data )
{
Node *curr = NULL, *prev = NULL;
Node
*new
= calloc( 1, sizeof(struct node
) ); if ( !new || !data ) // calloc failed or invalid data
return FALSE;
strncpy(new
->word
, data
, MAX_WLEN
-1); // initialize newly created node new->next = NULL; // ...
if ( list->head == NULL) { // list was empty, direct insertion
list->head = list->tail = new;
return TRUE;
}
// direct append
if ( strncmp(data
, list
->tail
->word
, MAX_WLEN
) >= 0 ) { list->tail->next = new;
list->tail = new;
return TRUE;
}
curr = prev = list->head; // general case (traversing)
// find spot for the insertion
while ( curr
&& strncmp(curr
->word
, data
, MAX_WLEN
) <= 0 ) { // use <= data, to allow duplicates
prev = curr; // save previous node
curr = curr->next;
}
// insert BEFORE 1st node
if ( list->head == curr /* && curr->data != data */ ) {
list->head = new;
new->next = curr;
return TRUE;
}
// insert AFTER 1st node
if ( !curr
|| strncmp(curr
->word
, data
, MAX_WLEN
) != 0 ) { prev->next = new;
new->next = curr;
}
return TRUE;
}
// ------------------------------------------------------------------------------------
void list_destroy( List *list )
{
Node *dummy = NULL;
while ( list->head ) {
dummy = list->head->next;
list->head = dummy;
}
return;
}
// ------------------------------------------------------------------------------------
int main( void )
{
List list = {.head=NULL, .tail=NULL}; // linked list for sorted words
// read file contents into a singly linked list in ascending order
FILE
*fp
= fopen("names.txt", "r"); if ( !fp ) // fopen error
while ( !feof(fp
) ) // read file-contents loop {
char s[ MAX_WLEN ] = {0}; // important! filling up with 0s
fscanf (fp
, "\"%[A-Z]\",", s
); // get clean uppercase words only if ( !list_insert( &list, s) ) { // if insertion to list fails
list_destroy( &list ); // cleanup
exit( EXIT_FAILURE
); // abnormal exit }
}
// traverse the sorted list & calc the total score, then destroy the list
Node *dummy = NULL; // temp for traversing the list
long int i, score = 0; // node counter & total score
for (i=0, dummy = list.head; dummy; i++ ) // traverse the sorted list
{ // ...
int wscore = 0; // temp for individual word score
char *cp = dummy->word; // calc score of individual word
while ( *cp ) // ...
wscore += LETTVAL( *cp++ ); // ...
score += (i+1) * wscore; // update total score
dummy = dummy->next; // move to next node
}
list_destroy( &list ); // free mem reserved for list
// output the total score
}