Desperately need help for project

Pages: 123
Hi

I have to create this program, which reads commands from a text file and draws them.
I have split them into stages.

The problem is reading text from the text file.

text file:

FORWARD 10 LEFT 90 FORWARD 20
LEFT 90
FORWARD 5
LEFT 90
FORWARD 10
LEFT 90
FORWARD 5

i am trying to read each line and store it in an array of strings

so it can be like this

stringarray =

FORWARD 10 LEFT 90 FORWARD 20
FORWARD 5
LEFT 90
...

i don't know how to use vectors in this situation.

my code is

1
2
3
4
5
6
7
8
9
10
11
if (myfile.is_open())
        {
            while ( myfile.good() )
            {
                getline(myfile,cmd);
            }
            myfile.close();
        }

        else cout << "Unable to open file";
The problem with this code is
if i output cmd

cout<<cmd<<endl;

it will output
FORWARD 5

and not
FORWARD 10 LEFT 90 FORWARD 20
LEFT 90
FORWARD 5
LEFT 90
FORWARD 10
LEFT 90
FORWARD 5

thanks
Last edited on
See the info + example on this page:

vector::push_back
http://www.cplusplus.com/reference/vector/vector/push_back/

and this one

vector::operator[]
http://www.cplusplus.com/reference/vector/vector/operator%5B%5D/

Andy

PS Also see firedraco's article: "How to use tags"
http://www.cplusplus.com/articles/z13hAqkS/

(you can go back and add tags to your opening post!)
getline erases the content of the string to replace it with the data from the file.
You need to use a variable in which you accumulate what you read
1
2
3
4
5
6
7
8
9
std::string alltext, currentline;
if (myfile.is_open())
{
    while ( getline(myfile,currentline) )
    {
        alltext += currentline + '\n';
    }
   myfile.close();
}
nice one toum, thanks. I understand what you mean and got it to work. i am new to getline function

one big problem in my project is that example

a command my program has to read might be something like this

REPEAT 10[LEFT 90 REPEAT 20[FORWARD 5 REPEAT 10[LEFT 90 FORWARD 10]]]

So for n number of repeat you need n number of nested loops, so i want to try and find a way to create a variable number of nested loops depending on how many repeat statements there are.

i tried recursive function, but i just don't understand how they work.

i was thinking something like the following, but when i go through it, it just falls apart.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
noofrepeat=3

repeat=
10
20
10

J=0;

curcmd =left 90
              forward 5
              left 90 forward 10

void varforloop(int noofloop, int noofrepeat, string curcmd)
{
   //i was thinking of including a variable for current command so it can know               
  //what command to do for a certain repeat.
  for(m=0;m<repeat(J); m++) //this will loop 10 time for first repeat
  {
     for(i=0;i<sizeof(curcmd); i++) loops through all the curcmd
     {
      curcmd(j,i); //only execute command of i'th array of line j
      if((curcmd(j,0)=='L')&&(curcmd(j,1)=='E'))
      {
           peform left rotate draw function
      }
      //do the above same for all 5 commands so 5 if statement
      if(j!=noofloop)  //base condition to exit my recursive function
     {
       varforloop(same parameters from beginning);
      }
     else return 0;
   }
 }
}


and with the recursive i was thinking that it would perform the current command and the loop command, which increases the index to the next current command for the next repeat and when finished comes out does the previous current command and the loop until the loop of the previous one ends and finish, but my function does not do that.

Also if i have a array of strings, which you helped with ofcourse

alltext=Left 90 forward 10
forward 10 right 20
forward 5 left 10

can i split them up into arrays so

alltext = [Left 90, forward 10]
[forward 10, right 20]

so alltext(0,0)=left 90
alltext(0,1)=forward 10
and so on.

thanks guys for all your help
Last edited on
It's possible to split the strings into arrays.
The way to do that depends on what you want to obtain.

