COMP 1006/1406 Winter 2009
Assignment #4: Table Shuffleboard
Due Date: Monday April 6 by 4:00pm
 

Assignments MUST be submitted electronically using Raven. The Raven Web site is:  raven.scs.carleton.ca
 



This assignment will give you practice using Graphics and Menus in Java.
 


Assignment Marking:

This assignment is based on 27 specific design requirements labeled R1.1... R3.1 and we will be awarding 2 marks for those you are able to satisfy for a total of 54 marks.  You will get 2 marks for each design requirement that is satisfied, well implemented and demonstrated through your testing output. You will get 1 mark for any requirement only partly met, or met but not well implemented or not demonstrated in your testing. You get 0 for any requirement not satisfied.

In addition there are some general programming and good practice requirements that you must satisfy. These requirements are numbered R0.1, R0.2 .... You do not get marks if you satisfy them, instead you loose 5 marks from your total score for each one of them not satisfied.
 


General Programming and Good Practice Requirements.

These requirements pertain regardless of what your application is supposed to do (i.e. regardless of the design requirements). These requirements are to ensure that your code is readable and maintainable by other programmers (or readable by the TA's in our case), and that your program is robust (It does not crash from bad object references). You will  loose  5 marks from your total  assignment mark for each of  the following  requirements that are not satisfied. If you do not satisfy requirement R0.0 you will get nothing for the assignment.
 

R0.0) IMPORTANT Uniqueness Requirement. The solution and code you submit MUST be unique. That is, it cannot be a copy, or be too similar, to someone else's assignment, or other code found elsewhere. A mark of 0 will be assigned to any assignment that is judged by us or the TA's not to be unique.
 

R0.1) All of your variables, methods and classes should have meaningful names that reflect their purpose. Do not follow the convention common in math courses where they say things like: "let x be the number of customers and let y be the number of products...". Instead call your variables numberOfCustomers or numberOfProducts. Your program should not have any variables called "x" unless there is a very good reason for them to be called "x". (One exception: It's OK to call  for-loop counters i,j and k etc. when the context is clear; that is common practice in programming.)
 

R0.2) All variables in your classes should be private, unless a specific design requirements asks for them to be public (which is unlikely). We will design objects that provide services to others through their public methods. How they store their variables is their own private business. (This will not pertain to assignment #1 since we are not building classes and objects yet.)
 

R0.3) Robustness Requirements: Your program should never crash because of a "null pointer exception". This exception means that you are using a variable that does not actually refer to an object. We instruct the TA's to try and crash your program for this reason so guard against it. (The TA's love this part of their job.)
 

R0.4) Comments in your code must coincide with what the code actually does. It is a very common bug in industry for people to modify code and forget to modify the comments and so you end up with comments that say one thing and code that actually does another. (By the way, try not to over-comment your code; instead choose good variable names and method names which make the code more "self commenting".)

R0.5) Java 1.5 Requirement: Your code should compile with the Java 1.5SDK or later without warnings. This requirement is to ensure that if you used older code from elsewhere (e.g. some older demonstration code.) that it has been updated to compile cleanly with Java 1.5. For example code like the following:

Vector people = new Vector();

...
Person p = (Person) people.elementAt(0);
p.getName();

would probably generate a warning with Java 1.5 compile. You should use template ArrayLists as follows instead.

ArrayList<Person> people = new ArrayList<Person>();
....
Person p = people.get(0);
p.getName();


VERY IMPORTANT: Any sample code fragments shown below may have bugs (although none have been put there intentionally). It is part of your task to identify errors in the requirements.
 


 


Design Requirements: Building a Shuffleboard Game

I'm sure most of you have played table shuffleboard at a pub at some point. The game consists of a long wooden table along which you shoot circular disks called weights or stones. The objective is to get your weights closer to the far end of the table than the opponent. Here is an explanation of the rules.

Rules Of The Game

The following rules were copied from the web site: http://www.shuffleboard.net. If  you are totally unfamiliar with what table shuffleboard is you should go out to a pub and play a few games.

