Goals for this homework

  • Practice the C# development cycle
  • Write a small C# program to get used to the syntax and packaging code in classes.
  • Write program that exercises functions and control.
  • Practice writing test code.

You are expected to complete this assignment individually. If you need help, you are invited to come to office hours and/or ask questions on Ed. Clarification questions about the assignments may be asked publicly. Once you have specific bugs related to your code, make the posts private.

Another note about Ed. Ed is your community resource - please use those as discussions amongst yourselves. We are not monitoring it all day, rather we each have check-in times once a day. Therefore, you need to start early enough to wait for feedback and/or build a vibrant, supportive community that helps each other while we are completing other necessary tasks (research, developing assignments, preparing for lecture, performing advising tasks, etc.).

In this homework, you'll create what we a sprite - a moveable object in a game. This sprite has specific elements of state. You will then write functions that modify the state based on actions in the game.

You should submit several files for this assignment (Lab1SampleFunctions.cs, Puppy.cs, TestLab1.cs, testlab1.txt, Sprite.cs, TestSprite.cs, testsprite.txt, Makefile). You will submit your work in a zip file to Gradescope. More detailed submission instructions will come later.

Error Handling

Error handling capabilities vary by language, and what you want to do in an error varies by the situation. When an error occurs in a function, the question becomes, what should you do, and how do you notify the caller that an error occurred?

In C#, there is a construct for this. We'll use this when calling functions that use it but, right now, we won't implement them ourselves. This is an exercise because if you write a function, and it's used in a variety of different circumstances, it is bad programming practice to determine within the function what will be done. For example, one program might want to exit, whereas another might want to notify the user that there was bad input and to try again.

In this course, we will print the error message in a special way (see below). If there is an opportunity, we will designate a specific return value for an error condition. If there is no available return value, then we will exit from within the function.

  Console.WriteLine("error: too many widgets for the number of grommets");
  Console.WriteLine("error: need ten boondoggles, but only have "+ num_bds);

Sometimes, these lines will be followed by exit(1); This immediately exits the program and returns a code. If you were writing a large program, you might assign a different code to each type of error that would result in an exit.

Makefile

Because you are adding a second set of files to your directory, you need to add a second target to your Makefile. In your makefile, add another two lines (with a space between these and the ones already there).
TestSprite.exe: Sprite.cs TestSprite.cs
	mcs TestSprite.cs Sprite.cs
To compile, type:
make TestSprite.exe

Set Up

The first thing to do is to create a skeleton project that will minimally execute. This week, I provided it for you. In the future, you will be expected to create it for yourself. A skeleton has the following in it:
  • Step 1: In the one file, put in the function declarations. In the body, print the fact that it is not yet implemented and return with the right type. For example:
    public float surface_area_cylinder(float height, float radius)
    {
    	Console.WriteLine("surface_area_cylinder not yet implemented");
    	return 0.0;
    }
    
  • Step 2: Create the test file with the Main method. For each method, put in a single call. For example:
    int Main(string[] args)
    {
    	float fval;
    	LabXMethods lxm = new LabXMethods();
    
    	fval = lxm.surface_area_cylinder(3.5, 7.9);
    }
    

First get this compiling and running. It won't do anything useful, but this will mean that your code will compile and execute with our infrastructure. This must work in order to get any points in this course. Do this first, not last.

Sprite Class

The Sprite class holds all information necessary to display a Sprite in a game. This may be set up slightly differently within Unity, which we will learn later, but we're making a generic class that could be the starting point for any game system. As our working example, we will use the players in Fortnite.

When we design a class, we split it up into two parts. First, what is the information we need to track at any moment in time? Second, what actions occur that change that information?

When we think of a character in a game, there are several attributes to keep track of. We need the character's location (x, y, z coordinates), direction they are pointing (of 360 degrees), their health points (out of 100) and, in the case of Fortnite, their shield points (out of 100). Of course, Fortnite has many more than that - the character's "skin" (the look), the items they currently possess, the item they currently hold. whether they are doing an emote, whether they are flying, etc. For this assignment, we will focus on location, health, and shield.

The next step is to identify the actions that affect location, health, and shield.

