To begin, fork this project. Notice it's structure. main.py contains the unit test, and each class described below has its own file. You won't write code into main.py; instead you'll write into the class files. If the unit test cannot create the objects it wishes to create, or it does not find the methods it wishes to call, it will raise an exception and quit. In this case no report will be generated.
Before the actual unit test are a number of lines of code that have been commented out. These will provide feedback as you work towards the point where the unit test will actually generate a report.
I won't unit test the Duck and Army classes. Thus your grade won't be determined by the unit test alone. But I will play a bit with your Duck and Army classes and give them full credit if they seem ballpark right. Let's say five points each.
Create a DomesticDuck class. The constructor for that class should set two attributes: breed and sex, both given as strings. The class should have these methods: the getBreed method, the getSex method, the says method, the walk method, the fly method and the court method. The last of these should take as argument a second duck that is courted by the duck on which the method is called. Also override the __repr__ method so that when we print a particular duck, we get a sentence that reports the breed and sex. (If you forget what that means, look back over the Human class we created.)
Add these lines to main:
Mallory = duck.DomesticDuck('Abacot Ranger', 'female')
Millard = duck.DomesticDuck('Buff Duck', 'male')
print(Mallory)
print(Millard)
print(Mallory.getBreed(), Mallory.getSex())
print(Mallory.says())
print(Mallory.walk())
print(Mallory.fly())
print(Mallory.court(Millard))
print(Millard.getBreed(), Millard.getSex())
print(Millard.says())
print(Millard.walk())
print(Millard.fly())
print(Millard.court(Mallory))
The output should be (something very much like):
A female duck of breed Abacot Ranger.
A male duck of breed Buff Duck.
Abacot Ranger female
A quack with a hint of sarcasm.
Waddle with a bit of sway.
I don't take orders!
Ducklings on the way!
Buff Duck male
A slightly befuddled quack.
Waddle with a bit of swagger.
Too lazy to get feathered butt off couch.
Rebuffed!
Feel free to insert your own jokes (but do keep it clean).
Create an Army class. The constructor for that class should set nine attributes: number of infantry (an int), number of archers (an int), number of cavalry (an int), food reserves (an int), stamina (a float between 0 and 1), army name (a str), army color (a str), a bool that answers the question, 'Does this army have a dragon?' and finally a bool that answers the question 'Has this army been defeated?' (You should be able to infer the intended names of these attributes from the code below.) The class should have these methods: makeCamp, march, pillage and attack. The makeCamp method should accept a positive integer that represents the number of days to make camp; it should increase stamina and decrease food reserves. The march method should accept a positive integer that represents the number of days to march; it should decrease both stamina and food reserves. The pillage method should accept a positive integer that represents the number of villages to pillage; it should increase food reserves and decrease stamina. A call to the attack method should specify a second army that will fight the army on which the method is called. I'll let you decide on a formula to determine which army wins. Also override the __repr__ method so that when we print an army, we get a clear, nicely formatted paragraph that gives its attributes.
Add these lines to main:
Targaryen = army.Army(1000, 100, 200, 10000, 0.8, 'Targaryen', 'silver', True, False)
Stark = army.Army(2000, 200, 300, 8000, 0.7, 'Stark', 'red', False, False)
print(Targaryen.name, Targaryen.color)
print(Targaryen.numInf, Targaryen.numArch, Targaryen.numCav)
print(Targaryen.food, Targaryen.stamina)
print(Targaryen.hasDragon, Targaryen.isDefeated)
print()
print(Stark.name, Stark.color)
print(Stark.numInf, Stark.numArch, Stark.numCav)
print(Stark.food, Stark.stamina)
print(Stark.hasDragon, Stark.isDefeated)
print()
Targaryen.makeCamp(2)
print(Targaryen.food, Targaryen.stamina)
Stark.march(12)
print(Stark.food, Stark.stamina)
Stark.pillage(3)
print(Stark.food, Stark.stamina)
Targaryen.attack(Stark)
print(Targaryen.isDefeated, Stark.isDefeated)
The output should look like this (though some details might differ):
Targaryen silver
1000 100 200
10000 0.8
True False
Stark red
2000 200 300
8000 0.7
False False
9000 0.9
6000 0.6
11000 0.5
False True
Create a Point class. The constructor for that class should set two attributes: the x-coordinate of the point and the y-coordinate of the point. Call these xCor and yCor. The class should have these methods: getX, getY, distance, slope, and direction.
Method directions:
getX and getY return the x- and y-coordinates respectively of a point
distance returns the distance between a pair of points. The syntax is point1.distance(point2). This means that the distance method will have two parameters, like this perhaps: def distance(self, second). self would take point1 for its value, and second would take point2.
slope returns the slope of the line through a pair of points. The syntax is point1.slope(point2). If there is no slope (as is the case when the line is vertical), have the function return None.
direction returns the angle (in radians) that the line through a pair of points makes with the x_axis. The syntax is point1.direction(point2). (Hint: arctangent. Google it.)
Also override the __repr__ method so that we get the usual representation of a point . Like this: (-12, 15).
Add these lines to main:
p1 = point.Point(1, 1)
p2 = point.Point(4, 0)
p3 = point.Point(2, 2)
print(p1, p2, p3)
print(p2.getX(), p3.getY())
print(p1.distance(p2))
print(p1.slope(p3))
print(p2.direction(p3))
The output should look (something very) like:
(1, 1) (4, 0) (2, 2)
4 2
3.16227766017
1.0
-0.785398163397
Create a Line class. The constructor should set three attributes: slope, yInt and equation. The first two of these are floats. The third is a function. What function? A function that takes an x and returns the y such that (x, y) is a point on the line. How do you create such a function? Look back to our work with higher order functions.
Note that the equation will not be sent to the class constructor. For instance, in the line of code (see below) L1 = line.Line(1, 0), we send only the slope 1 and the y-intercept 0. Where does the equation come from then? You create it in the constructor! That's perfectly cool. You can create a value in the constructor and make it the initial value of some attribute.
The class should have these methods: isParallel, isPerp, intAt, and getY.
Method directions:
isParallel should return True if (and only if) a pair of lines are parallel. Let us say that two lines are parallel when and only when they have the same slope. This implies that a line is parallel to itself. (Revised 4.21.2021.)
isPerp should return True if (and only if) a pair of lines are perpendicular.
intAt should return a tuple of the x- and y-coordinates of the intersection of a pair of lines. If they do not intersect at a single point (either because they don't intersect at all or because they intersect everywhere), have the function return None. (You'll have to do a little algebraic work on pencil and paper to get the formula you need. Also remember: if two lines have the same slope but different y-intercepts, the don't intersect anywhere; and if they have the same slope and the same y-intercept, they intersect everywhere.)
getY should return the y-coordinate of a point on the line given the x-coordinate of that point.
Also override the __repr__ method so that we get the usual representation of a line. Like this: y = -12x + 15.
Add these lines to main:
L1 = line.Line(1, 0)
L2 = line.Line(-1, 0)
L3 = line.Line(3, -9)
L4 = line.Line(3, 6)
print(L1)
print(L3)
print(L1.getY(2), L2.getY(-3), L3.getY(4), L4.getY(-5))
print(L1.isParallel(L2), L1.isPerp(L2))
print(L3.isParallel(L4), L3.isPerp(L4))
L5 = line.Line(4, 8)
L6 = line.Line(-4, -4)
print(L5.intAt(L6))
The output should be:
y = 1x + 0
y = 3x + -9
2 3 3 -9
False True
True False
(-1.5, 2.0)
Create a Triangle class. The constructor for that class should set three attributes: location of first vertex, location of second vertex and location of final vertex. Call them v1, v2 and v3. Those locations will be given as points. Yes, I mean points as defined in the Point class. Thus to create a triangle, you first create three points; and then you make those three points attributes of a triangle. The class should have these methods: getVertices, perimeter, area, isCongruent, slide and contains .
Method directions:
getVertices should return a three-member tuple of points.
You'll find distance between points to compute perimeter.
Hint for area: Heron's Formula.
isCongruent returns True if a pair of triangles are congruent; False otherwise. Syntax: triangle1.isCongruent(triangle2).
slide takes a triangle (call it S) and a pair of numbers of uses them to create a second triangle (call it T). How? The first number in the pair is how much we are to change each x-coordinate in a vertex of S; the second number in the pair is how much we are to change the y-coordinate in a vertex of S. Call these two numbers "xDelta" and "yDelta" respectively. (In math talk, "delta" means "change". Ever heard "delta-v"? That means change in velocity.) For instance, if S has vertices (-3, 4), (6, -9) and (0, 12) and xDelta and yDelta are 2 and -1 respectively, then T's vertices are (-1, 3), (8, -10) and (2, 11). So slide slides S over xDelta and up yDelta and so produces T. (I do mean for slide to create and return a triangle. So it will have a line of code like T = triangle.Triangle( ... ).
contains returns True if a given point lies inside a given triangle; False otherwise. Syntax: triangle.contains(point). Let's agree that if a point lies on a side, we'll count it as inside. (This is a bit of a challenge. I expect you'll have to think about it a bit.)
The class should also override the __repr__ method so that when we print a triangle, we get a tuple of its vertices.
A word of advice: don't test two floats for equality. Given that Python approximates when it does its computations, it might be that two floats which should be equal come out as not equal if we test for equality. So then, if our two floats are f1 and f2, don’t do this:
if f1 == f2:
Do this instead:
if abs(f1 - f2) < 0.000001:
This means that we will count f1 and f2 as equal if they are close enough.
Add these lines to main:
T1 = triangle.Triangle(p1, p2, p3)
print(T1)
print(T1.perimeter())
print(T1. area())
p4 = point.Point(3, 3)
p5 = point.Point(6, 2)
p6 = point.Point(4, 4)
p7 = point.Point(1, 3)
p8 = point.Point(1, 7)
p9 = point.Point(5, 11)
T2 = triangle.Triangle(p4, p5, p6)
T3 = triangle.Triangle(p7, p8, p9)
print(T1.isCongruent(T2))
print(T1.isCongruent(T3))
p10 = point.Point(3, 2)
print(T1.contains(p10))
p11 = point.Point(2, 1)
print(T1.contains(p11))
p12 = point.Point(-3, 4)
p13 = point.Point(6, -9)
p14 = point.Point(0, 12)
T4 = triangle.Triangle(p12, p13, p14)
T5 = T4.slide(2, -1)
print(T5)
My output was:
(1, 1) (4, 0) (2, 2)
7.404918347287666
2.0000000000000013
True
False
False
True
(-1, 3) (8, -10) (2, 11)