0

At my wit's end here. I've seen this question asked before, but I haven't really found an answer.

My speller program compiles perfectly fine in the VS Code workspace. It generates the same results as the staff version of the speller program, albeit slower:

Staff vs Student

It works with both the small and large dictionaries. I have not modified dictionary.h or speller.c. Valgrind returns no errors--there are no memory leaks or segmentation faults.

Furthermore, I have not created additional c files in the directory, nor have I created additional header files. I have only modified dictionary.c.

Nevertheless, when I submit this to check50, it fails the compilation test, saying that make exits with status 2, not 0.

Compilation error

https://submit.cs50.io/check50/64ae09d87178869b75608285cf4bb91067c76dea

My suspicion is that there is a bug in check50 and how it interacts with make and the Makefile. I downloaded the speller.zip directory locally onto my computer and switched the original dictionary.c for my completed dictionary.c. I then installed make on the Windows Subsystem for Linux and attempted to compile speller.c locally. This resulted in make returning an error, saying that clang was not recognized as a command.

WSL error

This may be because there's some issue with WSL that I don't know about, but I thought I'd include it just in case.

Note that, per Namy's answer below, running check50 locally results in success as well.

local check

Anyway, any help on this issue would be very much appreciated. Embedded below is my code for dictionary.c, dictionary.h, and speller.c. (Like I said, I did not modify dictionary.h or speller.c.)

Dictionary.c:

// Implements a dictionary's functionality

#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "dictionary.h"


// Represents a node in a hash table
typedef struct node
{
    char word[LENGTH + 1];
    struct node *next;
}
node;

// Set global dictionary size integer and loaded boolean
int DICTIONARYSIZE = 0;
bool DICTIONARYLOADED = false;

const unsigned int N = 110;

// Hash table
node *table[N];

// Returns true if word is in dictionary, else false
bool check(const char *word)
{
    int wordlength = strlen(word);

    // Convert string to lowercase
    char *standardword = malloc(sizeof(char) * wordlength + sizeof(char));
    if (standardword == NULL)
    {
        free(standardword);
        return false;
    }

    for (int i = 0; i < wordlength + 1; i++)
    {
        standardword[i] = tolower(word[i]);
    }

    // Assign hash index number to word
    int whichhash = hash(standardword);

    bool found = false;

    // Create iterator node, check to see if applicable hash table entry has been initialized. If not, word is not in dictionary.
    if (table[whichhash]->next == NULL)
    {
        found = false;
    }

    else
    {
        node *iterator = table[whichhash]->next;

        // Iterate through linked list, checking for a match
        do
        {

            // Compare word to current node. If found, set found boolean to true and break
            if (strcmp(iterator->word, standardword) == 0)
            {
                found = true;
                break;
            }

            // Else, move on to the next node
            else
            {
                iterator = iterator->next;
            }
        } while (iterator != NULL);
    }

    // If the word was found, return true. Else, return false. Also, free memory.
    free(standardword);
    return found;
}

// Hashes word to a number
unsigned int hash(const char *word)
{
    int wordlength = strlen(word);

    // Calculate additive decimal value of word
    int worddec = 0;
    for (int i = 0; i < wordlength; i++)
    {
        worddec = worddec + word[i];
    }

    // Assign word to hash table linked list number.
    // Hash value here is assigned by placing the word in terms of decimal quantity by 50s.
    int hashvalue = 0;
    for (int i = 0; i < N;)
    {
        if (worddec >= i * 50 && worddec < (i + 1)*50)
        {
            hashvalue = i;
            break;
        }
        else
        {
            i++;
        }
    }

    // return hashvalue
    return hashvalue;

}

// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
    for (int i = 0; i < N; i++)
    {
        table[i] = malloc(sizeof(node));
        table[i]->next = NULL;
    }

    // Open dictionary
    FILE *readdict = fopen(dictionary, "r");
    if (readdict == NULL)
    {
        return false;
    }

    else
    {
        // Create buffer to store largest word
        char *buffer = malloc(sizeof(char) * LENGTH + 1);

        bool success = true;

        // Read all words into memory
        while (fscanf(readdict, "%s", buffer) != EOF)
        {
            node *new_node = malloc(sizeof(node));
            if (new_node == NULL)
            {
                success = false;
            }

            // Assign new node to appropriate linked list in hash table
            else
            {
                strcpy(new_node->word, buffer);
                int whichlinked = hash(new_node->word);

                // Checks to see if this hash index has been initialized
                if (table[whichlinked]->next == NULL)
                {
                    table[whichlinked]->next = new_node;
                    new_node->next = NULL;
                    DICTIONARYSIZE++;
                }
                else
                {
                    new_node->next = table[whichlinked]->next;
                    table[whichlinked]->next = new_node;
                    DICTIONARYSIZE++;
                }
            }

        }

        fclose(readdict);
        free(buffer);

        // Check if memory allocation was successful
        if (success == true)
        {
            DICTIONARYLOADED = true;
            return true;
        }
        else
        {
            return false;
        }
    }
}

// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
    if (DICTIONARYLOADED == true)
    {
        return DICTIONARYSIZE;
    }
    else
    {
        return 0;
    }
}

// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
    bool success = false;
    for (int i = 0; i < N;)
    {
        if (table[i] == NULL)
        {
            i++;
        }

        else
        {
            node *iterator = table[i];
            node *tmp = iterator;
            while (iterator != NULL)
            {
                tmp = iterator;
                iterator = iterator->next;
                free(tmp);
                success = true;
            }
            i++;
        }
    }

    return success;
}

Dictionary.h:

// Declares a dictionary's functionality

#ifndef DICTIONARY_H
#define DICTIONARY_H

#include <stdbool.h>

// Maximum length for a word
// (e.g., pneumonoultramicroscopicsilicovolcanoconiosis)
#define LENGTH 45

// Prototypes
bool check(const char *word);
unsigned int hash(const char *word);
bool load(const char *dictionary);
unsigned int size(void);
bool unload(void);

#endif // DICTIONARY_H

And speller.c:

// Implements a spell-checker

#include <ctype.h>
#include <stdio.h>
#include <sys/resource.h>
#include <sys/time.h>

#include "dictionary.h"

// Undefine any definitions
#undef calculate
#undef getrusage

// Default dictionary
#define DICTIONARY "dictionaries/large"

// Prototype
double calculate(const struct rusage *b, const struct rusage *a);

int main(int argc, char *argv[])
{
    // Check for correct number of args
    if (argc != 2 && argc != 3)
    {
        printf("Usage: ./speller [DICTIONARY] text\n");
        return 1;
    }

    // Structures for timing data
    struct rusage before, after;

    // Benchmarks
    double time_load = 0.0, time_check = 0.0, time_size = 0.0, time_unload = 0.0;

    // Determine dictionary to use
    char *dictionary = (argc == 3) ? argv[1] : DICTIONARY;

    // Load dictionary
    getrusage(RUSAGE_SELF, &before);
    bool loaded = load(dictionary);
    getrusage(RUSAGE_SELF, &after);

    // Exit if dictionary not loaded
    if (!loaded)
    {
        printf("Could not load %s.\n", dictionary);
        return 1;
    }

    // Calculate time to load dictionary
    time_load = calculate(&before, &after);

    // Try to open text
    char *text = (argc == 3) ? argv[2] : argv[1];
    FILE *file = fopen(text, "r");
    if (file == NULL)
    {
        printf("Could not open %s.\n", text);
        unload();
        return 1;
    }

    // Prepare to report misspellings
    printf("\nMISSPELLED WORDS\n\n");

    // Prepare to spell-check
    int index = 0, misspellings = 0, words = 0;
    char word[LENGTH + 1];

    // Spell-check each word in text
    char c;
    while (fread(&c, sizeof(char), 1, file))
    {
        // Allow only alphabetical characters and apostrophes
        if (isalpha(c) || (c == '\'' && index > 0))
        {
            // Append character to word
            word[index] = c;
            index++;

            // Ignore alphabetical strings too long to be words
            if (index > LENGTH)
            {
                // Consume remainder of alphabetical string
                while (fread(&c, sizeof(char), 1, file) && isalpha(c));

                // Prepare for new word
                index = 0;
            }
        }

        // Ignore words with numbers (like MS Word can)
        else if (isdigit(c))
        {
            // Consume remainder of alphanumeric string
            while (fread(&c, sizeof(char), 1, file) && isalnum(c));

            // Prepare for new word
            index = 0;
        }

        // We must have found a whole word
        else if (index > 0)
        {
            // Terminate current word
            word[index] = '\0';

            // Update counter
            words++;

            // Check word's spelling
            getrusage(RUSAGE_SELF, &before);
            bool misspelled = !check(word);
            getrusage(RUSAGE_SELF, &after);

            // Update benchmark
            time_check += calculate(&before, &after);

            // Print word if misspelled
            if (misspelled)
            {
                printf("%s\n", word);
                misspellings++;
            }

            // Prepare for next word
            index = 0;
        }
    }

    // Check whether there was an error
    if (ferror(file))
    {
        fclose(file);
        printf("Error reading %s.\n", text);
        unload();
        return 1;
    }

    // Close text
    fclose(file);

    // Determine dictionary's size
    getrusage(RUSAGE_SELF, &before);
    unsigned int n = size();
    getrusage(RUSAGE_SELF, &after);

    // Calculate time to determine dictionary's size
    time_size = calculate(&before, &after);

    // Unload dictionary
    getrusage(RUSAGE_SELF, &before);
    bool unloaded = unload();
    getrusage(RUSAGE_SELF, &after);

    // Abort if dictionary not unloaded
    if (!unloaded)
    {
        printf("Could not unload %s.\n", dictionary);
        return 1;
    }

    // Calculate time to unload dictionary
    time_unload = calculate(&before, &after);

    // Report benchmarks
    printf("\nWORDS MISSPELLED:     %d\n", misspellings);
    printf("WORDS IN DICTIONARY:  %d\n", n);
    printf("WORDS IN TEXT:        %d\n", words);
    printf("TIME IN load:         %.2f\n", time_load);
    printf("TIME IN check:        %.2f\n", time_check);
    printf("TIME IN size:         %.2f\n", time_size);
    printf("TIME IN unload:       %.2f\n", time_unload);
    printf("TIME IN TOTAL:        %.2f\n\n",
           time_load + time_check + time_size + time_unload);

    // Success
    return 0;
}

