Commit 672852e8 authored by platypro's avatar platypro

Added preamble/multiple-question-per-exercise support

parent 544aaccd
......@@ -2,6 +2,7 @@
"name" : "Arithmetic",
"calculator" : "banned",
"randMax" : 400,
"preamble" : "Evaluate.",
"options" : {
"operation" : "add",
"max_1" : 20,
......
math.randomseed(questionID)
local a = math.random(options["max_1"])
local b = math.random(options["max_2"])
local ans
......
......@@ -7,7 +7,8 @@
"operation" : "add",
"max_1" : 100,
"max_2" : 100
}
},
"numQuestions": 5,
},
{
"id":"net.platypro.arith",
......@@ -15,7 +16,8 @@
"operation" : "sub",
"max_1" : 100,
"max_2" : 50
}
},
"numQuestions": 6,
},
{
"id":"net.platypro.arith",
......@@ -23,7 +25,8 @@
"operation" : "mul",
"max_1" : 15,
"max_2" : 15
}
},
"numQuestions": 1,
},
{
"id":"net.platypro.arith",
......@@ -31,7 +34,7 @@
"operation" : "div",
"max_1" : 20,
"max_2" : 20
}
},
}
]
}
......@@ -215,6 +215,8 @@ char* exercise_load(PG_STATE* state, char* filename)
exer->name = WJRStringLoad(NULL, exer_rdr);
else if(_IS(name, WJR_TYPE_STRING, KEY_CALCULATOR))
exer->calculator = WJRStringLoad(NULL, exer_rdr);
else if(_IS(name, WJR_TYPE_STRING, KEY_PREAMBLE))
exer->preamble = WJRStringLoad(NULL, exer_rdr);
else if(_IS(name, WJR_TYPE_OBJECT, KEYLIST(KEY_OPTION)))
exer->baseOptions = WJEOpenDocument(exer_rdr, name, NULL, NULL);
else if(_IS(name, WJR_TYPE_NUMBER, KEY_RAND_MAX))
......@@ -297,6 +299,9 @@ char* exercise_load(PG_STATE* state, char* filename)
exref->exercise = exer;
exref->options = WJEObject(NULL, NULL, WJE_NEW);
exref->numInstances = WJEUInt32(question, KEY_NUM_QUESTIONS, WJE_GET, 0);
if(exref->numInstances == 1) exref->numInstances = 0;
WJEMergeObjects(exref->options, exer->baseOptions, false);
WJEMergeObjects(exref->options, WJEObject(question, KEYLIST(KEY_OPTION), WJE_GET), true);
exref ++;
......@@ -366,18 +371,16 @@ QUIZ* exercise_mkQuiz(struct PG_State* state, PROBLEMSET* pset, GENMODE type, ui
q->qtype = type;
q->questionsLeft = numQuestions;
switch(type)
q->randStatus = calloc(pset->numExercises, sizeof(uint32));
if(type == QTYPE_EXAM_UNORDERED)
{
case QTYPE_NORMAL:
q->randStatus = calloc(pset->numExercises, sizeof(uint32));
break;
case QTYPE_EXAM_ORDERED:
q->ordered_rand.top = pset->numExercises;
q->ordered_rand.prime = 1;
break;
case QTYPE_EXAM_UNORDERED:
unirand_seed(&q->ordered_rand, pset->numExercises);
break;
unirand_seed(&q->ordered_rand, pset->numExercises);
}
else if(type == QTYPE_EXAM_ORDERED)
{
q->ordered_rand.top = pset->numExercises - 1;
q->ordered_rand.prime = 1;
}
return q;
......@@ -387,14 +390,14 @@ QUESTION* exercise_mkQuestion(PG_STATE* state, QUIZ* q)
{
QUESTION* result;
EXERCISEREF* exref = NULL;
uint32_t questionID = 0;
uint32_t choice, questionID;
// Pick the next exercise
switch(q->qtype)
{
case QTYPE_NORMAL:
{
uint16_t choice = (q->set->numExercises > 1) ? pcg32_random() % q->set->numExercises : 0;
choice = (q->set->numExercises > 1) ? pcg32_random() % q->set->numExercises : 0;
EXERCISEREF* mark;
mark = exref = q->set->exercises + choice;
while(q->randStatus[choice] >= exref->exercise->rand.top)
......@@ -406,19 +409,28 @@ QUESTION* exercise_mkQuestion(PG_STATE* state, QUIZ* q)
if(mark == exref) return NULL;
}
questionID = unirand(&exref->exercise->rand, &q->randStatus[choice]);
if(!q->questionsLeft--) return NULL;
break;
}
default:
exref = q->set->exercises + unirand(&q->ordered_rand, &q->rand_at);
questionID = pcg32_random() % exref->exercise->rand.top;
if(q->rand_at > q->set->numExercises) return NULL;
exref = q->currentExercise;
choice = q->rand_at;
if(exref && exref->numInstances && q->sequenceID < exref->numInstances)
q->sequenceID++;
else
{
exref = q->currentExercise = q->set->exercises + unirand(&q->ordered_rand, &q->rand_at);
if(exref->numInstances > 1)
q->sequenceID = 1;
else q->sequenceID = 0;
if(q->rand_at > q->set->numExercises) return NULL;
}
break;
}
questionID = unirand(&exref->exercise->rand, &q->randStatus[choice]);
// Check if the exercise is static
if(exref->exercise->staticQuestion.widgets)
result = &exref->exercise->staticQuestion;
......@@ -675,6 +687,7 @@ bool exercise_destroyQuestion(QUESTION* quiz)
bool exercise_destroyQuiz(QUIZ* q)
{
free(q->randStatus);
if(q) free(q);
return true;
}
......
......@@ -60,6 +60,7 @@ typedef struct Exercise
char* calculator;
char* name;
char* preamble;
char* id;
......@@ -71,6 +72,7 @@ typedef struct ExerciseRef
{
EXERCISE* exercise;
WJElement options;
uint32_t numInstances;
} EXERCISEREF;
......@@ -130,6 +132,10 @@ typedef struct Quiz
struct unirand_t ordered_rand;
uint32_t rand_at;
EXERCISEREF* currentExercise;
uint32_t sequenceID;
} QUIZ;
......
......@@ -352,7 +352,8 @@ bool script_genQuiz(PG_STATE* scr, QUESTION* question, EXERCISEREF* exercise, ui
lua_setfield(L, -2, KEYLIST(KEY_OPTION));
lua_setupvalue(L, -2, 1);
//lua_pushnumber(L, unirand(exercise->exercise->rand));
// Seed with question ID
srand(questionID);
if(lua_pcall(L, 0, LUA_MULTRET,0) != LUA_OK)
{
......
......@@ -24,7 +24,8 @@ struct SolutionBuffer
{
struct SolutionBuffer* next;
SOLUTIONTYPE type;
uint16_t qNum;
uint32_t qNum;
uint32_t sequenceID;
// Solution data appended to end of struct
};
......@@ -90,7 +91,7 @@ RsvgHandle* pdf_loadSvg(PROCESSEDSTRING* str)
return handle;
}
void pdf_question_begin(struct WidgetDrawCtx* ctx)
void pdf_question_begin(struct WidgetDrawCtx* ctx, char* title)
{
char exerNumber[13];
ctx->surface = cairo_recording_surface_create(CAIRO_CONTENT_ALPHA, NULL);
......@@ -100,12 +101,38 @@ void pdf_question_begin(struct WidgetDrawCtx* ctx)
pango_layout_context_changed(ctx->textLayout);
cairo_set_line_width(ctx->cairo, 1);
// Print Question Number and translate canvas to right
snprintf(exerNumber, 13, "%d.", ctx->exerciseAt);
pango_layout_set_text(ctx->textLayout, exerNumber, -1);
pango_cairo_show_layout(ctx->cairo, ctx->textLayout);
// Print Question Number
if(ctx->sequenceID < 2)
{
snprintf(exerNumber, 13, "%d.", ctx->exerciseAt);
pango_layout_set_text(ctx->textLayout, exerNumber, -1);
pango_cairo_show_layout(ctx->cairo, ctx->textLayout);
}
// Translate canvas to right
cairo_translate(ctx->cairo, PDF_QNUM_PADDING, 0);
cairo_get_matrix(ctx->cairo, &ctx->matrix );
// Print title
if(title && (ctx->sequenceID < 2))
{
PangoRectangle extents;
pango_layout_set_text(ctx->textLayout, title, -1);
pango_cairo_show_layout(ctx->cairo, ctx->textLayout);
pango_layout_get_extents(ctx->textLayout, NULL, &extents);
cairo_translate(ctx->cairo, 0, (extents.height + extents.y) / PANGO_SCALE);
}
// Print subquestion number (a, b, c, etc)
if(ctx->sequenceID > 0)
{
snprintf(exerNumber, 13, "%c)", ctx->sequenceID + '`');
pango_layout_set_text(ctx->textLayout, exerNumber, -1);
pango_cairo_show_layout(ctx->cairo, ctx->textLayout);
ctx->w -= PDF_QNUM_PADDING;
cairo_translate(ctx->cairo, PDF_QNUM_PADDING, 0);
cairo_get_matrix(ctx->cairo, &ctx->matrix );
}
}
void pdf_question_end(struct WidgetDrawCtx* ctx)
......@@ -114,6 +141,8 @@ void pdf_question_end(struct WidgetDrawCtx* ctx)
if(!ctx->surface) return;
if(ctx->sequenceID > 0) ctx->w += PDF_QNUM_PADDING;
cairo_recording_surface_ink_extents (
ctx->surface, NULL, NULL, NULL, &questionHeight);
......@@ -200,17 +229,18 @@ void pdf_generate_solutions(struct WidgetDrawCtx* ctx,
if(!sol) return;
ctx->hLeft = ctx->h;
ctx->exerciseAt = 0;
ctx->sequenceID = 0;
pdf_title_draw(ctx, "Solutions");
while(sol)
{
struct SolutionBuffer* sol_next = sol->next;
if(sol->qNum != ctx->exerciseAt)
if(sol->sequenceID != ctx->sequenceID || sol->qNum != ctx->exerciseAt)
{
ctx->exerciseAt = sol->qNum;
ctx->sequenceID = sol->sequenceID;
pdf_question_end(ctx);
//pdf_pushy(ctx, PDF_WPADDING_Y);
pdf_question_begin(ctx);
pdf_question_begin(ctx, NULL);
}
if(sol->type == SOLTYPE_INPUT)
......@@ -242,6 +272,7 @@ void* pdf_pushSolution(void* data, SOLUTIONTYPE type, char* format, va_list args
next->type = type;
next->next = NULL;
next->qNum = ctx->exerciseAt;
next->sequenceID = ctx->sequenceID;
struct SolutionBuffer* a = *ctx->solutions_at;
if(a)
......@@ -340,9 +371,10 @@ bool gen_pdf(PG_STATE* state, PROBLEMSET* pset)
QUESTION* q;
while((q = exercise_mkQuestion(state, quiz)))
{
{
WIDGET* wid = q->widgets;
pdf_question_begin (&qCtx);
qCtx.sequenceID = quiz->sequenceID;
pdf_question_begin (&qCtx, q->eref->preamble);
while(wid)
{
......@@ -353,7 +385,8 @@ bool gen_pdf(PG_STATE* state, PROBLEMSET* pset)
pdf_question_end(&qCtx);
exercise_destroyQuestion(q);
qCtx.exerciseAt ++;
if(qCtx.sequenceID < 2)
qCtx.exerciseAt ++;
}
exercise_destroyQuiz(quiz);
......
......@@ -51,7 +51,8 @@ typedef struct WidgetDrawCtx
PangoAttrList* attr_title;
double hLeft; //! Handled by pdf_question_begin and pdf_question_end
uint16_t exerciseAt;
uint32_t exerciseAt;
uint32_t sequenceID;
double w;
double h;
......
......@@ -39,6 +39,10 @@
#define KEY_SVG "svg"
#define KEY_IMAGE "image"
#define KEY_RAND_MAX "randMax"
#define KEY_PREAMBLE "preamble"
// PSet keys
#define KEY_NUM_QUESTIONS "numQuestions"
#define KEYLIST(k) k "s"
#define KEYCOUNT(k) k "Count"
......
......@@ -2,8 +2,9 @@
"name" : "{{name}}",
"type" : "normal",
"calculator" : "{{calculator}}",
"randmax" : 200,
"options": [ ],
"randMax" : 200,
"preamble" : "Evaluate.",
"options": { },
"static": [ ]
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment