Turning on random LEDs from a class - c++

For a school project I need to randomly turn on LEDs in a color that is randomly chosen. So for example you can choose between red, blue, yellow and green. Then you need to pick one color randomly and randomly situated LEDs of that specific color on. The amount of LEDs that need to be turned on is input from the main document, I am trying to write these functions in a different class.
I need different arrays that contain the different LEDs of that color like:
int GrLeds[] = {LED_1, LED_5}; //the amount of LEDs can be changed
int ReLeds[] = {LED_2, LED_6};
int BlLeds[] = {LED_3, LED_7};
int GrLeds[] = {LED_4, LED_8);
Then one of these colors needs to be chosen randomly. I thought about putting the different color option in an array like the following:
int randomClr[] = {ledG, ledR, ledB, ledY};
But doing it like this would require me to link the ledG to GrLeds[] etc.
Is there a possibility to choose one of the arrays randomly, or something what would result in the same? I know Java has the option to use a list but that does not seem to be possible within c++.

What you are basically looking for is the random() function, which gives you a random number between an initial and final input numbers.
To integrate it within your code, as you are gonna manage more than one set of LEDs which is integrated by multiple LEDs, I would just create a matrix for that, and then choose a random row from that matrix (each row will represent a color), and turn on all the LEDs from that row.
Some pseudo-code that you can work with:
int randomClr[4][2] = {
{LED_1, LED_5},
{LED_2, LED_6},
{LED_3, LED_7},
{LED_4, LED_8}
};
// some code...
// Get a random number from 0 to 3
int randNumber = random(4);
for (int i = 0; i < 2; i++) {
// Your code to turn on the LEDs, for example:
digitalWrite(randomClr[randNumber][i], HIGH);
delay(100);
digitalWrite(randomClr[randNumber][i], LOW);
}

Your problem is kinda similar to an application that I developed some time ago which also involved some LEDs and randomness.
I wrote the following code for running some tests before migrating the functionalities to the Arduino ecosystem.
Feel free to reuse and adapt my code to your needs. Keep in mind that I wrote it to be tested on C++17 using Codelite and not for the Arduino platform, therefore you can replace the random function with the one from Arduino.
Hope it helps. If so, just show a bit of appreciation including the link to this answer in your code, for posterity ;)
#include <iostream>
#include <random>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <time.h>
using namespace std;
#define MAX_COLORS 4
char textOut[100];
int cycles;
string colorNames[4] = { "RED", "BLUE", "GREEN", "PURPLE" };
typedef enum { RED, BLUE, GREEN, PURPLE } ColorList;
struct ColorsGroup {
ColorList colorCode;
string name;
};
ColorsGroup colorLED[4];
// Methods
int random(int, int);
ColorList retrieveColor(int);
void fillColors(void);
void printColors(int);
void setup()
{
fillColors();
cycles = 0;
}
int main()
{
cout << "********** Color picker *********" << endl;
setup();
while(cycles < 10) {
fillColors();
printColors(cycles);
cycles++;
}
return 0;
}
// From: https://stackoverflow.com/questions/7560114/random-number-c-in-some-range
int random(int min, int max)
{
static bool first = true;
if(first) {
srand(time(NULL));
first = false;
}
return min + rand() % ((max + 1) - min);
}
void fillColors(void)
{
for(int idx = 0; idx < MAX_COLORS; idx++) {
ColorList newColor = retrieveColor(random(0, MAX_COLORS - 1));
colorLED[idx].colorCode = newColor;
colorLED[idx].name = colorNames[newColor];
}
}
void printColors(int i)
{
sprintf(textOut, "%d. colorLED >> ", i);
cout << textOut;
for(int idx = 0; idx < MAX_COLORS; idx++) {
const char* nameStr = colorLED[idx].name.c_str(); // or &colorLED[idx].name[0];
sprintf(textOut, "%s[%d]", nameStr, colorLED[idx].colorCode);
cout << textOut;
if(idx <= MAX_COLORS - 2) {
sprintf(textOut, ", ");
cout << textOut;
}
else {
cout << ";" << endl;
}
}
}
ColorList retrieveColor(int col)
{
switch(col) {
case 0:
return RED;
break;
case 1:
return BLUE;
break;
case 2:
return GREEN;
break;
case 3:
return PURPLE;
break;
default:
return RED; // for the sake of completeness
break;
}
}
And this code spits out the following:

