summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhysick <96335032+DegustatorPonos@users.noreply.github.com>2026-08-02 22:52:27 +0500
committerPhysick <96335032+DegustatorPonos@users.noreply.github.com>2026-08-02 22:52:27 +0500
commitd1a1ebd54cabc6646f20f6b1b341751a2bf3c2ec (patch)
tree71f7d5d2d0c5960b25e95f99981c5a2c330302be
parent5af3200076b72e2f9f19c202ab9d2a7364004017 (diff)
puzzle parser
-rw-r--r--.clangd4
-rw-r--r--build.zig1
-rw-r--r--puzzle_specs_v1.md12
-rw-r--r--puzzles/test.pzbin0 -> 123 bytes
-rw-r--r--src/main.c17
-rw-r--r--src/puzzles/parser.c160
-rw-r--r--src/puzzles/puzzle.c6
-rw-r--r--src/puzzles/puzzle.h3
8 files changed, 191 insertions, 12 deletions
diff --git a/.clangd b/.clangd
new file mode 100644
index 0000000..87a3260
--- /dev/null
+++ b/.clangd
@@ -0,0 +1,4 @@
+#lsp config
+CompileFlags:
+ Add: [-Iwren/src/include]
+ Compiler: /usr/bin/cc
diff --git a/build.zig b/build.zig
index 49c6e86..d8d3037 100644
--- a/build.zig
+++ b/build.zig
@@ -36,6 +36,7 @@ pub fn build(b: *std.Build) void {
"src/scenes/resultView.c",
"src/scenes/puzzleScene.c",
"src/puzzles/puzzle.c",
+ "src/puzzles/parser.c",
}
});
diff --git a/puzzle_specs_v1.md b/puzzle_specs_v1.md
index ff9b9bc..360b9a3 100644
--- a/puzzle_specs_v1.md
+++ b/puzzle_specs_v1.md
@@ -2,7 +2,7 @@
## Puzzles
1. Termins
- The field is depicted as `(Data type) Name`
+ The field is depicted as `(Data type) Name`.
u8 is 8-bit integer (uint8_t in C).
u32 is a 32-bit integer (uint32_t in C).
string is a null-terminated ASCII byte array.
@@ -10,12 +10,12 @@
2. Puzzles are stored in binary format. The specification for it is as follows:
+----------------------------+---------------+----------------------+
| (u8) Specification version | (string) Name | (string) Description |
- +----------------------------+---------------+----------------------+
+ +----------------------------+-----+---------+----------------------+
| (string) Initial code | (u32) Test count |
+----------------------------------+--------------------------------+
| (Test) Test value |
| ... |
- +--------------------------------------+----------------------------+
+ +-------------------------------------------------------------------+
The amount of the tests must be the exact same as specified in test counter.
2. The test is a following struct:
@@ -36,7 +36,7 @@
+----------------+--------------+
4.1. The type is defined by the Type code. The following types are supported:
- +------+------+-------------------------------+
+ +------+---------+----------------------------+
| Code | Name | Value size |
+------+---------+----------------------------+
| 0 | Null | 0 bytes |
@@ -49,7 +49,7 @@
+------+---------+----------------------------+
| 4 | List | 4 bytes (See the item 3.2) |
+------+---------+----------------------------+
-
+
4.2 The list value descriptor contains the amount of elements in the list.
The following N Value descriptors will be treated as list elements.
- That is the only case where the expected output will contain more than one value
+ That is the only case where the expected output will contain more than one value.
diff --git a/puzzles/test.pz b/puzzles/test.pz
new file mode 100644
index 0000000..59b1153
--- /dev/null
+++ b/puzzles/test.pz
Binary files differ
diff --git a/src/main.c b/src/main.c
index e83454e..9ccb20e 100644
--- a/src/main.c
+++ b/src/main.c
@@ -5,6 +5,21 @@
int main(void) {
+ Puzzle *puzzle = ReadPuzzleFromFile("puzzles/test.pz");
+
+ // printf("Puzzle name: %s\n", puzzle->Name);
+ // printf("Puzzle description: %s\n", puzzle->Description);
+ // printf("Puzzle code: %s\n", puzzle->InitialCode);
+ // printf("Puzzle TestC: %zu\n", puzzle->TestC);
+
+ // for (int i = 0; i < puzzle->TestC; i++) {
+ // Test t = puzzle->Tests[i];
+ // printf("\n");
+ // printf("\tTest rec: %s\n", t.RecieverName);
+ // printf("\tTest sig: %s\n", t.MainMethodSignature);
+ // printf("\tTest argc: %zu\n", t.argc);
+ // }
+
if (!ReadConfig(DEFAULT_CONFIG_LOCATION)) {
return 1;
}
@@ -19,7 +34,7 @@ int main(void) {
return 1;
}
- if (!AddScene(CreatePuzzleScene(GetTestPuzzle()))) {
+ if (!AddScene(CreatePuzzleScene(puzzle))) {
printf("Failed to create a puzzle menu\n");
return 1;
}
diff --git a/src/puzzles/parser.c b/src/puzzles/parser.c
new file mode 100644
index 0000000..cb958e7
--- /dev/null
+++ b/src/puzzles/parser.c
@@ -0,0 +1,160 @@
+#include <string.h>
+#include "puzzle.h"
+#include <raylib.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+/// Reads a uint8_t from file.
+/// Assumes that the file is on the position
+uint8_t read_u8(FILE *file) {
+ uint8_t outp;
+ fread(&outp, 1, 1, file);
+ return outp;
+}
+
+/// Reads a uint32_t (little-endian) from file.
+/// Assumes that the file is on the position
+uint32_t read_u32(FILE *file) {
+ uint32_t outp = 0;
+ for (int i = 0; i < 4; i++) {
+ outp += ((uint32_t)read_u8(file)) << (i * 8);
+ }
+ return outp;
+}
+
+/// Reads a double from file.
+/// Assumes that the file is on the position
+double read_double(FILE *file) {
+ double outp;
+ fread(&outp, sizeof(double), 1, file);
+ return outp;
+}
+
+/// Reads a null terminated string from file.
+/// Assumes that the file if on the position.
+/// Allocates the string.
+char *read_string(FILE *file) {
+ char buf[8192];
+ size_t idx = 0;
+ int c;
+
+ while (idx < sizeof(buf) - 1) {
+ c = fgetc(file);
+
+ if (c == 0 || c == EOF) break;
+
+ buf[idx] = (char)c;
+ idx += 1;
+ }
+
+ buf[idx] = 0x0;
+ return strdup(buf);
+}
+
+typedef enum {
+ TYPE_NULL = 0,
+ TYPE_BOOL = 1,
+ TYPE_NUM = 2,
+ TYPE_STRING = 3,
+ TYPE_LIST = 4,
+} V1_IO_TYPE;
+
+void ReadPuzzleIO_V1(FILE *file, PuzzleIO *io) {
+ uint8_t type = read_u8(file);
+
+ switch (type) {
+ case TYPE_NULL: // NULL
+ io->type = WREN_TYPE_NULL;
+ io->value = NULL;
+ break;
+
+ case TYPE_BOOL:
+ {
+ bool *outp = malloc(sizeof(bool));
+ *outp = read_u8(file) != 0;
+ io->type = WREN_TYPE_BOOL;
+ io->value = outp;
+ break;
+ }
+
+ case TYPE_NUM:
+ {
+ double *outp = malloc(sizeof(double));
+ *outp = read_double(file);
+ io->type = WREN_TYPE_NUM;
+ io->value = outp;
+ break;
+ }
+
+ case TYPE_STRING:
+ io->type = WREN_TYPE_STRING;
+ io->value = read_string(file);
+ break;
+
+ case TYPE_LIST:
+ TraceLog(LOG_ERROR, "The arrays are not yet supported");
+ io->type = WREN_TYPE_NULL;
+ io->value = NULL;
+ break;
+
+ default:
+ return;
+ }
+}
+
+void readTestV1(FILE *file, Test *outp) {
+ outp->RecieverName = read_string(file);
+ outp->MainMethodSignature = read_string(file);
+ outp->argc = read_u32(file);
+ outp->argv = malloc(sizeof(PuzzleIO) * outp->argc);
+
+ for (size_t i = 0; i < outp->argc; i++) {
+ ReadPuzzleIO_V1(file, &outp->argv[i]);
+ }
+
+ ReadPuzzleIO_V1(file, &outp->ExpectedResult);
+}
+
+// Reads file with the specification from puzzle_specs_v1.md.
+// Assumes the first byte of the file was read and the file was not rewinded.
+Puzzle *parseFileV1(FILE *file) {
+ Puzzle *outp = malloc(sizeof(Puzzle));
+ if (outp == NULL) return NULL;
+
+ outp->Name = read_string(file);
+ outp->Description = read_string(file);
+ outp->InitialCode = read_string(file);
+ outp->TestC = read_u32(file);
+
+ outp->Tests = malloc(sizeof(Test) * outp->TestC);
+ for (size_t i = 0; i < outp->TestC; i++) {
+ readTestV1(file, &outp->Tests[i]);
+ }
+
+ return outp;
+}
+
+Puzzle *ReadPuzzleFromFile(const char *path) {
+ FILE *file = fopen(path, "r");
+ if (file == NULL) {
+ perror("Failed to parse the puzzle file");
+ return NULL;
+ }
+
+ uint8_t spec_version = read_u8(file);
+ printf("Version: %d\n", spec_version);
+ Puzzle *outp = NULL;
+ switch (spec_version) {
+ case 1:
+ outp = parseFileV1(file);
+ break;
+ default:
+ TraceLog(LOG_ERROR, "Failed to parse the puzzle file: unknown spec version");
+ break;
+ }
+
+ fclose(file);
+ return outp;
+}
diff --git a/src/puzzles/puzzle.c b/src/puzzles/puzzle.c
index 2da9ffc..6d0a4a0 100644
--- a/src/puzzles/puzzle.c
+++ b/src/puzzles/puzzle.c
@@ -41,9 +41,9 @@ 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);
+ if (t->RecieverName != NULL) free(t->RecieverName);
+ if (t->MainMethodSignature != NULL) free(t->MainMethodSignature);
+ if (t->argv != NULL) free(t->argv);
}
free(Tests);
}
diff --git a/src/puzzles/puzzle.h b/src/puzzles/puzzle.h
index e7aebb1..20193ca 100644
--- a/src/puzzles/puzzle.h
+++ b/src/puzzles/puzzle.h
@@ -56,8 +56,7 @@ typedef struct {
Test *Tests;
} Puzzle;
-// TODO
-void ReadPuzzleFromFile(const char *path);
+Puzzle *ReadPuzzleFromFile(const char *path);
void FreePuzzle(Puzzle *self);
/// A prebuilt puzzle. Used for debuging