Skip to main content Skip to local navigation

Automated Student Evaluations in C (Part 1)

Automated Student Evaluations in C (Part 1)

As with the Matlab and Java versions of this, I'm working on creating a set of C language exercises that can be assigned to students in an automated way, like on eClass (Moodle) using Virtual Programming Lab or the "lab test mode" that we use in the EECS department at York University. Here, I'm focusing on providing a flowchart to students and asking them to replicate it.

This page describes a proof of concept in which a professor or TA could set up flow chart examples for students to implement. This was developed on Windows using the JetBrains CLion IDE, targetting C11 using cmake (internally). As with the Java and Matlab versions of this, the flowcharts are in ASCII art and come from the Diagon project.

I use the Unity unit-testing framework for this. I posted a simple version here: VPL: Simple C Assignment with Unit Testing.

The following files are required:

  • main.c (put in the source directory)
  • StudentSolution.c and .h (put in the source directory)
  • TeacherReferenceSolutions.c and .h (put in the source directory)
  • diagon_flowcharts.txt (put in the compile directory)
  • The unit test framework from GitHub: unity.c, unity.h, as well as unity_internals.h (all put in the source directory)

Here is main.c:

#include <stdio.h>
#include "TeacherReferenceSolutions.h"
#include "StudentSolution.h"
#include "unity.h"
#include "unity_internals.h"
#include <stdlib.h>
#include <wchar.h>   // for printing characters to console...
#include <windows.h> // for printing characters to console when in CLion / Windows.  Don't need in VPL / Linux.
#include <stdint.h>
#include <time.h> // for randomization.

/* references:
 * file location: https://stackoverflow.com/questions/31495311/clion-c-cant-read-open-txt-file-in-project-directory
 * add these two b/c https://github.com/ThrowTheSwitch/Unity/blob/master/docs/UnityGettingStartedGuide.md
 * and https://stackoverflow.com/questions/61114392/linker-error-with-unity-c-unit-testing-framework
 * eclass example: https://eclass.yorku.ca/mod/vpl/forms/executionfiles.php?id=876284

 *
 * */
/* Manual testing.
 * Test solution 1 (index 0)  -> 6 Test passed.  Flowchart: good (one). 2: ?  3: fail (good)  4: fail (good)
 * Test solution 2 (index 1)  -> 6 Tests passed  Flowchart: good (two). 1: failed (good) 2: passed. (good)  3: failed (good) 4: fail (good)
 * Test solution 3 (index 2). -> 6 tests passed  Flowchart: good (three). 1: failed (good) 2: failed (good) 3: passed (good) 4: fail (good)
 * Test solution 4 (index 3). -> 6 tests passed Flowchart: good (four). 1: failed (good) 2: failed (good) 3: failed (good) 4: passed (good)
 */


// Define the flowchart file name.
#define FLOWCHARTFILE "diagon_flowcharts.txt" // needs to be in the directory where compiling happens.
// define the six pairs of inputs for the six unit tests.
#define TEST1_INPUT1 5                      // Unit test 1 inputs
#define TEST1_INPUT2 4
#define TEST2_INPUT1 7                      // Unit test 2 inputs
#define TEST2_INPUT2 9
#define TEST3_INPUT1 100                    // Unit test 3 inputs
#define TEST3_INPUT2 52
#define TEST4_INPUT1 1                      // Unit test 4 inputs
#define TEST4_INPUT2 2
#define TEST5_INPUT1 67                     // Unit test 5 inputs
#define TEST5_INPUT2 23
#define TEST6_INPUT1 1231                   // Unit test 6 inputs
#define TEST6_INPUT2 10231

/* In a global context,
 * Define a function pointer type for functions that take two int32_t and return an int32_t
 * */
typedef int32_t (*referenceSolutions)(int32_t, int32_t);
// Add more functions in the array if there are more... and align with TeacherReferenceSoloutions.c and diagon_flowcharts.txt
referenceSolutions teacherFunctions_g[] = {function1, function2, function3, function4};
int32_t teacherFuncIndex_g = 0;


void setUp(void){
    // set stuff up here
}

void tearDown(void){
    // clean stuff up here.
}


/* Define Test 1: test the student function against the teacher function for inputs 5 and 3. */
void test1(){
    // The specific test types are found in unity.h
    TEST_ASSERT_EQUAL_INT32(teacherFunctions_g[teacherFuncIndex_g](TEST1_INPUT1,TEST1_INPUT2), studentMethod(TEST1_INPUT1,TEST1_INPUT2));     // expected out vs. input
}

/* Define Test 2: test the student function against the teacher function for inputs 7 and 9. */
void test2(){
    // The specific test types are found in unity.h
    TEST_ASSERT_EQUAL_INT32(teacherFunctions_g[teacherFuncIndex_g](TEST2_INPUT1,TEST2_INPUT2), studentMethod(TEST2_INPUT1,TEST2_INPUT2));     // expected out vs. input
}

/* Define Test 3: test the student function against the teacher function for inputs 100 and 52. */
void test3(){
    // The specific test types are found in unity.h
    TEST_ASSERT_EQUAL_INT32(teacherFunctions_g[teacherFuncIndex_g](TEST3_INPUT1,TEST3_INPUT2), studentMethod(TEST3_INPUT1,TEST3_INPUT2));     // expected out vs. input
}

/* Define Test 4: test the student function against the teacher function for inputs 1 and 2. */
void test4(){
    // The specific test types are found in unity.h
    TEST_ASSERT_EQUAL_INT32(teacherFunctions_g[teacherFuncIndex_g](TEST4_INPUT1,TEST4_INPUT2), studentMethod(TEST4_INPUT1,TEST4_INPUT2));     // expected out vs. input
}

/* Define Test 5: test the student function against the teacher function for inputs 67 and 23. */
void test5(){
    // The specific test types are found in unity.h
    TEST_ASSERT_EQUAL_INT32(teacherFunctions_g[teacherFuncIndex_g](TEST5_INPUT1,TEST5_INPUT2), studentMethod(TEST5_INPUT1,TEST5_INPUT2));     // expected out vs. input
}

/* Define Test 6: test the student function against the teacher function for inputs 1231 and 10231. */
void test6(){
    // The specific test types are found in unity.h
    TEST_ASSERT_EQUAL_INT32(teacherFunctions_g[teacherFuncIndex_g](TEST6_INPUT1,TEST6_INPUT2), studentMethod(TEST6_INPUT1,TEST6_INPUT2));     // expected out vs. input
}