Related

Segmentation fault, while running a Monte-Carlo-random-walk simulation in c++

I am trying to simulate a random walk of 2000 particles, while the one boundary has the ability to make particles bound on that and merely perform a biased step.
There are of course probabilities for binding unbinding etc...
Below I have the whole code.
However I get segfault error.
I put some print statements in the code to see where the issue lies. But nothing. What I found strange though, is that although seed is fixed, the length of the output statement determined the loop, where code crushed.
I am totally inexperienced in these issues, so if you have any idea on what I could do, would be appreciated.
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <string>
using namespace std;
const int pi=6;
const int epsilon=10;
const int X=3000;
const int Y=30; //length
const int time_steps=100000;
const int N=2000; // number of molecules
int kinesins[N][3]={0};//[X,Y,bound or not]
int grid[X][Y][2]={0};
void place_kinesins(){
for (int i=0; i<N;i++){
int x= rand()%X;
int y= (rand()%(Y-2))+2;
if (grid[x][y][0]==0){
kinesins[i][0]=x;
kinesins[i][1]=y;
kinesins[i][2]=0;
grid[x][y][0]=1;
}else{i--;}
}
}
void create_boundaries(){
for(int i=0;i<Y;i++){
grid[0][i][1]=-1;
grid[X-1][i][1]=-3;
}
for (int i=0; i<X; i++){
grid[i][Y-1][1]=-2;
}
}
void create_filament(){ //in order to create binding affinity.
for(int i=0; i<X;i++){
grid[i][1][1]=pi;
}
}
void step(int kinesin, int x_step, int y_step){
int x=kinesins[kinesin][0];
int y=kinesins[kinesin][1];
int x_end=x+x_step;
int y_end=y+y_step;
if (grid[x_end][y_end][0]==0){
grid[x][y][0]=0;
kinesins[kinesin][0]=x_end;
kinesins[kinesin][1]=y_end;
grid[x_end][y_end][0]=1;
}
}
void bound(int kinesin){
int roll=rand()%10000 ;
if (roll<epsilon){
kinesins[kinesin][2]=0;
step(kinesin,0,1);
}else{
if (roll%63==0){ //controls the binding rate speed
step(kinesin, 1,0);
};
}
}
void unbound(int kinesin){
cout<<"1";
int x= kinesins[kinesin][0];
int y= kinesins[kinesin][1];
int type= grid[x][y][1];
switch(type){
case 0:{
cout<<"2";
int roll=rand()%4;
switch(roll){
case 0:
step(kinesin,-1,0);
break;
case 1:
step(kinesin,1,0);
break;
case 2:
step(kinesin,0,1);
break;
case 3:
step(kinesin,0,-1);
break;
}
break;
}
case -1:
step(kinesin,1,0);
break;
case -2:
step(kinesin,0,-1);
break;
case -3:
step(kinesin,-1,0);
break;
default:
int roll=rand()%10000;
if(roll<grid[x][y][1]){kinesins[kinesin][2]=1;}
else{ if(roll%2==0){step(kinesin,0,1);}}
}
}
void kinesin_move(int kinesin){
cout<<" "<<kinesins[kinesin][0]<<kinesins[kinesin][1];
if (kinesins[kinesin][2]==0){
unbound(kinesin);
}else{
cout<<"3";
bound(kinesin);
}
}
void simulation(){
for(int j=7000; j<time_steps;j++){
cout<<endl<< j<<" "<<endl;
for (int kin=0; kin<N; kin++){
cout<<kin;
kinesin_move(kin);
cout<<"E " ;
}
}
}
void programm(){
srand(1);
create_boundaries();
create_filament();
cout<<"Filament done"<<endl;
place_kinesins();
cout<<"Kines placed"<<endl;
simulation();
}
int main(){
programm();
return 0;
}
Problem:
In the function step you're accessing the array grid out of its bonds, which produces Undefined Behaviour and the segmentation fault.
This can be proven adding an assert before if (grid[x_end][y_end][0]==0):
if(!(x_end < X && x_end >= 0))
std::cerr << x_end << std::endl;
assert(x_end < X && x_end >= 0);
if (grid[x_end][y_end][0]==0){
grid[x][y][0]=0;
kinesins[kinesin][0]=x_end;
kinesins[kinesin][1]=y_end;
grid[x_end][y_end][0]=1;
}
Output:
3000
Assertion failed: x_end < X && x_end >= 0 main.cpp line 57
This application has requested the Runtime to terminate it in an unusual way.
Solution:
You will have to check the arguments for step won't make it go out of bonds before each call.
Additional information:
using namespace std; is considered a bad practice (More info here).
rand is not uniformly distributed. You may want use the <random> library instead (More info here).
Global variables are bad (More info here).
You may want to use std::array instead of raw, C-style arrays.

