CSCI 136
Fundamentals of Computer Science II
Spring 2013

Montana Tech
Computer Science & Software Engineering



ASSIGNMENT #2

In this assignment, you will be making a game in which you try and place blocks to minimize the height of the stack. You will be learning about Java exceptions, file I/O, and defensive programming.

Part 1: Creating block levels
In the first part, you will be creating a command line application CreateBlocks.java that builds random level files for use by the game. The program generates a certain number of blocks (rectangles). The blocks are given a random width and height and a random (x, y) location. The block has to be contained in the unit box (0.0) - (1.0, 1.0). The color of each block is choosen randomly from a file of possible color values.

Command line arguments. The program takes 3-5 command line arguments as shown below:
% java CreateBlocks
CreateBlocks <color file> <output file> <num blocks> [min size] [max size]
The first argument is a file containing a list of colors. The first line in the color file is the total colors in the file. Each subsequent line has four columns describing a color: name of the color, red, green, and blue components. The components are given as an integer between 0 and 255. The first line is the total number of colors in the file:
114
IndianRed   205  92   92
LightCoral  240	 128  128
Salmon      250	 128  114
DarkSalmon  233  150  122
...
The second argument is the filename which the program writes the generated data to. The third argument is a positive integer specifying the total number of random blocks to generate. If the fourth argument is given, it specifies the minimum width/height of a random block. If the fifth argument is given, it specifies the maximum width/height of a random block. The min and max size arguments must be in the interval (0.0, 1.0) (exclusive of the end points). The default min size is 0.05, the default max size if 0.25.

Reading colors. You can use our example file colors.txt which contains 114 colors. You should create a static method Color [] loadColors(String filename) that reads the color definition file and returns an array of Java Color objects. This method should handle any exceptions caused by a failure to open the input file or parse the data in the file: If everything went well, you should print out the number of colors read in, e.g. "Read in 114 colors."

