|
|
261920 |
|
|
|
261920 |
#include <helianthus.h></helianthus.h>
|
|
|
261920 |
#include <string.h></string.h>
|
|
|
261920 |
#include <stdio.h></stdio.h>
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
#define MAXREC 1000
|
|
|
261920 |
#define ANSWERS 4
|
|
|
261920 |
|
|
|
261920 |
#define MAXQUESTS 10
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
typedef struct {
|
|
|
261920 |
char text[128];
|
|
|
261920 |
} Date;
|
|
|
261920 |
|
|
|
261920 |
typedef struct {
|
|
|
261920 |
char text[1024];
|
|
|
261920 |
} Text;
|
|
|
261920 |
|
|
|
261920 |
typedef struct {
|
|
|
261920 |
char text[128];
|
|
|
261920 |
} Part;
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
typedef struct {
|
|
|
261920 |
Date date;
|
|
|
261920 |
Text text;
|
|
|
261920 |
Part part;
|
|
|
261920 |
} BaseRecord;
|
|
|
261920 |
|
|
|
261920 |
typedef struct {
|
|
|
261920 |
Date *date;
|
|
|
261920 |
Text *text;
|
|
|
261920 |
Part *part;
|
|
|
261920 |
int error;
|
|
|
261920 |
} Record;
|
|
|
261920 |
|
|
|
261920 |
typedef struct {
|
|
|
261920 |
Date *date;
|
|
|
261920 |
int error;
|
|
|
261920 |
} Answer;
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
BaseRecord recordsSrc[MAXREC];
|
|
|
261920 |
Record records[MAXREC];
|
|
|
261920 |
Part *parts[MAXREC];
|
|
|
261920 |
Date *dates[MAXREC];
|
|
|
261920 |
int partCount;
|
|
|
261920 |
int dateCount;
|
|
|
261920 |
int recCount;
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
int mode;
|
|
|
261920 |
int errorCount;
|
|
|
261920 |
int curError;
|
|
|
261920 |
int curRec;
|
|
|
261920 |
double questionFade;
|
|
|
261920 |
Answer answers[ANSWERS];
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
void load(const char *filename) {
|
|
|
261920 |
recCount = 0;
|
|
|
261920 |
partCount = 0;
|
|
|
261920 |
dateCount = 0;
|
|
|
261920 |
memset(recordsSrc, 0, sizeof(recordsSrc));
|
|
|
261920 |
memset(records, 0, sizeof(records));
|
|
|
261920 |
memset(parts, 0, sizeof(parts));
|
|
|
261920 |
memset(dates, 0, sizeof(dates));
|
|
|
261920 |
|
|
|
261920 |
FILE *f = fopen(filename, "r");
|
|
|
261920 |
if (!f) {
|
|
|
261920 |
printf("cannot open file for read: %s\n", filename);
|
|
|
261920 |
return;
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
int mode = 0;
|
|
|
261920 |
BaseRecord *rs = recordsSrc;
|
|
|
261920 |
char *p = rs->date.text;
|
|
|
261920 |
char *e = p + sizeof(rs->date.text);
|
|
|
261920 |
while(1) {
|
|
|
261920 |
int c = fgetc(f);
|
|
|
261920 |
if (c <= 0 || c >= 256) break;
|
|
|
261920 |
if (c == '\t') {
|
|
|
261920 |
if (mode == 0) {
|
|
|
261920 |
mode = 1; p = rs->text.text; e = p + sizeof(rs->text.text);
|
|
|
261920 |
} else
|
|
|
261920 |
if (mode == 1) {
|
|
|
261920 |
mode = 2; p = rs->part.text; e = p + sizeof(rs->part.text);
|
|
|
261920 |
} else
|
|
|
261920 |
if (mode == 2) {
|
|
|
261920 |
mode = 3;
|
|
|
261920 |
}
|
|
|
261920 |
} else
|
|
|
261920 |
if (c == '\n') {
|
|
|
261920 |
if (rs->date.text[0] || rs->text.text[0] || rs->part.text[0]) {
|
|
|
261920 |
++recCount; ++rs;
|
|
|
261920 |
if (recCount >= MAXREC) break;
|
|
|
261920 |
}
|
|
|
261920 |
mode = 0; p = rs->date.text; e = p + sizeof(rs->date.text);
|
|
|
261920 |
} else
|
|
|
261920 |
if (c != '\r' && p+1 < e) {
|
|
|
261920 |
*p++ = c;
|
|
|
261920 |
}
|
|
|
261920 |
}
|
|
|
261920 |
fclose(f);
|
|
|
261920 |
|
|
|
261920 |
if (recCount < MAXREC)
|
|
|
261920 |
if (rs->date.text[0] || rs->text.text[0] || rs->part.text[0])
|
|
|
261920 |
++recCount;
|
|
|
261920 |
|
|
|
261920 |
for(int i = 0; i < recCount; ++i) {
|
|
|
261920 |
Record *r = &records[i];
|
|
|
261920 |
BaseRecord *rs = &recordsSrc[i];
|
|
|
261920 |
for(int j = 0; j <= i; ++j) {
|
|
|
261920 |
BaseRecord *rrs = &recordsSrc[j];
|
|
|
261920 |
if (!r->date && !strcmp(rs->date.text, rrs->date.text)) r->date = &rrs->date;
|
|
|
261920 |
if (!r->text && !strcmp(rs->text.text, rrs->text.text)) r->text = &rrs->text;
|
|
|
261920 |
if (!r->part && !strcmp(rs->part.text, rrs->part.text)) r->part = &rrs->part;
|
|
|
261920 |
}
|
|
|
261920 |
if (r->part == &rs->part)
|
|
|
261920 |
parts[partCount++] = r->part;
|
|
|
261920 |
if (r->date == &rs->date)
|
|
|
261920 |
dates[dateCount++] = r->date;
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
6d1715 |
for(int i = 0; i < 2*recCount; ++i) {
|
|
|
6d1715 |
int a = randomNumber(0, recCount - 1);
|
|
|
6d1715 |
int b = randomNumber(0, recCount - 1);
|
|
|
6d1715 |
if (a == b) continue;
|
|
|
6d1715 |
Record r;
|
|
|
6d1715 |
memcpy(&r, &records[a], sizeof(r));
|
|
|
6d1715 |
memcpy(&records[a], &records[b], sizeof(r));
|
|
|
6d1715 |
memcpy(&records[b], &r, sizeof(r));
|
|
|
6d1715 |
}
|
|
|
261920 |
if (recCount > MAXQUESTS)
|
|
|
261920 |
recCount = MAXQUESTS;
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
void nextQuestion() {
|
|
|
261920 |
if (mode != 2) return;
|
|
|
261920 |
if (!curError) ++curRec;
|
|
|
261920 |
if (curRec >= recCount)
|
|
|
261920 |
{ mode = 3; return; }
|
|
|
261920 |
|
|
|
261920 |
curError = 0;
|
|
|
261920 |
mode = 1;
|
|
|
261920 |
|
|
|
261920 |
int q = randomNumber(curRec, recCount-1);
|
|
|
261920 |
if (q != curRec) {
|
|
|
261920 |
Record r;
|
|
|
261920 |
memcpy(&r, &records[curRec], sizeof(r));
|
|
|
261920 |
memcpy(&records[curRec], &records[q], sizeof(r));
|
|
|
261920 |
memcpy(&records[q], &r, sizeof(r));
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
for(int i = 0; i < ANSWERS; ++i) {
|
|
|
261920 |
for(int j = 0; j < 100; ++j) {
|
|
|
261920 |
answers[i].date = dates[randomNumber(0, dateCount-1)];
|
|
|
261920 |
if (answers[i].date != records[curRec].date) break;
|
|
|
261920 |
}
|
|
|
261920 |
answers[i].error = 0;
|
|
|
261920 |
}
|
|
|
261920 |
answers[randomNumber(0, ANSWERS-1)].date = records[curRec].date;
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
void startTest() {
|
|
|
261920 |
mode = 2;
|
|
|
261920 |
curRec = -1;
|
|
|
261920 |
curError = 0;
|
|
|
261920 |
errorCount = 0;
|
|
|
261920 |
for(int i = 0; i < recCount; ++i)
|
|
|
261920 |
records[i].error = 0;
|
|
|
261920 |
nextQuestion();
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
void giveAnswer(int i) {
|
|
|
261920 |
if (mode != 1) return;
|
|
|
261920 |
if (answers[i].date == records[curRec].date) {
|
|
|
261920 |
mode = 2;
|
|
|
261920 |
} else {
|
|
|
261920 |
curError = 1;
|
|
|
261920 |
if (!records[curRec].error) {
|
|
|
261920 |
records[curRec].error = 1;
|
|
|
261920 |
++errorCount;
|
|
|
261920 |
}
|
|
|
261920 |
for(int j = 0; j < ANSWERS; ++j)
|
|
|
261920 |
if (answers[j].date != records[curRec].date)
|
|
|
261920 |
answers[j].error = 1;
|
|
|
261920 |
}
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
void init() {
|
|
|
261920 |
load("data/history/ancient-dates.txt");
|
|
|
261920 |
startTest();
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
void draw() {
|
|
|
261920 |
saveState();
|
|
|
261920 |
double w = windowGetWidth();
|
|
|
261920 |
double h = windowGetHeight();
|
|
|
261920 |
translate(w/2, h/2);
|
|
|
261920 |
|
|
|
261920 |
double my = mouseTransformedY();
|
|
|
261920 |
|
|
|
261920 |
saveState();
|
|
|
261920 |
if (mode == 1 || mode == 2) {
|
|
|
261920 |
noFill();
|
|
|
261920 |
strokeWidth(2);
|
|
|
261920 |
stroke(COLOR_BLACK);
|
|
|
261920 |
textAlign(HALIGN_CENTER, VALIGN_CENTER);
|
|
|
261920 |
textf(0, -200, "%d / %d", curRec+1, recCount);
|
|
|
261920 |
textSize(32);
|
|
|
261920 |
text(0, -100, records[curRec].text->text);
|
|
|
261920 |
textSize(24);
|
|
|
261920 |
textAlign(HALIGN_LEFT, VALIGN_TOP);
|
|
|
261920 |
line(-150, -65, 150, -65);
|
|
|
261920 |
double y = -40, dy = 40;
|
|
|
261920 |
int hover = -1;
|
|
|
261920 |
for(int i = 0; i < ANSWERS; ++i) {
|
|
|
261920 |
if (answers[i].error) {
|
|
|
261920 |
stroke(COLOR_RED);
|
|
|
261920 |
} else
|
|
|
261920 |
if (mode == 2) {
|
|
|
261920 |
if (answers[i].date == records[curRec].date) {
|
|
|
261920 |
circle(-130, y+i*dy+20, 10);
|
|
|
261920 |
stroke(COLOR_BLUE);
|
|
|
261920 |
fill(COLOR_BLUE);
|
|
|
261920 |
circle(-130, y+i*dy+20, 5);
|
|
|
261920 |
noFill();
|
|
|
261920 |
}
|
|
|
261920 |
} else {
|
|
|
261920 |
circle(-130, y+i*dy+20, 10);
|
|
|
261920 |
if (my > y+i*dy && my < y+(i+1)*dy) {
|
|
|
261920 |
hover = i;
|
|
|
261920 |
stroke(COLOR_BLUE);
|
|
|
261920 |
fill(COLOR_BLUE);
|
|
|
261920 |
circle(-130, y+i*dy+20, 5);
|
|
|
261920 |
noFill();
|
|
|
261920 |
}
|
|
|
261920 |
}
|
|
|
261920 |
text(-100, y + i*dy, answers[i].date->text);
|
|
|
261920 |
stroke(COLOR_BLACK);
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
if (mode == 1) {
|
|
|
261920 |
questionFade += windowGetFrameTime();
|
|
|
261920 |
if (questionFade > 1) questionFade = 1;
|
|
|
261920 |
if (hover >= 0 && mouseWentDown("left"))
|
|
|
261920 |
giveAnswer(hover);
|
|
|
261920 |
} else {
|
|
|
261920 |
questionFade -= windowGetFrameTime();
|
|
|
261920 |
if (questionFade < 0) {
|
|
|
261920 |
questionFade = 0;
|
|
|
261920 |
nextQuestion();
|
|
|
261920 |
}
|
|
|
261920 |
}
|
|
|
261920 |
} else
|
|
|
261920 |
if (mode == 3) {
|
|
|
261920 |
textSize(64);
|
|
|
261920 |
textAlign(HALIGN_CENTER, HALIGN_CENTER);
|
|
|
261920 |
textf(0, 0, "%d / %d", recCount - errorCount, recCount);
|
|
|
261920 |
questionFade += windowGetFrameTime();
|
|
|
261920 |
if (questionFade > 1) questionFade = 1;
|
|
|
261920 |
}
|
|
|
261920 |
restoreState();
|
|
|
261920 |
|
|
|
261920 |
noStroke();
|
|
|
261920 |
fill(colorByRGBA(1, 1, 1, 1-questionFade));
|
|
|
261920 |
rect(-w/2, -h/2, w, h);
|
|
|
261920 |
|
|
|
261920 |
restoreState();
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
int main() {
|
|
|
261920 |
windowSetSize(800, 600);
|
|
|
261920 |
windowSetResizable(TRUE);
|
|
|
261920 |
windowSetVariableFrameRate();
|
|
|
261920 |
windowSetInit(&init);
|
|
|
261920 |
windowSetDraw(&draw);
|
|
|
261920 |
windowRun();
|
|
|
261920 |
return 0;
|
|
|
261920 |
}
|
|
|
261920 |
|