Basic Overview:
Games are played one-on-one or with two teams of two people    Team members play at opposite ends of the board and remain there for the duration of the game.  Games are played until one team scores 15 points at which time they are declared the winners.  Weights are considered in play if they are on the board and past the foul line closest to the shooter (i.e., a short foul line).

Play:

Initial shooting and weight color are decided by coin flip. The winner of the flip may elect to choose the "color" or the "hammer".   The hammer is the last weight shot in a round (i.e., the other team shoots first). It is considered advantageous to have the hammer.  When color and shooting order are decided play begins.

The team without the hammer shoots first. The other team shoots second. Players continue to alternate shooting until all weights are used. At this time, points are counted and play continues from the opposite end. The team that scored points on the previous round must shoot first on the next round.  If no points are scored on the preceding round (e.g., all weights are knocked off) then the hammer changes. In other words, the team that had the hammer during the round where no points were scored must shoot first the next round. Play continues in this manner until one team reaches 15 points.

Scoring:
Only one team scores in a round. 

The team which has their weight closest to the end of the board scores. All of their weights which are ahead of their opponent's deepest weight (closest to the end of the board) are added together for the score for that round.

A weight scores one point if it is located between the short foul line and the "2" line.

Weights completely across the "2" or "3" line count 2-points or 3-points respectively.  To judge if a weight is completely over the line it should be viewed from above (i.e., look down over the top of the weight. Again the entire weight must be over the line for it to count as the next higher point value. You should be able to see some wood between the line and the weight.

If any portion of the weight is hanging over the end of the board (not the side) it is called a "hanger" and counts four (4) points.  Close calls can be checked by holding a weight so the bottom of the weight is along the back end of the board. The weight is then slid along the back end of the board. If it hits the "disputed" hanger the weight is indeed hanging and is worth 4 points.

Scoring Example:

On the picture of the table shown below, the "red" team gets to score because the red weight is closest to the end of the board. The "red" team score 5 points.

Miscellaneous rules:
Before a player shoots, the player can dust the board with saw dust if dry spots are showing.
Note: In tournaments this may be restricted to the edge of the board.
Shooters must have one foot behind the playing surface while they are shooting. Hitting or shaking the table is never allowed.

 
GUI Requirements

                    

Your GUI should provide at least the following, but you can add more if you want. The close up view above is just for illustration, you application need not have a close up view.

R1.1) The main view representing the whole shuffleboard table should be oriented vertically so that shooting is up from the bottom of your screen towards the top

R1.2) The table top should look as much like wood as possible (Preferably a light, laminated, maple).
[Special Note: The ones that look most like wood, as judged by the TA, will get 2 marks. Ones that look sort-of wood-like get 1 mark. Ones that look unconvincing get 0.  --The picture above would get 0.]

R1.3) The scoring lines should be drawn on the table along with the designations "1", "2" and "3".

R1.4) The weights should be round and be in two colors to distinguish the two teams. There should be four weights for each team.

R1.5) Shooting a weight should work like shooting the cue ball did in tutorial #4. That is, you should click on the centre of the stone you wish to shoot, drag the mouse back and then release the mouse to shoot. The further you drag back the harder the shot will be. The line from the point where the mouse was released to the center of the stone will determine the direction of the stone when shot. You will need to experiment with the numbers to get the stone to depart with a reasonable speed. The travel down the table should seem "natural" and "correct".

R1.6) The weights should slide down the table in a realistic way when they are shot. You should use a Timer object to animate the motion.

R1.7) You should be able to induce a spin on the stone by dragging from off-center, rather than the center of the stone. The more off-center the more the induced spin should be. Dragging from the right of center of the stone should induce a spin towards the left. That is, it should spin to the opposite side.

R1.8) By inducing a spin on the stone you should be able to get it to curve as it travels up the table. This should allow you, if you shoot well, to position a stone behind another.

R1.9) There should be enough spin so that it is evident and useful to the shooter. You will have to experiment with how you might want to do this. Basically you will have to modify the horizontal velocity of the stone as it travels up the table.