Parsing the numeric arguments. Your program should defend itself against invalid command line input as follows: Generating blocks. Generate a random block and write a line to the output file. Each line in the output file should contain seven columns separated by whitespace: x-location, y-location, width, height, red, green, and blue. For example running "java CreateBlocks colors.txt out.txt 2" generated:
0.6428429106060277	0.3951860163442299	0.22882435786316718	0.22606163038598928	199	21	133
0.18094746559427116	0.6966589213411483	0.1185965813651805	0.22550766462918148	218	112	214
The width and height are randomly chosen using in the (min, max) range specified by either the default (0.05, 0.25) or by the command line arguments. The x-position and y-position are the center of the rectangle representing the block. The (x, y) position is chosen randomly but such that the entire block is inside the unit box (i.e. no part of the block extends outside of (0.0) - (1.0, 1.0). The RGB color triplet is given by one of the triplets from a randomly selected color from the read in set of colors (i.e. the three RGB integer values appear together on one of the lines in the color text file).

Drawing and area calculation. After generating each random block, draw the block using StdDraw. Also calculate the total area of all blocks generated and print it out. The area represents a best case "score" when the level is used in a game.

Example runs:
% java CreateBlocks missing.txt
CreateLevel <color file> <output file> <num blocks> [min size] [max size]

% java CreateBlocks missing.txt out.txt 10
Could not read color file: missing.txt

% java CreateBlocks StdDraw.class out.txt 1 0
Could not parse color file: StdDraw.class

% java CreateBlocks colors.txt . 10
Read in 114 colors.
Failed to open output file: .

% java CreateBlocks colors.txt out.txt -2
Read in 114 colors.
Invalid number of blocks: -2

% java CreateBlocks colors.txt out.txt 0
Read in 114 colors.
Invalid number of blocks: 0

% java CreateBlocks colors.txt out.txt ick
Read in 114 colors.
Invalid number of blocks: ick

% java CreateBlocks colors.txt out.txt 10 yo
Read in 114 colors.
Invalid min block size: yo

% java CreateBlocks colors.txt out.txt 10 0.0
Read in 114 colors.
Invalid min block size: 0.0

% java CreateBlocks colors.txt out.txt 10 0.1 -0.2
Read in 114 colors.
Invalid max block size: -0.2

% java CreateBlocks colors.txt out.txt 10 0.1 0.05
Read in 114 colors.
Invalid block size range: 0.1 > 0.05

% java CreateBlocks colors.txt out.txt 10 0.1 no
Read in 114 colors.
Invalid max block size: no

% java CreateBlocks colors.txt out.txt 10 0.1 0.3
Read in 114 colors.
Creating 10 random blocks.
Total area: 0.375
% java CreateBlocks colors.txt out.txt 1000 0.01
Read in 114 colors.
Creating 1000 random blocks.
Total area: 16.499

Part 2: Blocks game
You will create a game in which you drag blocks around trying to minimize the height of the stack. The game will loads a text level file create by CreateBlocks.

You need to develop three Java classes: You need to create these files from scratch. Here is the API you should implement for the Block class:
public class Block
--------------------------------------------------------------------------------------------------------------------
public Block(double x, double y,                  // Create a block at the given (x,y) center position, with the given 
             double width, double height,         // width, height, and RGB color value
             int red, int green, int blue)  
public Block(Block other)                         // Create a new object that is a copy of another 
public void draw(boolean filled)                  // Draw the block in either filled or outline form
public double getLeft()                           // Get the leftmost coordinate of the block
public double getRight()                          // Get the rightmost coordinate of the block
public double getTop()                            // Get the topmost coordinate of the block
public double getBottom()                         // Get the bottommost coordinate of the block
public boolean inside(double x, double y)         // See if the point (x,y) is inside the box
public boolean outsideUnitBox()                   // Check if the block extends outside the unit box
public boolean intersects(Block other)            // Check if this block intersects with another block
public Block move(double x, double y)             // Create a new block based on this block but at a new (x,y) position
public Block rotate()                             // Create a new block based on this block but rotated by 90 degrees
Here is the API you should implement for the Blocks class:
public class Blocks
--------------------------------------------------------------------------------------------------------------------
public Blocks(String filename)                    // Create a new collection of blocks based on a text file 
public void draw()                                // Draw all the blocks in the collection
public int size()                                 // Find out how many blocks are in the collection
public Block removeAtLocation(double x, double y) // Return block at (x,y) removing from collection, returns null if no block at (x,y)
public void add(Block block)                      // Add the given block to the collection
public double height()                            // Return the height of the topmost block in the collection
public boolean validLocation(Block block)         // Check if a Block's location is valid (doesn't intersect other blocks or go outside unit box)
Input file. The BlockGame program takes one command line option, the filename of the text file created by CreateLevel. If no command line options are given, the program should print "BlockGame <blocks file>" and terminate.

Coordinate system and screen. In this assignment, we will stick with the default StdDraw coordinate system. So (0.0, 0.0) is the lower-left corner and (1.0, 1.0) is the upper-right corner. You can also leave the canvas as the default 512 x 512 pixel size. You should draw a black square around the unit box showing the valid playing area.

Intersection of blocks. Two blocks intersect if any portion of the two axis-aligned rectangles overlap. It is actually easier to solve for when the two blocks do NOT overlap. This occurs if one rectangle is completely to the right of the other, or is to the left, or above, or below. For details, see here.

Moving blocks. In order to move a block, the user most put the mouse cursor over a block. If the mouse cursor is over multiple overlapping blocks, the block that is visually on top should be the one grabbed. When selected, the block immediately "snaps" to be centered on the mouse location. As long as the user holds the down the mouse button, the block will be dragged around. While dragging a block, it appears as a filled rectangle whenever the block could be placed at the current location. If a block intersects another block or extends outside the unit box, it should appear as the outline of a rectangle. When the mouse button is released and the block is in a valid location, it is dropped in the new location.

Rotating blocks. If the user is currently dragging a block (i.e. holding down the mouse button) and the user presses the spacebar, you should rotate the block 90 degrees. The block should draw itself as filled or outlined depending on whether the new rotated block is in a valid game location.

Height. At the top of the screen, the current height of the blocks should be displayed. A lower height is a better "score". The height should be displayed to three decimal places of precision. This is the maximum y-coordinate of the top side of any block in the game. If the user is currently dragging a block that is above all the other blocks, the height text should change to reflect the dragged block's current location.

Testing your classes. Here is the code we used to test our Block class along with sample output:
public static void main(String [] args)
{
	Block b1 = new Block(0.5, 0.5, 0.1, 0.2, 255, 0, 0);
	Block b2 = new Block(0.1, 0.1, 0.05, 0.1, 0, 0, 255);
	Block b4 = new Block(0.5, 0.5, 0.5, 0.3, 0, 255, 0);
	
	b1.draw(true);
	b2.draw(false);
	System.out.println("b1 " + b1.getLeft() + " " + b1.getTop() + " " + b1.getRight() + " " + b1.getBottom());		
			
	Block b3 = b2.rotate();
	b3.draw(false);
	b4.draw(false);
					
	System.out.println("b1 <-> b2 " + b1.intersects(b2) + " " + b2.intersects(b1));
	System.out.println("b2 <-> b3 " + b2.intersects(b3) + " " + b3.intersects(b2));
	System.out.println("b1 <-> b4 " + b1.intersects(b4) + " " + b4.intersects(b1));
	
	System.out.println("b4 outside " + b4.outsideUnitBox());
	System.out.println("(0.6, 0.6) in b4 " + b4.inside(0.6, 0.6));
	System.out.println("(0.8, 0.8) in b4 " + b4.inside(0.8, 0.8));
	
	Block b5 = b4.move(0.1, 0.7);
	b5.draw(true);
	System.out.println("b5 outside " + b5.outsideUnitBox());			
	System.out.println("b4 <-> b5 " + b4.intersects(b5) + " " + b5.intersects(b4));
}
b1 0.45 0.6 0.55 0.4
b1 <-> b2 false false
b2 <-> b3 true true
b1 <-> b4 true true
b4 outside false
(0.6, 0.6) in b4 true
(0.8, 0.8) in b4 false
b5 outside true
b4 <-> b5 true true

Here is the code we used to test our Blocks class along with sample output:
public static void main(String [] args)
{
	Blocks blocks = new Blocks("bogusfilename.txt");
	
	blocks.add(new Block(0.5, 0.5, 0.1, 0.1, 255, 0, 0));
	blocks.add(new Block(0.2, 0.2, 0.2, 0.3, 0, 0, 255));
	blocks.add(new Block(0.7, 0.7, 0.05, 0.4, 0, 255, 0));
	
	blocks.draw();
	System.out.println("size   = " + blocks.size());
	System.out.println("height = " + blocks.height());
	
	Block b1 = new Block(0.4, 0.45, 0.15, 0.05, 0, 0, 0);
	Block b2 = new Block(0.2, 0.45, 0.15, 0.05, 0, 0, 0);
	b1.draw(true);
	b2.draw(true);
	System.out.println("b1 valid = " + blocks.validLocation(b1));
	System.out.println("b2 valid = " + blocks.validLocation(b2));
	
	Block current = blocks.removeAtLocation(0.0, 0.0);
	System.out.println("current  = " + current);
	System.out.println("size now = " + blocks.size());
	
	current = blocks.removeAtLocation(0.5, 0.5);
	System.out.println("current = " + current);		
	System.out.println("size now = " + blocks.size());
}
Could not find blocks file: bogusfilename.txt
size   = 3
height = 0.8999999999999999
b1 valid = false
b2 valid = true
current  = null
size now = 3
current = Block@42472d48
size now = 2

Note: we got Block@42472d48 when we ran our program, but you'll get something different after the @ sign every time you run it.
I started dragging a block that was intially on top of another one and it was impossible to place it anywhere. Is that suppose to happen? Yes, this can happen since we didn't prevent overlaping blocks in CreateBlocks. So some levels may require careful choice of block order or may be in fact be impossible.

How did you get the height to print to just three decimal places? We used String.format("%.3f", value) to return a String representing a floating-point value to three decimal places.

What imports do I need for file I/O and colors? To read and write from text files, you'll get everything you need (and more) by adding import java.io.*, import java.util.*, and import java.awt.*
Extra credit. Make the game better. Please see the note below on how to submit your extra-credit creation. Be sure to submit your regular submission first and ensure it works per the assignment specifications.
Submission. Submit your programs CreateBlocks.java, BlockGame.java, Blocks.java, and Block.java using Moodle. Be sure each submitted source file has the required header with your name, username, and a description of the program. For extra-credit, please submit a single zip file containing all your programs/graphics/sounds via the special extra-credit dropbox.

Page last updated: May 15, 2013