// Run the tests...
int main(void)
{

    char *search_start_string = "% ----- start ----";
    char *search_end_string = "% ----- end ----";
    int32_t  numberOfStarts = 0;
    int32_t  numberOfEnds = 0;
    int32_t indexSpecificFlowchart = 0;
    int32_t indexFlowchartTemp = 0;
    int32_t specificFlowchartStart = 0;
    int32_t specificFlowchartEnd = 0;
    int32_t indexDrawFlowchart = 0;


  /* to-do: need to implement a method for choosing the flowchart and showing it to the student.
   *
   * -> scan the diagon_flowcharts file to figure out the number of flowcharts
   * -> choose the index for the reference solution here.  Base it on the size of the teacher solution array.
   * -> print the flowchart here.
   * */


    /* Set console code page to UTF-8 so console knows how to  UTF8 string data
     * and Enable buffering to prevent VS from chopping up UTF-8 byte sequences
     * From //https://stackoverflow.com/questions/45575863/how-to-print-utf-8-strings-to-stdcout-on-windows */
    // Set console code page to UTF-8 so console known how to interpret string data
    SetConsoleOutputCP(CP_UTF8);

    // Enable buffering to prevent VS from chopping up UTF-8 byte sequences
    setvbuf(stdout, NULL, _IOFBF, 1000);

    // load flowchart from the directory in which the compiling happens.
    FILE *file = fopen(FLOWCHARTFILE, "r");
    if (file == NULL) {
        printf("Error opening file\n");
        return 1;
    }

    fseek(file, 0, SEEK_END);
    long length = ftell(file);
    fseek(file, 0, SEEK_SET);

    char *buffer = malloc(length + 1);
    if (buffer == NULL) {
        printf("Error allocating memory\n");
        fclose(file);
        return 1;
    }


    fread(buffer, 1, length, file);
    buffer[length] = '\0';

    //printf("%s\n", buffer);

    // Find all the "starts" in the flowchart file.
    char *ptr1 = strstr(buffer, search_start_string);
    while ((ptr1 = strstr(ptr1, search_start_string)) != NULL) {
        int index = ptr1 - buffer;
        //printf("Index of '%s': %d\n", search_start_string, index);
        ptr1 += strlen(search_start_string);
        numberOfStarts = numberOfStarts +1;
    }

    // Find all the "ends" in the flowchart file.
    char *ptr2 = strstr(buffer, search_end_string);
    while ((ptr2 = strstr(ptr2, search_end_string)) != NULL) {
        int index = ptr2 - buffer;
        //printf("Index of '%s': %d\n", search_end_string, index);
        ptr2 += strlen(search_end_string);
        numberOfEnds = numberOfEnds +1;

    }


    if( numberOfStarts != numberOfEnds){
        printf("****** Warning! Flowchart file is wrong.  Mismatch in start vs. end. *******");
    }

    // Choose a pseudo-random teacher solution. (not very random)
    srand(time(NULL)); // Seed the randomization.
    indexSpecificFlowchart = ((int32_t) rand()) % numberOfStarts + 1; // Returns a pseudo-random integer between 1 and 100.
    teacherFuncIndex_g = indexSpecificFlowchart - 1;
    //printf("Choosing Flowchart number %d @ index %d ", indexSpecificFlowchart, teacherFuncIndex_g);

    // Find the specific start for the specific desired flowchart.
    char *ptr3 = strstr(buffer, search_start_string);
    while ((ptr3 = strstr(ptr3, search_start_string)) != NULL) {
        int index = ptr3 - buffer;
        ptr3 += strlen(search_start_string);
        indexFlowchartTemp = indexFlowchartTemp + 1;
        if (indexFlowchartTemp == indexSpecificFlowchart)
        {
            // Note: actual start needs to include the length of the start string.
            specificFlowchartStart = index + strlen(search_start_string);
            //printf("found the  START of flowchart  at %d\n", specificFlowchartStart);
        }
    }


    // Find the specific end for the specific desired flowchart.
    char *ptr4 = strstr(buffer, search_end_string);
    indexFlowchartTemp = 0; // reset it.
    while ((ptr4 = strstr(ptr4, search_end_string)) != NULL) {
        int index = ptr4 - buffer;
        ptr4 += strlen(search_end_string);
        indexFlowchartTemp = indexFlowchartTemp + 1;
        if (indexFlowchartTemp == indexSpecificFlowchart)
        {
            // Note: actual end begins BEFORE the end string.
            specificFlowchartEnd = index;
            //printf("found the END of flowchart  at %d\n", specificFlowchartEnd);
        }
    }


    printf("Dear student: implement this flowchart:\n ");
   for (int i = specificFlowchartStart; i < specificFlowchartEnd; i++) {
        printf("%c", buffer[i]);
    }

    printf("------------ end of flowchart ------------\n");
    printf("\n");
    /* run the unit tests */

    printf("Multiple tests underway...");
    UNITY_BEGIN();
    RUN_TEST(test1);
    RUN_TEST(test2);
    RUN_TEST(test3);
    RUN_TEST(test4);
    RUN_TEST(test5);
    RUN_TEST(test6);
    /* add more RUN_TEST()s, as needed */


    // wrap up and close down the file.

    free(buffer);
    fclose(file);

    // wrap up the unity testing...
    return UNITY_END();
}

Here is StudentSolution.h:

#ifndef EXAMPLEFLOWCHART1_STUDENTSOLUTION_H
#define EXAMPLEFLOWCHART1_STUDENTSOLUTION_H
#include <stdint.h>

int32_t studentMethod(int32_t s, int32_t t);
#endif //EXAMPLEFLOWCHART1_STUDENTSOLUTION_H

Here is an example StudentSolution.c:

#include "StudentSolution.h"
#include <stdint.h>

/* solution 4*/

int studentMethod(int s, int t) {
    int a = 0;

    int i = 1;

    while(i < 2)
    {

        if(s == 10){

            if(t > 5)
            {
                a = a+0;
            }
            else if(t > 2)
            {
                a = a+2;
            }
            else
            {
                a = a+4;
            }
        }
        else{

            if(t > 5)
            {
                a = a+32;
            }
            else if(t > 1)
            {
                a = a+20;
            }
            else
            {
                a = a+11;
            }

        }


        i = i+1;

    }

    return a;    }