C++ - Class for list with 3 elements

I'm attempting to make a data type that is basically an associative array/map, but it would have 3 elements instead of 2. It would be implemented like this:
myTable rouletteBoard;
rouletteBoard.push.back(0, "Green", "Neither");
rouletteBoard.push.back(00, "Green", "Neither");
rouletteBoard.push.back(1, "Red", "Odd");
So really just a map or list with 3 elements, the first one being the unique key.
Yes this is a Roulette game. And I understand how to basically have a class for each number and make a separate instance for each number with the appropriate properties, but I feel that would be rather inefficient, since I could just have a list of each number with it's associated properties.
I've gotten pretty much nowhere on creating the class for this. I keep wondering if there is a better way to do it and trying that, then getting frustrated and quitting.
First let's talk about the data. Note that 0 must be distinguished from 00 so we cannot store them both naively as integers. Second, note that the color and parity (odd/even) can be derived instantly from the number. There is no need to store them as separate data. So we can do this:
struct Pocket {
enum class Color { GREEN, RED, BLACK };
enum class Parity { NONE, ODD, EVEN };
Pocket(int8_t num) : number(num) {}
int8_t number; // -1 for "00" on American style wheel
Parity parity() const {
if (number < 1) return Parity::NONE;
if (number % 2) return Parity::ODD;
return Parity::EVEN;
}
Color color() const {
if (number < 1) return Color::GREEN;
if (number % 2) return Color::RED;
return Color::BLACK;
}
};
Then you can make a simple container:
std::vector<Pocket> wheel;
for (int8_t ii = is_american ? -1 : 0; ii <= 36; ++ii) {
wheel.emplace_back(ii);
}
Finally, you can add code for printing:
std::ostream& operator <<(std::ostream& out, Pocket pocket) {
if (pocket.number == -1) return out << "00";
return out << pocket.number;
}
const char* to_string(Pocket::Color color) {
switch (color) {
case Pocket::Color::GREEN: return "Green";
case Pocket::Color::RED: return "Red";
case Pocket::Color::BLACK: return "Black";
default: return "?";
}
}
If you want an associative array with multiple data, you create a map between a key and a data-structure.
For example here, if you only wanted to store strings, I'd suggest using a map between a key and a vector. Then you can add as many or as few strings as needed to each key, so it's a flexible system.
std::map<int,std::vector<std::string>> rouletteBoard;
Or, have that structure inside your "rouletteBoard" class.
As for the key, if you use literal ints, then you have a problem, as 0 and 00 would be the same int, you either need string keys, or to specify "00" interally with a special value such as -1. You can then create an enum relating to the different fields of the vector, a working prototype could look like:
#include<iostream>
#include<map>
#include<string>
#include<vector>
std::map<int, std::vector<std::string>> rouletteBoard;
enum
{
name,
color,
oddeven,
property_count
};
std::string colors[] = { "Green", "Black", "Red"};
std::string roulette_color(int i)
{
if (i < 1) return colors[0]; // Green
if (i < 11) return colors[1 + (i & 1)]; // Black=Even Red=Odd
if (i < 19) return colors[2 - (i & 1)]; // Black=Odd Red=Even
if (i < 29) return colors[1 + (i & 1)]; // Black=Even Red=Odd
return colors[2 - (i & 1)]; // Black=Even Red=Odd
}
int main()
{
rouletteBoard[-1] = {"00", roulette_color(-1), "Neither"};
rouletteBoard[ 0] = { "0", roulette_color(0), "Neither" };
for(int i = 1; i <=36; ++i)
{
rouletteBoard[i] = { std::to_string(i), roulette_color(i), (i & 1) ? "Odd" : "Even" };
}
for (int i = -1; i <= 36; ++i)
{
std::cout << rouletteBoard[i][name] << ": " << rouletteBoard[i][color] << ", " << rouletteBoard[i][oddeven] << "\n";
}
std::cin.get();
return 0;
}