Regarding the repeats, you need to store separately the lines so repeating them will be easy. I'd suggest using a vector.
1
2
3
4
5
6
7
8
9
10
std::string currentline;
std::vector<std::string> cmdlist;
if (myfile.is_open())
{
    while ( getline(myfile,currentline) )
    {
        cmdlist.push_back( currentline + '\n' );
    }
   myfile.close();
}



Generating the repeated command can be done using only loops.
The idea is to start from the end.
- create an empty string that will contain the repeated commands
- loop from the last command to the first
-- append current command to the beginning of your string
-- concatenate your string with itself the required number of times

Using your example REPEAT 10[LEFT 90 REPEAT 20[FORWARD 5 REPEAT 10[LEFT 90 FORWARD 10]]]:
- First iteration, append third command "LEFT 90 FORWARD 10" to the string. Since it was empty now it contains "LEFT 90 FORWARD 10".
- Concatenate the string 9 (10-1) times. It contains 10x"LEFT 90 FORWARD 10"
- Second iteration, append second command "FORWARD 5". The string contains
"FORWARD 5" + 10x"LEFT 90 FORWARD 10"
- Concatenate. The string contains 20x("FORWARD 5" + 10x"LEFT 90 FORWARD 10")
- Third iteration, append first command "LEFT 90". The string contains
"LEFT 90" + 20x("FORWARD 5" + 10x"LEFT 90 FORWARD 10").
- Concatenate. The string contains
10x("LEFT 90" + 20x("FORWARD 5" + 10x"LEFT 90 FORWARD 10"))

The code would be something like that:
1
2
3
4
5
6
7
8
9
10
11
void RepeatCommands( const std::vector<int>& nRepeats, const std::vector<std::string>& Cmds, std::string& Result )
{
    Result.clear(); // to make sure it's empty
    
    unsigned int nCmds = Cmds.size();
    for( unsigned int i = nCmds-1; i >= 0; --i )
    {
        AppendLeft( Cmds[i], Result ); // append current command at the left of "Result"
        Repeat( Result, nRepeats[i] ); // concatenate Result to itself the desired number of times
    }
}

I voluntarily used function calls so that you can understand the big steps and not focus on pointless implementation details.

In your case, these functions could be written
1
2
3
4
5
6
7
8
9
10
11
12
13
void AppendLeft( const std::string& LeftPart, std::string& RightPart )
{
    RightPart = LeftPart + RightPart; // string concatenation
}

void Repeat( std::string& mystring, const unsigned int N )
{
    // this function modifies "mystring" so that it contains N repeats of itself
    // it's a very inefficient implementation
    std::string tmp = mystring;
    for( unsigned int i = 0; i < N-1; ++i )
        mystring += tmp;
}
But all this has done is create a string on what to loop and how many times.
but how would you convert that to in this case 3 nested loops.

change
10x("LEFT 90" + 20x("FORWARD 5" + 10x"LEFT 90 FORWARD 10"))

