0

I was asked to do a simple 20x20 turtle graphics program but for some reason I'm having an issue that's probably related from line 42 to 150 (Only they were originally on the post but I edited it since someone asked me to on the comments):

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <locale.h>
#include <string.h>
int floor[20][20], step, p1 = 0, p2 = 0;
char compass[6] = "westm", com[1], pen[3] = "up";
int main()
{
    com[0] = 'x';
    for (int i = 0; i < 20; i++)
    {
        for (int j = 0; j < 20; j++)
        {
            floor[i][j] = 0;
        }

    }
    while (com[0] != 'e' && com[0] != 'E')
    {
        com[0] = 'x';
        printf("Enter a command \n");
        scanf("%c", &com[0]);
        getchar();
        if (com[0] == 'e' || com[0] == 'E')
        {
            printf("End \n");
        }
        else
        {
            if (com[0] == 'u' || com[0] == 'U')
            {
                strncpy(pen, "up", 3);
                printf("The pen was turned up \n");
            }
            if (com[0] == 'd' || com[0] == 'D')
            {
                strncpy(pen, "do", 3);
                floor[p1][p2] = 1;
                printf("The pen was turned down \n");
            }
            if (com[0] == 'r' || com[0] == 'R')
            {
                if (!strcmp(compass, "westm"))
                {
                    strncpy(compass, "south", 6);
                }
                if (!strcmp(compass, "south"))
                {
                    strncpy(compass, "eastm", 6);
                }
                if (!strcmp(compass, "eastm"))
                {
                    strncpy(compass, "north", 6);
                }
                if (!strcmp(compass, "north"))
                {
                    strncpy(compass, "westm", 6);
                }
                printf("The turtle turned right \n");
            }
            if (com[0] == 'l' || com[0] == 'L')
            {
                if (!strcmp(compass, "westm"))
                {
                    strncpy(compass, "north", 6);
                }
                if (!strcmp(compass, "south"))
                {
                    strncpy(compass, "westm", 6);
                }
                if (!strcmp(compass, "eastm"))
                {
                    strncpy(compass, "south", 6);
                }
                if (!strcmp(compass, "north"))
                {
                    strncpy(compass, "eastm", 6);
                }
                printf("The turtle turned left \n");
            }
            if (com[0] == 'w' || com[0] == 'W')
            {
                step = 2147483647;
                if (!strcmp(compass, "westm"))
                {
                    while (step + p2 > 19)
                    {
                        printf("Type a valid number of steps \n");
                        scanf("%d", &step);
                        getchar();
                    }
                    if (!strcmp(pen, "do"))
                    {
                        for (int i = 0; i <= p2 + step; i++)
                        {
                            floor[p1][p2 + i] = 1;
                        }
                    }
                    p2 = floor + p2;
                }
                if (!strcmp(compass, "north"))
                {
                    while (p1 - step < 0)
                    {
                        scanf("%d", &step);
                        getchar();
                    }
                    if (!strcmp(pen, "do"))
                    {
                        for (int i = 0; i <= p1 - step; i++)
                        {
                            floor[p1 - i][p2] = 1;
                        }
                    }
                    p1 = p1 - step;
                }
                if (!strcmp(compass, "eastm"))
                {
                    while (p2 - step < 0)
                    {
                        scanf("%d", &step);
                        getchar();
                    }
                    if (!strcmp(pen, "do"))
                    {
                        for (int i = 0; i <= p2 - step; i++)
                        {
                            floor[p1][p2 - i] = 1;
                        }
                    }
                    p2 = p2 - step;
                }
                if (!strcmp(compass, "south"))
                {
                    while (step + p2 > 19)
                    {
                        scanf("%d", &step);
                        getchar();
                    }
                    if (!strcmp(pen, "do"))
                    {
                        for (int i = 0; i <= p1 + step; i++)
                        {
                            floor[p1 + i][p2] = 1;
                        }
                    }
                    p1 = p1 + step;
                }
            }
            if (com[0] == 'p' || com[0] == 'P')
            {
                for (int i = 0; i < 20; i++)
                {
                    for (int j = 0; j < 20; j++)
                    {
                        if (floor[i][j] == 0)
                        {
                            printf(". ");
                        }
                        else
                        {
                            printf("* ");
                        }
                    }
                    printf("\n");
                }
            }
        }
    }
}

