CS303E Project 2

Instructor: Dr. Bill Young
Due Date: Monday, April 3, 2023 at 11:59pm

Driving a Toy Car

This assignment asks you to simulate driving a simple toy car around a grid. This requires you to implement a ToyCar class with specified attributes and methods. You will also write several functions outside the class that use the class to implement more complicated behaviors of the car.

The ToyCar class

Here are the specific requirements for your ToyCar class:
  1. Implement your toy car as a class, with the following three private data members: x coordinate of the location, the y coordinate of the location, the direction the car is heading. The x and y coordinates should default to 0. (Note that I may refer to the location as (x, y), but you shouldn't implement location as a pair; store x and y as separate data members.) The direction is represented as a number which must be in [0, 90, 180, 270], representing east, north, west and south, respectively. (The number is the angle of rotation counter-clockwise from the x axis.) Direction should default to 0 (east) when you create a ToyCar object.

  2. You should have at least the following methods in your class:
Of course, the definitions of these will all need the self parameter. See the Program Structure section below.

Notice that commands that change the location or direction of the car have to update the class data members appropriately.

Other Functions:

In addition to the class described above, you will define several others functions that makes use of the class. They should be defined outside of the class definition, but in the same file. Feel free to define auxiliary functions if you like, but you must have:
  1. randomDrive( car, n ): This function drives the specified car randomly for n "moves." A move means to select randomly a turn direction (left, right, none) and a random integer distance [0..100]. Check that n is non-negative. If not, print "ERROR: Illegal value entered." and leave the state unchanged. Turn in the selected direction and then go forward the selected distance. Do this n times, choosing random turn direction and distance for each move.

  2. goto( car, x, y ): This function drives the specified car to location (x, y). Notice that our cars can only travel in cardinal directions. I.e., they can't go diagonally on the grid. So you'll potentially have to set direction east or west and move in the X dimension, and then set direction north or south and move in the Y dimension. Be sure to handle the cases where you're already on proper coordinate(s). (For example, if you're at (0, 0) and you need to move to (0, 100), you don't have to move east or west, only north.) You can assume that x and y are integers.

  3. gasStation(): In this world, gas is dispensed from a gas truck that moves around. Generate random integer coordinates x and y in the range [-100, 100]. That's the current location of the gas station. Print a message stating the current location of the gas station and return the location as a pair (x, y). (By itself, this command isn't very useful, since the next time you look, the gas station will have moved. However, it's used in the following function.)

  4. gasUp( car ): Find the current location of the gas station and go there. (Program this by calling the previous two commands.)
Notice that you'd ordinarily have to print out the car location and direction ( print( car ) should work to do that) after every move to see if you're doing things right. To make the output more understandable, I added some debugging print statements to the commands that change the car location or direction. You'll see those in the output. I following the useful convention of prefixing each of those with "DEBUG" to make them easy to find. I would typically remove or comment those out for the "production" version of the software. You should just leave them in.

Notice also the use of print statements on the car object. This only works after you've defined __str__() for the class.

Possible Program Structure

Below is a possible structure for your program. You don't have to follow this exactly. But it's probably a good model.
# Program header and description here.

import random
# Maybe other imports if you need them.

# Constants:
EAST = 0
NORTH = 90
...

class ToyCar:

    def __init__( self, x = 0, ... ):
        # Command should fail and print "ERROR: Illegal direction entered." 
        # if the provided direction is invalid.
        ...

    def __str__( self ):
        """ Generate a string containing information on 
        the class object. """
        ...

    def turnLeft( self ):
        ...

    # the other ToyCar methods

# The other functions you'll need to supply.  You can have more, but
# must have at least these four.

def randomDrive( car, n ):
    ...

def goto( car, x, y ):
    ...

def gasStation():
    ...

def gasUp(car):
    ...

# You're welcome to add a main() function and a call to main() to 
# include some test code.  But you can also test it from the 
# interactive loop.  Both are illustrated below.

Expected Output:

Below is a sample output for this program. Note that this is run in the Python interactive loop started from the command line. You can run yours from your IDE, but the TAs should be able to run it from the command line.

> python
Python 3.6.9 (default, Nov 25 2022, 14:10:45) 
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from Project2 import *
>>> c1 = ToyCar( 100, -100, SOUTH )
>>> print( c1 )
Your car is at location (100, -100), heading South
>>> c2 = ToyCar()
>>> print( c2 )
Your car is at location (0, 0), heading East
>>> c3 = ToyCar( y = -50, d = 313 )
ERROR: Illegal direction entered.
>>> c3 = ToyCar( y = -50, d = 90 )
>>> print( c3 )
Your car is at location (0, -50), heading North
>>> c = ToyCar( d = NORTH )
>>> print( c )
Your car is at location (0, 0), heading North
>>> c.__x                            # attributes must be private
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'ToyCar' object has no attribute '__x'
>>> c.forward( 100 )
DEBUG: moving forward 100
>>> print( c )
Your car is at location (0, 100), heading North
>>> c.getDir()
90
>>> c.turnLeft()
DEBUG: turning West
>>> print( c )
Your car is at location (0, 100), heading West
>>> c.forward( -50 )
ERROR: Illegal distance entered.
>>> c.forward( 50 )
DEBUG: moving forward 50
>>> print( c )
Your car is at location (-50, 100), heading West
>>> c.setDir( SOUTH )
DEBUG: setting direction South
>>> print( c )
Your car is at location (-50, 100), heading South
>>> ( c.getX(), c.getY() )
(-50, 100)
>>> randomDrive( c, 5 )      # yours will be different
DEBUG: turning West
DEBUG: moving forward 93
DEBUG: turning North
DEBUG: moving forward 88
DEBUG: turning West
DEBUG: moving forward 100
DEBUG: turning South
DEBUG: moving forward 34
DEBUG: turning East
DEBUG: moving forward 38
>>> print( c )
Your car is at location (-205, 154), heading East
>>> gasUp( c )              # yours will be different
Located gas station at (91, 9)
DEBUG: setting direction East
DEBUG: moving forward 296
DEBUG: setting direction South
DEBUG: moving forward 145
>>> print( c )
Your car is at location (91, 9), heading South
>>> gasStation()            # yours will different
Located gas station at (-92, 42)
(-92, 42)
>>> print( c )
Your car is at location (91, 9), heading South
>>> c4 = ToyCar(20, 30, SOUTH)
>>> print(c4)
Your car is at location (20, 30), heading South
>>> goto( c4, -50, -25 )
DEBUG: setting direction West
DEBUG: moving forward 70
DEBUG: setting direction South
DEBUG: moving forward 55
>>> print(c4)
Your car is at location (-50, -25), heading South
>>> 

Your output for these same commands would be slightly different, because the commands randomDrive, gasStation, and gasUp involved randomly generated values.

The above shows running the program interactively in the Python loop. You could also add a main function in your Project2.py file that includes the same commands. This is a nice way to debug without having to enter all the commands in the interactive loop. That would look as follows:

def main():
    c1 = ToyCar( 100, -100, SOUTH )
    print( c1 )
    c2 = ToyCar()
    print( c2 )
    c3 = ToyCar( y = -50, d = 313 )
    c3 = ToyCar( y = -50, d = 90 )
    print( c3 )
    c = ToyCar( d = NORTH )
    print( c )
    c.forward( 100 )
    print( c )
    # This one needs a print, or you won't see it.
    print( c.getDir() )
    c.turnLeft()
    print( c )
    c.forward( -50 )
    c.forward( 50 )
    print( c )
    c.setDir( SOUTH )
    print( c )
    # This one needs a print, or you won't see it.
    print( c.getX(), c.getY() )
    randomDrive( c, 5 )
    print( c )
    gasUp( c )
    print( c )
    gasStation()
    print( c )
    c4 = ToyCar(20, 30, SOUTH)
    print(c4)
    goto( c4, -50, -25 )
    print(c4)

main()
You would then call the program like any other. You also might want to add additional prints to make the output more understandable. Please comment out the call to main() before you submit.

Turning in the Assignment:

The program should be in a file named Project2.py. Submit the file via Canvas before the deadline shown at the top of this page. Submit it to the assignment project2 under the assignments sections by uploading your Python file. Make sure that you following good coding style and use comments.

Your file must compile and run before submission. It must also contain a header with the following format:

# File: Project2.py
# Student: 
# UT EID:
# Course Name: CS303E
# 
# Date: 
# Description of Program: 

Programming Tips:

Program incrementally: Writing any complex program should be done in pieces. It's crazy to do the entire thing and then try to debug it. Yet that's what I frequently see students do. Instead, write the simplest version first and test it before you move on. For example, write a simple version of your ToyCar class, with the init and str functions. Get that working. Then start adding in the other methods and testing them. The extra time will more than pay off in reduced frustration!

Use functions: If you see that you're doing some computation multiple times (e.g., getting the name of a direction like "East" from the number 0) write a function to handle that. Always test the function before you move on.

Use print for debugging: If you don't quite see what's going wrong at any point, add print statements. Often seeing what values are being generated as you go is enough to understand your error. Just remember to comment out or remove the extra print statement before you submit.