If a person's health gets to 0, regardless of the state of the shield, the person is dead. Some actions affect the shield before the health, but others operate directly on the health.

  • public void HorizontalRotate(int degrees)
    rotate the person in degrees. Can be positive or negative. Resulting value must be between 0 and 359, so if it goes below or above those values, it must wrap around.
    There is no error condition - regardless of the input, you calculate the resulting position in degrees between 0 and 359.
  • public void VerticalRotate(int degrees)
    rotate the person in degrees. Can be positive or negative. Resulting value must be between 0 and 359, so if it goes below or above those values, it must wrap around.
    There is no error condition - regardless of the input, you calculate the resulting position in degrees between 0 and 359.
  • public void Move(int steps)
    Move the person towards or away from the direction they are pointing; positive numbers move forward, and negative numbers move backwards. This needs to take into account the number of steps, the direction, and their current position. For simplicity, we will only take into account their horizontal rotation (the Z coordinate will not be affected). Degrees = 0 indicates pointing in the positive X direction with no movement towards or away from Y. Degrees = 90 is positive Y direction, 180 is negative X direction, and 250 is negative Y direction. You will need sin and cos to calculate how much other angles affect the X and Y coordinates.
    There is no error condition - regardless of the input, you calculate the resulting position.
  • public uint DrinkSmallShieldPot()
    If shield is below 50, then add 25 shield (to a max of 50) and return 1. Otherwise, return 0.
  • public uint DrinkLargeShieldPot()
    If shield is below 100, then add 50 shield (to a max of 100) and return 1. Otherwise, return 0.
  • public uint ApplyBandage()
    If health is below 75, then add 15 health (to a max of 75) and return 1. Otherwise, return 0.
  • public uint UseMedKit()
    If health is below 100, then set health to 100 and return 1. Otherwise, return 0.
  • public uint WeaponDamage(uint damage)
    This takes off shield/health as a result of being hit by a weapon. If there is shield, take off damage amount, to a minimum of 0 shield. If damage is more than shield, take the rest off health, to a minimum of 1 health. Return 1 if person is still alive, 0 if they are dead. They are dead if health and shield are 0.
  • public uint Revive()
    This is applied only to a Sprite that is dead. If the Sprite is dead, restores it to 30 health, 0 shield. Return 1 if the person had been dead (and is now revived), 0 if it was called on an ineligible Sprite (and did nothing).
  • public uint Reboot()
    This is also applied only to a Sprite that is dead. If the Sprite is dead, restores it to 100 health, 0 shield. Return 1 if the person had been dead (and is now revived), 0 if it was called on an ineligible Sprite (and did nothing).

We must not forget to decide the starting state of the sprite. This is performed in the constructor

public Sprite()
Start at location 0, 0, 0, horizdirection 0, vertdirection 0. health 100 and shield 0.

Finally, we think about what methods would be useful for testing. For testing, we will provide a function that sets the state to whatever we want it to be.

  • public void SetState(int x, int y, int z, uint horiz, uint vert, uint h, uint s);
    This sets the variables XCoor, YCoor, ZCoor, Shield, Health, Vert, and Horiz.
  • public override bool Equals(Object obj)
    This checks to see if the state variables in one Sprite object are equal to another. I have provided the code for this method since it utilizes object-oriented concepts you have not yet covered. This is used to check if the state of the Sprite matches the expected state. DO NOT MODIFY THIS CODE.

Testing

For testing, the general test line will look like this:
TestSprite.exe start_state expected_end_state test# inputs expected_ret_val
The start state is given in the following order:
x, y, z, horiz, vert, health, shield We will have the following tests formats: (You need to add several tests for each function - I am just showing the format)
  • HorizontalRotate: TestSprite.exe x y z h v h s x y z h v h s 0 degrees
    TestSprite.exe 20 50 0 25 0 100 100 20 50 0 20 0 100 100 0 -5
  • VerticalRotate: TestSprite.exe x y z h v h s x y z h v h s 1 degrees
    TestSprite.exe 20 50 0 25 0 100 100 20 50 0 25 355 100 100 1 -5
  • Move: TestSprite.exe x y z h v h s x y z h v h s 2 steps
    TestSprite.exe 20 50 0 180 0 100 100 -3 50 0 180 0 100 100 2 23
  • DrinkSmallShieldPot: TestSprite.exe x y z h v h s x y z h v h s 3 ret_val
    TestSprite.exe 20 50 0 180 0 100 30 20 50 0 180 0 100 50 3 1
  • DrinkLargeShieldPot: TestSprite.exe x y z h v h s x y z h v h s 4 ret_val
    TestSprite.exe 20 50 0 180 0 100 30 20 50 0 180 0 100 80 4 1
  • ApplyBandage: TestSprite.exe x y z h v h s x y z h v h s 5 ret_val
    TestSprite.exe 20 50 0 180 0 33 30 20 50 0 180 0 48 30 5 1
  • UseMedKit: TestSprite.exe x y z h v h s x y z h v h s 6 ret_val
    TestSprite.exe 20 50 0 180 0 33 30 20 50 0 180 0 100 30 6 1
  • WeaponDamage: TestSprite.exe x y z h v h s x y z h v h s 7 damage ret_val
    TestSprite.exe 20 50 0 180 0 33 30 20 50 0 180 0 23 0 7 40 1
  • Revive: TestSprite.exe x y z h v h s x y z h v h s 8 ret_val
    TestSprite.exe 20 50 0 180 0 0 0 20 50 0 180 0 30 0 8 1
  • Reboot: TestSprite.exe x y z h v h s x y z h v h s 9 ret_val
    TestSprite.exe 20 50 0 180 0 0 0 20 50 0 180 0 100 0 9 1

Submit

Submit your code on Gradescope.