Basically, on the beginning of the code there is an "while" waiting for user inputs until the're different from both 'e' and 'E', and it seems to be working fine on most cases

The problem is that when I walk AFTER turning right or left (by inputting 'w' after either the characters 'r' or 'l') or try to use the 'w' input for multiple times the program keeps asking for inputs (for the variable com, not step) without reading 'w' for some reason

Any other inputs like 'p', 'e' or even 'l' and 'r' again are working fine, but 'w' specifically doesn't work, and if I use 'p' ('e' doesn't count because it'll stop the repetition) and then 'w' the input will be recognized too

The algorythm with all those ifs kinda sucks but it's the most simple and easy to explain I could think of by myself

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Hokster
  • 23
  • 4
  • 3
    Maybe unrelated, but using strings for state tracking, comparing and overwriting them is *extremely* inefficient and awkward to write. – Eugene Sh. Jul 30 '21 at 19:14
  • Not to mention using a sequence of independent `if` statements for sets of mutually exclusive conditions, rather than the normal `if` / `else if` chain. – Tom Karzes Jul 30 '21 at 19:15
  • 1
    what is `step` and why is it initialized to `INT_MAX`? What is `p1`, `p2`? [Signed integer overflow](https://www.wikiod.com/w/C_Undefined_behavior#Signed_integer_overflow) is undefined behavior, but really not sure if that's what's happening with `while (step + p2 > 19)`, for instance. Looks dangerous. – yano Jul 30 '21 at 19:22
  • @yano step is an int that represents how many frames the turtle will walk, p1 and p2 are ints that represent the current position of the turtle on the matrix floor[20][20], p1 is the line and p2 is the column I've set step to the highest possible value on the beggining of the while to avoid the possibility of the user entering inputs that will make either p1 or p2 be higher than 19 while being to enter on any of the 'w' whiles – Hokster Jul 30 '21 at 19:34
  • @andreas-wenzel ummmm should I edit the original post with the whole code or can I just put a pastebin link on my question or comment? I'm new to stackoverflow so I don't really know if that's allowed or not – Hokster Jul 30 '21 at 19:48
  • @Hokster: Normally, it is better to add the code to the question itself. However, for large projects consisting of several files, it would probably be better to add an external link. If possible, it would be better to post a [mre] of your problem, instead of your entire code. – Andreas Wenzel Jul 30 '21 at 19:55
  • @andreas-wenzel done it, I'm sorry for taking so long to do it – Hokster Jul 30 '21 at 21:55

2 Answers2

0
#include "turtle.h"

int main()
{
    turtle_init(300, 300);          // initialize the image to be 600x600

    turtle_forward(50);
    turtle_turn_left(90);
    turtle_forward(50);
    turtle_draw_turtle();

    turtle_save_bmp("output.bmp");  // save the turtle drawing

    return EXIT_SUCCESS;
}
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • I don't need to save anything as an image though, the drawing should be seen on the console being made with .'s and *'s also, the way I was asked to make it work is just as It's like the current code, the user has to input either w, l, r p, e, u or d on the console to make the "turtle" act – Hokster Jul 30 '21 at 22:13
0

When I run your program and enter l as the first command and w as the second command, then it will execute the following loop:

while (p2 - step < 0)
{
    scanf("%d", &step);
    getchar();
}

Because p2 == 0 at this point, this loop will run forever until the user enters either zero or a negative number. This does not seem intended. Also, it does not seem intended that the program reads input from the user without telling the user beforehand what input to enter.

The best way to diagnose such issues is to run your program line by line in a debugger while monitoring the values of all variables. That way, it is easy to see at which point your program stops behaving as intended.

Also, your program also has the following issues:


It seems that the statement getchar(); is supposed to discard the newline character. However, this will only work if the user's input is in the expected format, i.e. if the newline character is in the expected position. If the user enters no characters or too many characters, then this won't always work.

For line-based input, I recommend that you use the function fgets instead of scanf/getchar, because the function fgets always reads exactly one line at a time (assuming that the buffer is large enough to store the entire line). You may want to read this: A beginners' guide away from scanf()


Generally, it does not make sense to declare an array with only a single member. Therefore, it would probably make more sense to declare com as a simple char instead of an array of char.


The following code is a bit cumbersome:

if (com[0] == 'e' || com[0] == 'E')
{
    [...]
}
else
{
    if (com[0] == 'u' || com[0] == 'U')
    {
        [...]
    }
    if (com[0] == 'd' || com[0] == 'D')
    {
        [...]
    }
    if (com[0] == 'r' || com[0] == 'R')
    {
        [...]
    }
    if (com[0] == 'l' || com[0] == 'L')
    {
        [...]
    }
    if (com[0] == 'w' || com[0] == 'W')
    {
        [...]
    }
    if (com[0] == 'p' || com[0] == 'P')
    {
        [...]
    }
}

It can be simpified to the following:

switch ( toupper( (unsigned char)com[0] ) )
{
    case 'E':
        [...]
        break;
    case 'U':
        [...]
        break;
    case 'D':
        [...]
        break;
    case 'R':
        [...]
        break;
    case 'L':
        [...]
        break;
    case 'W':
        [...]
        break;
    case 'P':
        [...]
        break;
    default:
        fprintf( stderr, "unexpected error!\n" );
        exit( EXIT_FAILURE );
}

The following code is wrong:

if (!strcmp(compass, "westm"))
{
    strncpy(compass, "south", 6);
}
if (!strcmp(compass, "south"))
{
    strncpy(compass, "eastm", 6);
}
if (!strcmp(compass, "eastm"))
{
    strncpy(compass, "north", 6);
}
if (!strcmp(compass, "north"))
{
    strncpy(compass, "westm", 6);
}

For example, if compass holds the string "westm", then the first if block will change the string to "south". But now the condition of the second if block is true, so the second if block will change it to "eastm". Now the condition of the third if block is true, so the third if block will change it to "north". Now the condition of the fourth if block is true, so the fourth if block will change it back to "westm". That way, you will have done a full rotation. This is probably not what you want.

In order to break this chain, you should add an else before every if statement, except for the first if statement:

if (!strcmp(compass, "westm"))
{
    strncpy(compass, "south", 6);
}
else if (!strcmp(compass, "south"))
{
    strncpy(compass, "eastm", 6);
}
else if (!strcmp(compass, "eastm"))
{
    strncpy(compass, "north", 6);
}
else if (!strcmp(compass, "north"))
{
    strncpy(compass, "westm", 6);
}

However, instead of storing the direction (compass) as a string, it would be more efficient to store it as an enum, like this:

enum direction
{
    DIRECTION_NORTH,
    DIRECTION_SOUTH,
    DIRECTION_WEST,
    DIRECTION_EAST
};

That way, instead of writing a chain of if/else if statements, you could simply write the following (assuming compass is an int instead of a string):

switch ( compass )
{
    case DIRECTION_WEST:
        compass = DIRECTION_SOUTH;
        break;
    case DIRECTION_SOUTH:
        compass = DIRECTION_EAST;
        break;
    case DIRECTION_EAST:
        compass = DIRECTION_NORTH;
        break;
    case DIRECTION_NORTH:
        compass = DIRECTION_WEST;
        break;
    default:
        fprintf( stderr, "unexpected error!\n" );
        exit( EXIT_FAILURE );
}

The reason that this is more efficient is that computers are better at handling numbers than strings.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39