to
for i=0;i<10
left 90
for(j=0;j<20
forward 5
for(k=0;k<10
left 90 forward 10


regarding the array.
i want to split
first element of vector
from
REPEAT 10[ LEFT 90 REPEAT 5[ LEFT 10 FORWARD 9]

to
{REPEAT 10, LEFT 90, REPEAT 5, LEFT 10, FORWARD 9}

the comma represent end of each section of first element.

or i was thinking maybe just store the number part in to a variable called the commands

REPEAT = [10]
[5 ]

LEFT = [90 ]
[10 ]

and so on.

but there has to be a more efficient way to create a variable nested for loop function.

maybe a class, i don't know.
The code I wrote generates the string recursively.
If you do not want to store everything before performing the actions, I think this might work:
1
2
3
4
5
6
7
8
9
10
11
void RecursiveLoop( const std::vector<int>& nRepeats, const std::vector<std::string>& Cmds, const unsigned int index, const unsigned int nCmds )
{
    // the first time, call RecursiveLoop( nRepeats, Cmds, 0, Cmds.size() )
    for(int k = 0; k < nRepeats[index]; k++ )
    {
        // Perform the commands stored in Cmds[i]
        
        if( index < nCmds-1 )
            RecursiveLoop( nRepeats, Cmds, index+1, nCmds );
    }
}


As for the rest, I have 1 question: when the command is "REPEAT", does it always apply to everything that comes after it ?
For example, could you have REPEAT 10[ LEFT 90] REPEAT 5[ LEFT 10 FORWARD 9] ?
Yes

with the repeat function

it has to repeat every command that comes after it including repeat

so REPEAT 20[LEFT 90 REPEAT 10[FORWARD 5 LEFT 1]]

THE FIRST REPEAT
LEFT 90 REPEAT 10[FORWARD 5 LEFT 1]

SECOND REPEAT
FORWARD 5 LEFT 1

above implementation would be

for(i=0;i<20;i++)
{
left 90
for(j=0;j<10;j++)
{
forward 5
left 1
}
}


Also now that we have a vector of commands

REPEAT 10[ LEFT 90 REPEAT 5[ FORWARD 10]]
LEFT 90
FORWARD 10

i want to take each line store in a new vector called curcommand

split it into

curcommand = left 90
forward 10

repeat = 10
10

my problem is detecting the repeats and acquring the following number after repeat into repeat variable

i tried the following

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
for(unsigned int j=0;j<logcmd.size();j++)
    {
        char *chcmd=new char[(logcmd.at(j)).size()+1];
        chcmd[(logcmd.at(j)).size()]=0;
        memcpy(chcmd,(logcmd.at(j)).c_str(),(logcmd.at(j)).size());

        //now we are taking one line of the instruction converting it to char and search through for commands

        for(k=0;k<sizeof(chcmd);k++)
        if((chcmd[k]=='R')&&(chcmd[k+1]=='E'))
        {
            temp1 = chcmd[k] + chcmd[k+1] + chcmd[k+2]+chcmd[k+3]+chcmd[k+4]+chcmd[k+5];
            curcmd.push_back(temp1);
            for(m=1;chcmd[k+m]!='[';m++)
            {
                if(m==1)
                {
                    temp=chcmd[k+m];
                }
                else temp=temp+chcmd[k+m];

            }
            cout << temp <<endl;
        }
I think you should not try to parse the strings by yourself. It can be pretty complicated, especially for a beginner.
It's much simpler to use what already exists.

My first advice would be to use a structure to store your commands:
1
2
3
4
5
6
struct Command
{
    std::string Action;
    int Value;
    Command(const std::string& initAction, const int& initValue): Action(initAction), Value(initValue) {;}
};


Since the REPEAT command applies to everything that follows, if we can have an array of Command structures that describes your commands, it becomes easy to write a recursive function that will perform the actions in the desired order:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void PerformCommands( const std::vector<Command>& CommandList, const int index, const int nCommands )
{
    if( CommandList[index].Action == std::string("REPEAT") )
    {
        if( index < nCommands-1 )
            for(int i = 0; i < CommandList[index].Value; i++)
                PerformCommands( CommandList, index+1, nCommands );
    }
    else
    {
        // perform current command
        std::cout << "Performing (" << CommandList[index].Action << ',' << CommandList[index].Value << ")\n";
        // perform next command
        if( index < nCommands-1 )
            PerformCommands( CommandList, index+1, nCommands );
    }
}


Now, what you need to do is extract the commands from the string you read from the file.
It would be easy if those brackets were not there.
But since the REPEAT command applies to everything that comes after it, those brackets are in fact useless.
So let's erase them from the string.
Getting the commands is now easy:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void ReadCommands( std::string& line, std::vector<Command>& CommandList )
{
    // input: "line", a string containing the written commands
    // output: "CommandList", a vector of the commands stored in a usable format
    // warning: this function modifies "line"
    
    CommandList.clear();

    std::replace(line.begin(), line.end(), '[', ' ');
    std::replace(line.begin(), line.end(), ']', ' ');

    std::stringstream ss(line);
    std::string Action;
    int value;
    while( ss >> Action >> value )
    {
        CommandList.push_back( Command(Action,value) );
        std::cout << "new command added: (" << Action << ',' << value << ')' << '\n';
    }

}


Your code that reads from the file can then be rewritten like this:
1
2
3
4
5
6
7
8
9
10
11
if (myfile.is_open())
{
    std::string currentline;
    std::vector<Command> CommandList;
    while ( getline(myfile,currentline) )
    {
        ReadCommands( currentline, CommandList );
        PerformCommands( CommandList, 0, CommandList.size() );
    }
   myfile.close();
}



Last edited on
Would it make any easier for you if were to use classes and use polymorphism.

Class Forward

Class Left

Class Right

which is inherited from derived class

Class commands

float amount;
Command below produces error
std::stringstream ss(line);

variable std:stringstream ss has initializer but incomplete type.
I changed the struct command to class like below. so i can include polymorphism behaviour.
but i don't understand how do i make one class inherit from another class

e.g.

class Command
{
Private:
string Action;
int Value;

Public:
Command(const string& initAction, const int& initValue): Action(initAction), Value(initValue) {;}
};

note: i don't understand why you have : after the above function then action and value.

would i inherit by doing the following

class Forward
{
Protected or Private:
Command fwd;

Public:
forwd(float amount)
{
glmov(amount,0,0,1); %or whatever the function maybe to draw forward
etc
}
};

same with the other commands except for Repeat, i will have to use your function.

so what do you think



Command below produces error
std::stringstream ss(line);

variable std:stringstream ss has initializer but incomplete type.

You need to include <sstream>

Would it make any easier for you if were to use classes and use polymorphism.

It would make things harder.

note: i don't understand why you have : after the above function then action and value.

It's a constructor for the class.


Why do you want to define a class for every move ?
Using polymorphism here is completely useless and will only make things more complicated.
Neve rmind about that, i have already understood about polymorphism and classes, that's where the action takes place, but the important part is before it.

Here is the stage i have divided my project so i can tackle it in stages and what i have done and what stage i am at.

1. Input from file to vector (Done)

2. Loop through vector, take first string (done) using

1
2
3
4
5
for(i=0;i<vector,size;i++
{
   string curcmd;
   curcmd.cmd=vector.at(i);
}   


3. Remove Brackets from string (done, which you have shown)

4. Convert string to sections

change
curcmd = REPEAT 10 LEFT 90 REPEAT 100 FORWARD 10 LEFT 5

to

curmcd = [LEFT 90]
[FORWARD 10, LEFT 5]

repeat = [10]
[100]

so curcmd is 2d array, first row contains all the commands for first repeat
second row contains all commands for second repeat.
repeat contains the amount of repeat for each command.

so
curcmd(0,0)=LEFT 90
curcmd(1,0) = FORWARD 10
curcmd(1,1) = LEFT 5

how do i achieve this. i was thinking of maybe converting it to char but, the problem would be how to partition the string with the end of the number being a delimter, like when there is a change from number to letter.

after i acquire this i planned the repeat function would be something like

loop repeat(i) number of times
loop through amount of commands in curcmd vertically.

e.g perform curcmd(1), for repeat(1) times, then after perform curcmd(1) and curcmd(2) repeat(2) number of times.

formula = curcmd(i) = curcmd(i+1)

something like this
so i was thinking about the repeat function and i got below
would this work

curcmd = left 90 0
forward 10 1

repeat = 10 0
20 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
curcmd = left 90        0 
         forward 10     1

repeat = 10  0
         20  1

repeatcmd(int index, string current, int repeat)
{
  for(int i=0;i<repeat(index);i++)	
  {
     perform curcmd(index);				%perform current action
     if(index<lastelementindex;			%loop the number of repeat times
	perform repeatcmd(index+1, curcmd, repeat);		%perform the next command as well
     elseif(i==repeat(index)
     perform repeatcmd(index-1, curcmd, repeat);	%if it has reached the last command go back to previous command    	
  }
}

4. Convert string to sections

change
curcmd = REPEAT 10 LEFT 90 REPEAT 100 FORWARD 10 LEFT 5

to

curmcd = [LEFT 90]
[FORWARD 10, LEFT 5]

repeat = [10]
[100]


It's possible, you just need to rewrite the ReadCommands() function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void ReadCommands( std::string& line, std::vector< std::vector<Command> >& CommandList, std::vector<int>& nRepeats )
{

    CommandList.clear();
    nRepeats.clear();

    std::replace(line.begin(), line.end(), '[', ' ');
    std::replace(line.begin(), line.end(), ']', ' ');

    std::stringstream ss(line);
    std::string Action;
    int value;
    while( ss >> Action >> value )
    {
        if( std::string("REPEAT") == Action )
        {
            std::cout << "\nNew row : (REPEAT," << value << ") ";
            nRepeats.push_back(value);
            CommandList.push_back( std::vector<Command> () );
        }
        else
        {
            CommandList.back().push_back( Command(Action,value) );
            std::cout << '(' << Action << ',' << value << ')' << ' ';
        }
    }
}



As for your repeat function : it will work.
You just have to correct small mistakes:
- line 12 it should be if( index+1 < lastelementindex )
- remove lines 14 and 15. If you go back, then when you go back, you'll go forward, then go back, then go forward, then go back, ... and it will never end.

If you have difficulties making this function work, you can take a look at the RecursiveLoop() function I posted previously.

Also, now that the array of commands contains entire rows of commands, curcmd(index) contains several commands. In fact it's an array.
Last edited on
I will try implement the things you have shown as for the readcommands function gives error

invalid initialization of reference type std::vector<std::vector<command, s...
error in passing argument of 2 of void readcommand(std::string& std::vector<std vector..

also your vector

vector<Command> CommandList

where does this variable Command come from as it says Command not declared in this scope.


Last edited on
It's passed from outside.
It should be created in your file reading code:
1
2
3
4
5
6
7
8
9
10
11
12
f (myfile.is_open())
{
    std::string currentline;
    std::vector< std::vector<Command> > CommandList;
    std::vector<int> nRepeats;
    while ( getline(myfile,currentline) )
    {
        ReadCommands( currentline, CommandList, CommandList );
        // insert here the code to perform the commands
    }
   myfile.close();
}
im still getting error

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
string currentline;
    vector<Command> CommandList;

    ifstream myfile ("command.txt");    

    if(myfile.is_open())
    {
        std::vector< std::vector<Command> > CommandList;
        std::vector<int> nRepeats;
        while ( getline(myfile,currentline) )
        {
            ReadCommands( currentline, CommandList, CommandList );
            // insert here the code to perform the commands
        }
        myfile.close();
    }
    cout<<CommandList(0)<<endl;
    return CommandList;


the error comes from the function
ReadCommands( currentline, CommandList, CommandList );

invalid initialization of reference of type std::vector<int, std::allocator<in..

when stated
//insert here the code to perform the commands
i don't even know the current command is to know which command to perform.

ok, i found out that i can get a certain character from a string using
string.at(index) now from here i want to use the number as the last char before moving on to next section

command = REPEAT 10 LEFT 10 FORWARD 5

how can i tell the compiler add all the chars on the current index of a char array and when you see a change from number to letter.
stop, increment the index and then add that char to the next index including the int or better yet have 2 seperate arrays one from comd and one for amt. so loop through chars if char store in char array index 1 and when it comes to a number store in amt array. then increment index and repeat.

i will have to plan it out tonight. i'm not aware of all the different capabilities c++ has.

thanks
Last edited on
Pages: 123