218 lines
5.8 KiB
C++
218 lines
5.8 KiB
C++
// TODO(dev): use 'halfdelay()' to parse input for pi menu choice 'secret.'
|
|
|
|
// NOTE(dev): Gonna need this:
|
|
// mvwprintw(win, y, x, string); /* Move to (y, x) relative to window */
|
|
// /* co-ordinates and then print */
|
|
|
|
#include <ncurses.h>
|
|
|
|
#include "Problem.h"
|
|
|
|
#include <vector>
|
|
#include <chrono>
|
|
|
|
#define PROMPT " > "
|
|
|
|
std::vector<Problem*> GenerateProblems(bool addition, bool subtraction,
|
|
bool multiplication, bool division);
|
|
char MainMenu();
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
initscr();
|
|
noecho(); // don't show user input
|
|
cbreak(); // handle user input immediately (i.e. don't wait for the user to press enter)
|
|
keypad(stdscr, true); // enable the numpad, F1-F12 keys, arrow keys, etc.
|
|
|
|
while(true)
|
|
{
|
|
// game boolean flags:
|
|
bool addition = false;
|
|
bool subtraction = false;
|
|
bool multiplication = false;
|
|
bool division = false;
|
|
|
|
// Main Menu:
|
|
char menu_action = MainMenu();
|
|
if(menu_action == 'q')
|
|
break;
|
|
else if(menu_action == '1')
|
|
addition = true;
|
|
else if(menu_action == '2')
|
|
subtraction = true;
|
|
else if(menu_action == '3')
|
|
addition = subtraction = true;
|
|
else if(menu_action == '4')
|
|
multiplication = true;
|
|
else if(menu_action == '5')
|
|
division = true;
|
|
else if(menu_action == '6')
|
|
multiplication = division = true;
|
|
else
|
|
continue; // restart loop
|
|
|
|
clear();
|
|
wrefresh(stdscr);
|
|
|
|
std::vector<Problem*> problems = GenerateProblems(addition, subtraction,
|
|
multiplication, division);
|
|
std::vector<Problem*>::iterator selected = problems.begin();
|
|
|
|
INPUT_STYLE input_style = INPUT_STYLE::INPUT_APPEND;
|
|
for(auto *problem : problems)
|
|
if(problem)
|
|
problem->Draw(problem == *selected, input_style);
|
|
|
|
// Keep track of time:
|
|
auto start_time = std::chrono::steady_clock::now();
|
|
|
|
unsigned int input = 'q';
|
|
while(selected != problems.end() && *selected)
|
|
{
|
|
Problem *current_problem = *selected;
|
|
current_problem->Draw(true, input_style); // refresh with new input
|
|
input = getch();
|
|
if(input == 'q') // early exit
|
|
break;
|
|
switch(input)
|
|
{
|
|
case KEY_LEFT:
|
|
input_style = INPUT_STYLE::INPUT_PREPEND;
|
|
break;
|
|
case KEY_RIGHT:
|
|
input_style = INPUT_STYLE::INPUT_APPEND;
|
|
break;
|
|
case KEY_BACKSPACE:
|
|
current_problem->ClearInput();
|
|
break;
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
current_problem->Input(input_style, input);
|
|
break;
|
|
};
|
|
|
|
// NOTE(dev): If the answer is zero and they hit backspace to clear input, it treated it
|
|
// as if they had entered zero themselves.
|
|
if(input != KEY_BACKSPACE && current_problem->Correct())
|
|
++selected;
|
|
current_problem->Draw(current_problem == *selected, input_style); // refresh with new input
|
|
}
|
|
|
|
if(input != 'q') // if you didn't quit
|
|
{
|
|
// Keep track of time:
|
|
auto end_time = std::chrono::steady_clock::now();
|
|
auto elapsed_time = std::chrono::duration_cast<std::chrono::seconds>(end_time - start_time);
|
|
int middle_h = COLS / 2;
|
|
int middle_v = LINES / 2;
|
|
clear();
|
|
mvprintw(middle_v - 3, middle_h - 5,
|
|
"You did it!");
|
|
mvprintw(middle_v - 1, middle_h - 14,
|
|
"Questions Answered: %7d\n",
|
|
problems.size());
|
|
mvprintw(middle_v - 0, middle_h - 8,
|
|
"Time Elapsed: %02dm %02ds\n",
|
|
elapsed_time / 60, elapsed_time % 60);
|
|
mvprintw(middle_v + 1, middle_h - 13,
|
|
"Time Per Question: %02dm %02ds\n",
|
|
(elapsed_time / problems.size() / 60),
|
|
(elapsed_time / problems.size() % 60));
|
|
mvprintw(middle_v + 3, middle_h - 8,
|
|
"Press Q to quit.");
|
|
box(stdscr, '*', '*');
|
|
wrefresh(stdscr);
|
|
|
|
while(!(getch() == 'q'))
|
|
;
|
|
}
|
|
|
|
for(auto *problem : problems)
|
|
if(problem)
|
|
delete problem;
|
|
}
|
|
|
|
endwin();
|
|
return 0;
|
|
}
|
|
|
|
char MainMenu()
|
|
{
|
|
int middle_h = COLS / 2;
|
|
int middle_v = LINES / 2;
|
|
|
|
clear();
|
|
mvprintw(middle_v - 6, middle_h - 20, "\n");
|
|
mvprintw(middle_v - 5, middle_h - 20, " Welcome to Math Practice.\n");
|
|
mvprintw(middle_v - 4, middle_h - 20, " Please choose from the following menu:\n");
|
|
mvprintw(middle_v - 3, middle_h - 20, " 1. Addition\n");
|
|
mvprintw(middle_v - 2, middle_h - 20, " 2. Subtraction\n");
|
|
mvprintw(middle_v - 1, middle_h - 20, " 3. Both / Mixed\n");
|
|
mvprintw(middle_v - 0, middle_h - 20, " 4. Multiplication\n");
|
|
mvprintw(middle_v + 1, middle_h - 20, " 5. Division\n");
|
|
mvprintw(middle_v + 2, middle_h - 20, " 6. Both / Mixed\n");
|
|
mvprintw(middle_v + 3, middle_h - 20, " q. Quit\n");
|
|
mvprintw(middle_v + 4, middle_h - 20, "\n");
|
|
mvprintw(middle_v + 5, middle_h - 20, PROMPT);
|
|
refresh();
|
|
|
|
return getch();
|
|
}
|
|
|
|
std::vector<Problem*> GenerateProblems(bool addition, bool subtraction,
|
|
bool multiplication, bool division)
|
|
{
|
|
std::vector<Problem*> rtn;
|
|
for(auto y = 1; y < LINES - 10; y += 9)
|
|
{
|
|
for(auto x = 2; x < COLS - 16; x += 15)
|
|
{
|
|
Problem *test = nullptr;
|
|
if(addition || subtraction)
|
|
{
|
|
if(addition != subtraction)
|
|
{
|
|
if(addition)
|
|
test = new Problem(EASY_ADDITION, 3, x, y);
|
|
if(subtraction)
|
|
test = new Problem(EASY_SUBTRACTION, 3, x, y);
|
|
}
|
|
else
|
|
{
|
|
if(rand() % 2)
|
|
test = new Problem(HARD_ADDITION, 5, x, y);
|
|
else
|
|
test = new Problem(HARD_SUBTRACTION, 5, x, y);
|
|
}
|
|
rtn.push_back(test);
|
|
}
|
|
|
|
if(multiplication || division)
|
|
{
|
|
if(multiplication && !division)
|
|
test = new Problem(EASY_MULTIPLICATION, 2, x, y);
|
|
else if(division && !multiplication)
|
|
test = new Problem(EASY_DIVISION, 2, x, y);
|
|
else
|
|
{
|
|
// TODO(dev): Add hard problems
|
|
if(rand() % 2)
|
|
test = new Problem(HARD_MULTIPLICATION, 3, x, y);
|
|
else
|
|
test = new Problem(HARD_DIVISION, 3, x, y);
|
|
}
|
|
rtn.push_back(test);
|
|
}
|
|
}
|
|
}
|
|
return rtn;
|
|
}
|