This uses a linked list to store the data. I used a linked list as it's the only type of data structure that I know currently. Using chrono
, I can get microseconds into my timer and <random>
for a completely random scramble. A main goal of this program is to be fast in the sense of less keystrokes to get scramble and start/stop timer, so I used the Getkeystate
as an input method. It works, but I don't have to press enter every time I enter a command, so it guess it fulfilled my goal.
I'm looking for any advice about how to improve my coding. I feel like that the programming course was pretty much throw you into the deep end and just "make things work" so my code is not a robust as it can be. I also pretty much don't know any common practices that people usually implement, so any advice on that would be good too. I tried my best to make the code look good in terms of spacing and the style, but I have it all in one file. Should I have made an include.h for my classes?
#include <iostream>
#include <chrono>
#include <algorithm>
#include <random>
#include <vector>
#include <windows.h>
class times {
struct split {
//linked list to store data
std::chrono::microseconds elapsed;
split *next;
};
split *head;
split best;
int num_split;
public:
//int num_split;
times();
void new_split(std::chrono::microseconds add);
void display(split);
void display_all();
void display_latest();
void display_best();
void clear();
void avg();
void avg5();
void avg10();
};
times::times() {
//initialize head and num_split
head = 0;
num_split = 0;
}
void times::new_split(std::chrono::microseconds add) {
//always push onto head of the list
if (num_split == 0) {
//if list is empty
split *temp = new split;
temp->elapsed = add;
head = temp;
temp->next = 0;
num_split++;
//assign best split
best.elapsed = add;
best.next = 0;
}
else {
split *temp = new split;
temp->elapsed = add;
temp->next = head;
head = temp;
num_split++;
//check for best time
if (add < best.elapsed) {
best.elapsed = add;
}
}
}
void times::display(split x) {
using namespace std::chrono;
//cout time in format "min:sec:milli:micro"
if (num_split == 0) {
std::cerr << "No splits recorded" << std::endl;
return;
}
//conversion from duration
unsigned long long int total = x.elapsed.count();
unsigned min = 0;
unsigned sec = 0;
unsigned milli = 0;
unsigned micro = 0;
min = total/60000000;
total = total%60000000;
sec = total/1000000;
total = total%1000000;
milli = total/1000;
total = total%1000;
micro = total;
std::cout << min << ":" << sec << ":" << milli << ":" << micro << std::flush;
return;
}
void times::display_best() {
//displays best time
display(this->best); return;
}
void times::display_latest() {
//displays latest time
display(*head); return;
}
void times::display_all() {
//displays all recorded time, starting from most recent
if (num_split == 0) {
//error when list empty
std::cerr << "No splits recorded." << std::endl;
}
int j = num_split; //numbers each displayed time
split *i = head; //starting from latest (head)
while (i) {
std::cout << j << ". ";
display(*i); // using display() to print
std::cout << std::endl;
i = i->next;
j--;
}
return;
}
void times::clear() {
//clear linked list, reset num_split, reset head;
split *i = head;
while (i != 0) {
split *j = i->next;
delete i;
i = j;
num_split--;
}
head = 0;
}
void times::avg() {
//finds and display avg of all recorded time
if (num_split == 1) {
display_latest();
return;
}
split *i = head;
split temp;
temp.elapsed = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::duration::zero());
while (i) {
temp.elapsed = temp.elapsed + i->elapsed; //sum up total time
i = i->next;
}
temp.elapsed = temp.elapsed/num_split; //divided by number of splits
display(temp);
}
void times::avg5() {
//find avg of top 5 time
using namespace std::chrono;
if (num_split < 5) {
std::cerr << "Less than 5 recorded splits.";
return;
}
//create array with all recorded time
microseconds a_sort[num_split];
split *i = head;
for (int j = 0; j < num_split; j++) {
a_sort[j] = i->elapsed;
i = i->next;
}
//using vectors, partial sort for top 5 spllit
std::vector<microseconds> v_sort(a_sort, a_sort + num_split);
std::partial_sort (v_sort.begin(), v_sort.begin()+5, v_sort.end());
//total time for top 5 split
microseconds top5;
for (int i = 0; i < 5; i++) {
top5 += v_sort[i];
}
//find avg of top 5
split avg_of_5;
avg_of_5.elapsed = top5/5;
display (avg_of_5);
return;
}
void times::avg10() {
//find avg of top 10 time
using namespace std::chrono;
if (num_split < 10) {
std::cerr << "Less than 10 recorded splits." << std::endl;
return;
}
//create array with all recorded time
microseconds a_sort[num_split];
split *i = head;
for (int j = 0; j < num_split; j++) {
a_sort[j] = i->elapsed;
i = i->next;
}
//using vectors, partial sort for top 10 spllit
std::vector<microseconds> v_sort(a_sort, a_sort + num_split);
std::partial_sort (v_sort.begin(), v_sort.begin() + 10, v_sort.end());
//total time for top 10 split
microseconds top10;
for (int i = 0; i < 10; i++) {
top10 += v_sort[i];
}
//find avg of top 10, return as split
split avg_of_10;
avg_of_10.elapsed = top10/10;
display(avg_of_10);
return;
}
class stop_watch {
std::chrono::high_resolution_clock::time_point start_time; //time point start
std::chrono::high_resolution_clock::time_point stop_time; //time point stop
public:
stop_watch(); //constuctor to initialize stop & stop to time_point::min
void start();
void stop();
std::chrono::microseconds get_time(); //returns duration in microseconds
};
stop_watch::stop_watch() {
//initialize time_point to min
start_time = std::chrono::high_resolution_clock::time_point::min();
stop_time = std::chrono::high_resolution_clock::time_point::min();
}
void stop_watch::start() {
//start stop watch, initialize time_point start
start_time = std::chrono::high_resolution_clock::now();
}
void stop_watch::stop() {
//stops stop watch, initialize time_point stop
stop_time = std::chrono::high_resolution_clock::now();
}
std::chrono::microseconds stop_watch::get_time() {
//find duration between stop & start in microseconds
using namespace std::chrono;
return duration_cast<microseconds>(stop_time - start_time);
}
class rubik {
std::string notation[18] = {"U ","L ","F ","R ", "B ", "D ",
" U' ", "L' ", "F' ", "R' ", "B' ", "D' ",
"U2 ","L2 ","F2 ","R2 ","B2 ", "D2 "};
std::mt19937 engine; //generate mt19937 engine
public:
rubik();
int r();
void scramble();
};
rubik::rubik() {
//generate random device, seed mt19937 with random device
std::random_device seeder;
engine.seed(seeder());
}
int rubik::r() {
//run MT engine and return random number
std::uniform_int_distribution<int> distribution(0,17);
return distribution(engine);
}
void rubik::scramble() {
//display 25 random rotations
//display in groups of 5
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
std::cout << notation[r()] << std::flush;
}
if (i != 4) {
std::cout << "| " << std::flush;
}
}
}
void key_start() {
//start button using left & right shift key
bool x = false;
while (true) {
if (GetAsyncKeyState(VK_LSHIFT) < 0 && GetAsyncKeyState(VK_RSHIFT) < 0) {
while (true) {
if (GetAsyncKeyState(VK_LSHIFT) >= 0 && GetAsyncKeyState(VK_RSHIFT) >= 0) {
x = true;
break;
}
}
}
if (x == true) {
return;
}
}
}
void key_stop() {
//stop button using space bar
while (true) {
if (GetAsyncKeyState(VK_SPACE) < 0) {
return;
}
}
}
void display_menu1() {
using namespace std;
cout << " 'Left Shift' AND 'Right Shift' - Timer start" << endl
<< " 'Space' - Timer stop" << endl;
}
void display_menu2() {
using namespace std;
cout << " 'Space' - Reset & generate new scramble" << endl
<< " 'A' - Show all recorded times" << endl
<< " 'C' - Clear all recorded times" << endl
<< " 'ESC' - Exit program" << endl;
//<< " 'S' - Save time as txt" << endl; //implement this feature later, prompt user for file name
}
int options() {
//using keystate to select options
while (true) {
if (GetAsyncKeyState(0x43) < 0) return 1; // 'C'
else if(GetAsyncKeyState(VK_ESCAPE) < 0) return 2; // 'ESC'
else if (GetAsyncKeyState(VK_SPACE) < 0) return 3; // spacebar;
else if (GetAsyncKeyState(0x41) < 0) return 4; // 'A';
}
}
void clear_console() {
//clears clear_console
for (int i = 0; i < 100; i++) {
std::cout << std::endl;
}
}
int main() {
using namespace std;
//class decleartions
stop_watch timer;
times my_time;
rubik cube;
while (true) {
clear_console();
display_menu1(); cout << endl;
cube.scramble(); cout << endl; //display scramble
//timer
key_start();
timer.start();
key_stop();
timer.stop();
my_time.new_split(timer.get_time()); //save time, add to linked list
cout << endl
<< "Latest: "; my_time.display_latest(); cout << endl << endl;
cout << "Best: "; my_time.display_best(); cout << endl;
cout << "Average: "; my_time.avg(); cout << endl;
cout << "Average of 5: "; my_time.avg5(); cout << endl;
cout << "Average of 10: "; my_time.avg10(); cout << endl;
display_menu2(); cout << endl;
Sleep(400);
int input = options();
if (input != 3) {
while (true) {
//clear linked list
if (input == 1) {
my_time.clear();
cout << endl << "Cleared!" << endl;
Sleep(500);
clear_console();
display_menu2();
Sleep(400);
input = options();
if (input == 3) break;
}
if (input == 4) {
//display all recorded time
clear_console();
my_time.display_all(); cout << endl;
display_menu2(); cout << endl;
Sleep(400);
input = options();
if (input == 3) break;
}
if (input == 2) {
//exit program
return 0;
}
}
}
}
}