// Returns number of seconds between b and a
double calculate(const struct rusage *b, const struct rusage *a)
{
    if (b == NULL || a == NULL)
    {
        return 0.0;
    }
    else
    {
        return ((((a->ru_utime.tv_sec * 1000000 + a->ru_utime.tv_usec) -
                  (b->ru_utime.tv_sec * 1000000 + b->ru_utime.tv_usec)) +
                 ((a->ru_stime.tv_sec * 1000000 + a->ru_stime.tv_usec) -
                  (b->ru_stime.tv_sec * 1000000 + b->ru_stime.tv_usec)))
                / 1000000.0);
    }
}

4 Answers 4

2

This is one of those rare circumstances where check50 actually is broken. Seems that a bug was introduced with the latest update. It's being rolled back as I type, and may well be working again as you read this.

Try it again. If it still isn't working, wait a while and try again later or tomorrow.

[edit] It should be working now.

2
  • Thanks so much @Cliff B. Really appreciate it. I'll give it another shot. Commented Jun 16, 2022 at 17:50
  • Resolved. Thanks so much. Commented Jun 16, 2022 at 19:00
1

been getting the same error since I started optimizing my solution today. Was working fine till today. Think this is a bug in check50.

2
  • Interesting. Thanks so much for posting, good to know I'm not the only one. Commented Jun 15, 2022 at 22:26
  • no problem. running check50 cs50/problems/2022/x/speller --local should work for now.
    – Namy
    Commented Jun 15, 2022 at 22:57
0

I had the same issue and I think that this is a a bug on CS50's end. check50 seems to work 1/10 of the time. check50 worked the first time I ran it on speller but it stopped working after that.

1
  • thank you my friend. appreciate it. Commented Jun 16, 2022 at 17:51
0

Had the exact same issue, thanks for creating this.

This check50 link works just fine: ie: check50 cs50/problems/2022/x/speller

but the link from the spring online course doesnt: ie: check50 cs50/problems/2022/spring/speller

Went mad trying to find the solution. redownloaded the entire get SPELLER.zip and repasted my dictionary.c work into it just to make sure i hadn't accidentally messed with speller.c or any of the reference txt files. eventually found this thread. i'm just glad it's a check50 bug and i'm not crazy.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .