andy0130tw
5/29/2017 - 5:13 PM

Sample code to IOTM (brainfuck-like language), NTU DSA 2017, Group 42.

Sample code to IOTM (brainfuck-like language), NTU DSA 2017, Group 42.

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "brainfuck.h"

struct probdesc {
    int tick_limit;
    int length;
    char** list_in;
    char** list_out;
};

#undef BF_LOG
#define BF_LOG(format, ...) \
        fprintf(stderr, "%s: " format, __func__,## __VA_ARGS__)

char* getsamp(size_t* szptr) {
    char* line = NULL;
    ssize_t len = getline(&line, szptr, stdin);
    while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) {
        len--;
        line[len] = '\0';
    }
    return line;
}

int main() {
    int n;
    scanf("%d", &n);

    struct probdesc pdes[n];

    for (int i = 0; i < n; i++) {
        int sampcnt, tlim;
        scanf("%d%d", &sampcnt, &tlim);
        size_t sz = sampcnt * sizeof(char*);
        pdes[i] = (struct probdesc) {
            .tick_limit = tlim,
            .length = sampcnt,
            .list_in = (char**) malloc(sz),
            .list_out = (char**) malloc(sz),
        };

        while (getchar() != '\n'); // bogus newline

        for (int j = 0; j < sampcnt; j++) {
            size_t linesz;
            linesz = 0, pdes[i].list_in[j]  = getsamp(&linesz);
            // printf("get sample in [%s]\n", pdes[i].list_in[j]);
            linesz = 0, pdes[i].list_out[j] = getsamp(&linesz);
            // printf("get sample out [%s]\n", pdes[i].list_out[j]);
        }
    }

    int s;
    scanf("%d", &s);
    for (int i = 0; i < s; i++) {
        int pid;
        char code[CODE_LEN_LIMIT];

        int correct_count = 0;
        int wtf_count = 0;
        int tle_count = 0;

        scanf("%d%s", &pid, code);

        char* code_bf = bfexpand(code);
        if (!code_bf) {
            printf("IE 0\n");
        } else {
            int syntax_error = 0;

            for (int tid = 0, tlen = pdes[pid].length; tid < tlen; tid++) {
                size_t tick_limit = pdes[pid].tick_limit;
                size_t outsz = 4096;
                char* out = (char*) malloc(outsz);

                int ret = brainfuck(code_bf, pdes[pid].list_in[tid], &out, &outsz, &tick_limit);
                if (ret < 0) {
                    syntax_error = 1;
                    break;
                } else if (ret == 0) {
                    if (strcmp(out, pdes[pid].list_out[tid]) == 0) {
                        correct_count++;
                    } else {
                        wtf_count++;
                    }
                } else if (ret == 1) {
                    wtf_count++;
                } else if (ret == 2) {
                    tle_count++;
                }
                free(out);
            }

            if (syntax_error) {
                printf("IE 0\n");
                // skip all the tests
                continue;
            }

            // verdict
            const char* verdict = "AC";
            if (correct_count < pdes[pid].length || wtf_count > 0) {
                verdict = "WTF";
            }
            if (tle_count > 0) {
                verdict = "TLE";
            }

            // rounding
            int score = correct_count * 1000 / pdes[pid].length;
            if (score % 10 >= 5) score += 10 - score % 10;
            score /= 10;

            printf("%s %d\n", verdict, score);
        }
    }
}
#ifndef BRAINFUCK_H_
#define BRAINFUCK_H_

#define MEMORY_SIZE 4096
#define CODE_LEN_LIMIT 65536

char* bfexpand(char*);
int brainfuck(char* c, const char* i, char** o, size_t* osize, size_t* tick);

#endif  // BRAINFUCK_H_
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include "brainfuck.h"

