0da1c5dcca
First commit of the 4.5 version (latest version available)
972 lines
46 KiB
Java
972 lines
46 KiB
Java
package mars.tools;
|
|
import mars.*;
|
|
import mars.mips.hardware.*;
|
|
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import javax.swing.*;
|
|
import java.util.*;
|
|
import javax.swing.JOptionPane;
|
|
import mars.util.*;
|
|
|
|
/**
|
|
* Demo of Mars tool capability. Ken Vollmar, 27 Oct 2006 KenVollmar@missouristate.edu
|
|
* This tool displays movements by a series of players in a game of ScavengerHunt.
|
|
* Players will read and write MIPS memory-mapped locations to move and regain energy.
|
|
* See accompanying documentation for memory-mapped addresses, rules of the game, etc.
|
|
*/
|
|
|
|
public class ScavengerHunt implements Observer, MarsTool
|
|
{
|
|
private static final int GRAPHIC_WIDTH = 712;
|
|
private static final int GRAPHIC_HEIGHT = 652;
|
|
|
|
private static final int NUM_PLAYERS = 22; // Number of players in the game, including "baseline" player
|
|
private static final int MAX_X_MOVEMENT = 2; // Max. movement in X direction
|
|
private static final int MAX_Y_MOVEMENT = 2; // Max. movement in X direction
|
|
private static final double MAX_MOVE_DISTANCE = 2.5; // Max. distance (Euclidean measure)
|
|
private static final int ENERGY_AWARD = 20; // Energy awarded for each task completion
|
|
private static final int ENERGY_PER_MOVE = 1; // Energy used in making each move (regardless of distance)
|
|
private static final int SIZE_OF_TASK = 20; // Number of elements in the task
|
|
|
|
private static final int NUM_LOCATIONS = 7; // Number of locations to which ScavengerHunt players travel.
|
|
// The first (n-1) locations are "random" locations, the last is START_AND_END_LOCATION
|
|
|
|
private static final int START_AND_END_LOCATION = 255; // Start and end location of the ScavengerHunt
|
|
private static final int ADMINISTRATOR_ID = 999; // Special ID for administrator
|
|
|
|
|
|
// MIPS addresses of administrative memory space.
|
|
// The administrator MIPS program writes a value to the Authentication field prior to writing a new
|
|
// value to the PlayerID field. The value of the Authentication field is not itself verified until
|
|
// the PlayerID field changes. Each new value of the Authentication field is one of some sequence
|
|
// known to the Tool program (one-time pad system).
|
|
// Each change of data in the PlayerID field is a signal for the Tool to check the value of the Authentication
|
|
// field. If the value of the Authentication field is correct, the value of the PlayerID field is used
|
|
// for memory bounds checking. A new value of the Authentication field is expected at each change of the
|
|
// PlayerID field (one-time pad system).
|
|
private static final int ADDR_AUTHENTICATION = 0xffffe000; // MIPS byte address of Authentication field
|
|
private static final int ADDR_PLAYER_ID = 0xffffe004; // MIPS byte address of PlayerID field
|
|
private static final int ADDR_GAME_ON = 0xffffe008; // MIPS byte address of signal that administration has initialized data
|
|
private static final int ADDR_NUM_TURNS = 0xffffe00c; // MIPS byte address of number of turns remaining in the game
|
|
|
|
|
|
|
|
// MIPS addresses of various data in each player's memory space.
|
|
// Each player's assigned memory is the MEM_PER_PLAYER bytes which begin at
|
|
// location ADDR_BASE + (ID * MEM_PER_PLAYER)
|
|
private static final int ADDR_BASE = 0xffff8000; // MIPS byte address of memory space for first player
|
|
private static final int MEM_PER_PLAYER = 0x400; // MIPS bytes of memory space given to each player
|
|
private static final int OFFSET_WHERE_AM_I_X = 0x0; // MIPS byte offset to this field
|
|
private static final int OFFSET_WHERE_AM_I_Y = 0x4; // MIPS byte offset to this field
|
|
private static final int OFFSET_MOVE_TO_X = 0x8; // MIPS byte offset to this field
|
|
private static final int OFFSET_MOVE_TO_Y = 0xc; // MIPS byte offset to this field
|
|
private static final int OFFSET_MOVE_READY = 0x10; // MIPS byte offset to this field
|
|
private static final int OFFSET_ENERGY = 0x14; // MIPS byte offset to this field
|
|
private static final int OFFSET_NUMBER_LOCATIONS = 0x18; // MIPS byte offset to this field
|
|
private static final int OFFSET_PLAYER_COLOR = 0x1c; // MIPS byte offset to this field
|
|
private static final int OFFSET_SIZE_OF_TASK = 0x20; // MIPS byte offset to this field
|
|
private static final int OFFSET_LOC_ARRAY = 0x24; // MIPS byte offset to this field
|
|
private static final int OFFSET_TASK_COMPLETE = 0x124; // MIPS byte offset to this field
|
|
private static final int OFFSET_TASK_ARRAY = 0x128; // MIPS byte offset to this field
|
|
// Other MIPS memory locations are available to the player's use.
|
|
|
|
private ScavengerHuntDisplay graphicArea;
|
|
private int authenticationValue = 0;
|
|
private boolean GameOn = false; // MIPS programs readiness
|
|
private static int SetWordCounter = 0;
|
|
private static int accessCounter = 0;
|
|
private static int playerID = ADMINISTRATOR_ID; // Range 0...(NUM_PLAYERS-1), plus ADMINISTRATOR_ID
|
|
private boolean KENVDEBUG = false;
|
|
|
|
|
|
// Used to define (X,Y) coordinate of a location to which ScavengerHunt players
|
|
// will travel.
|
|
private class Location
|
|
{
|
|
public int X;
|
|
public int Y;
|
|
}
|
|
|
|
// private inner class to provide the data on each player needed for display
|
|
private class PlayerData
|
|
{
|
|
int whereAmIX = START_AND_END_LOCATION; // Read only. Memory Address: Base
|
|
int whereAmIY = START_AND_END_LOCATION; // Read only. Memory Address: Base + 0x4
|
|
//int moveToX; // Memory Address: Base + 0x8
|
|
//int moveToY; // Memory Address: Base + 0xc
|
|
//int goalX; // Read only. Memory Address: Base + 0x10
|
|
//int goalY; // Read only. Memory Address: Base + 0x14
|
|
int energy = 20; // Read only. Memory Address: Base + 0x18
|
|
int color = 0; // Memory Address: Base + 0x1c
|
|
long finishTime;
|
|
//int locID; // ID of the location to which ScavengerHunt players are headed. Not used by player.
|
|
boolean hasVisitedLoc[] = new boolean[NUM_LOCATIONS]; // boolean: player has visited each location
|
|
boolean finis = false;
|
|
|
|
// Class PlayerData has no constructor
|
|
|
|
public void setWhereAmI(int gX, int gY) { whereAmIX = gX; whereAmIY = gY; }
|
|
//public void setGoal(int gX, int gY) { goalX = gX; goalY = gY; }
|
|
public void setEnergy(int e) { energy = e; }
|
|
public void setColor(int c) { color = c; }
|
|
public int getWhereAmIX() {
|
|
return whereAmIX; }
|
|
public int getWhereAmIY() {
|
|
return whereAmIY; }
|
|
public int getColor() {
|
|
return color; }
|
|
public boolean hasVisited(int i) {
|
|
return hasVisitedLoc[i]; }
|
|
public void setVisited(int i) { hasVisitedLoc[i] = true; }
|
|
public void setFinished() { finis = true; }
|
|
public boolean isFinished() {
|
|
return finis; }
|
|
public long getFinishTime() {
|
|
return finishTime; }
|
|
public long getFinishMin() {
|
|
return (finishTime / 60000); } // Minutes portion of finishTime
|
|
public long getFinishSec() {
|
|
return (finishTime % 60000) / 1000; // Seconds portion of finishTime
|
|
} // Seconds portion of finishTime
|
|
public long getFinishMillisec() {
|
|
return (finishTime % 1000); } // Millisec portion of finishTime
|
|
public void setFinishTime(long t) { finishTime = t; }
|
|
//public int getGoalX() { return goalX; }
|
|
//public int getGoalY() { return goalY; }
|
|
//public int getMoveToX() { return moveToX; }
|
|
//public int getMoveToY() { return moveToY; }
|
|
public int getEnergy() {
|
|
return energy; }
|
|
//public int getLocationID() { return locID; }
|
|
} // end class PlayerData
|
|
|
|
private static PlayerData[] pd = new PlayerData[NUM_PLAYERS];
|
|
private static Location[] loc = new Location[NUM_LOCATIONS];
|
|
private Random randomStream;
|
|
private long startTime;
|
|
|
|
|
|
// private inner class
|
|
private class ScavengerHuntRunnable implements Runnable
|
|
{
|
|
JPanel panel;
|
|
public ScavengerHuntRunnable() // constructor
|
|
{
|
|
// final JFrame frame = new JFrame("ScavengerHunt");
|
|
// Recommended by Pete Sanderson, 2 Nov. 2006, so that the Tool window and
|
|
// MARS window can be on the screen at the same time.
|
|
final JDialog frame = new JDialog(Globals.getGui(),"ScavengerHunt");
|
|
|
|
// System.out.println("ScavengerHuntRunnable.constructor: starting....");
|
|
|
|
panel = new JPanel(new BorderLayout());
|
|
graphicArea = new ScavengerHuntDisplay(GRAPHIC_WIDTH, GRAPHIC_HEIGHT);
|
|
JPanel buttonPanel = new JPanel();
|
|
JButton resetButton = new JButton("Reset");
|
|
resetButton.addActionListener(
|
|
new ActionListener()
|
|
{
|
|
public void actionPerformed(ActionEvent e)
|
|
{
|
|
graphicArea.clear();
|
|
|
|
// TBD ------- TBD
|
|
// Reset actions here
|
|
initializeScavengerData();
|
|
//JOptionPane.showMessageDialog(null, "Reset needs to be implemented!" );
|
|
|
|
}
|
|
|
|
});
|
|
buttonPanel.add(resetButton);
|
|
|
|
|
|
panel.add(graphicArea, BorderLayout.CENTER);
|
|
panel.add(buttonPanel, BorderLayout.SOUTH);
|
|
|
|
|
|
// Snippet by Pete Sanderson, 2 Nov. 2006, to be a window-closing sequence
|
|
frame.addWindowListener(
|
|
new WindowAdapter() {
|
|
public void windowClosing(WindowEvent e) {
|
|
frame.setVisible(false);
|
|
frame.dispose();
|
|
}
|
|
});
|
|
|
|
frame.getContentPane().add(panel);
|
|
frame.setLocationRelativeTo(null);
|
|
frame.pack();
|
|
frame.setVisible(true);
|
|
frame.setTitle(" This is the ScavengerHunt");
|
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // TBD --- This should close only the Tool, not the entire MARS
|
|
frame.setPreferredSize(new Dimension(GRAPHIC_WIDTH, GRAPHIC_HEIGHT)); // TBD SIZE
|
|
frame.setVisible(true); // show();
|
|
|
|
} // end ScavengerHuntRunnable() constructor
|
|
|
|
public void run()
|
|
{
|
|
|
|
double tempAngle;
|
|
|
|
// infinite loop: play the Scavenger Hunt game
|
|
do
|
|
{
|
|
|
|
// Pause to slow down the redisplay of the game. This is separate from
|
|
// the execution speed of the MIPS program, so the display may lag behind
|
|
// the state of the MIPS program.
|
|
try
|
|
{
|
|
// System.out.println(" Hello from the ScavengerHuntRunnable runner, sleeping here ...");
|
|
// System.out.print(".");
|
|
Thread.sleep(100); // millisec
|
|
}
|
|
catch (InterruptedException exception)
|
|
{// no action
|
|
}
|
|
|
|
panel.repaint(); // show new ScavengerHunt position
|
|
} while (true);
|
|
|
|
} // end run method of ScavengerHuntRunnable class
|
|
|
|
} // end ScavengerHuntRunnable class
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/** ScavengerHuntDisplay does not have access to the same MIPS Memory class object used by
|
|
* the ScavengerHunt class object. Need read-only access to a similar data structure maintained
|
|
* within ScavengerHunt.
|
|
*
|
|
*/
|
|
private class ScavengerHuntDisplay extends JPanel
|
|
{
|
|
private int width;
|
|
private int height;
|
|
private boolean clearTheDisplay = true;
|
|
|
|
|
|
public ScavengerHuntDisplay(int tw, int th)
|
|
{
|
|
// System.out.println("ScavengerHuntDisplay.constructor: starting....");
|
|
width = tw;
|
|
height = th;
|
|
|
|
}
|
|
|
|
public void redraw()
|
|
{
|
|
repaint();
|
|
}
|
|
|
|
public void clear()
|
|
{
|
|
// clear the graphic display
|
|
clearTheDisplay = true;
|
|
//System.out.println("ScavengerHuntDisplay.clear: called to clear the display");
|
|
repaint();
|
|
}
|
|
|
|
/** paintComponent does not have access to the same MIPS Memory class object used by
|
|
* the ScavengerHunt class object. Need read-only access to a similar data structure maintained
|
|
* within ScavengerHunt.
|
|
*
|
|
*/
|
|
public void paintComponent(Graphics g)
|
|
{
|
|
long tempN;
|
|
int xCoord;
|
|
int yCoord;
|
|
|
|
// System.out.println("ScavengerHuntDisplay.paintComponent: I'm painting! n is " + n);
|
|
|
|
|
|
// Recover Graphics2D
|
|
Graphics2D g2 = (Graphics2D) g;
|
|
|
|
if (! GameOn) // Make sure game is ready before continuing
|
|
{
|
|
g2.setColor(Color.lightGray);
|
|
g2.fillRect(0, 0, width - 1, height - 1); // Clear all previous drawn information
|
|
g2.setColor(Color.black);
|
|
g2.drawString(" ScavengerHunt not yet initialized by MIPS administrator program.",
|
|
100, 200);
|
|
return;
|
|
}
|
|
|
|
// Clear all previous drawn information
|
|
g2.setColor(Color.lightGray);
|
|
g2.fillRect(0, 0, width - 1, height - 1);
|
|
|
|
|
|
// Draw the locations to which the players will be moving
|
|
// All players have the same location data.
|
|
for (int i = 0; i < NUM_LOCATIONS; i++)
|
|
{
|
|
xCoord = loc[i].X; // toolReadPlayerData(0, OFFSET_LOC_ARRAY + (i*8) + 0);
|
|
yCoord = loc[i].Y; // toolReadPlayerData(0, OFFSET_LOC_ARRAY + (i*8) + 4);
|
|
g2.setColor(Color.blue);
|
|
g2.fillRect( xCoord, yCoord, 20, 20); // coord is upper left corner of oval
|
|
g2.setColor(Color.white);
|
|
g2.drawString(" " + i, xCoord + 4, yCoord + 15); // coord is lower left corner of string text box
|
|
|
|
/*
|
|
System.out.println("ScavengerHuntDisplay.paintComponent: drew loc " + i + " at (" +
|
|
xCoord +
|
|
", " +
|
|
yCoord +
|
|
")" );
|
|
*/
|
|
}
|
|
|
|
//System.out.println("ScavengerHuntDisplay.paintComponent: special exit!");
|
|
//System.exit(0);
|
|
|
|
|
|
// Draw scoreboard
|
|
g2.setColor(Color.black);
|
|
g2.drawString("Player", width - 160, 30);
|
|
g2.drawString("Locations", width - 110, 30);
|
|
g2.drawString("Energy", width - 50, 30);
|
|
g2.drawLine( width - 160, 35, width - 10, 35); // line under column headings
|
|
g2.drawLine( width - 120, 35, width - 120, 35 + (NUM_PLAYERS * 15)); // vertical line for location-visited marks
|
|
g2.drawLine( width - 50, 35, width - 50, 35 + (NUM_PLAYERS * 15)); // vertical line for location-visited marks
|
|
for (int i = 0; i < NUM_PLAYERS; i++)
|
|
{
|
|
// Draw player's symbol
|
|
g2.setColor( new Color(pd[i].getColor()) );
|
|
// g2.setColor(Color.red); // TBD hardcoded
|
|
|
|
|
|
xCoord = pd[i].getWhereAmIX();
|
|
yCoord = pd[i].getWhereAmIY();
|
|
|
|
// ystem.out.println("paintComponent loop " + i + ": Loc coord is (" + xCoord + ", " + yCoord);
|
|
|
|
// Draw player symbol and label it with player ID number.
|
|
// Hardcoded size of location graphic and label.
|
|
g2.drawOval( xCoord, yCoord, 20, 20); // coord is upper left corner of oval
|
|
g2.drawString(" " + i, xCoord + 4, yCoord + 15); // coord is lower left corner of string text box
|
|
|
|
// Draw player's info on scoreboard
|
|
g2.setColor(Color.black);
|
|
g2.drawString(" " + i, width - 150, 50 + (i*15)); // Player's ID on scoreboard
|
|
g2.drawString(" " + pd[i].getEnergy(), width - 40, 50 + (i*15)); // Player's energy on scoreboard
|
|
|
|
// Display player's progress or finishing time, whichever is applicable
|
|
if (pd[i].isFinished())
|
|
{
|
|
// Display finishing time
|
|
// This doesn't display leading zeroes to align time components (e.g. 27 millisec ought to print as 027)
|
|
g2.drawString (pd[i].getFinishMin() + ":" + // Minutes
|
|
pd[i].getFinishSec() + ":" + // Seconds
|
|
pd[i].getFinishMillisec() , // Milliseconds
|
|
width - 115, 50 + (i*15));
|
|
// System.out.println("Time is " + pd[i].getFinishTime());
|
|
//g2.drawString (" " + pd[i].getFinishTime(),
|
|
// width - 255, 50 + (i*15));
|
|
}
|
|
else // player either has not finished or is just now finishing
|
|
{
|
|
int visCount = 0;
|
|
for (int j = 0; j < NUM_LOCATIONS; j++)
|
|
{
|
|
if (pd[i].hasVisited(j)) // count number of locations that player has visited
|
|
{
|
|
visCount++;
|
|
}
|
|
}
|
|
if (visCount == NUM_LOCATIONS) // player has visited every location -- finished!
|
|
{
|
|
pd[i].setFinished();
|
|
pd[i].setFinishTime( System.currentTimeMillis() - startTime );
|
|
|
|
}
|
|
else // player has not yet visited every location
|
|
{
|
|
// Display locations that the player has actually visited
|
|
for (int j = 0; j < NUM_LOCATIONS; j++)
|
|
{
|
|
if (pd[i].hasVisited(j)) // Player has visited this location
|
|
{
|
|
g2.fillRect ((width -120)+(j*10), 42 + (i*15), 10, 8);
|
|
}
|
|
}
|
|
}
|
|
} // end player had not previously finished
|
|
|
|
} // end display score/results for each player
|
|
|
|
// System.out.println("paintComponent: Player " + 0 + " is at (" + pd[0].getWhereAmIX() + ", " + pd[0].getWhereAmIY() + ")" );
|
|
|
|
|
|
|
|
|
|
/*
|
|
g2.setColor(Color.blue);
|
|
g2.setFont(new Font(g2.getFont().getName(), g2.getFont().getStyle(), 20) ); // same font and style in larger size
|
|
g2.drawOval( width/2 - 30, // TBD Hardcoded oval size
|
|
height/2 - 30,
|
|
60,
|
|
60);
|
|
g2.drawString(" " + n, width/2, height/2);
|
|
*/
|
|
|
|
|
|
}
|
|
|
|
} // end private inner class ScavengerHuntDisplay
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
// A constructor for ScavengerHunt would be called immediately on MARS startup, perhaps
|
|
// before all MARS classes have been created. Do not include any action in a constructor
|
|
// but rather postpone any action until the action() class,
|
|
// at which time all MARS classes will be ready for use.
|
|
public ScavengerHunt()
|
|
{
|
|
// System.out.println("ScavengerHunt.constructor: starting....");
|
|
}
|
|
|
|
public String getName()
|
|
{
|
|
return "ScavengerHunt";
|
|
}
|
|
|
|
/*
|
|
* This will set up the ScavengerHunt's GUI. Invoked when ScavengerHunt menu item selected.
|
|
*/
|
|
public void action()
|
|
{
|
|
|
|
|
|
|
|
ScavengerHuntRunnable shr = new ScavengerHuntRunnable();
|
|
Thread t1 = new Thread(shr);
|
|
t1.start();
|
|
|
|
|
|
// Register as observer for a particular MIPS data range. Other ranges
|
|
// are not used by this Tool.
|
|
try {
|
|
Globals.memory.addObserver(this, 0xffff8000, 0xfffffff0); // must be on word boundaries
|
|
}
|
|
catch (AddressErrorException e)
|
|
{
|
|
System.out.println("\n\nScavengerHunt.action: Globals.memory.addObserver caused AddressErrorException.\n\n");
|
|
System.exit(0);
|
|
}
|
|
|
|
} // end ScavengerHunt.action()
|
|
|
|
/*
|
|
* This method observes MIPS memory for directives to modify ScavengerHunt activity (that is,
|
|
* MIPS program write to MMIO) and updates instance variables to reflect that directive.
|
|
* This method takes action when it "observes" MIPS memory changes -- but this method
|
|
* must not write to those memory locations in order to prevent an infinite cycle of events.
|
|
* This method observes certain locations and then may (and does) write to OTHER locations.
|
|
*/
|
|
public void update(Observable o, Object arg)
|
|
{
|
|
MemoryAccessNotice notice;
|
|
int address;
|
|
int data;
|
|
boolean isWrite;
|
|
boolean isRead;
|
|
int energyLevel;
|
|
|
|
// Here we are only interested in MemoryAccessNotice. For anything else, just return.
|
|
if ( ! (arg instanceof MemoryAccessNotice))
|
|
return;
|
|
|
|
// Get pertinent information about this MemoryAccessNotice.
|
|
notice = (MemoryAccessNotice) arg;
|
|
address = notice.getAddress();
|
|
data = notice.getValue();
|
|
isWrite = (notice.getAccessType() == AccessNotice.WRITE);
|
|
isRead = ! isWrite;
|
|
|
|
|
|
// If we are only interested in MIPS memory WRITES, then just return on READS.
|
|
// That's a matter of policy: perhaps players should be prohibited from
|
|
// reading each other's memory spaces.
|
|
if ( ! isWrite)
|
|
return;
|
|
|
|
//System.out.println("ScavengerHunt.update: observed write access by player " + playerID + " on Mem[ " +
|
|
// Binary.intToHexString(address) + " ]");
|
|
|
|
// TBD TBD DEBUGGING SPECIAL
|
|
/*
|
|
accessCounter++;
|
|
if (accessCounter > 100000)
|
|
{
|
|
System.out.println("\n\nScavengerHunt.update: hardcoded exit to prevent runaway" );
|
|
System.exit(0);
|
|
}
|
|
*/
|
|
// TBD TBD DEBUGGING SPECIAL
|
|
|
|
|
|
|
|
|
|
// Take the appropriate action, depending on data written and priority of user.
|
|
if (isWrite && playerID == ADMINISTRATOR_ID && address == ADDR_GAME_ON)
|
|
{
|
|
|
|
// ADMINISTRATOR_ID can write to any location, because it's trusted software
|
|
//System.out.println( "ScavengerHunt.update(): Administrator wrote to Mem[ " +
|
|
// Binary.intToHexString(address) + " ] == " + Binary.intToHexString(data) );
|
|
|
|
// No need to authenticate since administrator runs first, then
|
|
// this location has no effect thereafter.
|
|
GameOn = true;
|
|
// System.out.println( "ScavengerHunt.update(): Administrator wrote GAME_ON!" );
|
|
|
|
initializeScavengerData();
|
|
}
|
|
else if (isWrite && address == ADDR_AUTHENTICATION)
|
|
{
|
|
// Anyone is allowed to write to the authentication location -- but if that value is not
|
|
// correct (authentic) then action can be taken.
|
|
// NO ACTION HERE
|
|
}
|
|
else if (isWrite && address == ADDR_NUM_TURNS)
|
|
{
|
|
// Anyone is allowed to write to the "number of turns" location
|
|
// NO ACTION HERE
|
|
}
|
|
else if (isWrite && address == ADDR_PLAYER_ID) // if the data written will change the PlayerID, authenticate the write
|
|
{
|
|
// 2006 Oct 31 dummy validation scheme, suitable for distribution
|
|
// to students for development: Initial authentication value is zero.
|
|
// Each successive authentication value is one greater
|
|
// than the preceding value, modulo 0xffffffff.
|
|
authenticationValue += 1; // "server's" updated version of the authenticationValue
|
|
if (toolGetWord(ADDR_AUTHENTICATION) == authenticationValue) // Compare to "client's" version of the authenticationValue
|
|
{
|
|
playerID = toolGetWord(ADDR_PLAYER_ID); // Use the new player ID
|
|
//System.out.println( "ScavengerHunt.update(): New playerID of " + playerID);
|
|
}
|
|
else
|
|
{
|
|
System.out.println( "ScavengerHunt.update(): Invalid write of player ID! \nPlayer " +
|
|
playerID + " tried to write. Expected: " +
|
|
Binary.intToHexString(authenticationValue) +
|
|
", got: " + Binary.intToHexString(toolGetWord(ADDR_AUTHENTICATION)) + "\n" );
|
|
}
|
|
}
|
|
|
|
else if (isWrite &&
|
|
address == (ADDR_BASE + (playerID * MEM_PER_PLAYER) + OFFSET_MOVE_READY) &&
|
|
data != 0) // Player wrote data to his/her assigned MoveReady location
|
|
{
|
|
/*
|
|
System.out.println(" ******** ScavengerHunt.update: Player " + playerID + " requests move to (" +
|
|
toolReadPlayerData(playerID, OFFSET_MOVE_TO_X) + ", " +
|
|
toolReadPlayerData(playerID, OFFSET_MOVE_TO_Y) + ")" );
|
|
*/
|
|
|
|
energyLevel = toolReadPlayerData(playerID, OFFSET_ENERGY); // find if player has energy
|
|
if (energyLevel <= 0) // No energy. Player not allowed to move
|
|
{
|
|
//JOptionPane.showMessageDialog(null, "Player " + playerID + " can't move -- no energy.\n" +
|
|
// "(This msg. in ScavengerHunt.update()" );
|
|
// System.out.println("Player " + playerID + " can't move -- no energy.");
|
|
return;
|
|
}
|
|
|
|
if (toolReadPlayerData(playerID, OFFSET_MOVE_TO_X) < 0 ||
|
|
toolReadPlayerData(playerID, OFFSET_MOVE_TO_X) > GRAPHIC_WIDTH ||
|
|
toolReadPlayerData(playerID, OFFSET_MOVE_TO_Y) < 0 ||
|
|
toolReadPlayerData(playerID, OFFSET_MOVE_TO_Y) > GRAPHIC_HEIGHT
|
|
) // Out of bounds. Player not allowed to move
|
|
{
|
|
//JOptionPane.showMessageDialog(null, "Player " + playerID + " can't move -- out of bounds.\n" +
|
|
// "(This msg. in ScavengerHunt.update()" );
|
|
System.out.println("Player " + playerID + " can't move -- out of bounds.");
|
|
return;
|
|
}
|
|
|
|
|
|
// Verify movement is allowed (does not exceed maximum movement)
|
|
if (Math.sqrt(
|
|
Math.pow( toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_X) -
|
|
toolReadPlayerData(playerID, OFFSET_MOVE_TO_X), 2.0)
|
|
+
|
|
Math.pow( toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_Y) -
|
|
toolReadPlayerData(playerID, OFFSET_MOVE_TO_Y), 2.0) )
|
|
<= MAX_MOVE_DISTANCE)
|
|
{
|
|
|
|
// Write the new position of the player
|
|
toolWritePlayerData(playerID, OFFSET_WHERE_AM_I_X,
|
|
toolReadPlayerData(playerID, OFFSET_MOVE_TO_X));
|
|
toolWritePlayerData(playerID, OFFSET_WHERE_AM_I_Y,
|
|
toolReadPlayerData(playerID, OFFSET_MOVE_TO_Y));
|
|
pd[playerID].setWhereAmI( toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_X),
|
|
toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_Y) );
|
|
|
|
// Write the new (reduced) energy of the player
|
|
// Policy: Constant ENERGY_PER_MOVE for any move regardless of length.
|
|
toolWritePlayerData(playerID, OFFSET_ENERGY,
|
|
toolReadPlayerData(playerID, OFFSET_ENERGY) - ENERGY_PER_MOVE);
|
|
pd[playerID].setEnergy( toolReadPlayerData(playerID, OFFSET_ENERGY) );
|
|
|
|
|
|
// TBD FUTURE --- need to keep track of locations that the player has actually got to
|
|
// -- be able to tell that the player has reached a certain location
|
|
// -- be able to tell that the player has reached every location
|
|
for (int i = 0; i < NUM_LOCATIONS; i++)
|
|
{
|
|
if (toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_X) == loc[i].X &&
|
|
toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_Y) == loc[i].Y )
|
|
{
|
|
pd[playerID].setVisited(i); // Player has visited this location
|
|
}
|
|
}
|
|
|
|
|
|
// Write 0 to "move ready" location, signifying that the move request was processed
|
|
// Here we must write to the same location that we're now reading from, and we
|
|
// can't cause an infinite loop. Temporarily switch player ID to that of the administrator,
|
|
// and restore ID after the write. With the playerID set to administrator, the event
|
|
// caused by the write will not go through this same logic.
|
|
int tempPlayerID = playerID;
|
|
playerID = ADMINISTRATOR_ID;
|
|
toolWritePlayerData(tempPlayerID, OFFSET_MOVE_READY, 0);
|
|
playerID = tempPlayerID;
|
|
|
|
}
|
|
else
|
|
{
|
|
System.out.println( "Player " + playerID + " can't move -- exceeded max. movement." );
|
|
System.out.println(" Player is at (" +
|
|
toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_X) + ", " +
|
|
toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_Y) + "), wants to go to (" +
|
|
toolReadPlayerData(playerID, OFFSET_MOVE_TO_X) + "," +
|
|
toolReadPlayerData(playerID, OFFSET_MOVE_TO_Y) + ")" );
|
|
|
|
return;
|
|
}
|
|
|
|
} // end if Player wrote nonzero data to his/her assigned MoveReady location
|
|
|
|
|
|
else if (isWrite &&
|
|
address == (ADDR_BASE + (playerID * MEM_PER_PLAYER) + OFFSET_TASK_COMPLETE) &&
|
|
data != 0) // Player wrote data to his/her assigned TaskComplete location
|
|
{
|
|
|
|
// System.out.println(" ******** ScavengerHunt.update: Player " + playerID + " requests more energy (task complete)" );
|
|
|
|
// Player indicates he/she has completed a task. Check to see if task is completed correctly.
|
|
// Task for this assignment: Numbers are sorted in ascending order.
|
|
int prevData, currentData;
|
|
prevData = toolReadPlayerData(playerID, OFFSET_TASK_ARRAY);
|
|
for (int i = 1; i < SIZE_OF_TASK; i++)
|
|
{
|
|
currentData = toolReadPlayerData(playerID, OFFSET_TASK_ARRAY + (i*4));
|
|
if (prevData > currentData)
|
|
{
|
|
// Task failure! Task not completed correctly!
|
|
System.out.println("Whoops! Player has NOT completed task correctly");
|
|
return;
|
|
}
|
|
prevData = currentData; // update for next iteration
|
|
|
|
}
|
|
|
|
// If program flow has reached this point, the task is completed correctly.
|
|
// Award energy, reset TaskComplete, and set new task values for future use.
|
|
toolWritePlayerData(playerID, OFFSET_ENERGY, ENERGY_AWARD);
|
|
toolWritePlayerData(playerID, OFFSET_TASK_COMPLETE, 0);
|
|
for (int j = 0; j < SIZE_OF_TASK; j++) // Initialize the task data for this player
|
|
{
|
|
toolWritePlayerData(playerID, OFFSET_TASK_ARRAY + (j * 4),
|
|
(int)(randomStream.nextDouble() * Integer.MAX_VALUE) ); // Set a random number for the task (sort them)
|
|
}
|
|
// System.out.println("Player has completed task correctly and been awarded energy");
|
|
pd[playerID].setEnergy( ENERGY_AWARD );
|
|
|
|
} // end if Player wrote nonzero data to his/her assigned TaskComplete location
|
|
|
|
|
|
else if (isWrite &&
|
|
address == (ADDR_BASE + (playerID * MEM_PER_PLAYER) + OFFSET_PLAYER_COLOR))
|
|
// Player wrote data to his/her assigned PlayerColor location
|
|
{
|
|
// PPlayer indicates he/she has changed the color of display
|
|
pd[playerID].setColor( toolReadPlayerData(playerID, OFFSET_PLAYER_COLOR) );
|
|
}
|
|
|
|
|
|
// TBD TBD TBD
|
|
// TBD TBD TBD
|
|
// TBD TBD TBD
|
|
// Yet to be implemented: Enforce only one write of MoveRequest per player per turn
|
|
|
|
|
|
else if (isWrite &&
|
|
address >= (ADDR_BASE + (playerID * MEM_PER_PLAYER)) &&
|
|
address < (ADDR_BASE + ((playerID + 1) * MEM_PER_PLAYER)) )
|
|
// Player wrote data elsewhere within his/her assigned location
|
|
{
|
|
// Player can write to any location within his/her assigned location
|
|
//System.out.println( "ScavengerHunt.update(): Player " + playerID + " wrote to valid location");
|
|
}
|
|
else if (isWrite && playerID == ADMINISTRATOR_ID)
|
|
{
|
|
// ADMINISTRATOR_ID can write to any location, because it's trusted software
|
|
//System.out.println( "ScavengerHunt.update(): Administrator wrote to Mem[ " +
|
|
// Binary.intToHexString(address) + " ] == " + Binary.intToHexString(data) );
|
|
}
|
|
else if (isWrite)
|
|
{
|
|
// This player is writing outside his/her assigned memory location
|
|
/*
|
|
System.out.println("ScavengerHunt.update(): Player " + playerID + " writing outside assigned mem. loc. at address " +
|
|
Binary.intToHexString(address) +
|
|
" -- not implemented!");
|
|
*/
|
|
|
|
JOptionPane.showMessageDialog(null,
|
|
"ScavengerHunt.update(): Player " + playerID + " writing outside assigned mem. loc. at address " +
|
|
Binary.intToHexString(address) +
|
|
" -- not implemented!");
|
|
}
|
|
else if (isRead)
|
|
{
|
|
// Policy: anyone can read any location.
|
|
}
|
|
|
|
|
|
|
|
} // end ScavengerHunt.update()
|
|
|
|
|
|
|
|
|
|
// Write one word to MIPS memory. This is a wrapper to isolate the try..catch blocks.
|
|
private void toolSetWord(int address, int data)
|
|
{
|
|
|
|
if (KENVDEBUG)
|
|
{
|
|
System.out.println(" ScavengerHunt.toolSetWord: Setting MIPS Memory[" +
|
|
Binary.intToHexString(address) + "] to " + Binary.intToHexString(data) + " = " + data );
|
|
}
|
|
SetWordCounter++;
|
|
|
|
try {
|
|
Globals.memory.setWord(address, data); // Write
|
|
}
|
|
catch ( AddressErrorException e) {
|
|
System.out.println("ScavengerHunt.toolSetWord: deliberate exit on AEE exception.");
|
|
System.out.println(" SetWordCounter = " + SetWordCounter);
|
|
System.out.println(" address = " + Binary.intToHexString(address) );
|
|
System.out.println(" data = " + data );
|
|
System.exit(0);
|
|
}
|
|
catch ( Exception e) {
|
|
System.out.println("ScavengerHunt.toolSetWord: deliberate exit on " + e.getMessage() + " exception.");
|
|
System.out.println(" SetWordCounter = " + SetWordCounter);
|
|
System.out.println(" address = " + Binary.intToHexString(address) );
|
|
System.out.println(" data = " + data );
|
|
System.exit(0);
|
|
}
|
|
|
|
if (KENVDEBUG)
|
|
{
|
|
// Verify data written correctly
|
|
int verifyData = toolGetWord(address);
|
|
if (verifyData != data)
|
|
{
|
|
System.out.println("\n\nScavengerHunt.toolSetWord: Can't verify data! Special exit.");
|
|
System.out.println(" address = " + Binary.intToHexString(address) );
|
|
System.out.println(" data = " + data);
|
|
System.out.println(" verifyData = " + verifyData);
|
|
System.exit(0);
|
|
}
|
|
else
|
|
System.out.println(" ScavengerHunt.toolSetWord: Mem[" +
|
|
Binary.intToHexString(address) +
|
|
" verified as " + Binary.intToHexString(data));
|
|
}
|
|
|
|
} // end toolSetWord
|
|
|
|
|
|
|
|
|
|
// Read one word from MIPS memory. This is a wrapper to isolate the try..catch blocks.
|
|
private int toolGetWord(int address)
|
|
{
|
|
|
|
int returnValue;
|
|
|
|
//System.out.println("ScavengerHunt.toolGetWord: called with address " +
|
|
// Binary.intToHexString(address) );
|
|
|
|
try {
|
|
/*
|
|
System.out.println("ScavengerHunt.toolGetWord: returning " +
|
|
Binary.intToHexString(Globals.memory.getWord(address)) +
|
|
" which is at MIPS Memory[" + Binary.intToHexString(address) + "]" );
|
|
*/
|
|
returnValue = Globals.memory.getWord(address);
|
|
|
|
/*
|
|
System.out.println("ScavengerHunt.toolGetWord: Mem[" +
|
|
Binary.intToHexString(address) + "] = " +
|
|
Binary.intToHexString(returnValue) + " --- returning normally");
|
|
*/
|
|
|
|
return returnValue;
|
|
}
|
|
catch ( AddressErrorException e) {
|
|
System.out.println("ScavengerHunt.toolGetWord: deliberate exit on AEE exception.");
|
|
System.out.println(" SetWordCounter = " + SetWordCounter);
|
|
System.out.println(" address = " + Binary.intToHexString(address) );
|
|
System.exit(0);
|
|
}
|
|
catch ( Exception e) {
|
|
System.out.println("ScavengerHunt.toolGetWord: deliberate exit on " + e.getMessage() + " exception.");
|
|
System.out.println(" SetWordCounter = " + SetWordCounter);
|
|
System.out.println(" address = " + Binary.intToHexString(address) );
|
|
System.exit(0);
|
|
}
|
|
|
|
return 0; // Must have return statement
|
|
} // end toolGetWord
|
|
|
|
|
|
|
|
|
|
// Read player's data field.
|
|
private int toolReadPlayerData(int p, int offset)
|
|
{
|
|
|
|
if (KENVDEBUG)
|
|
{
|
|
System.out.println("ScavengerHunt.toolReadPlayerData: called with player " + p +
|
|
", offset = " +
|
|
Binary.intToHexString(offset) + " ---> address " +
|
|
Binary.intToHexString(ADDR_BASE + (p * MEM_PER_PLAYER) + offset)
|
|
);
|
|
}
|
|
|
|
int returnValue = toolGetWord( ADDR_BASE + (p * MEM_PER_PLAYER) + offset);
|
|
|
|
if (KENVDEBUG)
|
|
{
|
|
//if ((ADDR_BASE + (p * MEM_PER_PLAYER) + offset) >= 0xffff8000 &&
|
|
// (ADDR_BASE + (p * MEM_PER_PLAYER) + offset) < 0xffff8800) // Show debug for player 0 and 1 only
|
|
//{
|
|
//System.out.println(" ScavengerHunt.toolReadPlayerData: Reading MIPS Memory[" +
|
|
// Binary.intToHexString(ADDR_BASE + (p * MEM_PER_PLAYER) + offset) +
|
|
// "] which is " + Binary.intToHexString(returnValue) +
|
|
// " = " + Binary.intToHexString( returnValue) );
|
|
//}
|
|
|
|
System.out.println("ScavengerHunt.toolReadPlayerData: Mem[" +
|
|
Binary.intToHexString(ADDR_BASE + (p * MEM_PER_PLAYER) + offset) + "] = " +
|
|
Binary.intToHexString(returnValue) + " --- returning normally");
|
|
}
|
|
|
|
return returnValue;
|
|
} // end toolReadPlayerData
|
|
|
|
// Write player's data field.
|
|
private void toolWritePlayerData(int p, int offset, int data)
|
|
{
|
|
|
|
int address = ADDR_BASE + (p * MEM_PER_PLAYER) + offset;
|
|
|
|
if (KENVDEBUG)
|
|
{
|
|
System.out.println("ScavengerHunt.toolWritePlayerData: called with player " + p +
|
|
", offset = " + Binary.intToHexString(offset) +
|
|
", data = " + Binary.intToHexString(data) );
|
|
}
|
|
|
|
toolSetWord( address, data);
|
|
|
|
if (KENVDEBUG)
|
|
{
|
|
int verifyData = toolGetWord(address);
|
|
if (data != verifyData)
|
|
{
|
|
System.out.println("\n\nScavengerHunt.toolWritePlayerData: MAYDAY data not verified !");
|
|
System.out.println(" requested data to be written was " + Binary.intToHexString(data));
|
|
System.out.println(" actual data at that loc is " + Binary.intToHexString(toolGetWord(address)));
|
|
System.exit(0);
|
|
}
|
|
else
|
|
System.out.println(" ScavengerHunt.toolWritePlayerData: Mem[" +
|
|
Binary.intToHexString(address) +
|
|
" verified as " + Binary.intToHexString(data));
|
|
}
|
|
|
|
} // end toolWritePlayerData
|
|
|
|
|
|
private void initializeScavengerData()
|
|
{
|
|
//GameOn = false; // MIPS programs readiness
|
|
authenticationValue = 0;
|
|
playerID = ADMINISTRATOR_ID;
|
|
startTime = System.currentTimeMillis(); // Clock time for program run
|
|
|
|
// This is a dubious use of the tool (this Java program) to initialize data values for the game.
|
|
// The administrator portion of the MIPS program should initialize all data for each
|
|
// player -- but it's easier to work with random numbers in the Java program
|
|
// Initialize the locations and task data for each player
|
|
randomStream = new Random(42); // TBD Use a seed for development. Remove seed for randomizing.
|
|
|
|
for (int j = 0; j < NUM_LOCATIONS - 1; j++) // The first (n-1) locations are "random"
|
|
{ // Two coordinates (x and y) for each location
|
|
loc[j] = new Location(); // Initialize each location element
|
|
loc[j].X = (int)(randomStream.nextDouble() * GRAPHIC_WIDTH); // X coord.
|
|
loc[j].Y = (int)(randomStream.nextDouble() * (GRAPHIC_HEIGHT - 50)); // Y coord. (leave room for buttons in window)
|
|
|
|
/*
|
|
System.out.println("ScavengerHunt.update(): set up a location at (" +
|
|
loc[j].X +
|
|
", " +
|
|
loc[j].Y +
|
|
")" );
|
|
*/
|
|
|
|
}
|
|
loc[NUM_LOCATIONS - 1] = new Location(); // The last location is a return to the starting position
|
|
loc[NUM_LOCATIONS - 1].X = START_AND_END_LOCATION;
|
|
loc[NUM_LOCATIONS - 1].Y = START_AND_END_LOCATION;
|
|
|
|
for (int i = 0; i < NUM_PLAYERS; i++) // Initialize data for each player
|
|
{
|
|
|
|
//System.out.println("ScavengerHunt.update(): Player loop " + i);
|
|
|
|
pd[i] = new PlayerData(); // Initialize each player data structure.
|
|
|
|
for (int j = 0; j < NUM_LOCATIONS; j++) // Initialize the locations this player goes to
|
|
{
|
|
toolWritePlayerData(i, OFFSET_LOC_ARRAY + (j * 8) + 0, loc[j].X); // Set the same locations for each player
|
|
toolWritePlayerData(i, OFFSET_LOC_ARRAY + (j * 8) + 4, loc[j].Y); // Set the same locations for each player
|
|
}
|
|
|
|
for (int j = 0; j < SIZE_OF_TASK; j++) // Initialize the task data for this player
|
|
{
|
|
toolWritePlayerData(i, OFFSET_TASK_ARRAY + (j * 4),
|
|
(int)(randomStream.nextDouble() * Integer.MAX_VALUE) ); // Set a random number for the task (sort them)
|
|
}
|
|
}
|
|
|
|
} // end initializeScavengerData
|
|
|
|
} // end ScavengerHunt
|
|
|