R1.10) When weights collide with each other they should bounce and deflect in a realistic way. At the end of this assignment description is an explanation of the mathematics of how to do this. Note you can do this yourself from scratch or make use of the Pool Game code in tutorial #4 which is based on this math.

R1.11) The main shuffle board should have are area around the table which is "out of bounds" or "off the table". The code must do bounds checking and if a weight falls off the table it should stop and be drawn more "grayed-out". A weight is deemed to have fallen off the table once it's center is past the edge of the table. (Note that weights can go off the end or off the sides of the table.)

R1.12) Your application window should also have a scoreboard which shows, for any given configuration of stones, what the score count should be and for which team it counts.

R1.13) The weights that are currently counting towards the score should have their score indicated on, or beside, them on the shuffleboard window. In the example closeup above the red team is currently scoring 5 with their two weights, but this needs to be indicated on the actual game GUI.

R1.14) Your code should have a Weight class that represents the weight objects being shot. These objects could be analogous to the Ball objects from tutorial #4 (The Pool Game).  Each Weight object should keep track of its own color, position and current velocity.


Menu Requirements

R2.1) Your main GUI window should a "Game" menu in the menu bar of the main window with the following options.

R2.2) There should be a "Reset" menu item that resets the weights all at the bottom of the table ready for the next shooting round.

R2.3) There should a cascaded submenu in the "Game" menu called "Speed" and it should have three radio button menu items labled "Fast", "Regular" and "Slow". These menu items should cause the table friction to be adjusted so that you can simulate a fast, or slippery, table, a regular table and a slow, or "sticky" table. Set the friction values so that the results seem realistic and fun to play.

R2.4) There should be some menu items to allow each team to pick from several weight colors.

R2.5) Two teams should not be allowed to choose the same color.

R2.6) There should be an "Exit" item which will cause the game to close.

R2.7) There should be an "Undo" menu item.

R2.8) When the "Undo" item is selected the game position should go back to how it looked before the last shot. That is, go back as though the last shot was never taken.

R2.9) When the "Undo" item is selected the score should also revert back to what it was before the shot being undone.

R2.10) The "Undo" menu should allow the player to repeat a shot attempt over and over by repeatedly undoing after each shot.

R2.11) There should be a  "Replay" menu item.

R2.12) When the "Replay" menu item is selected a replay of the last shot should be shown. That is it should like and play just like when the stone was actually shot by the user. A suggestion for doing this is to capture the stone positions, or state, for each timer event during a shot and then play them back when the replay item is selected.

 

Launch Requirements
R3.1) Your code that contains the main() method should be in a class called ShuffleBoardApplication. This should be the only class with a main() method.


The Mathematics of Shuffleboard

There are two ways you can go about programming how the weights move and collide with each other. The first is to understand the mathematical explanation below and program it from scratch -the more fun and challenging approach, the second is to reuse the code provided for the pool game in tutorial #4. That code, by the way, is based exactly on the explanation below.
Here is a quick summary of the physics (or math) of two weights hitting each other.


In the simple situation above the red weight is stationary and the yellow weight hits it. When two weights collide, the line through their centers is called the "line of impact". (Two weights collide when their centers are two weight radius lengths from each other). Suppose the yellow weight is traveling with velocity Vy at an angle "a" with the line of impact. After collision, the red weight will travel along the line of impact with a velocity Ur = Vy*cos(a). The yellow weight will travel away with the same angle "a" but on the opposite side of the line of impact, with a velocity of Vy*sin(a). -Simple enough right? In the next situation both weights are moving.


If both weights are moving the situation is similar but each imparts some velocity to the other. For example suppose the yellow weight is traveling with velocity Vy at an angle "a" with the impact line and the red weight is traveling with velocity Vr at an angle of "b" with the impact line. The yellow weight will bounce off again at an angle "a" with the impact line, but with velocity Uy = Vy*sin(a) + Vr*cos(b). The red weight will bounce off at angle "b" with velocity Ur = Vr*sin(b) + Vy*cos(a).

Your task is to model these collisions. You will have to dust off your high school geometry book if you don't remember sines and cosines etc. In the pool game code provided in tutorial #4 the velocity of the balls in the vertical direction is stored separately from the velocity in the horizontal direction. This makes it easy do to the animation, but you will have to translate the above pictures into that situation or change the way velocities are stored in the weight objects. It is entirely up to you, you do not have to leave the code we provide as is.

The pool game code provided in tutorial #4 in based on this analysis and works as follows:
For each timer event, see which weights are moving and move them. Next see if any weights have collided. If so, adjust their speeds accordingly. (You need to make sure you don't get a double hit, that is, the next timer event does the same collision again) In the pool game code each ball has a boolean "moving" that is true if a ball is moving and false otherwise. This boolean makes it a bit easier to test for motion.



Hints with the Math.
In case some of you are having trouble with the math for collisions. Here is an explanation of how you might compute the new velocities after a collision.

Lets examine the simple situation where weight 1 is moving and hits a stationary weight 2.

At the point of collision what do we know? We know the positions of each weight, (x1,y1) and (x2,y2). We know horizontal and vertical velocity of each weight, v1x, v1y,  v2x=0, v2y = 0. We know the weight's centers are 2R apart (R is the radius of a weight).
The velocity vector of weight1, called V, has magnitude sqrt(v1x*v1x + v1y*v1y); Weight2 is stopped so its velocity is 0.

Our objective is to figure out the new vx and vy for the two weights after the collision. Looking at the situation 1 picture above we know the moving weight will depart at the same angle to the line of impact as it arrived -but on the opposite side, and we know the struck weight will depart along the line of impact. So everything we know at the point of collision is shown on the picture below.


Now first off, realize that the the model we are using is a very simple (first order) approximation. That is, you will notice V1 = V*sin(a) and V2 = V*cos(a). This is not strictly accurate. It is based on the approximation that sin(a) + cos(a) ~= 1. The approximation has it maximum error at a=45 degrees. A better approximation would be use the correct formula sin2(a) + cos2(a) = 1. If you want to use the more accurate interpretation for your code that is fine. In that case the speeds should be V1=V*sin2(a); V2=V*cos2(a). I will continue though with the simpler model as it should suffice for this level of animation.

Now our objective is to figure out the new v1x, v1y, v2x, and v2y. Thus we want to resolve all the angles relative to vertical and horizontal.
The picture below now shows the important angles and how they can be found.

Angle a is the angle the moving weight velocity makes with the line of impact.
Angle b is the angle that the line of impact makes with the horizontal.
The angle then that the moving weight would depart would be angle c=b-a.

First remember the old velocity
V = sqrt(v1x*v1x + v1y*v1y);

Here is how the angles can be found.

b = arcsin((y2-y1)/2R)   //i.e. computed from the known positions of the stones.
                                    //In Java Math class there is an asin() method that does this
                                    //there also the sin() and cos() methods.

d = arcsin(vx/V); //moving weight's angle with the vertical

a = pi/2 - b -d //i.e. angle a is 90 degrees less angle b and d; remember we have to work in radians so 90 degrees is pi/2.

So then the angle c at which the moving weight departs at relative to the horizon is:  c = b - a.      

So now we know all the important angles so let's calculate the new speeds.

The new speeds are:
V1 = V*sin(a); V2 = V*cos(a);

Resolve the speeds back into vertical and horizontal components for the two weights.

v1x = V1*cos(c); v1y = V1*sin(c); v2x = V2*cos(b); v2y = V2*sin(b);

So now the weights are on their way with new speeds and directions and will move when the next timers event occurs. There are other details you may have to resolve. To handle two weights moving at once you can work out the additional details. It will involve a few more angles and speeds but won't be much harder. The other way you could do two weights moving is to handle it as two cases. In each case one is stopped and the other moving. Then just add the horizontal and vertical velocities of the two cases together at the end and you will have your answer (this is how the demonstration code for the pool game works.)

Submission

You should hand in your .java source files, .class files, and a readme.txt files if you have any special instructions that you feel the TA's needs to know about before they mark your assignment.