And it's going to be compared against the TeacherReferenceSolutions.c:

#include "TeacherReferenceSolutions.h"

int function1(int s, int t) {

/* s and t are inputs */
    int a = 0;

    int i = 1;


    while (i < 10) {

        if (s >= 0) {

            if (t < 10) {
                a = a + 2;
            } else if (t < 20) {
                a = a + 4;
            } else {
                a = a + 5;
            }
        } else {

            if (t < 5) {
                a = a + 3;
            } else if (t < 10) {
                a = a + 2;
            } else {
                a = a + 1;
            }

        }


        i = i + 1;

    }


    return a;
}

int function2(int s, int t) {

/* s and t are inputs */

    int a = 0;

    int i = 1;

    while(i < 5)
    {

        if(s <= 10){

            if(t < 5)
            {
                a = a+1;
            }
            else if(t < 10)
            {
                a = a+5;
            }
            else
            {
                a = a+2;
            }
        }
        else{

            if(t > 20)
            {
                a = a+32;
            }
            else if(t > 13)
            {
                a = a+20;
            }
            else
            {
                a = a+11;
            }

        }


        i = i+1;

    }

    return a;    }

int function3(int s, int t) {
    int a = 0;

    int i = 1;

    while(i < 10)
    {

        if(s <= 5){

            if(t < 2)
            {
                a = a+3;
            }
            else if(t < 8)
            {
                a = a+2;
            }
            else
            {
                a = a+1;
            }
        }
        else{

            if(t > 10)
            {
                a = a+3;
            }
            else if(t > 5)
            {
                a = a+2;
            }
            else
            {
                a = a+1;
            }

        }


        i = i+1;

    }



    return a;
}

int function4(int s, int t) {
/* s and t are inputs */
    int a = 0;

    int i = 1;

    while(i < 2)
    {

        if(s == 10){

            if(t > 5)
            {
                a = a+0;
            }
            else if(t > 2)
            {
                a = a+2;
            }
            else
            {
                a = a+4;
            }
        }
        else{

            if(t > 5)
            {
                a = a+32;
            }
            else if(t > 1)
            {
                a = a+20;
            }
            else
            {
                a = a+11;
            }

        }


        i = i+1;

    }

    return a;    }



Which only has four solutions, but it could be more. There is a corresponding header file, TeacherReferenceSolutions.h:

#ifndef EXAMPLEFLOWCHART1_TEACHERREFERENCESOLUTIONS_H
#define EXAMPLEFLOWCHART1_TEACHERREFERENCESOLUTIONS_H

#include <stdint.h>

int function1(int s, int t);
int function2(int s, int t);
int function3(int s, int t);
int function4(int s, int t);

// Define a function pointer type for functions that take two ints and return an int
typedef int32_t (*referenceSolutions)(int32_t, int32_t);

#endif //EXAMPLEFLOWCHART1_TEACHERREFERENCESOLUTIONS_H

The four reference solutions are represented in the following flowchart file, diagon_flowcharts.txt:

% diagon_flowcharts.txt
% first
% ----- start ----
       ┌─────┐
       │START│
       └──┬──┘
       ┌──▽──┐
       │a = 0│
       └──┬──┘
        ┌─▽─┐
        │i=1│
        └─┬─┘
          ▽___
         ╱    ╲
    ____╱ i<10 ╲____________________________________________________
   │ no ╲      ╱                                                    │
   │     ╲____╱                                                     │
   │       │yes                                                     │
   │     __▽___                        ______                       │
   │    ╱      ╲                      ╱      ╲            ┌───────┐ │
   │   ╱ s >= 0 ╲____________________╱ t < 10 ╲___________│a = a+2│ │
   │   ╲        ╱yes                 ╲        ╱yes        └───┬───┘ │
   │    ╲______╱                      ╲______╱                │     │
   │       │no                           │no                  │     │
   │     __▽__                          _▽__                  │     │
   │    ╱     ╲             ┌───────┐  ╱    ╲    ┌───────┐    │     │
   │   ╱ t < 5 ╲____________│a = a+3│ ╱ t<20 ╲___│a = a+4│    │     │
   │   ╲       ╱yes         └───┬───┘ ╲      ╱yes└───┬───┘    │     │
   │    ╲_____╱                 │      ╲____╱        │        │     │
   │       │no                  │        │no         │        │     │
   │      _▽__                  │    ┌───▽───┐       │        │     │
   │     ╱    ╲    ┌───────┐    │    │a = a+5│       │        │     │
   │    ╱ t<10 ╲___│a = a+2│    │    └───┬───┘       │        │     │
   │    ╲      ╱yes└───┬───┘    │        │           │        │     │
   │     ╲____╱        │        │        │           │        │     │
   │       │no         │        │        │           │        │     │
   │   ┌───▽───┐       │        │        │           │        │     │
   │   │a = a+1│       │        │        │           │        │     │
   │   └───┬───┘       │        │        │           │        │     │
   │       └───────────┴─┬──────┴────────┴───────────┴────────┘     │
   │                 ┌───▽───┐                                      │
   │                 │i = i+1│                                      │
   │                 └───┬───┘                                      │
   │                     └──────────────────────────────────────────┘
 ┌─▽─┐
 │END│
 └───┘
