From 8b79b8149d1e23d90b5871f980aec65d8aa7ae23 Mon Sep 17 00:00:00 2001 From: physick Date: Wed, 15 Jul 2026 17:46:13 +0500 Subject: Puzzle infrastructure pt 1. --- src/puzzles/puzzle.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/puzzles/puzzle.h | 32 +++++++++++++++- 2 files changed, 133 insertions(+), 3 deletions(-) (limited to 'src/puzzles') diff --git a/src/puzzles/puzzle.c b/src/puzzles/puzzle.c index cbad998..22f7a4d 100644 --- a/src/puzzles/puzzle.c +++ b/src/puzzles/puzzle.c @@ -1,21 +1,121 @@ #include "puzzle.h" +#include "../wren_inter/wren_inter.h" #include #include +#include + +/// Returns 1 test puzzle for the GetTestPuzzle segment +Test *getTestPuzzleTests() { + Test *outp = malloc(sizeof(Test)); + outp->RecieverName = strdup("main"); + outp->MainMethodSignature = strdup("call(_)"); + + outp->argc = 1; + outp->argv = malloc(sizeof(PuzzleIO)); + outp->argv->type = WREN_TYPE_STRING; + outp->argv->value = strdup("Hello"); + + outp->ExpectedResult.type = WREN_TYPE_STRING; + outp->ExpectedResult.value = strdup("Hello"); + return outp; +} Puzzle *GetTestPuzzle() { Puzzle *outp = malloc(sizeof(Puzzle)); if (outp == NULL) return NULL; outp->Name = strdup("Test puzzle"); - outp->Description = strdup("In this puzzle you will have to return the input string\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\n-\nThis is a line that should be somewhere down"); + outp->Description = strdup("In this puzzle you will have to return the input string"); + outp->InitialCode = strdup( "var main = Fn.new { |name| \n System.print(\"Hello, %(name)!\")\n}\n\nmain.call(\"world\")"); + outp->TestC = 1; + outp->Tests = getTestPuzzleTests(); return outp; } +void FreeTests(Test *Tests, size_t TestC) { + if (TestC == 0 || Tests == NULL) return; + for (size_t i = 0; i < TestC; i++) { + Test *t = &Tests[i]; + free(t->RecieverName); + free(t->MainMethodSignature); + free(t->argv); + } + free(Tests); +} + void FreePuzzle(Puzzle *self) { if (self == NULL) return; if (self->Name != NULL) free(self->Name); if (self->Description != NULL) free(self->Description); - if (self->Tests != NULL) free(self->Tests); + if (self->InitialCode != NULL) free(self->InitialCode); + FreeTests(self->Tests, self->TestC); free(self); } + + +enum PUZZLE_TEST_RESULT RunTest(Test *test, WrenVM *vm) { + // TODO: finish this + return RESULT_OK; +} + +// Returns the res ptr. Does not modify the address +SubmitionResult *TryEvaluateUserCode(WrenVM *vm, char *script, SubmitionResult *res) { + WrenInterpretResult result = wrenInterpret(vm, "main", script); + res->result = RESULT_OK; + switch (result) { + case WREN_RESULT_COMPILE_ERROR: + res->result = RESULT_COMPTIME_ERR; + return res; + case WREN_RESULT_RUNTIME_ERROR: + res->result = RESULT_RUNTIME_ERR; + return res; + default: break; + } + return res; +} + +// Frees the vm. Does not modify the self address. +SubmitionResult *PrepareResult(SubmitionResult *self, + WrenVM *vm, + enum PUZZLE_TEST_RESULT result) { + if (vm == NULL || self == NULL) return self; + self->result = result; + self->stdout = GetMachineOutput(vm); + InterpreterFreeMachine(vm); + return self; +} + +SubmitionResult *SubmitSolution(Puzzle *self, char *script) { + if (self == NULL || script == NULL) return NULL; + SubmitionResult *res = malloc(sizeof(SubmitionResult)); + + WrenVM *vm = InterpreterPrepareMachine(); + if (vm == NULL) { + res->result = RESULT_INIT_ERROR; + return res; + } + + res = TryEvaluateUserCode(vm, script, res); + if (res->result != RESULT_OK) { + return PrepareResult(res, vm, res->result); + } + + for (size_t i = 0; i < self->TestC; i++) { + enum PUZZLE_TEST_RESULT test_res = RunTest(&self->Tests[i], vm); + switch (test_res) { + case WREN_RESULT_COMPILE_ERROR: + return PrepareResult(res, vm, RESULT_COMPTIME_ERR); + case WREN_RESULT_RUNTIME_ERROR: + return PrepareResult(res, vm, RESULT_RUNTIME_ERR); + default: break; + } + } + + return PrepareResult(res, vm, RESULT_OK); +} + +void FreeSunmitionResult(SubmitionResult *res) { + if (res->stdout != NULL) free(res->stdout); + free(res); +} diff --git a/src/puzzles/puzzle.h b/src/puzzles/puzzle.h index d530a89..6c72a18 100644 --- a/src/puzzles/puzzle.h +++ b/src/puzzles/puzzle.h @@ -3,6 +3,16 @@ #ifndef PUZZLE_H +enum PUZZLE_TEST_RESULT : char { + RESULT_OK = 0, + RESULT_COMPTIME_NO_ENTRY, + RESULT_COMPTIME_ERR, + RESULT_RUNTIME_ERR, + // TODO: add async evaluation to prevent `while (true)` situation + RESULT_TIMEOUT, + RESULT_INIT_ERROR, +}; + // This is a param that would be placed in a slot // https://wren.io/embedding/slots-and-handles.html#writing-slots typedef struct { @@ -13,22 +23,32 @@ typedef struct { } PuzzleIO; typedef struct { + // https://wren.io/embedding/calling-wren-from-c.html#setting-up-a-receiver + char *RecieverName; // https://wren.io/embedding/calling-wren-from-c.html#getting-a-method-handle char *MainMethodSignature; size_t argc; PuzzleIO *argv; + PuzzleIO ExpectedResult; } Test; + +/// The user submitted code should be evaluated by this point. +/// The entry point should be registered and the absence of the +/// Main method signatire will be concidered an error +enum PUZZLE_TEST_RESULT RunTest(Test *test, WrenVM *vm); + typedef struct { char *Name; char *Description; + char *InitialCode; /// The tests that are accessible for an end user size_t TestC; Test *Tests; } Puzzle; - + // TODO void ReadPuzzleFromFile(const char *path); @@ -36,5 +56,15 @@ void FreePuzzle(Puzzle *self); /// A prebuilt puzzle. Used for debuging Puzzle *GetTestPuzzle(); +typedef struct { + char *stdout; + enum PUZZLE_TEST_RESULT result; +} SubmitionResult; + +// Don't put anything in out_res, it will be overriten. +// The stdout of the VM will be there +SubmitionResult *SubmitSolution(Puzzle *self, char *script); +void FreeSunmitionResult(SubmitionResult *res); + #define PUZZLE_H #endif // PUZZLE_H -- cgit v1.3