|
/*
FourByFour
To run:
appletviewer FourByFour.html
Press the "Instructions" button to get instructions on
how to play FourByFour.
Four By Four
Description:
A three dimensional game of tic-tac-toe on a 4x4x4 cube.
Object:
Be the first to score four in a row.
Instructions:
1. It's you versus the computer.
2. There are five skill levels. Press the "Skill Level" button to select your level
of play. The program defaults to the hardest level. Changing the skill level in
the middle of a game will force the start of a new game.
3. The screen on the left is a 3D window. A mouse drag in this window will rotate the
view to any desired position.
4. The screen on the right is a 2D window which displays all 18 faces that exist in the
4x4x4 array.
5. Click on any of the small gray spheres (in either the 2D or 3D window) to select a position.
6. Positions owned by you will be marked in red. Positions owned by the computer will be
marked in blue.
7. Click the "Undo Move" button to take back a move.
8. Clicking on any of words "Face X" in the 2D window will cause that particular face to highlight
in the 3D window. Clicking the word again will un-highlight the face.
9. The final score is based on skill level, number of moves, and time. Select the button
"High Scores" to see a list of the top 20 scores. There is no penalty for using the
undo button.
10. Good luck.
General Strategy:
1. There are a 64 positions from which to choose. In total, there are 72 possible winning
combinations.
2. The outer four corners and the inner "core" of eight have the most winning combinations,
7 each, and should perhaps be chosen first.
3. Use the 2D window to keep an eye on all the faces.
4. The computer plays well at the highest skill level (the default). There are, however,
faults in it's logic that can be exploited. Thus the human player can win even at the
highest skill level. In the beginning, however, you may want to start at the lower skill
levels and work your way up.
*/
/*
* @(#)FourByFour.java 1.17 02/10/21 13:39:27
*
* Copyright (c) 1996-2002 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN
* OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR
* FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
* PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
* LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that Software is not designed,licensed or intended
* for use in the design, construction, operation or maintenance of
* any nuclear facility.
*/
import java.applet.Applet;
import java.awt.AWTEvent;
import java.awt.Button;
import java.awt.Canvas;
import java.awt.Checkbox;
import java.awt.CheckboxGroup;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Label;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.net.URL;
import java.util.BitSet;
import java.util.Enumeration;
import javax.media.j3d.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.Behavior;
import javax.media.j3d.BoundingLeaf;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.CapabilityNotSetException;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.Group;
import javax.media.j3d.Material;
import javax.media.j3d.Node;
import javax.media.j3d.PickRay;
import javax.media.j3d.QuadArray;
import javax.media.j3d.SceneGraphPath;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Switch;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.View;
import javax.media.j3d.WakeupCriterion;
import javax.media.j3d.WakeupOnAWTEvent;
import javax.media.j3d.WakeupOr;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.universe.SimpleUniverse;
/**
* Class FourByFour
*
* Description: High level class for the game FourByFour
*
* Version: 1.2
*
*/
public class FourByFour extends Applet implements ActionListener {
String host; // Host from which this applet came from
int port; // Port number for writing high scores
Image backbuffer2D; // Backbuffer image used for 2D double buffering
int width, height; // Size of the graphics window in pixels
int score; // Final game score
int level_weight; // Weighting factor for skill level
int move_weight; // Weighting factor for number of moves to win
int time_weight; // Weighting factor for amount of time it took to win
int skill_level; // Skill level, 0 - 4
Canvas2D canvas2D; // 2D rendering canvas
Canvas3D canvas3D; // 3D rendering canvas
Board board; // Game board object
Panel b_container; // Container to hold the buttons
Panel c_container; // Container to hold the canvas
Panel l_container; // Container to hold the labels
Panel skill_panel; // Panel to hold skill levels
Panel instruct_panel; // Panel to hold instructions
Panel winner_panel; // Panel to hold winner announcement
Panel high_panel; // Panel to hold high scores
Button instruct_button; // Instructions button
Button new_button; // New Game button
Button skill_button; // Skill Level button
Button high_button; // High Scores button
Button undo_button; // Undo Move button
Label skill_label; // Label on skill panel
Label winner_label; // Label on winner panel
Label winner_score_label; // Score label on winner panel
Label winner_name_label; // Name label on winner panel
Label winner_top_label; // Top 20 label on winner panel
Label high_label; // High score label
Label high_places[]; // Labels to hold places
Label high_names[]; // Labels to hold names
Label high_scores[]; // Labels to hold scores
TextArea instruct_text; // TextArea object that holds instructions
TextArea high_text; // TextArea object that holds top 20 scores
TextField winner_name; // TextField object that holds winner's name
Button instruct_return_button; // Return button for instruction panel
Button skill_return_button; // Return button for skill level panel
Button winner_return_button; // Return button for winner panel
Button high_return_button; // Return button for high scores panel
CheckboxGroup group; // CheckboxGroup object for skill level panel
InputStream inStream; // Input stream for reading instructions and high
// scores
OutputStream outStream; // Output stream for writing high scores
static boolean appletFlag = true; // Applet flag
boolean winner_flag = false; // Winner flag
byte text[]; // Temporary storage area for reading instructions file
byte outText[]; // Temporary storage area for writing high scores file
String textString; // Storage area for instructions
String scoresString; // String used for writing high scores file
int places[]; // Storage area for high score places
int scores[]; // Storage area for high score scores
String names[]; // Storage area for high score names
Positions positions; // Positions object, used to render player positions
private SimpleUniverse universe = null;
/**
* Initialization
*/
public void init() {
// Set the port number.
port = 4111;
// Set the graphics window size.
width = 350;
height = 350;
// Set the weighting factors used for scoring.
level_weight = 1311;
move_weight = 111;
time_weight = 1000;
// Create the "base" color for the AWT components.
setBackground(new Color(200, 200, 200));
// Read the instructions file.
if (appletFlag) {
// Get the host from which this applet came.
host = getCodeBase().getHost();
try {
inStream = new BufferedInputStream(new URL(getCodeBase(),
"instructions.txt").openStream(), 8192);
text = new byte[5000];
int character = inStream.read();
int count = 0;
while (character != -1) {
text[count++] = (byte) character;
character = inStream.read();
}
textString = new String(text);
inStream.close();
} catch (Exception e) {
System.out.println("Error: " + e.toString());
}
} else {
try {
inStream = new BufferedInputStream(new FileInputStream(
"instructions.txt"));
text = new byte[5000];
int character = inStream.read();
int count = 0;
while (character != -1) {
text[count++] = (byte) character;
character = inStream.read();
}
textString = new String(text);
inStream.close();
} catch (Exception e) {
System.out.println("Error: " + e.toString());
}
}
// Read the high-scores file.
places = new int[20];
scores = new int[20];
names = new String[20];
if (appletFlag) {
try {
inStream = new BufferedInputStream(new URL(getCodeBase(),
"scores.txt").openStream(), 8192);
Reader read = new BufferedReader(
new InputStreamReader(inStream));
StreamTokenizer st = new StreamTokenizer(read);
st.whitespaceChars(32, 44);
st.eolIsSignificant(false);
int count = 0;
int token = st.nextToken();
boolean scoreFlag = true;
String string;
while (count < 20) {
places[count] = (int) st.nval;
string = new String("");
token = st.nextToken();
while (token == StreamTokenizer.TT_WORD) {
string += st.sval;
string += " ";
token = st.nextToken();
}
names[count] = string;
scores[count] = (int) st.nval;
token = st.nextToken();
count++;
}
inStream.close();
} catch (Exception e) {
System.out.println("Error: " + e.toString());
}
} else {
try {
inStream = new BufferedInputStream(new FileInputStream(
"scores.txt"));
Reader read = new BufferedReader(
new InputStreamReader(inStream));
StreamTokenizer st = new StreamTokenizer(read);
st.whitespaceChars(32, 44);
st.eolIsSignificant(false);
int count = 0;
int token = st.nextToken();
boolean scoreFlag = true;
String string;
while (count < 20) {
places[count] = (int) st.nval;
string = new String("");
token = st.nextToken();
while (token == StreamTokenizer.TT_WORD) {
string += st.sval;
string += " ";
token = st.nextToken();
}
names[count] = string;
scores[count] = (int) st.nval;
token = st.nextToken();
count++;
}
inStream.close();
} catch (Exception e) {
System.out.println("Error: " + e.toString());
}
}
// The positions object sets up the switch nodes which
// control the rendering of the player's positions.
positions = new Positions();
// Create the game board object which is responsible
// for keeping track of the moves on the game board
// and determining what move the computer should make.
board = new Board(this, positions, width, height);
positions.setBoard(board);
// Create a 2D graphics canvas.
canvas2D = new Canvas2D(board);
canvas2D.setSize(width, height);
canvas2D.setLocation(width + 10, 5);
canvas2D.addMouseListener(canvas2D);
board.setCanvas(canvas2D);
// Create the 2D backbuffer
backbuffer2D = createImage(width, height);
canvas2D.setBuffer(backbuffer2D);
// Create a 3D graphics canvas.
canvas3D = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
canvas3D.setSize(width, height);
canvas3D.setLocation(5, 5);
// Create the scene branchgroup.
BranchGroup scene3D = createScene3D();
// Create a universe with the Java3D universe utility.
universe = new SimpleUniverse(canvas3D);
universe.addBranchGraph(scene3D);
// Use parallel projection.
View view = universe.getViewer().getView();
view.setProjectionPolicy(View.PARALLEL_PROJECTION);
// Set the universe Transform3D object.
TransformGroup tg = universe.getViewingPlatform()
.getViewPlatformTransform();
Transform3D transform = new Transform3D();
transform.set(65.f, new Vector3f(0.0f, 0.0f, 400.0f));
tg.setTransform(transform);
// Create the canvas container.
c_container = new Panel();
c_container.setSize(720, 360);
c_container.setLocation(0, 0);
c_container.setVisible(true);
c_container.setLayout(null);
add(c_container);
// Add the 2D and 3D canvases to the container.
c_container.add(canvas2D);
c_container.add(canvas3D);
// Turn off the layout manager, widgets will be sized
// and positioned explicitly.
setLayout(null);
// Create the button container.
b_container = new Panel();
b_container.setSize(720, 70);
b_container.setLocation(0, 360);
b_container.setVisible(true);
b_container.setLayout(null);
// Create the buttons.
instruct_button = new Button("Instructions");
instruct_button.setSize(135, 25);
instruct_button.setLocation(10, 10);
instruct_button.setVisible(true);
instruct_button.addActionListener(this);
new_button = new Button("New Game");
new_button.setSize(135, 25);
new_button.setLocation(150, 10);
new_button.setVisible(true);
new_button.addActionListener(this);
undo_button = new Button("Undo Move");
undo_button.setSize(135, 25);
undo_button.setLocation(290, 10);
undo_button.setVisible(true);
undo_button.addActionListener(this);
skill_button = new Button("Skill Level");
skill_button.setSize(135, 25);
skill_button.setLocation(430, 10);
skill_button.setVisible(true);
skill_button.addActionListener(this);
high_button = new Button("High Scores");
high_button.setSize(135, 25);
high_button.setLocation(570, 10);
high_button.setVisible(true);
high_button.addActionListener(this);
b_container.add(new_button);
b_container.add(undo_button);
b_container.add(skill_button);
b_container.add(high_button);
b_container.add(instruct_button);
// Add the button container to the applet.
add(b_container);
// Create the "Skill Level" dialog box.
skill_panel = new Panel();
skill_panel.setSize(400, 300);
skill_panel.setLocation(200, 20);
skill_panel.setLayout(null);
skill_label = new Label("Pick your skill level:");
skill_label.setSize(200, 25);
skill_label.setLocation(25, 20);
skill_label.setVisible(true);
skill_panel.add(skill_label);
group = new CheckboxGroup();
Checkbox skill_1 = new Checkbox("Babe in the Woods ", group,
false);
Checkbox skill_2 = new Checkbox("Walk and Chew Gum ", group,
false);
Checkbox skill_3 = new Checkbox("Jeopardy Contestant ", group,
false);
Checkbox skill_4 = new Checkbox("Rocket Scientist ", group,
false);
Checkbox skill_5 = new Checkbox("Be afraid, be very afraid", group,
true);
skill_1.setSize(170, 25);
skill_1.setLocation(80, 60);
skill_1.setVisible(true);
skill_2.setSize(170, 25);
skill_2.setLocation(80, 100);
skill_2.setVisible(true);
skill_3.setSize(170, 25);
skill_3.setLocation(80, 140);
skill_3.setVisible(true);
skill_4.setSize(170, 25);
skill_4.setLocation(80, 180);
skill_4.setVisible(true);
skill_5.setSize(170, 25);
skill_5.setLocation(80, 220);
skill_5.setVisible(true);
skill_return_button = new Button("Return");
skill_return_button.setSize(120, 25);
skill_return_button.setLocation(300, 370);
skill_return_button.setVisible(false);
skill_return_button.addActionListener(this);
skill_panel.add(skill_1);
skill_panel.add(skill_2);
skill_panel.add(skill_3);
skill_panel.add(skill_4);
skill_panel.add(skill_5);
skill_panel.setVisible(false);
add(skill_return_button);
add(skill_panel);
// Create the "Instructions" panel.
instruct_return_button = new Button("Return");
instruct_return_button.setLocation(300, 370);
instruct_return_button.setSize(120, 25);
instruct_return_button.setVisible(false);
instruct_return_button.addActionListener(this);
instruct_text = new TextArea(textString, 100, 200,
TextArea.SCROLLBARS_VERTICAL_ONLY);
instruct_text.setSize(715, 350);
instruct_text.setLocation(0, 0);
instruct_text.setVisible(false);
add(instruct_text);
add(instruct_return_button);
high_panel = new Panel();
high_panel.setSize(715, 350);
high_panel.setLocation(0, 0);
high_panel.setVisible(false);
high_panel.setLayout(null);
high_label = new Label("High Scores");
high_label.setLocation(330, 5);
high_label.setSize(200, 30);
high_label.setVisible(true);
high_panel.add(high_label);
high_places = new Label[20];
high_names = new Label[20];
high_scores = new Label[20];
for (int i = 0; i < 20; i++) {
high_places[i] = new Label(Integer.toString(i + 1));
high_places[i].setSize(20, 30);
high_places[i].setVisible(true);
high_names[i] = new Label(names[i]);
high_names[i].setSize(150, 30);
high_names[i].setVisible(true);
high_scores[i] = new Label(Integer.toString(scores[i]));
high_scores[i].setSize(150, 30);
high_scores[i].setVisible(true);
if (i < 10) {
high_places[i].setLocation(70, i * 30 + 40);
high_names[i].setLocation(100, i * 30 + 40);
high_scores[i].setLocation(260, i * 30 + 40);
} else {
high_places[i].setLocation(425, (i - 10) * 30 + 40);
high_names[i].setLocation(455, (i - 10) * 30 + 40);
high_scores[i].setLocation(615, (i - 10) * 30 + 40);
}
high_panel.add(high_places[i]);
high_panel.add(high_names[i]);
high_panel.add(high_scores[i]);
}
high_return_button = new Button(
|