Blame onefile/multiply.c

261920
261920
#include <math.h></math.h>
261920
#include <stdio.h></stdio.h>
261920
#include <string.h></string.h>
261920
261920
#include <helianthus.h></helianthus.h>
261920
261920
261920
#include "data-h/sound/begin.ogg.h"
261920
#include "data-h/sound/win.ogg.h"
261920
261920
261920
261920
#define BASE_TIME 1.0  // 1000.0 - to disable timeout
261920
//#define EXTRA_TASKS
261920
261920
enum {
261920
	ACTION_SUM = 0,
261920
	ACTION_SUB = 1,
261920
	ACTION_MUL = 2,
261920
	ACTION_DIV = 3,
261920
  ACTIONS_COUNT = 4,
261920
261920
	MASK_SUM = 1 << ACTION_SUM,
261920
	MASK_SUB = 1 << ACTION_SUB,
261920
	MASK_MUL = 1 << ACTION_MUL,
261920
	MASK_DIV = 1 << ACTION_DIV,
261920
	MASK_ALL = ~0,
261920
261920
	TASKS_COUNT = 20,
261920
261920
	TASK_MODE_NONE = 0,
261920
	TASK_MODE_ERROR = 1,
261920
	TASK_MODE_TIMEOUT = 2,
261920
	TASK_MODE_DONE = 3,
261920
261920
  MODE_MENU = 0,
261920
  MODE_BEGIN = 1,
261920
	MODE_PLAYING = 2,
261920
	MODE_FINISHED = 3,
261920
};
261920
261920
const char *signs[] = {"+", "-", "·", ":"};
261920
261920
typedef struct {
261920
	char question[1024];
261920
	char answer[1024];
261920
	char userAnswer[1024];
261920
	double time;
261920
	int mode;
261920
	double x, y, vx, vy;
261920
} Task;
261920
261920
typedef struct {
261920
  char name[1024];
261920
	double time;
261920
	int actions;
261920
	int minA, maxA;
261920
	int minB, maxB;
261920
	int minC, maxC;
261920
	int advanced;
261920
} Level;
261920
261920
261920
261920
Level levels[] = {
261920
	{ "Счёт до 10", 5*BASE_TIME, MASK_SUM | MASK_SUB,  1,  9,  1,  9,  2,  10, 0 },
261920
	{ "Число 10",   3*BASE_TIME, MASK_SUM | MASK_SUB,  1,  9,  1,  9, 10,  10, 0 },
261920
	{ "Сложение и вычитание однозначных чисел",
261920
                  7*BASE_TIME, MASK_SUM | MASK_SUB,  1,  9,  1,  9,  2,  18, 0 },
261920
	{ "Сложение и вычитание двузначных чисел",
261920
                 20*BASE_TIME, MASK_SUM | MASK_SUB, 10, 99, 10, 99, 20, 100, 0 },
261920
  {}, {}, {}, {}, {}, {}, {}, {}, // Умножение на 2..9
261920
  #ifdef EXTRA_TASKS
261920
  {}, {}, {}, {}, {}, {}, {}, {}, // Деление на 2..9
261920
  {}, {}, {}, {}, {}, {}, {}, {}, // Умножение и деление на 2..9
261920
  #endif
261920
	{ "Умножение однозначных чисел",
261920
                  8*BASE_TIME, MASK_MUL,             2,  9,  2,  9,  0, 100, 0 },
261920
	{ "Деление однозначных чисел",
261920
                 12*BASE_TIME, MASK_DIV,             2,  9,  2,  9,  0, 100, 0 },
261920
	{ "Умножение и деление однозначных чисел",
261920
	               12*BASE_TIME, MASK_MUL | MASK_DIV,  2,  9,  2,  9,  0, 100, 0 },
261920
	{ "Умножение и деление больших чисел",
261920
	               24*BASE_TIME, 0,                    0,  0,  0,  0,  0,   0, 1 }
261920
};
261920
261920
int levelsCount = sizeof(levels)/sizeof(*levels);
261920
int currentLevel = 0;
261920
261920
Task tasks[TASKS_COUNT];
261920
int currentTask = 0;
261920
int errorsCount = 0;
261920
int timeoutsCount = 0;
261920
double taskStartTime = 0;
261920
261920
int mode = MODE_MENU;
261920
261920
261920
double regularSize = 18;
261920
double bigSize = 24;
261920
double lineSpace = 7;
261920
261920
double ladderStep = 18 + 5;
261920
double ladderCurrent = 0;
261920
double ladderTarget = 0;
261920
double ladderSpeed = 5;
261920
261920
261920
Sound soundBegin;
261920
Sound soundWin;
261920
261920
261920
void init() {
261920
  background(colorByName("#4c4c4c"));
261920
261920
  soundBegin = createSoundFromMemory(data_sound_begin_ogg, data_sound_begin_ogg_len);
261920
  soundWin = createSoundFromMemory(data_sound_win_ogg, data_sound_win_ogg_len);
261920
261920
  for(int i = 2; i < 10; ++i) {
261920
    Level *level = &levels[4 + i - 2];
261920
    sprintf(level->name, "Умножение на %d",i);
261920
    level->time = 5*BASE_TIME;
261920
    level->actions = MASK_MUL;
261920
    level->minA = level->maxA = i;
261920
    level->minB = 2; level->maxB = 9;
261920
    level->minC = 0; level->maxC = 100;
261920
261920
    #ifdef EXTRA_TASKS
261920
    Level *levelD = &levels[4 + 8 + i - 2];
261920
    memcpy(levelD, level, sizeof(Level));
261920
    sprintf(levelD->name, "Деление на %d",i);
261920
    levelD->time = 7*BASE_TIME;
261920
    levelD->actions = MASK_DIV;
261920
261920
    Level *levelMD = &levels[4 + 8 + 8 + i - 2];
261920
    memcpy(levelMD, level, sizeof(Level));
261920
    sprintf(levelMD->name, "Умножение и деление на %d",i);
261920
    levelMD->time = 7*BASE_TIME;
261920
    levelMD->actions = MASK_MUL | MASK_DIV;
261920
    #endif
261920
  }
261920
}
261920
261920
261920
void generateAdvancedTask(Level *level, Task *task) {
261920
  memset(task, 0, sizeof(*task));
261920
261920
  int a = randomNumber(2, 20);
261920
  int b = randomNumber(2, 10);
261920
	if (randomNumber(0, 100) < 20) a *= 10;
261920
	if (randomNumber(0, 100) < 20) b *= 10;
261920
	if (a < 10 && b < 10) {
261920
		if (randomNumber(0, 1))
261920
      a *= 10; else b *= 10;
261920
  }
261920
  if (randomNumber(0, 1))
261920
    { int x = a; a = b; b = x; }
261920
	int c = a * b;
261920
261920
  if (randomNumber(0, 100) < 25) {
261920
    sprintf(task->question, "%d %s %d = ", c, signs[ACTION_DIV], a);
261920
    sprintf(task->answer, "%d", b);
261920
	} else {
261920
    sprintf(task->question, "%d %s %d = ", a, signs[ACTION_MUL], b);
261920
    sprintf(task->answer, "%d", c);
261920
	}
261920
  task->time = level->time;
261920
}
261920
261920
261920
void generateTask(Level *level, Task *task) {
261920
  if (level->advanced)
261920
    { generateAdvancedTask(level, task); return; }
261920
261920
  memset(task, 0, sizeof(*task));
261920
261920
	int action = 0;
261920
	while(1) {
261920
    action = randomNumber(0, ACTIONS_COUNT-1);
261920
    if (level->actions & (1 << action)) break;
261920
	}
261920
261920
	int a = 0, b = 0, c = 0;
261920
	while(1) {
261920
    a = randomNumber(level->minA, level->maxA);
261920
    b = randomNumber(level->minB, level->maxB);
261920
		if (action == ACTION_SUM || action == ACTION_SUB)
261920
			c = a + b; else c = a * b;
261920
		if (c >= level->minC && c <= level->maxC)
261920
			break;
261920
  }
261920
261920
	if (randomNumber(0, 1))
261920
    { int x = a; a = b; b = x; }
261920
261920
	if (action == ACTION_SUM || action == ACTION_MUL) {
261920
    sprintf(task->question, "%d %s %d = ", a, signs[action], b);
261920
    sprintf(task->answer, "%d", c);
261920
	} else {
261920
    sprintf(task->question, "%d %s %d = ", c, signs[action], a);
261920
    sprintf(task->answer, "%d", b);
261920
	}
261920
  task->time = level->time;
261920
}
261920
261920
261920
void nextTask() {
261920
  int remains = 0;
261920
  for(int i = 0; i < TASKS_COUNT; ++i)
261920
    if (tasks[i].mode != TASK_MODE_DONE) ++remains;
261920
261920
  if (!remains) {
261920
    ladderTarget = -100;
261920
    mode = MODE_FINISHED;
261920
    soundPlay(soundWin, FALSE);
261920
    return;
261920
  }
261920
261920
  ladderTarget = remains - 1;
261920
261920
  while(1) {
261920
    currentTask = randomNumber(0, TASKS_COUNT-1);
261920
    if (tasks[currentTask].mode != TASK_MODE_DONE) break;
261920
  }
261920
261920
  Task *task = &tasks[currentTask];
261920
  task->userAnswer[0] = 0;
261920
  task->mode = TASK_MODE_NONE;
261920
  task->x = 0;
261920
  task->y = (remains - 1)*ladderStep;
261920
  task->vx = 0;
261920
  task->vy = 0;
261920
  taskStartTime = windowGetSeconds();
261920
}
261920
261920
261920
void verifyTask() {
261920
  Task *task = &tasks[currentTask];
261920
  if (strcmp(task->answer, task->userAnswer) != 0) {
261920
    task->mode = TASK_MODE_ERROR;
261920
    ++errorsCount;
261920
  } else
261920
  if (windowGetSeconds() - taskStartTime > task->time) {
261920
    task->mode = TASK_MODE_TIMEOUT;
261920
    ++timeoutsCount;
261920
  } else {
261920
    task->mode = TASK_MODE_DONE;
261920
  }
261920
  nextTask();
261920
}
261920
261920
261920
void drawTask(Task *task) {
261920
  saveState();
261920
  double dt = windowGetFrameTime();
261920
261920
  noFill();
261920
  stroke(colorByName("white"));
261920
  textSize(regularSize);
261920
  if (task->mode == TASK_MODE_NONE) {
261920
    if (task != &tasks[currentTask]) noStroke();
261920
  } else
261920
  if (task->mode == TASK_MODE_ERROR) {
261920
    task->vy += 100*dt;
261920
    stroke(colorByRGBA(1, 1, 1, 0.25));
261920
  } else
261920
  if (task->mode == TASK_MODE_TIMEOUT) {
261920
    task->vx -= 20*dt;
261920
    stroke(colorByRGBA(1, 1, 1, 0.25));
261920
  }
261920
  task->x += task->vx;
261920
  task->y += task->vy;
261920
261920
  textf(task->x, task->y, "%s%s", task->question, task->userAnswer);
261920
  restoreState();
261920
}
261920
261920
261920
void drawLadder() {
261920
	double d = 5*windowGetFrameTime();
261920
	if (fabs(ladderCurrent - ladderTarget) < d) {
261920
		ladderCurrent = ladderTarget;
261920
	} else
261920
	if (ladderCurrent < ladderTarget) {
261920
		ladderCurrent += d;
261920
	} else {
261920
		ladderCurrent -= d;
261920
  }
261920
261920
  saveState();
261920
261920
  stroke(colorByName("white"));
261920
  strokeWidth(1);
261920
261920
	double w = 30;
261920
	int i0 = -100;
261920
	int i1 = 300;
261920
	for(int i = i0; i <= i1; ++i)
261920
    line(-w, i*ladderStep, w, i*ladderStep);
261920
  line(-w, i0*ladderStep, -w, i1*ladderStep);
261920
  line( w, i0*ladderStep,  w, i1*ladderStep);
261920
261920
  double k = ladderStep;
261920
	double iy = floor(ladderCurrent/2)*2;
261920
	double py = ladderCurrent - iy;
261920
	double ly = iy + (py < 1 ? py : 1)*2;
261920
	double ry = iy + (py < 1 ? 0 : py-1)*2 + 1;
261920
	double yy = (ly + ry)/2;
261920
261920
	strokeWidth(0.6*k);
261920
	point(0, yy*k);
261920
	strokeWidth(1);
261920
	line(0, (yy+0.3)*k,  0, (yy+2)*k);
261920
	line(0, (yy+0.7)*k, -k,     ly*k);
261920
	line(0, (yy+0.7)*k,  k,     ry*k);
261920
	line(0, (yy+2.0)*k, -k, (ly+3)*k);
261920
	line(0, (yy+2.0)*k,  k, (ry+3)*k);
261920
	restoreState();
261920
}
261920
261920
261920
void startLevel() {
261920
  currentTask = 0;
261920
  errorsCount = 0;
261920
  timeoutsCount = 0;
261920
  for(int i = 0; i < TASKS_COUNT; ++i)
261920
    generateTask(&levels[currentLevel], &tasks[i]);
261920
  ladderTarget = ladderCurrent = TASKS_COUNT - 1;
261920
  mode = MODE_BEGIN;
261920
  soundPlay(soundBegin, FALSE);
261920
}
261920
261920
261920
void drawLevel(int width, int height) {
261920
  double t = windowGetSeconds();
261920
261920
  Level *level = &levels[currentLevel];
261920
261920
  saveState();
261920
  noFill();
261920
  stroke(colorByName("white"));
261920
261920
  textSize(regularSize);
261920
  textf(0, 0, "Уровень %d", (currentLevel + 1));
261920
261920
  textSize(bigSize);
261920
  text(0, regularSize + lineSpace, level->name);
261920
261920
  textSize(regularSize);
261920
  textf(0, 80, "Ошибки: %d", errorsCount);
261920
  textf(0, 80 + regularSize + lineSpace, "Промедления: %d", timeoutsCount);
261920
261920
  if (mode == MODE_BEGIN || mode == MODE_FINISHED) {
261920
    textSize(bigSize);
261920
    double alpha = (1 + cos(10*t))/4 + 0.5;
261920
    stroke(colorByRGBA(1, 1, 1, alpha));
261920
    textAlign(HALIGN_LEFT, VALIGN_BOTTOM);
261920
    text(0, height, "Нажмите Enter...");
261920
    textAlign(HALIGN_LEFT, VALIGN_TOP);
261920
  }
261920
261920
  if (mode != MODE_BEGIN) {
261920
    saveState();
261920
    translate(width - 300, height - TASKS_COUNT*ladderStep);
261920
    for(int i = 0; i < TASKS_COUNT; ++i)
261920
      drawTask(&tasks[i]);
261920
    restoreState();
261920
  }
261920
261920
  saveState();
261920
  translate(width - 30, height - TASKS_COUNT*ladderStep);
261920
  drawLadder();
261920
  restoreState();
261920
261920
  restoreState();
261920
261920
  if (keyWentDown("escape")) mode = MODE_MENU;
261920
261920
  if (mode == MODE_BEGIN) {
261920
    if (keyWentDown("any return")) { mode = MODE_PLAYING; nextTask(); }
261920
  } else
261920
  if (mode == MODE_PLAYING) {
261920
    Task *task = &tasks[currentTask];
261920
    char *ua = task->userAnswer;
261920
    int i = strlen(ua);
261920
    if (i > 0 && keyWentDown("backspace")) ua[--i] = 0;
261920
    if (i == 1 && *ua == '0') i = 0;
261920
    int ii = i;
261920
    if (i < 10 && keyWentDown("any 0")) ua[i++] = '0';
261920
    if (i < 10 && keyWentDown("any 1")) ua[i++] = '1';
261920
    if (i < 10 && keyWentDown("any 2")) ua[i++] = '2';
261920
    if (i < 10 && keyWentDown("any 3")) ua[i++] = '3';
261920
    if (i < 10 && keyWentDown("any 4")) ua[i++] = '4';
261920
    if (i < 10 && keyWentDown("any 5")) ua[i++] = '5';
261920
    if (i < 10 && keyWentDown("any 6")) ua[i++] = '6';
261920
    if (i < 10 && keyWentDown("any 7")) ua[i++] = '7';
261920
    if (i < 10 && keyWentDown("any 8")) ua[i++] = '8';
261920
    if (i < 10 && keyWentDown("any 9")) ua[i++] = '9';
261920
    if (i > ii) ua[i] = 0;
261920
    if (keyWentDown("any return")) verifyTask();
261920
  } else
261920
  if (mode == MODE_FINISHED) {
261920
    if (keyWentDown("any return")) {
261920
      ++currentLevel;
261920
      if (currentLevel >= levelsCount) {
261920
        currentLevel = levelsCount - 1;
261920
        mode = MODE_MENU;
261920
      } else {
261920
        startLevel();
261920
      }
261920
    }
261920
  }
261920
}
261920
261920
261920
void drawMenu(int width, int height) {
261920
  saveState();
261920
  noFill();
261920
  stroke(colorByName("white"));
261920
  double scrollY = currentLevel*(regularSize+lineSpace) + bigSize + lineSpace - height;
261920
  if (scrollY < 0) scrollY = 0;
261920
  double y = -scrollY;
261920
  for(int i = 0; i < levelsCount; ++i) {
261920
    double size = i == currentLevel ? bigSize : regularSize;
261920
    textSize(size);
261920
    text(0, y, levels[i].name);
261920
    y += size + lineSpace;
261920
  }
261920
  restoreState();
261920
261920
  if (keyWentDown("escape")) windowStop();
261920
261920
  if (keyWentDown("up"  )) --currentLevel;
261920
  if (keyWentDown("down")) ++currentLevel;
261920
  if (keyWentDown("home")) currentLevel = 0;
261920
  if (keyWentDown("end" )) currentLevel = levelsCount - 1;
261920
  if (currentLevel >= levelsCount) currentLevel = levelsCount - 1;
261920
  if (currentLevel < 0) currentLevel = 0;
261920
261920
  if (keyWentDown("any return")) startLevel();
261920
}
261920
261920
261920
void draw() {
261920
  saveState();
261920
  translate(70, 50);
261920
  int width  = windowGetWidth() - 70 - 70;
261920
  int height = windowGetHeight() - 50 - 80;
261920
  if (mode == MODE_MENU)
261920
    drawMenu(width, height);
261920
  else
261920
    drawLevel(width, height);
261920
  restoreState();
261920
}
261920
261920
261920
int main() {
261920
  windowSetTitle("Arithm");
261920
	windowSetVariableFrameRate();
261920
	windowSetSize(1000, 700);
261920
	windowSetInit(&init);
261920
	windowSetDraw(&draw);
261920
	windowRun();
261920
	return 0;
261920
}