Rectangle Object-Oriented Development - Equals() method - java

need some help here. Working on "Classes and Object-Oriented Development" and could use some help with both my logic and code for a question in the textbook.
Question: I am asked to Modify my previous example of my Rectangle class to override the equals() and toString() methods. Two rectangles are equal when they both have the same length and width.
My approach: I tried to change it to do this, and then decided it would be easier to compare by areas, rather than comparing both by width and length, so below is what I have so far. Let me know if you have any ideas to help. There is a previous example of the equals() method that compares a circle's radius but isnt helping when comparing 2 different things. Thanks before hand all ! If your wondering why they are all not their own separate files, I haven't gotten there in the chapter yet so it's alot to look at I know ;P
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package chapter8javaExamples;
/**
*
* #author Eric
*/
public class Rectangle {
private double length, width;
/**
* constructor
* pre: none
* post: A rectangle class is created.
*/
public Rectangle() {
length = 2; //default length
width = 4; //default width
}
/**
* constructor
* pre: none
* post: A rectangle object is created with length and width.
*/
public Rectangle (double l, double w) {
length = l;
width = w;
}
/**
* Changes the length of the rectangle
* pre: none
* post: Length has been changed.
*/
public void setLength (double newLength) {
length = newLength;
}
/**
* Changes the width of the rectangle.
* pre: none
* post: Width has been changed.
*/
public void setWidth (double newWidth) {
width = newWidth;
}
/**
* Returns the length of the rectangle.
* pre: none
* post: The length of the rectangle has been returned.
*/
public double getLength() {
return(length);
}
/**
* Returns the width of the rectangle.
* pre: none
* post: The width of the rectangle has been returned.
*/
public double getWidth() {
return(width);
}
/**
* Returns the area of rectangle
* pre: none
* post: The area of the rectangle is returned
*/
public double area() {
double triArea;
triArea = length * width;
return(triArea);
}
/**
* Returns the perimeter of the rectangle
* pre: none
* post: The perimeter of the rectangle is returned
*/
public double perimeter() {
double triPer;
triPer = length + width + length + width;
return(triPer);
}
/**
* Displays the formula for area of a rectangle.
* pre: none
* post: The formula is displayed.
*/
public static void displayAreaFormula(){
System.out.println("The formula for the area of a rectangle is a=l*w");
}
/**
* Determines if the object is equal to another
* Circle object.
* pre: c is a Circle object.
* post: true has been returned if the objects have
* the same radii, false otherwise.
*/
public boolean equals(Object r) {
Rectangle testObj = (Rectangle) r;
Rectangle testObj2 = (Rectangle) r2;
if (testObj.getArea() == area && testObj2.getArea == area()) {
return(true);
} else {
return(false);
}
}
/**
* Returns a String that represents the Circle object.
* pre: none
* post: A string representing the Circle object has
* been returned.
*/
public String toString(){
String rectangleString;
rectangleString = "Rectangle has the Area " + length*width;
return(rectangleString);
}
/**
*
* #param args
*/
public static void main(String [] args){
Rectangle spot = new Rectangle();
Rectangle spot2 = new Rectangle(5, 9);
System.out.println("Area is: " + spot.area());
System.out.println("Perimeter: " + spot.perimeter());
Rectangle.displayAreaFormula();
}
}

I don't think that comparing the areas is a good idea in the equals method, because a rectangle that is 2x8 would equal a rectangle that is 4x4, because both areas are 16. This contradicts your requirement:
Two rectangles are equal when they both have the same length and width.
Here, your r2 variable is undefined. But beyond that, the equals method shouldn't compare two other objects, it should compare this object to another object.
You should return true if the r object is a Rectangle and this rectangle's length matches the other rectangle's length and this rectangle's width matches the other rectangle's width.

Your equals method should always have the following structure:
public boolean equals(Object r) {
if(r == null || !r instanceof Rectangle) return false;
// rest of the code
}
This is because you don't want to perform operations on a null-reference (which would throws errors), and this can't equals null anyway. Secondly: if r is not an instance of the rectangle class, we can quit before having to perform other operations, because a Rectangle would not equal a String, or a Circle.
Now, to get to your question: if you want to check on equality purely by width and length, I would write the method like this:
public boolean equals(Object r) {
if(r == null || !r instanceof Rectangle) return false;
if(length == r.length && width == w.width) return true;
return false;
}
This compares the width and length. If they are both equal, two rectangles are equal. You are comparing the area, but this could give false positives. Take this example:
Rectangle r1;
Rectangle r2;
r1.width = 10;
r1.length = 5;
r2.width = 5;
r2.length = 10;
Your code would produce a positive, while these rectangles are orientated differently.

Two rectangles are equal when they have both same length and width.
Are these following rectangles the same?
and
Hey! Both have the same area don't they?
So in short, no you have use && to see if the length and breadth of both are equal or you could compare the .toString()s of both rectangles to see if they are exact.

To add to the other answers (read those first)
If you are comparing "physical" objects (a block of wood for example) then you might want to compare length to width and width to length as well. The orientation a user inputs might be submitted differently for two of the same objects. Consider this when reading your requirements.

Related

Maximal size for a child within a circle's sector