#ifdef BF_DEBUG
#define BF_LOG(format, ...) \
        fprintf(stderr, "%s: " format, __func__,## __VA_ARGS__)
#else  // BF_DEBUG
#define BF_LOG(...)
#endif  // BF_DEBUG

// expand our syntax sugar (similar to run-length encoding) to traditional
// brainfuck program; return NULL if the code exceeds the limit
//
// in order to know how long it will be, the expansion will be done in two
// passes: decide the length the first pass, allocate a space to be filled,
// and fill it in the second pass.
char* bfexpand(char* code) {
    char* p = code;
    int count = 0;
    // first pass
    int actlen = 0;
    while (*p) {
        if (isdigit(*p)) {
            if (count == 0 && *p == '0') {
                BF_LOG("expected positive integer, got 0\n");
                return NULL;
            }
            count = count * 10 + *p - '0';
        } else {
            if (count != 0) {
                actlen += count;
                count = 0;
            } else actlen++;
        }
        p++;
    }

    // reject
    if (count != 0) {
        BF_LOG("stray number after code\n");
        return NULL;
    }
    BF_LOG("expanded len = %d\n", actlen);

    if (actlen > CODE_LEN_LIMIT) {
        BF_LOG("code too long\n");
        return NULL;
    }

    // second pass
    p = code;
    char* ret = (char*) malloc(actlen + 1);
    int filled = 0;
    while (*p) {
        if (isdigit(*p)) {
            count = count * 10 + *p - '0';
        } else {
            if (count != 0) {
                while (--count) {
                    ret[filled++] = *p;
                }
            }
            ret[filled++] = *p;
        }
        p++;
    }
    ret[actlen] = '\0';
    return ret;
}

// run on some brainfuck code
// return  0 if success,
//        -1 if interpret error,
//         1 if runtime error,
//         2 if tick limit exceeded
int brainfuck(char* code, const char* input, char** output,
              size_t* output_sz, size_t* tick_lim) {
    char memory[MEMORY_SIZE] = {};
    int memptr = 0;
    int stack[32];  // XXX: what is the stack size?
    int stsize = 0;
    int jmptable[CODE_LEN_LIMIT];  // XXX: can be improved by using map

    size_t tick_cnt = 0;
    int do_tick_check = tick_lim && *tick_lim > 0;

    // syntax checking; build jump table
    int pos = 0;
    while (code[pos]) {
        if (code[pos] == '[') {
            stack[stsize++] = pos;
        } else if (code[pos] == ']') {
            if (stsize == 0) {
                // stack underflow owo
                BF_LOG("unexpected ] at position %d\n", pos + 1);
                return -1;
            }
            // fill it to jump table; bi-directional
            stsize--;
            jmptable[pos] = stack[stsize];
            jmptable[stack[stsize]] = pos;
        } else if (strchr(".,<>+-", code[pos]) == NULL) {
            // the character is not in allowing list of characters
            BF_LOG("unallowed character '%c' at position %d\n",
                code[pos], pos + 1);
            return -1;
        }
        pos++;
    }

    if (stsize > 0) {
        BF_LOG("expected ] at end\n");
        return -1;
    }

    // syntax checked; start execution
    int len = pos;
    size_t output_len = 0;
    pos = 0;
    while (pos != len) {
        char c = code[pos];
        switch (c) {
        case '<':  // move left
            if (memptr == 0) {
                BF_LOG("attempting to move left at the left end of memory"
                        " at position %d\n", pos + 1);
                return 1;
            }
            memptr--;
            break;
        case '>':  // move right
            if (memptr == MEMORY_SIZE - 1) {
                BF_LOG("attempting to move right at the right end of memory"
                        " at position %d\n", pos + 1);
                return 1;
            }
            memptr++;
            break;
        case '+':  // increment
            memory[memptr]++; tick_cnt++; break;
        case '-':  // decrement
            memory[memptr]--; tick_cnt++; break;
        case ',':  // read from input; no-op if exhausted
            if (*input) {
                memory[memptr] = *input;
                input++;
            }
            tick_cnt++; break;
        case '.':  // write to output
            if (memory[memptr] == 0) {
                BF_LOG("attempting to print zero character to output"
                        " at position %d\n", pos + 1);
                return 1;
            }
            (*output)[output_len++] = memory[memptr];
            // if output is longer than expected, the space is relocated
            if (output_len == *output_sz) {
                *output_sz *= 2;
                // printf("relocating memory to expand output to"
                //        " size %zd\n", *output_sz);
                *output = (char*) realloc(*output, *output_sz);
            }
            tick_cnt++; break;
        case '[':  // loop construct left
            if (memory[memptr] == 0) {
                pos = jmptable[pos];
            }
            tick_cnt++; break;
        case ']':  // loop construct right
            if (memory[memptr] != 0) {
                pos = jmptable[pos];
            }
            tick_cnt++; break;
        }

        // check tick limit
        if (do_tick_check && tick_cnt > *tick_lim) {
            BF_LOG("tick limit exceeded\n");
            return 2;
        }
        pos++;
    }

    // zero-terminate the output string
    (*output)[output_len] = '\0';
    if (tick_lim) {
        *tick_lim = tick_cnt;
    }
    return 0;
}