% ----- end ----
% second
% ----- start ----
       ┌─────┐
       │START│
       └──┬──┘
       ┌──▽──┐
       │a = 0│
       └──┬──┘
       ┌──▽──┐
       │i = 1│
       └──┬──┘
         _▽___
        ╱     ╲
   ____╱ i < 5 ╲________________________________________________________
  │ no ╲       ╱                                                        │
  │     ╲_____╱                                                         │
  │        │yes                                                         │
  │     ___▽___                           _____                         │
  │    ╱       ╲                         ╱     ╲              ┌───────┐ │
  │   ╱ s <= 10 ╲_______________________╱ t < 5 ╲_____________│a = a+1│ │
  │   ╲         ╱yes                    ╲       ╱yes          └───┬───┘ │
  │    ╲_______╱                         ╲_____╱                  │     │
  │        │no                              │no                   │     │
  │      __▽___                           __▽___                  │     │
  │     ╱      ╲              ┌────────┐ ╱      ╲    ┌───────┐    │     │
  │    ╱ t > 20 ╲_____________│a = a+32│╱ t < 10 ╲___│a = a+5│    │     │
  │    ╲        ╱yes          └────┬───┘╲        ╱yes└───┬───┘    │     │
  │     ╲______╱                   │     ╲______╱        │        │     │
  │        │no                     │        │no          │        │     │
  │      __▽___                    │    ┌───▽───┐        │        │     │
  │     ╱      ╲    ┌────────┐     │    │a = a+2│        │        │     │
  │    ╱ t > 13 ╲___│a = a+20│     │    └───┬───┘        │        │     │
  │    ╲        ╱yes└────┬───┘     │        │            │        │     │
  │     ╲______╱         │         │        │            │        │     │
  │        │no           │         │        │            │        │     │
  │   ┌────▽───┐         │         │        │            │        │     │
  │   │a = a+11│         │         │        │            │        │     │
  │   └────┬───┘         │         │        │            │        │     │
  │        └─────────────┴┬────────┴────────┴────────────┴────────┘     │
  │                   ┌───▽───┐                                         │
  │                   │i = i+1│                                         │
  │                   └───┬───┘                                         │
  │                       └─────────────────────────────────────────────┘