Can I have a single-dimension array of booleans that skip indexes?

I'm pretty new to Arduino programming, but am getting the hang of it. I've got a pretty strong PHP & Javascript background, so that's where I'm coming from.
I've been working on a project with an LCD touchscreen, with grids of buttons that I expect other developers to customize to their needs. There's an array that a developer can freely add items to, and additional pages of buttons are added dynamically. Clicking one simply sends that button's index to a receiver.
Now I want to make some buttons "sticky" so they stick on until turned off. My idea was to set up a simple array of booleans, like so:
boolean stickyTriggers[1] = 1;
boolean stickyTriggers[2] = 0;
boolean stickyTriggers[3] = 0;
boolean stickyTriggers[9] = 1;
What I'd expect is that buttons 1, 2, 3, and 9 are sticky, and buttons 1 and 9 are "on" by default. I figured when I'm rendering the buttons, I can simply say something like:
if (stickyTriggers[i]) {
// highlight button
}
The error I'm getting on compile is:
array must be initialized with a brace-enclosed initializer
I sort of realize I'm re-declaring the variable 3 times here, but I've tried other ways and got other errors.
What is the correct way to hold a simple array of flags? I want to be able to look up a key and get a true or false (or null), without having to hard-code an array length. I'm sure it's all possible, but is there a simple way?
Thanks
Clarification: The key here is that triggers 4-8 are NOT in the stickyTriggers array at all, since they are NOT sticky. I want to be able to skip indexes, or use arbitrary IDs as keys, as the title suggests. Most of the answers miss this.
I think you need somethig like this
#include <iostream>
using namespace std;
int main()
{
bool b[5] = {true, false, true, false, true};
cout << "Hello World" << endl;
for (int i=0; i < 5; i++) {
if (b[i]) {
cout<< "Index " << i << " is true" <<endl;
} else {
cout<< "Index " << i << " is false"<<endl;
}
}
return 0;
}
Try this:
#define ARRAY_SIZE 10
#define BOOL unsigned int
#define TRUE 1
#define FALSE 0
int main()
{
BOOL stickyTriggers[ARRAY_SIZE] = { FALSE };
stickyTriggers[1] = TRUE ;
stickyTriggers[9] = TRUE ;
return 0;
}
Yes, you are re-defining the array several times.
In C++, you may want to use bitset, check this link
http://en.cppreference.com/w/cpp/utility/bitset
You can try vector (which can dynamically change its size) too, like this
#include<iostream>
#include<vector>
using namespace std;
enum trigger_status {ON, OFF, NON_STICKY};
int main(){
vector<trigger_status> sticky_triggers(251, trigger_status::OFF); // you can add element to it dynamically, default all to off
sticky_triggers[0] = trigger_status::ON;
sticky_triggers[9] = trigger_status::ON;
sticky_triggers.push_back(trigger_status::ON); // sticky_triggers[251] = true, now u have 252 elements
}
Maybe I should have left out the C++ tag, to only invite Arduino-specific solutions.
But here's what I found, which solves my problem:
Alexander Brevig's HashMap Library:
http://playground.arduino.cc/Code/HashMap
As discussed here: http://forum.arduino.cc/index.php?PHPSESSID=q7rt05n43aa4enp6hepri50pi1&topic=42114.msg305435#msg305435
#include <HashMap.h>
const byte HASH_SIZE = 5;
HashType<int,boolean> hashRawArray[HASH_SIZE];
HashMap<int,boolean> hashMap = HashMap<int,boolean>(hashRawArray, HASH_SIZE);
void setup() {
Serial.begin(9600);
hashMap[0](1,true);
hashMap[1](2,false);
hashMap[2](3,false);
hashMap[3](4,false);
hashMap[4](9,true);
Serial.println(hashMap.getValueOf(1));
Serial.println(hashMap.getValueOf(2));
Serial.println(hashMap.getValueOf(9));
}
I can add a quick wrapper to add items to the hashMap without having to hard-code the index, but this gives me an easy way to set up an associative array of booleans, using arbitrary integers as the keys.
Thanks for trying everyone, sorry none of the answers worked on Arduino.
You might be able to use this it does not quite fulfil your desire to have no references to non-sticky triggers but it is fast and only uses 64 bytes of memory to map out 256 triggers.
I already had it in single boolean mode so I adapted it to make two maps and renamed it to suit your question..
Basically it is a bitmap.
Compiles and tests run OK for Arduino Duo.
// bitmapped booleans
const int IntBits = 16;
const int NumTriggers = 256;
const int idxSticky = 0;
const int idxDown = 1;
unsigned int TriggerMap[NumTriggers/IntBits][2];
void setup() {
Serial.begin(9600);
clearTriggerMap; // set all to not sticky and not down
// tests
setStickyTrigger(1, true, true);
setStickyTrigger(2, true, false);
setStickyTrigger(3, true, false);
setStickyTrigger(9, true, true);
setStickyTrigger(30, true, true);
setStickyTrigger(128, true, true);
setStickyTrigger(255, true, true);
}
void loop() {
// tests
Test(0);
Test(1);
Test(2);
Test(3);
Test(9);
Test(30);
Test(128);
Test(255);
delay(5000);
}
void Test( int ATrigger) {
// testing
if (IsStickyTrigger(ATrigger)) {
Serial.print( "Trigger ");
Serial.print(ATrigger);
Serial.print(" is sticky");
if (IsStickyTriggerDown(ATrigger)) {
Serial.print(" and it is down");
}
}
Serial.println();
}
void clearTriggerMap() {
for (int i = 0; i < NumTriggers/IntBits; i++) {
for (int j = 0; j < 2; j++){
TriggerMap[i][j] = 0;
}
}
}
void setStickyTrigger(int AIndex, boolean ASticky, boolean IsDown) {
unsigned int o;
unsigned int b = 1;
o = AIndex / IntBits;
b = b << (AIndex % IntBits);
if (ASticky) {
TriggerMap[o][idxSticky] = TriggerMap[o][idxSticky] | b;
} else {
b = ~b;
TriggerMap[o][idxSticky] = TriggerMap[o][idxSticky] & b;
}
if (IsDown) {
TriggerMap[o][idxDown] = TriggerMap[o][idxDown] | b;
} else {
b = ~b;
TriggerMap[o][idxDown] = TriggerMap[o][idxDown] & b;
}
}
boolean IsStickyTrigger(int AIndex) {
unsigned int b = 1;
b = b << (AIndex % IntBits);
return (TriggerMap[AIndex / IntBits][idxSticky] & b) != 0;
}
boolean IsStickyTriggerDown(int AIndex) {
unsigned int b = 1;
b = b << (AIndex % IntBits);
return (TriggerMap[AIndex / IntBits][idxDown] & b) != 0;
}