Description
Given the following parameters:
Outer radius
Inner radius
Amount of regions
Starting angle (that second picture starts at 180 degrees)
Angle drawn (that second picture draws a total of 320 degrees from its starting angle)
How does one calculate the maximal size of the Images to draw so that they remain within their sector? On the left picture, I'm just using a hard-coded value because I haven't specifically been able to think of a nice, general, mathematical equation for that.
More variables could eventually come into play, if I ever decide to expand my library even more. Things such as the width of the separators or of the circumference's outline.
Code
This is what I use to draw everything but the children Actors:
/**
* Takes care of drawing everything that {#link #layout()} didn't.
* (Basically everything but the children Actors.)
*
* #param batch a Batch used to draw Drawables. The {#link #sd} is used to
* draw everything else.
* #param parentAlpha
*/
protected void drawWithShapeDrawer(Batch batch, float parentAlpha) {
/* Pre-calculating */
float bgRadian = MathUtils.degreesToRadians*style.totalDegreesDrawn;
float tmpOffset = MathUtils.degreesToRadians*style.startDegreesOffset;
int size = getChildren().size;
float tmpRad = bgRadian / size;
/* Background image */
if(style.background != null)
style.background.draw(batch, getX(), getY(), getWidth(), getHeight());
/* Rest of background */
if(style.backgroundColor != null) {
sd.setColor(style.backgroundColor);
sd.sector(getX()+style.radius, getY()+style.radius, style.radius-BG_BUFFER, tmpOffset, bgRadian);
}
/* Children */
vector2.set(getX()+style.radius, getY()+style.radius);
for(int i=0; i<size; i++) {
float tmp = tmpOffset + i*tmpRad;
drawChildWithoutSelection(vector2, i, tmp, tmpRad);
/* Separator */
drawChildSeparator(vector2, tmp);
}
/* The remaining last separator to be drawn */
drawChildSeparator(vector2, tmpOffset + size*tmpRad);
}
protected void drawChildSeparator(Vector2 vector2, float drawnRadianAngle) {
if(getChildren().size > 1 && style.separatorColor != null)
sd.line(pointAtAngle(vector22, vector2, style.innerRadius, drawnRadianAngle),
pointAtAngle(vector23, vector2, style.radius, drawnRadianAngle),
style.separatorColor, style.separatorWidth);
}
protected void drawChildWithoutSelection(Vector2 vector2, int index, float startAngle, float radian) {
if(style.childRegionColor != null) {
if(style.alternateChildRegionColor != null) {
sd.setColor(index%2 == 0 ? style.childRegionColor : style.alternateChildRegionColor);
sd.arc(vector2.x, vector2.y, (style.radius+style.innerRadius)/2, startAngle, radian, style.radius-style.innerRadius);
} else {
sd.setColor(style.childRegionColor);
sd.arc(vector2.x, vector2.y, (style.radius+style.innerRadius)/2, startAngle, radian, style.radius-style.innerRadius);
}
}
drawChildCircumference(vector2, startAngle, radian, style.radius - style.circumferenceWidth/2);
}
protected void drawChildCircumference(Vector2 vector2, float startAngle, float radian, float radius) {
if(style.circumferenceColor != null && style.circumferenceWidth > 0) {
sd.setColor(style.circumferenceColor);
sd.arc(vector2.x, vector2.y, radius, startAngle, radian, style.circumferenceWidth);
}
}
And this is how I'm laying out those children:
#Override
public void layout() {
float degreesPerChild = style.totalDegreesDrawn / getChildren().size;
float half = (float)1 / 2;
for (int i = 0; i < getChildren().size; i++) {
Actor actor = getChildren().get(i);
vector2.set((style.radius+style.innerRadius)/2, 0);
vector2.rotate(degreesPerChild*(i + half) + style.startDegreesOffset);
if(actor instanceof Image) { // todo: do this properly !
actor.setSize(30, 30);
}
actor.setPosition(vector2.x+style.radius, vector2.y+style.radius, Align.center);
}
}
Here is what I ended up doing:
/**
* Used to estimate the radius of a circle to be constrained within the widget
* according to the input parameters. Doubling the returned value would give
* you the size of a contained Actor which would roughly fill most of its
* sector, or possibly overflow slightly. It is suggested to adjust slightly
* the returned value by multiplying it with a factor of your choice.<br>
* It's basically the minimum between 3 different possible radius values
* based on certain layout properties.
*
* #param degreesPerChild the amount of degrees that a child's sector takes.
* #param actorDistanceFromCenter the distance at which the child Actor is
* positioned from the center of the widget.
* #return an estimated radius value for an Actor placed with the given
* constraints.
*/
public float getEstimatedRadiusAt(float degreesPerChild, float actorDistanceFromCenter) {
float tmp1 = actorDistanceFromCenter * MathUtils.sinDeg(degreesPerChild/2);
float tmp2 = getMaxRadius() - actorDistanceFromCenter;
float tmp3 = actorDistanceFromCenter - getInnerRadiusLength();
return Math.min(Math.min(tmp1, tmp2), tmp3);
}

circle class with methods

I am creating a circle class program that checks whether circles have overlapped each other. There are six initial given methods: getter methods for x,y, and radius and setter methods for x,y, and radius(6). There are two additional given methods: getArea method (returns the area's value) and doesOverLap method (returns which circles overlap or do not overlap). There are many demos within this source folder, but the following is the bulk (excuse indentation):
public class MyCircle
{
private double x;
private double y;
private double radius;
private double other_x; //keeps user input for other_x
private double other_y; //keeps user input for other_y
private double other_radius; //keeps user input for other_radius
private double third_x; //keeps user input for third_x
private double third_y; //keeps user input for third_y
private double third_radius; //keeps user input for third_radius
/*
The setX method stores a value in the x field.
# param value The value to store in x.
*/
public void setX(double value)
{
x = value;
}
/*
The setY method stores a value in the y field.
# param value The value to store in y.
*/
public void setY(double value)
{
y = value;
}
/*
The setRadius method stores a value in the radius field.
#param value The value to store in radius.
*/
public void setRadius(double value)
{
radius = value;
}
/*
The getX method returns x's value.
#return The value in the x field.
*/
public double getX()
{
return x; //returns value input for x
}
/*
The getY method return y's value.
#return The value in the y field.
*/
public double getY()
{
return y; //returns value input for y
}
/*
The getRadius method returns radius's value.
#return The value in the radius field.
*/
public double getRadius()
{
return radius; //returns value input for radius
}
/*
The getArea method returns the circle object's area.
#return The product of 3.14159 * radius * radius (area = 3.14159(pie) * radius * radius).
*/
public double getArea()
{
return radius * radius * 3.14159;
}
public boolean doesOverlap(MyCircleTest OtherCircleTest)
{
double distance;
distance = Math.sqrt(Math.pow(other_x - x, 2) + Math.pow(other_y - y, 2));
return distance < other_radius + radius;
if(distance < other_radius + radius)
{
System.out.println("My other circle overlaps my circle");
}
else
{
System.out.println("My other circle does not overlap my circle");
}
}
/*
The setOtherX method stores a value in the x field.
# param value The value to store in x.
*/
public void setOtherX(double value)
{
other_x = value;
}
/*
The setOtherY method stores a value in the y field.
# param value The value to store in y.
*/
public void setOtherY(double value)
{
other_y = value;
}
/*
The setOtherRadius method stores a value in the radius field.
#param value The value to store in radius.
*/
public void setOtherRadius(double value)
{
other_radius = value;
}
/*
The getOtherX method returns x's value.
#return The value in the x field.
*/
public double getOtherX()
{
return other_x; //returns value input for x
}
/*
The getY method return y's value.
#return The value in the y field.
*/
public double getOtherY()
{
return other_y; //returns value input for y
}
/*
The getRadius method returns radius's value.
#return The value in the radius field.
*/
public double getOtherRadius()
{
return other_radius; //returns value input for radius
}
/*
The getArea method returns the circle object's area.
#return The product of 3.14159 * radius * radius (area = 3.14159(pie) * radius * radius).
*/
public double getOtherArea()
{
return other_radius * other_radius * 3.14159;
}
public boolean doesOverlap(MyCircleTest OtherCircleTest)
{
//Equation to see whether circles overlap
double distance_2;
distance_2 = Math.sqrt(Math.pow(other_x - third_x, 2) + Math.pow(other_y - third_y, 2));
return distance < other_radius + third_radius;
if(distance_2 < other_radius + third_radius)
{
System.out.println("My other circle overlaps my third circle");
}
else
{
System.out.println("My other circle does not overlap third circle");
}
}
public void setThirdX(double value)
{
third_x = value;
}
/*
The setThirdY method stores a value in the third y field.
# param value The value to store in third y.
*/
public void setThirdY(double value)
{
third_y = value;
}
/*
The setThirdRadius method stores a value in the third radius field.
#param value The value to store in third radius.
*/
public void setThirdRadius(double value)
{
third_radius = value;
}
/*
The getThirdX method returns third x's value.
#return The value in the third x field.
*/
public double getThirdX()
{
return third_x; //returns value input for third x
}
/*
The getY method return third y's value.
#return The value in the third y field.
*/
public double getThirdY()
{
return third_y; //returns value input for third y
}
/*
The getThirdRadius method returns third radius's value.
#return The value in the third radius field.
*/
public double getThirdRadius()
{
return third_radius; //returns value input for third radius
}
/*
The getArea method returns the circle object's area.
#return The product of 3.14159 * radius * radius (area = 3.14159(pie) * radius * radius).
*/
public double getThirdArea()
{
return third_radius * third_radius * 3.14159;
}
public boolean doesOverlap (MyCircleTest ThirdCircleTest)
{
//Equation to see whether circles overlap
double distance_3;
distance_3 = Math.sqrt(Math.pow(third_x - x, 2) + Math.pow(third_y - y, 2));
return distance_3 < third_radius + radius;
if(distance_3 < third_radius + radius)
{
System.out.println("My third circle overlaps circle");
}
else
{
System.out.println("My third circle does not overlap circle");
}
}
}
I am having trouble on the final method, doesOverLap method, which should state the overall results of the program. It should state whether or not two circles overlap. Also, I am supposed to display the area values of each circle, which isn't in my code.
Requirements are the following:
There are three circle objects, two of them should overlap and two of them should not. The areas of the three circles are to be displayed. The doesOverLap method should indicate which circles over lap and which do not. Any help will be greatly appreciated.
Hinted Given: two circles overlap if the sum of their radius' is greater than the distance between their centers.
First of all, as #JF Meier pointed out in the comments, this is not the OO approach. You should have a class Circle which contains data and logic relevant for only a single circle and then create multiple instances of the Circle class with which you can work.
Also, I don't know if this code template was provided or created by yourself, but the initial data should be passed through a constructor rather than having only setters. Which makes sense, because a circle should be immutable in my opinion.
For example:
public class Circle {
private final double x;
private final double y;
private final double radius;
public Circle(double x, double y, double radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
}
With this, you need only one variant of getArea() and doesOverlap(Circle other) because now each of those methods "belong" to one Circle object alone.
Now you can simply create new instances of multiple circles and use your methods on them.
BTW: If you have parameters in one of your methods like doesOverlap(Circle other) make sure you actually use other inside.

Java test cases

import java.text.DecimalFormat;
public class Cylinder
{
// Instance Variables
private String label = "";
private double radius;
private double height;
/**
*#param labelIn Represents label value
*#param radiusIn Represents radius value
*#param heightIn Represents height value
*/
public Cylinder(String labelIn, double radiusIn, double heightIn)
{
label = labelIn.trim();
radius = radiusIn;
height = heightIn;
}
/**
*#return String representation of label.
* Accepts no parameters and returns
* a string representing the label field.
*/
public String getLabel()
{
return label;
}
/**
* Takes string parameter and returns boolean.
*#param labelIn Command Line used
*#return checks to see if labelIn exist.
*/
public boolean setLabel(String labelIn)
{
if (labelIn == null)
{
return false;
}
else
{
label = labelIn.trim();
return true;
}
}
/**
*#return a method to display a double radius.
* Accepts no parameter and returns double representing
* radius.
*/
public double getRadius()
{
return radius;
}
/**
*#param radiusIn Command Line used.
* Accepts a double parameter, sets radius field, and
* returns nothing.
*/
public void setRadius(double radiusIn)
{
radius = radiusIn;
}
/**
*#return a method to display a double height.
* Accepts no parameters and returns double
* representing the height field.
*/
public double getHeight()
{
return height;
}
/**
*#param heightIn Command Line used.
* Accepts a double parameter, sets radius field, and
* returns nothing.
*/
public void setHeight(double heightIn)
{
height = heightIn;
}
/**
*#return displays diameter when method is called.
* Accepts no parameters and returns double value
* for diameter calculated using radius.
*/
public double diameter()
{
return radius * 2;
}
/**
*#return displays circumference when method is called.
* Accepts no parameters and returns double value
* for circumference calculated using Math.PI and radius.
*/
public double circumference()
{
return Math.PI * radius * 2;
}
/**
*#return displays area when method is called.
* Accepts no parameters and returns double value
* for surface area calculated using Math.PI, radius, and height.
*/
public double area()
{
return 2 * Math.PI * radius * radius + 2 * Math.PI * radius * height;
}
/**
*#return displays volume when method is called.
* Accepts no parameters and returns double value
* for volume calculated using Math.PI, radius, and height.
*/
public double volume()
{
return Math.PI * radius * radius * height;
}
/**
*#return displays cylinder information.
* Returns a string containing the information about
* the cylinder object.
*/
public String toString()
{
DecimalFormat fmt = new DecimalFormat("#,##0.0##");
return "\"" + label
+ "\" is a cylinder with radius = " + fmt.format(getRadius())
+ " units and height = " + fmt.format(getHeight()) + " units,"
+ "\nwhich has diameter = " + fmt.format(diameter())
+ " units, circumference = " + fmt.format(circumference())
+ " units,"
+ "\narea = " + fmt.format(area()) + " square units,"
+ " and volume = " + fmt.format(volume()) + " cubic units.";
}
}
I need to write test cases for each method but I'm not really sure how to do that. I tried this..
#Test public void getLabelTest() {
Cylinder c = new String("Cyl");
But I get an incompatible type error. Any help would be appreciated.
Since you are using the #Test annotation, I assume you are supposed to use JUnit testing.
Check here for a short overview how it is done.
You are trying to assign a String to your Cylinder, that's where the incompatible type error comes from. I think you are meant to use
Cylinder c = new Cylinder("Cyl", 1.0, 1.0); // label = "Cyl", radius = 1.0, height = 1.0
Generally you are creating your (JUnit)-Test in a separate java file called a JUnit Test Case and there you are going to create the single tests for your methods annotated with #Test.
Here is simple example how such a JUnit Test Case could look like (related to your setLabel-method):
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
public class JUnitTest {
private Cylinder c;
#Before
public void setUp() {
// initializing Cylinder instance to work on
c = new Cylinder("Cyl", 1.0, 1.0);
}
#Test
public void setLabelTest() {
// parameter is null, return value should be false
assertFalse(c.setLabel(null));
// parameter is NOT null, return value should be true
assertTrue(c.setLabel("Foo"));
}
}
Also check Assert doc for the different assertions.

The type of the expression must be an array type but it is resolved to ArrayList<Point2D.Double>

I am trying to figure out the proper way to call arrays from the area method, which are then supposed to calculate the area of the points given. Not sure what the proper way to select the specific x and y coordinates from each array is.
MyPolygon class
import java.util.ArrayList;
import java.awt.geom.Point2D;
import java.awt.geom.Point2D.Double;
/**
* A class that represents a geometric polygon. Methods are provided for adding
* a point to the polygon and for calculating the perimeter and area of the
* polygon.
*/
class MyPolygon {
// list of the points of the polygon
private ArrayList<Point2D.Double> points;
/**
* Constructs a polygon with no points in it.
*/
public MyPolygon() {
points = new ArrayList<Point2D.Double>();
}
/**
* Adds a point to the end of the list of points in the polygon.
*
* #param x
* The x coordinate of the point.
* #param y
* The y coordinate of the point.
*/
public void add(double x, double y) {
points.add(new Point2D.Double(x, y));
}
/**
* Calculates and returns the perimeter of the polygon.
*
* #return 0.0 if < 2 points in polygon, otherwise returns the sum of the
* lengths of the line segments.
*/
public double perimeter() {
if (points.size() < 2) {
return 0.0;
}
int i = 0;
double d = 0;
double total = points.get(0).distance(points.get(points.size() - 1));
while (i < points.size() - 1) {
Point2D.Double point1 = points.get(i);
// double x = point1.x;
// double y = point1.y;
Point2D.Double point2 = points.get(i + 1);
// double x1 = point2.x;
// double y1 = point2.y;
d = point1.distance(point2);
// d = Math.sqrt(Math.pow(x1 - x,2) + Math.pow(y1 - y, 2));
total = total + d;
i++;
}
return total;
}
/**
* Calculates and returns the area of the polygon.
*
* #return 0.0 if < 3 points in the polygon, otherwise returns the area of
* the polygon.
*/
public double area() {
int i = 0;
double a = 0;
double total = 0;
total = total + a;
if (points.size() < 3) {
return 0.0;
}
for (int m = 0; m < points.size(); m++) {
total = total + (points[m].x() * points[m + 1].y()) - (points[m].y() * points[m + 1].x());
}
return 0.5 * total;
}
}
Tester Class
class PolygonTester {
public static void main(String args[]) {
MyPolygon poly = new MyPolygon();
poly.add(1.0,1.0);
poly.add(3.0,1.0);
poly.add(1.0,3.0);
System.out.println(poly.perimeter());
System.out.println(poly.area());
}
}
Your headline is actually already the solution. You use points[m] which is array notation. But points ist not an array. It is a list. Use points.get(int i) instead, as you did in perimeter().
You're going to run out of bounds on the list. Your for loop continues while m < size(). However you access m+1 in your calculation. So if the list contains 5 elements and m = 4, (4 < 5) so keep looping, you will then access m + 1 which is 5. You don't have an index of 5 since these lists are 0 based.
Also the code is probably not compiling because you're using array syntax to access a list. You should say points.get(m)
The solution is quite simple, and is given away by your title (which I assume to be a compiler error.)
You are treating points as an array, which it is not. You access elements of an ArrayList slightly differently: you use points.get(m) instead of points[m]. If you make that change in area, it will work.
ArrayLists are not arrays. They are objects that are indexed with the get(int) method.
Wherever you have points[m], or something similar, replace it with points.get(m). The line would then become:
total = total + (points.get(m).x() * points.get(m + 1).y()) - (points.get(m).y() * points.get(m + 1).x());
That should clear up that issue, but you will still probably get an IndexOutOfBoundsException on the last iteration of the loop, because you will be trying to index m + 1 when m is the last index. You should change your code depending on how you want it to handle the last element.

Tic Tac Toe Java Program - CPU not responding

I've been working on this program for a couple weeks now, adding components to an applet in order to output a simple tic tac toe game. I've gotten some of it to work, but as of now, when you run, all it does is that you click it once, the CPU projects its mark in the upper left corner, and then you can click anywhere else and it automatically says you win. I can't get the CPU to keep playing. I don't expect anyone to tell me what to do, but I'm just confused as to which method I need to work on in order to get the CPU to respond. My professor has left some very helpful pseudocode, but I still don't quite understand. I've been working with the method "gameEnd," checking for winners horizontally, vertically and diagonally to see if that's the source to getting the game to continue beyond just two marks, but it's not working. Anyone got any suggestions? Thanks.
import java.awt.Color;
import java.awt.Event;
import java.awt.Font;
import java.awt.Graphics;
import java.util.Random;
public class TicTacToe extends java.applet.Applet {
// Ignore this constant
private static final long serialVersionUID = 1942709821640345256L;
// You can change this boolean constant to control debugging log output
private static final boolean DEBUGGING = false;
// Constants
// Size of one side of the board in pixels
private static final int BOARD_SIZE_PIXELS = 600;
// Number of squares on one side of the board
private static final int SQUARES = 3;
// Diameter of the circle drawn in each square
private static final int CIRCLE_WIDTH = 90;
// Colors to be used in the game
private static final Color BACKGROUND_COLOR = Color.WHITE;
private static final Color SQUARE_BORDER_COLOR = Color.BLACK;
private static final Color GAME_OVER_MESSAGE_COLOR = Color.BLACK;
private static final Color HUMAN_COLOR = Color.RED;
private static final Color HUMAN_WINNING_COLOR = Color.MAGENTA;
private static final Color CPU_COLOR = Color.BLUE;
private static final Color CPU_WINNING_COLOR = Color.CYAN;
// Status constant values for the game board
private static final int EMPTY = 0;
private static final int HUMAN = 1;
private static final int HUMAN_WINNING = 2;
private static final int CPU = -1;
private static final int CPU_WINNING = -2;
// String displayed when the game ends
private static final String GAME_WIN_MESSAGE = "You win! Click to play again.";
private static final String GAME_LOSE_MESSAGE = "You lose......Click to play again.";
private static final String GAME_DRAW_MESSAGE = "No one wins? Click to play again...";
// Instance variables that control the game
// Whether or not the user just clicked the mouse
private boolean mouseClicked = false;
// Whether or not to start the game again
private boolean restart = false;
// Whether or not the CPU should start playing a move.
// USED ONLY WHEN THE CPU PLAYS FIRST!
private boolean onFirstMove = false;
// The column and row of the SQUARE the user clicked on
private int xMouseSquare; // column
private int yMouseSquare; // row
// The width (and height) of a single game square
private int squareWidth = BOARD_SIZE_PIXELS / SQUARES;
// An array to hold square status values on the board.
// The status values can be EMPTY, HUMAN, CPU or other values
private int[][] gameBoard;
// The column and row of the SQUARE the CPU player will move on
private int xCPUMove;
private int yCPUMove;
// Add the rest of your instance variables here, if you need any. (You won't
// need to, but you may if you want to.)
// Ignore these instance variables
// CPUinMove represents if the CPU is thinking (generating the CPU move).
// If it is true, it means the CPUMove() method is running and no new move
// should be added
private boolean CPUinMove;
// Methods that you need to write:
/*
* Pre: x and y are x-coordinate and y-coordinate where the user just
* clicks. squareWidth is the width (and height) of a single game square.
*
* Post: xMouseSquare and yMouseSquare are set to be the column and row
* where the user just clicked on (depending on x and y).
*
* Hint: You need only two statements in this method.
*/
// Setting MouseSquare equal to x and y divided by the Square Width to create a location for
// the user after clicking
private void setMouseSquare(int x, int y) {
//
xMouseSquare = x/squareWidth;
yMouseSquare = y/squareWidth;
}
/*
* Pre: SQUARES is an int that holds the number of game squares along one
* side of the game board. xSquare is an int such that 0 <= xSquare <
* SQUARES. CIRCLE_WIDTH is an int that holds the diameter of the circle to
* be drawn in the center of a square. squareWidth is an int that holds the
* width and height in pixels of a single game square.
*
* Post: Return the correct x-coordinate (in pixels) of the left side of the
* circle centered in a square in the column xSquare.
*
* Hint: This method should be very simple. What you need to do is to find
* the right equation.
*/
private int getIconDisplayXLocation(int xSquare) {
// This line is an example of using DEBUGGING variable
if (DEBUGGING) {
System.out.println("The input that getIconDisplayXLocation() receives is: " + xSquare);
}
// equation that returns the correct variable in the column xSquare
return squareWidth * xSquare + (squareWidth - CIRCLE_WIDTH)/2;
}
/*
* Pre: SQUARES is an int that holds the number of game squares along one
* side of the game board. ySquare is an int such that 0 <= ySquare <
* SQUARES. CIRCLE_WIDTH is an int that holds the diameter of the circle to
* be drawn in the center of a square. squareWidth is an int that holds the
* width and height in pixels of a single game square.
*
* Post: Return the correct y-coordinate (in pixels) of the top of the
* circle centered in a square in the row ySquare.
*
* Hint: This method should be very simple. What you need to do is to find
* the right equation.
*/
private int getIconDisplayYLocation(int ySquare) {
// This line is an example of using DEBUGGING variable
if (DEBUGGING) {
System.out.println("The input that getIconDisplayYLocation() receives is: " + ySquare);
}
// equation that returns the correct variable in the column ySquare
return squareWidth * ySquare + (squareWidth - CIRCLE_WIDTH)/2;
}
/*
* The instance variable gameBoard will be created and initialized
*
* Pre: SQUARES is set to an int. gameBoard is a 2-dimensional array type
* variable that holds the status of current game board. Each value in the
* array represents a square on the board
*
* Post: gameBoard must be assigned a new 2-dimensional array. Every square
* of gameBoard should be initialized to EMPTY.
*
* Hint: A loop.
*/
private void buildBoard() {
// Setting the two methods equal to local variables, x and y
int x = xMouseSquare;
int y = yMouseSquare;
// This line creates the gameBoard array. You must write several more
// lines to initialize all its values to EMPTY
// Write game board using the equation of three across and down for each column
// Constructs a 3 by 3 array of integers for the board
gameBoard = new int[3][3];
// Initialize variables i and j, set equal to 0; this establishes a connection for loop
for (x = 0; x < 3; x++) {
for (y = 0; y < 3; y++) {
gameBoard[x][y] = EMPTY;
}
}
}
/*
* Returns whether the most recently clicked square is a legal choice in the
* game.
*
* Pre: gameBoard is a 2-dimensional array type variable that holds the
* status of current game board. xSquare and ySquare represent the column
* and row of the most recently clicked square.
*
* Post: Returns true if and only if the square is a legal choice. If the
* square is empty on current game board, it is legal and the method shall
* return true; if it is taken by either human or CPU or it is not a square
* on current board, it is illegal and the method shall return false.
*
* Hint: Should be simple but think carefully to cover all cases.
*/
private boolean legalSquare(int xSquare, int ySquare) {
if (gameBoard[xSquare][ySquare] == EMPTY)
return true;
else return false;
}
/*
* Pre: gameBoard is an array that holds the current status of the game
* board. xSquare and ySquare represent the column and row of the most
* recently clicked square. player represent the current player (HUMAN or
* CPU). This method is always called after checking legalSquare().
*
* Post: Set the square as taken by current player on the game board if the
* square is empty.
*
* Hint: Very simple.
*/
private void setMovePosition(int xSquare, int ySquare, int player) {
player = HUMAN;
player = CPU;
this.xMouseSquare = xSquare;
this.yMouseSquare = ySquare;
}
/*
* Check if HUMAN or CPU wins the game.
*
* Pre: gameBoard is an array to hold square status values on the board. The
* status values can be EMPTY, HUMAN, CPU or other values.
*
* Post: The method will return true if and only if a player wins the game.
* Winning a game means there is at least one row, one column, or one
* diagonal that is taken by the same player. The method does not need to
* indicate who wins because if it is implemented correctly, the winner must
* be the one who just made a move.
*
* Hint: Complicated method. Use loops to shorten your code. Think about how
* to represent "a player takes 3 squares in a row/column/diagonal".
*/
private boolean gameEnd() {
// Setting local variables
int i = 0;
int x = xMouseSquare;
int y = yMouseSquare;
int sw = squareWidth;
int[][] g = gameBoard;
// Checking for a winner
// Checking columns
for (y = 0; y < 3; y++) {
for (x = 0; x < 3; x++) {
i = g[x][y] + i;
}
}
{
// Checking rows
for (x = 0; x < 3; x++) {
for (y = 0; y < 3; y++) {
i = g[x][y] + i;
}
}
}
// Checking first diagonal
{
for (x = 0; x < 3; x++) {
g[x][x] = 3;
}
}
for (x = 1; x < 3; x++) {
g[x][x] = 3;
}
for (x = 2; x < 3; x++) {
g[x][x] = 3;
}
// Checking second diagonal
for (y = 0; y < 3; y++) {
g[y][2 - y] = 3;
}
for (y = 1; y < 3; y++) {
g[y][2 - y] = 3;
}
for (y = 2; y < 3; y++) {
g[y][2 - y] = 3;
}
return true;
}
/*
* Check if the game ends as a draw.
*
* Pre: gameBoard is an array to hold square status values on the board. The
* status values can be EMPTY, HUMAN, CPU or other values. This method is
* always called after gameEnd().
*
* Post: The method will return true if and only if all squares on the game
* board are taken by HUMAN or CPU players (no EMPTY squares left).
*
* Hint: Should be simple. Use loops.
*/
// Value of...
private boolean gameDraw() {
if (squareWidth == (3^2 - 1)) {
}
return true;
}
/*
* Marks circles on the line on which a player wins.
*
* Pre: g is Graphics object that is ready to draw on. HUMAN_WINNING_COLOR
* and CPU_WINNING_COLOR are both Color objects that represent the color to
* show when HUMAN/CPU wins. gameBoard is gameBoard is an array to hold
* square status values on the board. The status values can be EMPTY, HUMAN,
* CPU or other values.
*
* Post: ALL the row(s)/column(s)/diagonal(s) on which a player wins will be
* marked as the special color.
*
* Hint: You must draw a new circle with the special color (to replace the
* old circle) on the square if the square belongs to a winning
* row/column/diagonal. You can change gameBoard because the board will be
* reset after displaying the winning line. You can use helper methods
* (existing ones or your own) to finish this method. Pay attention that
* many functions in this method is similar to gameEnd(). You should think
* about reusing code.
*
* Hint2: This method is not necessary for the game logic. You don't have to
* start it early. Start when you know everything else is correct.
*/
private void markWinningLine(Graphics g) {
// TODO
}
/*
* Generates the next square where the CPU plays a move.
*
* Pre: gameBoard is an array to hold square status values on
* the board. The status values can be EMPTY, HUMAN, CPU or other values.
*
* Post: Set xCPUMove and yCPUMove to represent the column and the row which
* CPU plans to play a move on. The respective square MUST BE EMPTY! It will
* cause a logical error if this method returns a square that is already
* taken!
*
* Hint: Don't start too early -- currently this method works (though
* naively). Make sure that everything else works before touching this
* method!
*/
private void CPUMove() {
// TODO
// The following block gives a naive solution -- it finds the first
// empty square and play a move on it. You can use this method to test
// other methods in the beginning. However, you must replace this block
// with your own algorithms eventually.
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (gameBoard[i][j] == 0) {
xCPUMove = i;
yCPUMove = j;
return;
}
}
}
}
/* Put any helper methods you wish to add here. */
/* You will not need to change anything below this line. */
/*
* DO NOT change this method.
*
* Set the game board to show a new, blank game.
*/
private void wipeBoard(Graphics g) {
g.setColor(BACKGROUND_COLOR);
g.fillRect(0, 0, BOARD_SIZE_PIXELS, BOARD_SIZE_PIXELS);
}
/*
* DO NOT change this method.
*
* Displays a circle on g, of the given color, in the center of the given
* square.
*/
private void displayHit(Graphics g, Color color, int xSquare, int ySquare) {
g.setColor(color);
g.fillOval(getIconDisplayXLocation(xSquare),
getIconDisplayYLocation(ySquare), CIRCLE_WIDTH, CIRCLE_WIDTH);
}
/*
* DO NOT change this method.
*
* This method handles mouse clicks. You will not need to call it.
*/
#Override
public boolean mouseDown(Event e, int xMouse, int yMouse) {
if (isClickable()) {
mouseClicked = true;
setMouseSquare(xMouse, yMouse);
}
repaint();
return true;
}
/*
* DO NOT change this method.
*
* This method handles drawing the board. You will not need to call it.
*/
#Override
public void update(Graphics g) {
paint(g);
}
/*
* DO NOT change this method.
*
* Draws the border between game squares onto canvas. Also, draws the moves
* that are already made.
*/
private void displayGame(Graphics canvas) {
canvas.setColor(SQUARE_BORDER_COLOR);
for (int i = 0; i < BOARD_SIZE_PIXELS; i += squareWidth) {
for (int j = 0; j < BOARD_SIZE_PIXELS; j += squareWidth) {
canvas.drawRect(i, j, squareWidth, squareWidth);
}
}
for (int i = 0; i < SQUARES; i++) {
for (int j = 0; j < SQUARES; j++) {
switch (gameBoard[i][j]) {
case HUMAN:
case HUMAN_WINNING:
displayHit(canvas, HUMAN_COLOR, i, j);
break;
case CPU:
case CPU_WINNING:
displayHit(canvas, CPU_COLOR, i, j);
break;
default:
break;
}
}
}
}
/*
* DO NOT change this method.
*
* This method relays information about the availability of mouse clicking
* in the game. You will not need to call it.
*/
private boolean isClickable() {
return !CPUinMove;
}
/*
* DO NOT change the contents this method.
*
* If this method is changed to public void paint(Graphics canvas), it will
* execute the program with the CPU-first order.
*
* This method is like the "main" method (but for applets). You will not
* need to call it. It contains most of the game logic.
*/
// #Override
public void paint(Graphics canvas) {
displayGame(canvas);
if (mouseClicked) {
if (onFirstMove) {
CPUMove();
setMovePosition(xCPUMove, yCPUMove, CPU);
displayHit(canvas, CPU_COLOR, xCPUMove, yCPUMove);
onFirstMove = false;
} else {
if (restart) {
wipeBoard(canvas);
setUpGame();
repaint();
} else if (legalSquare(xMouseSquare, yMouseSquare)) {
setMovePosition(xMouseSquare, yMouseSquare, HUMAN);
displayHit(canvas, HUMAN_COLOR, xMouseSquare, yMouseSquare);
if (gameEnd()) {
markWinningLine(canvas);
canvas.setFont(new Font("SansSerif", Font.PLAIN, 30));
canvas.setColor(GAME_OVER_MESSAGE_COLOR);
canvas.drawString(GAME_WIN_MESSAGE, squareWidth / 2,
squareWidth);
restart = true;
} else {
CPUinMove = true;
CPUMove();
setMovePosition(xCPUMove, yCPUMove, CPU);
displayHit(canvas, CPU_COLOR, xCPUMove, yCPUMove);
CPUinMove = false;
if (gameEnd()) {
markWinningLine(canvas);
canvas
.setFont(new Font("SansSerif", Font.PLAIN,
30));
canvas.setColor(GAME_OVER_MESSAGE_COLOR);
canvas.drawString(GAME_LOSE_MESSAGE,
squareWidth / 2, squareWidth);
restart = true;
} else if (gameDraw()) {
canvas
.setFont(new Font("SansSerif", Font.PLAIN,
30));
canvas.setColor(GAME_OVER_MESSAGE_COLOR);
canvas.drawString(GAME_DRAW_MESSAGE,
squareWidth / 2, squareWidth);
restart = true;
}
}
}
}
mouseClicked = false;
}
}
/*
* DO NOT change this method.
*
* This method is like the "main" method (but for applets). You will not
* need to call it. It contains most of the game logic.
*/
public void paint_game(Graphics canvas) {
// display the current game board
displayGame(canvas);
// the following block will run every time the user clicks the mouse
if (mouseClicked) { // when the user clicks the mouse
// if the game is ready to start or to be restarted
if (restart) {
// clear the window and set up the game again
wipeBoard(canvas);
setUpGame();
repaint();
}
// else, if the game is in play, check if the click is on a legal
// square
else if (legalSquare(xMouseSquare, yMouseSquare)) {
// if the square is legal, mark the corresponding position as
// taken by HUMAN
setMovePosition(xMouseSquare, yMouseSquare, HUMAN);
// display the new position with a HUMAN_COLOR circle
displayHit(canvas, HUMAN_COLOR, xMouseSquare, yMouseSquare);
// check if the game ends (if it is, HUMAN wins)
if (gameEnd()) {
// if HUMAN wins, mark the winning line as a special color.
markWinningLine(canvas);
// display the human winning message
canvas.setFont(new Font("SansSerif", Font.PLAIN, 30));
canvas.setColor(GAME_OVER_MESSAGE_COLOR);
canvas.drawString(GAME_WIN_MESSAGE, squareWidth / 2,
squareWidth);
// mark the game as ready to restart
restart = true;
} else if (gameDraw()) {
// if HUMAN doesn't win but the board is full, it is a draw
// display the draw message
canvas.setFont(new Font("SansSerif", Font.PLAIN, 30));
canvas.setColor(GAME_OVER_MESSAGE_COLOR);
canvas.drawString(GAME_DRAW_MESSAGE, squareWidth / 2,
squareWidth);
// mark the game as ready to restart
restart = true;
} else {
// if HUMAN doesn't win and the board is not full, the CPU
// is ready to move
CPUinMove = true;
// calculates the next CPU move
CPUMove();
// mark the corresponding position as taken by CPU
setMovePosition(xCPUMove, yCPUMove, CPU);
// display the new position with a CPU_COLOR circle
displayHit(canvas, CPU_COLOR, xCPUMove, yCPUMove);
CPUinMove = false;
if (gameEnd()) {
// if CPU wins, mark the winning line as a special
// color.
markWinningLine(canvas);
// display the human losing message
canvas.setFont(new Font("SansSerif", Font.PLAIN, 30));
canvas.setColor(GAME_OVER_MESSAGE_COLOR);
canvas.drawString(GAME_LOSE_MESSAGE, squareWidth / 2,
squareWidth);
// mark the game as ready to restart
restart = true;
}
// else (if the game is not ended after the CPU move), the
// game is ready to get the next HUMAN move
}
}
mouseClicked = false;
}
}
/*
* DO NOT change this method.
*
* This method initializes the applet. You will not need to call it.
*/
#Override
public void init() {
setSize(BOARD_SIZE_PIXELS, BOARD_SIZE_PIXELS);
setBackground(BACKGROUND_COLOR);
setUpGame();
}
/*
* DO NOT change this method.
*
* Creates a fresh game board and sets up the game state to get ready for a
* new game.
*/
private void setUpGame() {
buildBoard();
CPUinMove = false;
restart = false;
onFirstMove = true;
}
}

Categories