Thread: Turtle Graphics (debug help)

  1. #1
    Registered User
    Join Date
    Jul 2006
    Posts
    3

    Turtle Graphics (debug help)

    Hello,

    First post to the board, though I've lurked for a while after becoming interested in programming. Started with C, and I am going through some exercises in a textbook I have. The program I am working on will take user input in a basic form, and convert it to a simple graphics display based on the input.

    My problems are twofold:
    1) I cannot figure out how to make the second argument of the "move forward" command take more than a single digit number
    2) The program produces unpredicted output, and though I've modified the code over and over, I cannot figure out exactly where the problem lies. When feeding it slightly different input, sometimes it will segfault. Sometimes it will print absolutely nothing. And sometimes it will print the asterisks, but not properly.

    So I call on the expertise of these boards to help me to through it. I have not learned much (if anything) beyond arrays, so my knowledge is very limited. Also, I'm sure my code is much longer than it should be, but I wanted to get the program fully functional first. Any code-related tips are appreciated also.

    Code:
    /************************************************************
    	Turtle Graphics: turtle.c
    
    	Use 50x50 array initialized to zeros to simulate a floor.
    	Turtle starts at 0,0 with pen up.  When pen is down, set
    	array elements to 1.  Display asterisk wherever there is
    	a 1, when program is complete.
    
    	Command list:
    		1		Pen up
    		2		Pen down
    		3		Turn right
    		4		Turn left
    		5,10	Move forward 10 spaces
    		6		Print the array
    		9		End of data (sentinel)
    ************************************************************/
    
    #include <stdio.h>
    #define ARRAYMAX 50	/* grid length and width */
    #define COMMANDMAX 20	/* max amount of prog commands */
    #define BUFMAX 10	/* max length of a command */
    
    enum Direction {NORTH, EAST, SOUTH, WEST};
    enum Position {UP, DOWN};
    
    int readProg(int prog[][2], int max);	/* read the program */
    void doCommand(int command1, int command2, int floor[][ARRAYMAX]);
    		/* process command to the array */
    
    enum Direction dir = NORTH;	/* direction turtle is facing */
    enum Position pos = UP;			/* position of the pen */
    int turtlePos[2] = {0};			/* position of the turtle */
    
    int main(void)
    {
    	int floor[ARRAYMAX][ARRAYMAX] = {{0}, {0}};	/* grid to print */
    	int prog[COMMANDMAX][2] = {{0}, {0}};	/* program commands */
    	int commands = 0;	/* number of commands in program */
    	int i = 0;
    
    	commands = readProg(prog, COMMANDMAX);
    
    	while (i <= commands) {	/* process program */
    		doCommand(prog[i][0], prog[i][1], floor);
    		i++;
    	}
    
    	return 0;
    }
    
    int readProg(int prog[][2], int max)
    {
    	int getCommand(char buffer[], int lim);	/* get one command */
    	void clearBuf(char buffer[], int lim);		/* clear temp buffer */
    
    	char buffer[BUFMAX];
    	int i = 0;
    
    	while (i < max && getCommand(buffer, BUFMAX)) { /* process until sentinel */
    		prog[i][0] = buffer[0];
    		if (buffer[0] == '5')	/* if command 5, get the argument also */
    			prog[i][1] = buffer[2];
    		i++;
    		clearBuf(buffer, BUFMAX);
    	}
    
    	return i; /* the number of commands, minus sentinel */
    }
    
    int getCommand(char buffer[], int lim)
    {
    	int c, i;
    
    	for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; i++)
    		buffer[i] = c;
    
    	if (buffer[0] == '9')
    		return 0;
    	else
    		return 1;
    }
    
    void clearBuf(char buffer[], int lim)
    {
    	int i;
    
    	for (i = 0; i < lim; i++)
    		buffer[i] = 0;
    }
    
    void doCommand(int command1, int command2, int floor[][ARRAYMAX])
    {
    	void printFloor(int floor[][ARRAYMAX]); /* print the finished grid */
    
    	int i;
    
    	switch(command1) {
    		case '1':	/* put pen up */
    			pos = UP;
    			break;
    		case '2':	/* put pen down */
    			pos = DOWN;
    			floor[turtlePos[0]][turtlePos[1]] = 1; /* activate current position */
    			break;
    		case '3':	/* turn right */
    			if (dir == WEST)
    				dir = NORTH;
    			else
    				dir += 1;
    			break;
    		case '4':	/* turn left */
    			if (dir == NORTH)
    				dir = WEST;
    			else
    				dir -= 1;
    			break;
    		case '5':	/* move forward amount of spaces in command2 */
    			if (dir == NORTH)
    				for (i = 1; i <= command2; i++) {
    					turtlePos[1]++; /* go up on y-axis */
    					if (pos == DOWN)	/* if pen is down, activate each passing spot */
    						floor[turtlePos[0]][turtlePos[1]] = 1;
    				}
    			else if (dir == EAST) /* go right on x-axis */
    				for (i = 1; i <= command2; i++) {
    					turtlePos[0]++;
    					if (pos == DOWN)
    						floor[turtlePos[0]][turtlePos[1]] = 1;
    				}
    			else if (dir == SOUTH) /* go down on y-axis */
    				for (i = 1; i <= command2; i++) {
    					turtlePos[1]--;
    					if (pos == DOWN)
    						floor[turtlePos[0]][turtlePos[1]] = 1;
    				}
    			else if (dir == WEST) /* go left on x-axis */
    				for (i = 1; i <= command2; i++) {
    					turtlePos[0]--;
    					if (pos == DOWN)
    						floor[turtlePos[0]][turtlePos[1]] = 1;
    				}
    			break;
    		case '6':
    			printFloor(floor);	/* print the grid */
    			break;
    		default:
    			break;
    	}
    }
    
    void printFloor(int floor[][ARRAYMAX])
    {	/* position 0,0 will be lower left corner */
    	int i, j;
    
    	printf("\n");
    	for (i = ARRAYMAX - 1; i >= 0; i--) { /* in each row... */
    		for (j = ARRAYMAX - 1; j >= 0; j--) { /* for each column... */
    			if (floor[j][i] == 1) /* ...print '*' if activated... */
    				printf("*");
    			else						/* ...and a blank if not */
    				printf(" ");
    		}
    		printf("\n");	/* after the last column, print a newline */
    	}
    }
    And the output:
    Code:
    jason@lappy:~/c$ ./a.out
    2
    5,5
    3
    5,5
    3
    5,5
    3
    5,5
    1
    6
    9
    Segmentation fault
    jason@lappy:~/c$ ./a.out
    2
    5,7
    3
    5,7
    3
    5,7
    3
    5,7
    1
    6
    9
    jason@lappy:~/c$
    Thanks for any help.

  2. #2
    Dump Truck Internet valis's Avatar
    Join Date
    Jul 2005
    Posts
    357
    I have a few quick suggestions for your coding style:
    - Rather than using '9' '6' '3' etc. #define or enum COMMAND_STOP COMMAND_PRINT COMMAND_TURN_RIGHT etc.
    - Don't prototype functions inside other functions, do it at the top or in a header.
    - Don't make useless comments like /* if command 5, get the argument also */, instead use something like /* command 5 is move forward so get its movement argument */
    - Your main should take argc and argv not void.
    - Your passing things like '5' ('5' == 53) not 5 to doCommand, use strtol to fix it (look below).

    You are only taking one digit prog[i][1] = buffer[2]--instead convert it to an integer--here
    is a simple solution:
    Code:
    /* no: prog[i][1] = buffer[2]; */
    /* yes: prog[i][1] = strtol(&buffer[2], (char **) NULL, 10); */
    I imagine this may fix your segfault issue as well.

  3. #3
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    - Your main should take argc and argv not void.
    Why? Either is equally acceptable.

    http://faq.cprogramming.com/cgi-bin/...&id=1043284376

    [edit] And why cast NULL? [/edit]
    Last edited by dwks; 07-12-2006 at 07:50 PM.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  4. #4
    Registered User
    Join Date
    Jul 2006
    Posts
    3
    First off, thank you for the help and suggestions. I took many of them into account, and slapped myself on the head multiple times for using getchar() instead of scanf(). I have got everything to work perfectly now, and hopefully my code is also more readable. This was my toughest project yet, and I look forward to exploring much more.

    There is one other (minor) issue though. When I compile my program, I am issued a compiler warning as seen below. Could anyone elaborate on that warning for me? It would be much appreciated, as I don't understand what is causing it.

    Code:
    jason@lappy:~/c$ cc turtle.c -o turtle
    turtle.c: In function ‘doCommand’:
    turtle.c:141: warning: passing argument 1 of ‘printFloor’ from incompatible pointer type
    It is referencing this function call:
    Code:
    		case COMMAND_PRINT:
    			printFloor(floor);
    			break;
    And here is my final, revised code:

    Code:
    /************************************************************
    	Turtle Graphics: turtle.c (final rev.)
    
    	Use 50x50 array initialized to zeros to simulate a floor.
    	Turtle starts at 0,0 with pen up.  When pen is down, set
    	array elements to 1.  Display asterisk wherever there is
    	a 1, when program is complete.
    
    	Command list:
    		1     Pen up
    		2     Pen down
    		3     Turn right
    		4     Turn left
    		5,10  Move forward 10 spaces
    		6     Print the array
    		9     End of data (sentinel)
    ************************************************************/
    
    #include <stdio.h>
    #define ARRAYMAX 50    /* length and width of grid */
    #define COMMANDMAX 20  /* max amount of program commands */
    #define COMMAND_UP 1
    #define COMMAND_DOWN 2
    #define COMMAND_RIGHT 3
    #define COMMAND_LEFT 4
    #define COMMAND_MOVE 5
    #define COMMAND_PRINT 6
    #define COMMAND_END 9
    
    enum Direction {NORTH, EAST, SOUTH, WEST};
    enum Status {UP, DOWN};
    
    int readProg(int prog[][2], const int max);
    int getCommand(int buffer[]);
    void doCommand(const int command, const int arg, int floor[][ARRAYMAX]);
    void printFloor(const int floor[][ARRAYMAX]);
    
    enum Direction dir = NORTH;  /* direction turtle is facing */
    enum Status stat = UP;       /* status of the pen          */
    int turtlePosX = 0;          /* x-coord of turtle          */
    int turtlePosY = 0;          /* y-coord of turtle          */
    
    int main(void)
    {
    	int floor[ARRAYMAX][ARRAYMAX] = {{0}, {0}};  /* grid to print */
    	int prog[COMMANDMAX][2] = {{0}, {0}};        /* instructions  */
    	int commands = 0;        /* number of commands in the program */
    	int i = 0;
    	
    	commands = readProg(prog, COMMANDMAX);
    	
    	while (i < commands) {   /* process the instructions */
    		doCommand(prog[i][0], prog[i][1], floor);
    		i++;
    	}
    	
    	return 0;
    }
    
    /* Read the entire set of instructions into array prog[][] */
    int readProg(int prog[][2], const int max)
    {
    	int buffer[2];
    	int i = 0;
    	
    	while (i < max && getCommand(buffer)) {  /* process until sentinel */
    		prog[i][0] = buffer[0];  /* transfer valid command into program */
    		prog[i][1] = buffer[1];  /* transfer argument into program      */
    		i++;
    	}
    	
    	return i;  /* amount of commands, minus sentinel */
    }
    
    /* Read a single instruction from input */
    int getCommand(int buffer[])
    {
    	scanf("%d,%d", &buffer[0], &buffer[1]);  /* read command to buffer */
    	
    	if (buffer[0] != COMMAND_MOVE)  /* set argument to 0 for any */
    		buffer[1] = 0;               /* command except move */
    	
    	if (buffer[0] == COMMAND_END)
    		return 0;
    	else
    		return 1;
    }
    
    /* Process a command onto the floor array */
    void doCommand(const int command, const int arg, int floor[][ARRAYMAX])
    {
    	int i;
    	
    	switch(command) {
    		case COMMAND_UP:
    			stat = UP;
    			break;
    		case COMMAND_DOWN:
    			stat = DOWN;
    			floor[turtlePosX][turtlePosY] = 1;  /* activate current loc */
    			break;
    		case COMMAND_RIGHT:
    			if (dir == WEST)
    				dir = NORTH;
    			else
    				dir += 1;
    			break;
    		case COMMAND_LEFT:
    			if (dir == NORTH)
    				dir = WEST;
    			else
    				dir -= 1;
    			break;
    		case COMMAND_MOVE:
    			if (dir == NORTH)
    				for (i = 1; i <= arg; i++) {
    					turtlePosY++;
    					if (stat == DOWN)
    						floor[turtlePosX][turtlePosY] = 1;
    				}
    			else if (dir == EAST)
    				for (i = 1; i <= arg; i++) {
    					turtlePosX++;
    					if (stat == DOWN)
    						floor[turtlePosX][turtlePosY] = 1;
    				}
    			else if (dir == SOUTH)
    				for (i = 1; i <= arg; i++) {
    					turtlePosY--;
    					if (stat == DOWN)
    						floor[turtlePosX][turtlePosY] = 1;
    				}
    			else if (dir == WEST)
    				for (i = 1; i <= arg; i++) {
    					turtlePosX--;
    					if (stat == DOWN)
    						floor[turtlePosX][turtlePosY] = 1;
    				}
    			break;
    		case COMMAND_PRINT:
    			printFloor(floor);
    			break;
    		default:
    			break;
    	}
    }
    
    /* Print the entire array to output.  0,0 will be lower left corner */
    void printFloor(const int floor[][ARRAYMAX])
    {
    	int i,j;
    	
    	printf("\n");
    	for (i = ARRAYMAX - 1; i >= 0; i--) {     /* in each row...     */
    		for (j = 0; j <= ARRAYMAX - 1; j++) {  /* for each column... */
    			if (floor[j][i] == 1)        /* print '*' if activated... */
    				printf("*");
    			else                         /* and a blank if not        */
    				printf(" ");
    		}
    		printf("\n");  /* print a newline after the last column */
    	}
    }
    And for the output:

    Code:
    jason@lappy:~/c$ ./turtle
    2
    5,15
    3
    5,15
    3
    5,15
    3
    5,15
    1
    6
    9
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    ****************
    *              *
    *              *
    *              *
    *              *
    *              *
    *              *
    *              *
    *              *
    *              *
    *              *
    *              *
    *              *
    *              *
    *              *
    ****************
    jason@lappy:~/c$
    Last edited by impact201; 07-13-2006 at 10:10 AM.

  5. #5
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    You're passing this:
    Code:
    int floor[][ARRAYMAX]
    to this:
    Code:
    const int floor[][ARRAYMAX]
    You can't really have const pointers to pointers. I can't explain why very coherently; try searching google or looking up in your book "const pointer".
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  6. #6
    Registered User
    Join Date
    Jul 2006
    Posts
    3
    Will do...thanks again.

    The only reason I made it const in the first place, was the book stresses the "principle of least privilege". That is, not giving a function the ability to modify something unless it actually needs to. That was also the reason for prototyping functions inside other functions in my original version -- to only give that one function the ability to call it. (And yes, I do realize I violated that principle with the global vars. I may revisit that in the future.)

    For reference, the book is "C How to Program" by Deitel. It starts with C, then gets into some C++ and touches on Java. I am partway through the chapter covering pointers, so hopefully things will become more clear shortly.
    Last edited by impact201; 07-14-2006 at 01:48 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Turtle Graphics, how does it work?
    By freddyvorhees in forum C++ Programming
    Replies: 15
    Last Post: 08-28-2009, 09:57 AM
  2. turtle graphics with c
    By datainjector in forum C Programming
    Replies: 4
    Last Post: 08-11-2002, 07:11 PM
  3. turtle graphics
    By condorx in forum C Programming
    Replies: 2
    Last Post: 05-10-2002, 02:27 PM
  4. turtle graphics problem??
    By atif in forum C++ Programming
    Replies: 2
    Last Post: 04-29-2002, 12:48 PM
  5. turtle graphics
    By korbbd in forum C++ Programming
    Replies: 0
    Last Post: 03-06-2002, 03:14 PM