Neural network output is always 1

I'm developing a neural network application, but I have a problem when I'm trying to print the output of the feedforward: the console shows this value:
The output is always 1 1 1 1 1
I'm using 3 layers consisting of 2500 input nodes, 1800 hidden nodes, and 5 output nodes.
I'm using sigmoid binary as the activation function, the weight for testing is fixed at 0.5.
When I'm trying to use a very simple architecture (2 input, 3 hidden, 1 output) it works perfectly, but right now with so many nodes I can't manually calculate the output so I don't know if the result is true or false.
Is my code wrong, or can't I give a fixed weight? I only want to do one feedforward, not back-propagation, and the input is random from 0 to 1.
This is network.cpp:
#include <stdio.h>
#include <iostream>
#include "network.h"
#include "neuron.h"
using namespace std;
layer::layer(int numberOfNeuron,int numberOfInput,int numberOfOutput):
neuron_layer(numberOfNeuron)
{
for(int i=0;i<numberOfNeuron;i++)
{
neuron_layer[i] = new neuron(numberOfInput,numberOfOutput);
}
}
layer::~layer()
{
}
int layer::get_number_of_neuron()
{
return neuron_layer.size();
}
network::network(int layerNumber,int hiddenNeuronNumber):
layer_network(layerNumber)
{
this->layer_numbers = layerNumber;
for(int i=0;i<layerNumber;i++)
{
if(i==0)
{
layer_network[i] = new layer(2500,5,hiddenNeuronNumber);
}
else if(i==1)
{
layer_network[i] = new layer(hiddenNeuronNumber,2500,5);
}
else if(i==2)
{
layer_network[i] = new layer(5,hiddenNeuronNumber,1);
}
}
cout<<endl<<"Input layer : "<<layer_network[0]->get_number_of_neuron()<<endl;
cout<<"Hidden layer : "<<layer_network[1]->get_number_of_neuron()<<endl;
cout<<"Output layer : "<<layer_network[2]->get_number_of_neuron()<<endl;
}
network::~network()
{
}
void network::init_input_layer(int inputNeuronNumber,int hiddenNeuronNumber)
{
for(int i=0;i<inputNeuronNumber;i++)
{
for(int j=0;j<hiddenNeuronNumber;j++)
{
layer_network[0]->neuron_layer[i]->outputs[j]->weights = 0.5f;
}
}
}
void network::init_hidden_layer(int inputNeuronNumber,int hiddenNeuronNumber,int outputNeuronNumber)
{
for(int i=0;i<hiddenNeuronNumber;i++)
{
for(int j=0;j<inputNeuronNumber;j++)
{
layer_network[1]->neuron_layer[i]->inputs[j]->weights = layer_network[0]->neuron_layer[j]->outputs[i]->weights;
}
}
for(int k=0;k<hiddenNeuronNumber;k++)
{
for(int l=0;l<outputNeuronNumber;l++)
{
layer_network[1]->neuron_layer[k]->outputs[l]->weights = 0.5f;
}
}
}
void network::init_ouput_layer(int hiddenNeuronNumber,int outputNeuronNumber)
{
for(int i=0;i<outputNeuronNumber;i++)
{
for(int j=0;j<hiddenNeuronNumber;j++)
{
layer_network[2]->neuron_layer[i]->inputs[j]->weights = layer_network[1]->neuron_layer[j]->inputs[i]->weights;
}
}
}
This is neuron.cpp:
#include "neuron.h"
#include <stdio.h>
#include <iostream>
using namespace std;
synapse::synapse()
{
}
synapse::~synapse()
{
}
neuron::neuron(int numberOfInput,int numberOfOutput):
inputs(numberOfInput),outputs(numberOfOutput)
{
for(int i=0;i<numberOfInput;i++)
{
inputs[i] = new synapse();
}
for(int i=0;i<numberOfOutput;i++)
{
outputs[i] = new synapse();
}
}
neuron::~neuron()
{
}
int neuron::get_input_size()
{
int input_length;
input_length=(int) inputs.size();
return input_length;
}
int neuron::get_output_size()
{
int output_length;
output_length=(int) outputs.size();
return output_length;
}
void neuron::input_fire()
{
output_value = inputs[0]->activation_values;
for(int i=0;i<get_output_size();i++)
{
outputs[i]->activation_values = output_value;
}
}
void neuron::fire()
{
output_value = 0.0f;
for(int i=0;i<get_input_size();i++)
{
output_value+=(inputs[i]->activation_values)*(inputs[i]->weights);
}
//cout<<endl<<"Before Sigmoid"<<output_value;
output_value = 1.0f / (1.0f+ exp(-output_value));
//cout<<" After Sigmoid"<<output_value;
for(int i=0;i<get_output_size();i++)
{
outputs[i]->activation_values = output_value;
}
}
I think I don't need to post neuron.h and network.h since both of them only consist of declarations. I define the functions in the .cpp. This is how I created the object:
srand (time(NULL));
float inputTest[2500];
network test(3,1800);
test.init_network(1800);
for(int i=0;i<2500;i++)
{
inputTest[i]=(float)rand()/(float)RAND_MAX;
}
test.feedforward(inputTest);
I didn't check your code but...
No, you can't use fixed weight like that. Each node in your hidden layer will get larger and larger values as you increase the amount of inputs. Sigmoid will scale the large values to 1.
Think about it:
Let's say that you have 100 inputs each having "random" input value of 0.1. For simplicity, let's just forget everything else. Since your weights are constant 0.5, all the nodes in the hidden layer will get a same value that consists of "sigmoided" sum of each input*weight, that is sigm(0.1*0.5*100) = sigm(5) -> ~1
So, the more you have positive inputs with constant positive weights the more close all the hidden layers' outputs will become to 1.

