To be done in class.
package edu.lmu.cs.geometry;
/**
* An immutable 2-D point class.
*/
public class Point {
private double x;
private double y;
/**
* Constructs a point.
*/
public Point(double x, double y) {
this.x = x;
this.y = y;
}
/**
* Returns the x coordinate.
*/
public double getX() {
return x;
}
/**
* Returns the y coordinate.
*/
public double getY() {
return y;
}
/**
* Returns the distance from this point to the given point.
*/
public double distanceTo(Point p) {
return Math.sqrt((x-p.x)*(x-p.x) + (y-p.y)*(y-p.y));
}
@Override
public int hashCode() {
final int PRIME = 31;
int result = 1;
long temp = Double.doubleToLongBits(x);
result = PRIME * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(y);
result = PRIME * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
final Point other = (Point) obj;
if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
return false;
if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
return false;
return true;
}
/**
* Returns a conventional string representation of this point,
* namely "(x,y)".
*/
@Override
public String toString() {
return "(" + x + "," + y + ")";
}
}
package edu.lmu.cs.geometry;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import org.junit.Test;
/**
* A unit test for points.
*/
public class PointTest {
private static double TOLERANCE = 0.0000000001;
@Test
public void testGetters() {
Point p = new Point(3, 5);
assertEquals(p.getX(), 3.0, TOLERANCE);
assertEquals(p.getY(), 5.0, TOLERANCE);
}
@Test
public void testToString() {
Point p = new Point(3, 5);
assertEquals(p.toString(), "(3.0,5.0)");
}
@Test
public void testEquals() {
Point p = new Point(3, 5);
Point q = new Point(3.0000000000001, 5);
assertEquals(p, p);
assertEquals(p, new Point(3, 5));
assertFalse(p.equals(q));
assertFalse(p.equals(new Point(3, 5.2)));
assertFalse(p.equals("I am not a point"));
assertFalse(p.equals("(3.0,5.0)"));
}
/**
* Some distance checks. We also check that the distance computation
* is symmetric in all cases.
*/
@Test
public void testDistance() {
Point p = new Point(3, 5);
Point q = new Point(9, 5);
// Check points with same y value
assertEquals(p.distanceTo(q), 6.0, TOLERANCE);
assertEquals(q.distanceTo(p), 6.0, TOLERANCE);
// Check points with same x value
q = new Point(3, 12);
assertEquals(p.distanceTo(q), 7.0, TOLERANCE);
assertEquals(q.distanceTo(p), 7.0, TOLERANCE);
// A 5-12-13 right triangle
q = new Point(-2, 17);
assertEquals(p.distanceTo(q), 13.0, TOLERANCE);
assertEquals(q.distanceTo(p), 13.0, TOLERANCE);
}
}
package edu.lmu.cs.geometry;
/**
* A 2-D line class.
*/
public class Line {
// A line is represented as a point and a direction, specified as
// the number of radians rotated counterclockwise from the x-axis.
// Invariant: the angle is always stored as a value between 0.0
// inclusive and 2*Math.PI (exclusive).
private Point base;
private double direction;
/**
* Constructs a line from base point coordinates and a direction.
* The line constructor takes in point coordinates rather than
* points so we never have to worry about pesky nulls.
*/
public Line(double x, double y, double direction) {
this.base = new Point(x, y);
this.direction = direction;
normalizeDirection();
}
/**
* Returns a point on this line.
*/
public Point getBase() {
return base;
}
/**
* Returns the direction of this line, in radians counterclockwise
* from the x axis.
*/
public double getDirection() {
return direction;
}
/**
* Shift the line so that it contains a point with the given
* coorindates.
*/
public void moveTo(double x, double y) {
base = new Point(x, y);
}
/**
* Rotate the line by theta radians around its base.
*/
public void rotate(double theta) {
direction += theta;
normalizeDirection();
}
/**
* Returns the distance from this line to the given point. Adapted
* from http://softsurfer.com/Archive/algorithm_0102/algorithm_0102.htm,
* which gave the distance formula assuming that two points on a line
* were given. The two points chosen were p and p+<cos(direction),
* sin(direction)> then the equation simplified quite a bit.
*/
public double distanceTo(Point p) {
return Math.abs(Math.sin(direction) * (base.getX() - p.getX())
+ Math.cos(direction) * (p.getY() - base.getY()));
}
/**
* Returns whether the direction of this line is within a given
* number of radians to another line.
*/
public boolean isParallelTo(Line other, double tolerance) {
return Math.abs(direction - other.getDirection())
< Math.abs(tolerance);
}
/**
* Returns a conventional string representation of this line,
* something like <tt>(2,4)+<8,2>u</tt>.
*/
@Override
public String toString() {
return base + " + <" + Math.cos(direction) + ","
+ Math.sin(direction) + ">u";
}
/**
* Force the direction to be between 0 and 2π.
*/
private void normalizeDirection() {
direction %= 2.0 * Math.PI;
if (direction < 0.0) {
direction += 2.0 * Math.PI;
}
}
}
package edu.lmu.cs.geometry;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import org.junit.Test;
/**
* A unit test for lines.
*/
public class LineTest {
private static double TOLERANCE = 0.0000000001;
@Test
public void testGetters() {
Line line = new Line(3, 5, 0);
assertNotNull(line);
assertNotNull(line.getBase());
assertEquals(line.getBase(), new Point(3, 5));
assertEquals(line.getDirection(), 0.0, TOLERANCE);
}
@Test
public void testToString() {
Line line = new Line(3, 5, 0);
assertEquals(line.toString(), "(3.0,5.0) + <1.0,0.0>u");
}
@Test
public void testMutators() {
Line line = new Line(2, 9, Math.PI/4);
assertEquals(line.getBase(), new Point(2, 9));
line.moveTo(12, 6);
assertEquals(line.getBase(), new Point(12, 6));
assertEquals(line.getDirection(), Math.PI/4, TOLERANCE);
line.rotate(3.0*Math.PI/4);
assertLineDirection(line, Math.PI);
line.rotate(0.3);
line.rotate(101*Math.PI);
assertLineDirection(line, 0.3);
line = new Line(4, 4, 0);
line.rotate(-101*Math.PI);
assertLineDirection(line, Math.PI);
}
@Test
public void testParallel() {
Line l1 = new Line(3, 5, 4);
Line l2 = new Line(33, -529, 4 + 8 * Math.PI);
Line l3 = new Line(0, 0, 4 - 200 * Math.PI);
Line l4 = new Line(6, 8888, 1000);
assertTrue(l1.isParallelTo(l2, TOLERANCE));
assertTrue(l2.isParallelTo(l1, TOLERANCE));
assertTrue(l1.isParallelTo(l3, TOLERANCE));
assertFalse(l1.isParallelTo(l4, TOLERANCE));
}
/**
* For various lines -- horizontal, vertical, positive slope,
* negative slope -- test distance to points in all four quadrants
* around the base point as well as points on the line.
*/
@Test
public void testDistances() {
// Vertical line in up direction
Line line = new Line(3, 4, Math.PI / 2.0);
assertDistance(line, 0, -5, 3.0);
assertDistance(line, 5, -3, 2.0);
assertDistance(line, 2, 9, 1.0);
assertDistance(line, 6, 7, 3.0);
assertDistance(line, 3, 3, 0.0);
assertDistance(line, 3, 5, 0.0);
// Vertical line in down direction
line = new Line(3, 4, 3 * Math.PI / 2.0);
assertDistance(line, 0, -5, 3.0);
assertDistance(line, 5, -3, 2.0);
assertDistance(line, 2, 9, 1.0);
assertDistance(line, 6, 7, 3.0);
assertDistance(line, 3, 3, 0.0);
assertDistance(line, 3, 5, 0.0);
// Horizontal line going from left to right
line = new Line(3, 4, 0);
assertDistance(line, -3, -5, 9.0);
assertDistance(line, 5, -3, 7.0);
assertDistance(line, 2, 9, 5.0);
assertDistance(line, 6, 7, 3.0);
assertDistance(line, 2, 4, 0.0);
assertDistance(line, 4, 4, 0.0);
// Horizontal line going from right to left
line = new Line(3, 4, Math.PI);
assertDistance(line, -3, -5, 9.0);
assertDistance(line, 5, -3, 7.0);
assertDistance(line, 2, 9, 5.0);
assertDistance(line, 6, 7, 3.0);
assertDistance(line, 2, 4, 0.0);
assertDistance(line, 4, 4, 0.0);
// Positive slope from quadrant 3 to quadrant 1
line = new Line(4, 3, Math.atan2(3, 4));
assertDistance(line, -4, 7, 8.0);
assertDistance(line, 0, -5, 4.0);
assertDistance(line, 8, 1, 4.0);
assertDistance(line, 12, 59, 40.0);
assertDistance(line, -4, -3, 0.0);
assertDistance(line, 16, 12, 0.0);
// Positive slope from quadrant 1 to quadrant 3
line = new Line(4, 3, Math.atan2(-3, -4));
assertDistance(line, -4, 7, 8.0);
assertDistance(line, 0, -5, 4.0);
assertDistance(line, 8, 1, 4.0);
assertDistance(line, 12, 59, 40.0);
assertDistance(line, -4, -3, 0.0);
assertDistance(line, 16, 12, 0.0);
// Negative slope from quadrant 4 to quadrant 2
line = new Line(-4, 3, Math.atan2(3, -4));
assertDistance(line, -4, -7, 8.0);
assertDistance(line, 0, 5, 4.0);
assertDistance(line, -12, 8.5, 0.4);
assertDistance(line, -12, 24, 12.0);
assertDistance(line, -12, 9, 0.0);
assertDistance(line, 8, -6, 0.0);
// Negative slope from quadrant 2 to quadrant 4
line = new Line(-4, 3, Math.atan2(-3, 4));
assertDistance(line, -4, -7, 8.0);
assertDistance(line, 0, 5, 4.0);
assertDistance(line, -12, 8.5, 0.4);
assertDistance(line, -12, 24, 12.0);
assertDistance(line, -12, 9, 0.0);
assertDistance(line, 8, -6, 0.0);
}
// Helper for direction tests.
private void assertLineDirection(Line line, double expected) {
assertEquals(line.getDirection(), expected, TOLERANCE);
}
// Helper for distance tests.
private void assertDistance(Line line, double x, double y, double expected) {
assertEquals(line.distanceTo(new Point(x, y)), expected, TOLERANCE);
}
}
package edu.lmu.cs.games;
/**
* An immutable playing card.
*/
public class Card {
private Rank rank;
private Suit suit;
public static enum Rank {
ACE("A"), TWO("2"), THREE("3"), FOUR("4"), FIVE("5"),
SIX("6"), SEVEN("7"), EIGHT("8"), NINE("9"), TEN("10"),
JACK("J"), QUEEN("Q"), KING("K");
private String displayValue;
private Rank(String displayValue) {
this.displayValue = displayValue;
}
/**
* Returns the rank whose display value is the given string.
*/
public static Rank fromString(String text) {
for (Rank r: values()) {
if (r.displayValue.equals(text)) return r;
}
throw new IllegalArgumentException("No rank for " + text);
}
@Override
public String toString() {
return displayValue;
}
}
public static enum Suit {
CLUBS, DIAMONDS, HEARTS, SPADES;
@Override
public String toString() {
return name().substring(0, 1);
}
/**
* Returns the suit whose display value is the given string.
*/
public static Suit fromString(String text) {
for (Suit s: values()) {
if (s.toString().equals(text)) return s;
}
throw new IllegalArgumentException("No suit for " + text);
}
}
// Pre-allocate a cache of all possible cards
private static Card[][] cache =
new Card[Rank.values().length][Suit.values().length];
// Fill in the cache with all possible cards
static {
for (Rank rank: Rank.values()) {
for (Suit suit: Suit.values()) {
cache[rank.ordinal()][suit.ordinal()] = new Card(rank, suit);
}
}
}
/**
* Returns the unique card whose display value is the given
* string, e.g. "10S", "KH", "6D".
*/
public static Card fromString(String descriptor) {
if (descriptor == null || descriptor.length() < 2) {
throw new IllegalArgumentException("Bad card description: "
+ descriptor);
}
String rank = descriptor.substring(0, descriptor.length() - 1);
String suit = descriptor.substring(descriptor.length() - 1);
return fromRankAndSuit(Rank.fromString(rank),
Suit.fromString(suit));
}
/**
* Return the unique card with the given rank and suit.
*/
public static Card fromRankAndSuit(Rank rank, Suit suit) {
return cache[rank.ordinal()][suit.ordinal()];
}
/**
* Constructs a card with the given rank and suit. This constructor
* is private because the only way to get a card object from outside
* of this class is through the factory method, which always pulls
* the proper card from the cache.
*/
private Card(Rank rank, Suit suit) {
this.rank = rank;
this.suit = suit;
}
/**
* Returns the rank of this card.
*/
public Rank getRank() {
return rank;
}
/**
* Returns the suit of this card.
*/
public Suit getSuit() {
return suit;
}
@Override
public String toString() {
return rank.toString() + suit.toString();
}
}
and unit tests
package edu.lmu.cs.games;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import edu.lmu.cs.games.Card.Rank;
import edu.lmu.cs.games.Card.Suit;
/**
* Unit tester for the card class.
*/
public class CardTest {
@Test
public void testRankAndSuitFactoryMethod() {
Card c = Card.fromRankAndSuit(Rank.SIX, Suit.CLUBS);
assertEquals(c.getRank(), Rank.SIX);
assertEquals(c.getSuit(), Suit.CLUBS);
assertFalse(c.getRank() == Rank.KING);
assertFalse(c.getSuit() == Suit.SPADES);
assertEquals(c.toString(), "6C");
}
@Test
public void testDescriptorFactoryMethod() {
assertCardEquals(Card.fromString("AS"), Rank.ACE, Suit.SPADES);
assertCardEquals(Card.fromString("2S"), Rank.TWO, Suit.SPADES);
assertCardEquals(Card.fromString("3S"), Rank.THREE, Suit.SPADES);
assertCardEquals(Card.fromString("4S"), Rank.FOUR, Suit.SPADES);
assertCardEquals(Card.fromString("5S"), Rank.FIVE, Suit.SPADES);
assertCardEquals(Card.fromString("6C"), Rank.SIX, Suit.CLUBS);
assertCardEquals(Card.fromString("7S"), Rank.SEVEN, Suit.SPADES);
assertCardEquals(Card.fromString("8S"), Rank.EIGHT, Suit.SPADES);
assertCardEquals(Card.fromString("9S"), Rank.NINE, Suit.SPADES);
assertCardEquals(Card.fromString("10H"), Rank.TEN, Suit.HEARTS);
assertCardEquals(Card.fromString("JS"), Rank.JACK, Suit.SPADES);
assertCardEquals(Card.fromString("QD"), Rank.QUEEN, Suit.DIAMONDS);
assertCardEquals(Card.fromString("KS"), Rank.KING, Suit.SPADES);
}
@Test
public void testUnique() {
Card c1 = Card.fromRankAndSuit(Rank.NINE, Suit.CLUBS);
Card c2 = Card.fromRankAndSuit(Rank.NINE, Suit.CLUBS);
assertTrue(c1 == c2);
c1 = Card.fromString("10S");
c2 = Card.fromString("10S");
assertTrue(c1 == c2);
}
@Test
public void testToString() {
assertCardStringEquals(Rank.ACE, Suit.SPADES, "AS");
assertCardStringEquals(Rank.TWO, Suit.DIAMONDS, "2D");
assertCardStringEquals(Rank.THREE, Suit.SPADES, "3S");
assertCardStringEquals(Rank.FOUR, Suit.SPADES, "4S");
assertCardStringEquals(Rank.FIVE, Suit.SPADES, "5S");
assertCardStringEquals(Rank.SIX, Suit.HEARTS, "6H");
assertCardStringEquals(Rank.SEVEN, Suit.SPADES, "7S");
assertCardStringEquals(Rank.EIGHT, Suit.SPADES, "8S");
assertCardStringEquals(Rank.NINE, Suit.SPADES, "9S");
assertCardStringEquals(Rank.TEN, Suit.SPADES, "10S");
assertCardStringEquals(Rank.JACK, Suit.SPADES, "JS");
assertCardStringEquals(Rank.QUEEN, Suit.SPADES, "QS");
assertCardStringEquals(Rank.KING, Suit.SPADES, "KS");
}
// Helper for factory method tests
private void assertCardEquals(Card card, Rank rank, Suit suit) {
assertEquals(card.getRank(), rank);
assertEquals(card.getSuit(), suit);
}
// Helper for toString tests
private void assertCardStringEquals(Rank rank, Suit suit, String expected) {
assertEquals(Card.fromRankAndSuit(rank, suit).toString(), expected);
}
}