┌─▽─┐
│END│
└───┘
% ----- end ----
% third
% ----- start ----
       ┌─────┐
       │START│
       └──┬──┘
       ┌──▽──┐
       │a = 0│
       └──┬──┘
       ┌──▽──┐
       │i = 1│
       └──┬──┘
         _▽____
        ╱      ╲
   ____╱ i < 10 ╲___________________________________________________
  │ no ╲        ╱                                                   │
  │     ╲______╱                                                    │
  │        │yes                                                     │
  │      __▽___                        _____                        │
  │     ╱      ╲                      ╱     ╲             ┌───────┐ │
  │    ╱ s <= 5 ╲____________________╱ t < 2 ╲____________│a = a+3│ │
  │    ╲        ╱yes                 ╲       ╱yes         └───┬───┘ │
  │     ╲______╱                      ╲_____╱                 │     │
  │        │no                           │no                  │     │
  │      __▽___                        __▽__                  │     │
  │     ╱      ╲            ┌───────┐ ╱     ╲    ┌───────┐    │     │
  │    ╱ t > 10 ╲___________│a = a+3│╱ t < 8 ╲___│a = a+2│    │     │
  │    ╲        ╱yes        └───┬───┘╲       ╱yes└───┬───┘    │     │
  │     ╲______╱                │     ╲_____╱        │        │     │
  │        │no                  │        │no         │        │     │
  │      __▽__                  │    ┌───▽───┐       │        │     │
  │     ╱     ╲    ┌───────┐    │    │a = a+1│       │        │     │
  │    ╱ t > 5 ╲___│a = a+2│    │    └───┬───┘       │        │     │
  │    ╲       ╱yes└───┬───┘    │        │           │        │     │
  │     ╲_____╱        │        │        │           │        │     │
  │        │no         │        │        │           │        │     │
  │    ┌───▽───┐       │        │        │           │        │     │
  │    │a = a+1│       │        │        │           │        │     │
  │    └───┬───┘       │        │        │           │        │     │
  │        └───────────┴─┬──────┴────────┴───────────┴────────┘     │
  │                  ┌───▽───┐                                      │
  │                  │i = i+1│                                      │
  │                  └───┬───┘                                      │
  │                      └──────────────────────────────────────────┘
┌─▽─┐
│END│
└───┘
% ----- end ----
% fourth
% ----- start ----
       ┌─────┐
       │START│
       └──┬──┘
       ┌──▽──┐
       │a = 0│
       └──┬──┘
       ┌──▽──┐
       │i = 1│
       └──┬──┘
         _▽___
        ╱     ╲
   ____╱ i < 2 ╲______________________________________________________
  │ no ╲       ╱                                                      │
  │     ╲_____╱                                                       │
  │        │yes                                                       │
  │     ___▽___                          _____                        │
  │    ╱       ╲                        ╱     ╲             ┌───────┐ │
  │   ╱ s == 10 ╲______________________╱ t > 5 ╲____________│a = a+0│ │
  │   ╲         ╱yes                   ╲       ╱yes         └───┬───┘ │
  │    ╲_______╱                        ╲_____╱                 │     │
  │        │no                             │no                  │     │
  │      __▽__                           __▽__                  │     │
  │     ╱     ╲              ┌────────┐ ╱     ╲    ┌───────┐    │     │
  │    ╱ t > 5 ╲_____________│a = a+32│╱ t > 2 ╲___│a = a+2│    │     │
  │    ╲       ╱yes          └────┬───┘╲       ╱yes└───┬───┘    │     │
  │     ╲_____╱                   │     ╲_____╱        │        │     │
  │        │no                    │        │no         │        │     │
  │      __▽__                    │    ┌───▽───┐       │        │     │
  │     ╱     ╲    ┌────────┐     │    │a = a+4│       │        │     │
  │    ╱ t > 1 ╲___│a = a+20│     │    └───┬───┘       │        │     │
  │    ╲       ╱yes└────┬───┘     │        │           │        │     │
  │     ╲_____╱         │         │        │           │        │     │
  │        │no          │         │        │           │        │     │
  │   ┌────▽───┐        │         │        │           │        │     │
  │   │a = a+11│        │         │        │           │        │     │
  │   └────┬───┘        │         │        │           │        │     │
  │        └────────────┴─┬───────┴────────┴───────────┴────────┘     │
  │                   ┌───▽───┐                                       │
  │                   │i = i+1│                                       │
  │                   └───┬───┘                                       │
  │                       └───────────────────────────────────────────┘
┌─▽─┐
│END│
└───┘
% ----- end ----

More to come, including how to put this into Virtual Programming Lab.


James Andrew Smith is a Professional Engineer and Associate Professor in the Electrical Engineering and Computer Science Department of York University's Lassonde School, with degrees in Electrical and Mechanical Engineering from the University of Alberta and McGill University.  Previously a program director in biomedical engineering, his research background spans robotics, locomotion, human birth and engineering education. While on sabbatical in 2018-19 with his wife and kids he lived in Strasbourg, France and he taught at the INSA Strasbourg and Hochschule Karlsruhe and wrote about his personal and professional perspectives.  James is a proponent of using social media to advocate for justice, equity, diversity and inclusion as well as evidence-based applications of research in the public sphere. You can find him on Twitter. Originally from Québec City, he now lives in Toronto, Canada.