How to efficiently structure a terminal application with multiple menus?

I'm writing a console based program for my coursework, and am wondering how best to structure it so that it is both stable and efficient. I currently have
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
int choice;
do
{
cout << "\E[H\E[2J" // Clear the console
<< "Main menu" << endl << endl
<< "Please select one of the following options by entering it's "
<< "number in the prompt below and pressing [ENTER]. "
<< endl << endl
<< "1. Pay my bill as a guest" << endl
<< "3. Log in" << endl
<< "2. Create an account" << endl
<< "4. Quit program" << endl;
cin >> choice;
switch (choice)
{
case 1: // Pay the bill as a guest to the system
case 2: // Log in to the system
case 3: // Create an account with the system
case 4: // Quit the program
default: // Prompt the user to choose again
}
} while !(default);
// Await user input to terminate the program
cout << "Please press [ENTER] to continue...";
cin.get();
return 0;
}
The purpose of the above code is to provide a list of options for the user to choose from, with the do-while loop working alongside the default statement in the switch to catch any unexpected input. Each case would call a function that presented another menu with it's own list of options, which would be structured using the same do-while, switch method. My concern is that as my program grows, the number of function calls being nested within other functions is going to increase, so that I would eventually end up with a function being called from within a function being called from within a function and so on. This would obviously have severe implications for the maintainability of the program, with the function calls moving further and further away from main(), and the output of these functions weaving a tangled path about the program.
Is it possible to structure my program in such a way as to return execution to main() as often as possible, or is the problem described above simply a consequence of this kind of programming?
NB: I ask this question in the understanding that user-defined functions are supposed to be ancillary to main(), and that they should perform a task before returning control to main() as the earliest possible convenience. I've only been at this a couple of months now so please bear with my ignorance/misunderstanding. Also, ignore any potential compiler errors in the code, I've not tested it yet and it's only provided as an aide to my conceptual question.
I would apply some OO-design and create a menu-class which basically stores items/sub-menus in a vector. This would make it easy to extemd to hierarchical menus
There is nothing particularly wrong with what you've done.
I don't see why it harms maintainability to have functions called from functions and so on. If anything it AIDS maintainability as you can move common code operations into seperate functions. This way you make a fix in one place and instantly fix the rest of the places its used as well.
Well, you can implement your menu structure as a state-machine, so you will be almost always in your main loop. But this can bring your code to the lower level, because you will be effectively programming not in C++ but in your state-machine processor-near code. If your state machine will be good enough, this is not a problem.
Or you can a simple menu-runner class, which will output as a result a request for submenu, so you will just exchange (perhaps using a stack) the description of the currently running menu.
By the way, I don't see any problems in deep nesting of the functions.
Another possible approach is to make a class defined as a list of (menu_option, function) pairs and the know-how to turn them into menus. Then the function can be a call to another class instance's menu or it can do some operation on your database. That lets you keep your data organized away from the business "how to display this menu" logic and add menus and menu items easily.
Don't worry about that or your current approach spending too much time away from main though. As you've structured it, your program won't automatically turn itself into a horrible mess just because you're calling functions from functions. More functions will tend to add to maintainability, as long as you keep them focused.
Think of it this way: a function does one thing, but at a higher level of abstraction than its body. So main() runs your program. create_account() will create an account, which is part of running the program. create_account itself calls several other things that do the building blocks necessary for creating an account. Is determining the new account's name one thing? It goes in its own function. Determining the index of the new account in the database? Too low-level. Put it in the "stuff it in the database" function.
The complexity of the code will correlate to the functionality offered by the program. I would not worry about this right now, revisit refactoring once you have two or three hundred lines.
for use this code you must add:
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "kharid");
memcpy(st_menues[cnt++].layer, "100000", sizeof(st_menues[cnt].layer));
this block code for insert menu item .
for change item to subitem you must change:
"100000"
to
"010000"
for do a difference work per every item you must define multiple:
void tmp_func(char* str)
void tmp_func1(char* str)
void tmp_func2(char* str)
...
and insert in
st_menues[cnt].function_pointer = tmp_func1;
...
st_menues[cnt].function_pointer = tmp_func2;
...
and insert your source code in these functions.
this source can compile in vc++.
i didn't test its in linux . but perhups works.
#include <iostream>
#include <stdio.h>
#include <conio.h>
#include<stdlib.h>
char title_str[20] = " main menu \n";
void print_this(bool with_title,const char* str, ...)
{
if(with_title)printf(title_str);
printf(str);
}
void clear()
{
#ifdef _WIN32
system("cls");
#else
std::cout << "\033[2J\033[1;1H";
#endif
print_this(true,"");
}
struct def_struct_menu
{
void (*function_pointer)(char*);
char menu_string[24];
char layer[7];
int pos;
};
void set_title(char* str)
{
sprintf(title_str," %s \n",str);
}
void tmp_func(char* str)
{
clear();
set_title(str);
printf("calc okokok");
_getch();
}
def_struct_menu st_menues[100] = { 0 };
def_struct_menu st_cur_menues[100] = { 0 };
void back_to_main_menu(int& highlight_line, int& cur_layer, int& start)
{
highlight_line = 0;
cur_layer = 0;
start = 0;
set_title((char*)"main menu");
}
int main()
{
int cnt = 0;
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "kharid");
memcpy(st_menues[cnt++].layer, "100000", sizeof(st_menues[cnt].layer));
{
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "asan");
memcpy(st_menues[cnt++].layer, "010000", sizeof(st_menues[cnt].layer));
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "shenase");
memcpy(st_menues[cnt++].layer, "010000", sizeof(st_menues[cnt].layer));
}
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "sharj");
memcpy(st_menues[cnt++].layer, "100000", sizeof(st_menues[cnt].layer));
{
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "ramz");
memcpy(st_menues[cnt++].layer, "010000", sizeof(st_menues[cnt].layer));
{
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "yekbarmasraf");
memcpy(st_menues[cnt++].layer, "001000", sizeof(st_menues[cnt].layer));
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "qrcode");
memcpy(st_menues[cnt++].layer, "001000", sizeof(st_menues[cnt].layer));
}
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "mostaghim");
memcpy(st_menues[cnt++].layer, "010000", sizeof(st_menues[cnt].layer));
}
const int ST_SIZE = cnt;
int input = 0;
int highlight_line = 0;
int cur_layer = 0;
int start = 0;
while (input != -1)
{
int size = 0;
memset(st_cur_menues, 0, sizeof(def_struct_menu) * ST_SIZE);
for (int i = start; i < ST_SIZE; i++)
{
if (cur_layer > 0)
{
if (st_menues[i].layer[cur_layer - 1] == '1')
{
break;
}
}
if (st_menues[i].layer[cur_layer] == '1')
{
memcpy(&st_cur_menues[size++], &st_menues[i], sizeof(def_struct_menu));
}
}
clear();
if (size == 0)
{
back_to_main_menu(highlight_line, cur_layer, start);
}
for (int i = 0; i < size; i++)
{
if (highlight_line == i)
print_this(false,"*");
else
print_this(false," ");
print_this(false,st_cur_menues[i].menu_string);
print_this(false,"\n");
}
//print_this("enter number\n");
input = _getch();
switch (input)
{
case 'x':
{
exit(0);
}
case 27://escape button
{
back_to_main_menu(highlight_line, cur_layer, start);
break;
}
case 13://enter button
{
if (size == 0)
{
back_to_main_menu(highlight_line, cur_layer, start);
break;
}
st_cur_menues[highlight_line].function_pointer(st_cur_menues[highlight_line].menu_string);
start = st_cur_menues[highlight_line].pos + 1;
cur_layer++;
highlight_line = 0;
}
break;
case 72://up arrow key
{
if (highlight_line == 0)
highlight_line = (size - 1);
else
highlight_line--;
}
break;
case 80://down arrow key
{
if (highlight_line == (size - 1))
highlight_line = 0;
else
highlight_line++;
}
break;
default:
break;
}
}
return 0;
}
tnx