home *** CD-ROM | disk | FTP | other *** search
Java Source | 1996-08-14 | 16.0 KB | 594 lines |
- /*
- * @(#)ServerBoard.java
- */
- package games.Battle.server.ServerBoard;
-
- import java.awt.*;
- import java.util.*;
- import java.lang.*;
-
- import games.Battle.shared.sys.*;
- import games.Battle.server.GeoMorph.*;
-
- /**
- * ServerBoard extends the board to keep track of things that the server
- * side wants to know about the game board.
- *
- * @version 1.00
- * @author Jay Steele
- * @author Alex Nicolaou
- */
-
- public class ServerBoard extends Board {
- /**
- * the log file to record interesting events and some debug output.
- */
- Logger logfile;
-
- /**
- * the referee responsible for watching this game.
- */
- GameReferee referee;
-
- /**
- * a random number machine for this class
- */
- static Random rand = new Random();
-
- /**
- * this is the maximum number of actual troops permitted in
- * a cell at one time.
- */
- public static final int maxTroops = 100;
-
- /**
- * the number of players actually on this board.
- */
- int numPlayers;
- /**
- * an array of booleans that indicates who is still alive
- */
- boolean[] alive;
- /**
- * an array that indicates who has how many troops to help determine
- * who is still alive
- */
- int[] troops;
-
- /**
- * given a referee and a log file, construct a new game board.
- * @param ref the referee
- * @param logfile the log service
- */
- public ServerBoard(GameReferee ref, Logger logfile) {
- super();
- this.logfile = logfile;
- referee = ref;
-
- troops = new int[Symbols.MAX_PLAYERS];
- alive = new boolean[Symbols.MAX_PLAYERS];
- for (int i = 0; i < alive.length; i++)
- alive[i] = false;
- }
-
- /**
- * makes a cell for the server board
- */
- public Cell makeCell(int r, int c) {
- return new ServerCell(r, c);
- }
-
- /**
- * a geomorph world generator to ensure nice terrain
- */
- GeoMorph world;
-
- /**
- * produce terrain based on the geomorph tiles.
- */
- public void makeRandomTerrain() {
- try {
- int maxDim = Math.max(Rules.rows, Rules.cols);
- int numTiles = maxDim / GeoTile.GEO_TILESIZE;
- if (maxDim % GeoTile.GEO_TILESIZE > 0)
- numTiles++;
-
- world = new GeoMorph("tiles/tile", numTiles);
-
- for (int r=0; r<Rules.rows; r++) {
- for (int c=0; c<Rules.cols; c++) {
- ServerCell cell = (ServerCell)getCell(r,c);
- int level = world.terrain(r, c);
- cell.setTerrain(level);
- }
- }
-
- }
- catch (Exception e) {
- /* this should never happen */
- logfile.log("ServerBoard::makeRandomTerrain: GeoMorph err");
- for (int r=0; r<Rules.rows; r++) {
- for (int c=0; c<Rules.cols; c++) {
- ServerCell cell = (ServerCell)getCell(r,c);
- int level = Math.abs(rand.nextInt()) % Symbols.NUM_TERRAIN_LEVELS;
- cell.setTerrain(level);
- }
- }
- }
- }
-
- /**
- * the amount that the cities produce each turn.
- */
- static final int GROW = 5;
-
- /**
- * given a player number give the player a new city on the map.
- */
- public void placePlayer(int player) {
- if (player >= numPlayers)
- numPlayers = player + 1;
-
- alive[player] = true;
-
- boolean done = false;
- while (!done) {
- Point p = world.getRandomCity();
- int row = p.x;
- int col = p.y;
- ServerCell cell = (ServerCell)getCell(row, col);
- if (cell.getTerrain() > 0
- && cell.getOccupancy() == Symbols.UNOCCUPIED)
- {
- cell.setOccupancy(player);
- cell.setCity(Rules.maxCitySize);
- addVisibility(row, col, player);
- done = true;
- }
- }
- }
-
- /**
- * makes the given cell and the surrounding region to the
- * horizon visible to the given player
- * @param row the row of the cell to adjust from
- * @param col the column of the cell to adjust from
- * @param player the player who is now in this position
- */
- public void addVisibility(int row, int col, int player) {
- int horizon = Rules.horizon;
- int startr = row - horizon;
- int startc = col - horizon;
- int endr = row + horizon;
- int endc = col + horizon;
- if (startr < 0) startr = 0;
- if (startc < 0) startc = 0;
- if (endr > Rules.rows-1) endr = Rules.rows - 1;
- if (endc > Rules.cols-1) endc = Rules.cols - 1;
- for (int r=startr; r<=endr; r++) {
- for (int c=startc; c<=endc; c++) {
- ServerCell cell = (ServerCell)getCell(r,c);
- cell.setVisible(player, true);
- }
- }
- }
-
- /**
- * returns true if the given row,col cell is visible to
- * the given player
- * @param row the row
- * @param col the column
- * @param player the player who is trying to see row, col
- */
- public boolean isVisible(int row, int col, int player) {
- int horizon = Rules.horizon;
- int startr = row - horizon;
- int startc = col - horizon;
- int endr = row + horizon;
- int endc = col + horizon;
- if (startr < 0) startr = 0;
- if (startc < 0) startc = 0;
- if (endr > Rules.rows-1) endr = Rules.rows - 1;
- if (endc > Rules.cols-1) endc = Rules.cols - 1;
- for (int r=startr; r<=endr; r++) {
- for (int c=startc; c<=endc; c++) {
- ServerCell cell = (ServerCell)getCell(r,c);
- if (cell.getOccupancy() == player)
- return true;
- }
- }
- return false;
- }
-
- /**
- * Removes visibility of adjacent cells for player assuming player
- * just lost occupancy of the cell at row, col.
- * row, col can't be occupied by player, or method will not
- * not work properly--i.e. unoccupy it first.
- * @param row the row of the cell that has been vacated
- * @param col the column of the cell that has been vacated
- * @param player the player who is no longer in this cell
- */
- public void removeVisibility(int row, int col, int player) {
-
- // if the player is not alive, we do not remove visibility
- // (that way surrendered players can continue to view the
- // entire board
- if (!alive[player])
- return;
-
- int horizon = Rules.horizon;
- int startr = row - horizon;
- int startc = col - horizon;
- int endr = row + horizon;
- int endc = col + horizon;
- if (startr < 0) startr = 0;
- if (startc < 0) startc = 0;
- if (endr > Rules.rows-1) endr = Rules.rows - 1;
- if (endc > Rules.cols-1) endc = Rules.cols - 1;
- for (int r=startr; r<=endr; r++) {
- for (int c=startc; c<=endc; c++) {
- ServerCell cell = (ServerCell)getCell(r,c);
- if (! isVisible(r, c, player))
- cell.setVisible(player, false);
- }
- }
- }
-
- /**
- * produce debugging output
- */
- public void asciiDump(int player) {
- for (int r=0; r< Rules.rows; r++) {
- for (int c=0; c< Rules.cols; c++) {
- ServerCell cell = (ServerCell)getCell(r,c);
- if (! cell.isVisible(player)) {
- System.out.print("X ");
- } else {
- switch (cell.getOccupancy()) {
- case Symbols.PLAYER0:
- System.out.print("0 ");
- break;
- case Symbols.PLAYER1:
- System.out.print("1 ");
- break;
- case Symbols.PLAYER2:
- System.out.print("2 ");
- break;
- case Symbols.PLAYER3:
- System.out.print("3 ");
- break;
- case Symbols.PLAYER4:
- System.out.print("4 ");
- break;
- case Symbols.UNOCCUPIED:
- System.out.print(". ");
- break;
- case Symbols.INVISIBLE:
- System.out.print("I ");
- break;
- case Symbols.UNMODIFIED:
- System.out.print("U ");
- break;
- default:
- System.out.print("? ");
- break;
- }
- }
- }
- System.out.println();
- }
- }
-
- /**
- * A static list of cells that can be shuffled quickly to do random
- * board update
- */
- static final int cellList[] = new int[Rules.rows*Rules.cols];
- static {
- for (int i=0; i<Rules.rows*Rules.cols; i++) {
- cellList[i] = i;
- }
- }
-
- /**
- * shuffle the cells in the cellList so that the board updater doesn't
- * introduce artifacts by traversing the cells in some fixed order.
- */
- void shuffle(int list[]) {
- int size = list.length;
- while (size > 0) {
- int index = Math.abs(rand.nextInt()) % size;
- int temp = list[size-1];
- list[size-1] = list[index];
- list[index] = temp;
- size--;
- }
- }
-
- /**
- * update the board to reflect the passage of one unit of xbattle time.
- */
- public void update() {
- for (int i = 0; i < numPlayers; i++)
- troops[i] = 0;
-
- long time = System.currentTimeMillis();
- shuffle(cellList);
- time = howMuchTime(time, "shuffling list");
- clearModified();
- time = howMuchTime(time, "clearmodify");
- for (int i = 0; i < cellList.length; i++) {
- int r = Common.indexToRow(cellList[i]);
- int c = Common.indexToCol(cellList[i]);
- updateCell(r, c);
- ServerCell cell = (ServerCell)getCell(r,c);
- int occ = cell.getOccupancy();
- if (occ != Symbols.UNOCCUPIED) {
- troops[occ] += cell.getTroops();
- }
- }
-
- for (int i = 0; i < numPlayers; i++) {
- if (troops[i] == 0 && alive[i] == true) {
- killPlayer(i);
- }
- }
-
- time = howMuchTime(time, "work of update");
- }
-
- /**
- * kill the player given as an argument. allows the player to watch the
- * whole game board until the game ends.
- * @param player the player who is killed
- */
- public void killPlayer(int player) {
- if (!alive[player])
- return;
- logfile.log("Player "+player+" died.");
- referee.playerDied(player);
- alive[player] = false;
- for (int r=0; r<Rules.rows; r++) {
- for (int c=0; c<Rules.cols; c++) {
- ServerCell cell = (ServerCell)getCell(r,c);
- cell.setVisible(player, true);
- }
- }
- }
-
- /**
- * return true if the game has ended - that is only one player remains
- */
- public boolean isGameOver() {
- int numP = 0;
- int lastP = -1;
- for (int i = 0; i < numPlayers; i++)
- if (alive[i]) {
- numP++;
- lastP = i;
- }
-
- if (numP <= 1) {
- if (lastP != -1)
- killPlayer(lastP);
- return true;
- }
-
- return false;
- }
-
- /**
- * a vector of directions to update.
- */
- static final Vector updateDir = new Vector();
- static {
- for (int i=0; i<4; i++) {
- updateDir.addElement(new Integer(i));
- }
- }
- /**
- * update a particular cell on the game board.
- */
- public void updateCell(int row, int col) {
- ServerCell cell = (ServerCell)getCell(row, col);
-
- // terrain must not be water
- if (cell.getTerrain() == 0) return;
-
- // if the cell is a city, then grow some troops
- if (cell.getCity() == Rules.maxCitySize) {
- cell.setTroops( cell.getTroops() + GROW );
-
- // bottleneck troop production, if neccessary
- if (cell.getTroops() > Symbols.MAX_SERVER_TROOPS)
- cell.setTroops(Symbols.MAX_SERVER_TROOPS);
- }
-
- // airborne attacks are resolved first (incoming paratroops/guns)
- int shells = 0;
- for (int i = 0; i < Symbols.MAX_PLAYERS; i++) {
- shells += cell.getShells(i);
- }
- cell.clearArtillery();
- if (shells > 0) {
- // all incoming shells are treated as one group attack
- double percent = shells / (double)cell.getTroops();
-
- int killed = (int)(percent * cell.getTroops());
-
- if (killed > cell.getTroops()) {
- int oldPlayer = cell.getOccupancy();
- cell.setOccupancy(Symbols.UNOCCUPIED);
- cell.setTroops(0);
- cell.clearPipes();
- removeVisibility(row, col, oldPlayer);
- }
- else {
- cell.setTroops(cell.getTroops() - killed);
- }
- }
- for (int i = 0; i < Symbols.MAX_PLAYERS; i++) {
- int troops = cell.getParatroops(i);
- if (troops <= 0)
- continue;
-
- if (i == cell.getOccupancy()) {
- troops = (int)(0.9 * troops);
- if (troops + cell.getTroops() > Symbols.MAX_SERVER_TROOPS)
- troops = Symbols.MAX_SERVER_TROOPS - cell.getTroops();
- cell.setTroops(cell.getTroops() + troops);
- }
- else if (cell.getOccupancy() == Symbols.UNOCCUPIED) {
- cell.setOccupancy(i);
- cell.setTroops(troops);
- addVisibility(row, col, cell.getOccupancy());
- }
- else {
- // cell is owned by an enemy player
- // hope we confuse his pipes
- double percent = troops / (double)cell.getTroops();
-
- double roll = rand.nextGaussian() + 0.3;
-
- if (roll < percent)
- cell.clearPipes();
-
- troops = (int)(0.5 * troops);
-
- percent = troops / (double)cell.getTroops();
- int killed = (int)(percent * cell.getTroops()) + 1;
- if (killed > cell.getTroops()) {
- int oldPlayer = cell.getOccupancy();
- cell.setOccupancy(i);
- cell.setTroops(troops);
- cell.clearPipes();
- addVisibility(row, col, i);
- removeVisibility(row, col, oldPlayer);
- }
- else {
- cell.setTroops(cell.getTroops() - killed);
- }
- }
- }
- cell.clearParatroops();
-
- // in random order of n, s, e, w -- import troops
- // from neighboring cells
- Vector dir = (Vector)updateDir.clone();
- while (! dir.isEmpty()) {
- int index = Math.abs(rand.nextInt()) % dir.size();
- int d = ((Integer)dir.elementAt(index)).intValue();
- dir.removeElementAt(index);
-
- ServerCell srcCell = null;
- if (d == Symbols.NORTH) {
- if (row == 0) continue;
- srcCell = (ServerCell)getCell(row-1, col);
- if (! srcCell.getPipe(Symbols.SOUTH)) continue;
- } else if (d == Symbols.SOUTH) {
- if (row == Rules.rows-1) continue;
- srcCell = (ServerCell)getCell(row+1, col);
- if (! srcCell.getPipe(Symbols.NORTH)) continue;
- } else if (d == Symbols.WEST) {
- if (col == 0) continue;
- srcCell = (ServerCell)getCell(row, col-1);
- if (! srcCell.getPipe(Symbols.EAST)) continue;
- } else if (d == Symbols.EAST) {
- if (col == Rules.cols-1) continue;
- srcCell = (ServerCell)getCell(row, col+1);
- if (! srcCell.getPipe(Symbols.WEST)) continue;
- }
- if (srcCell != null) {
- // if we are not currently occupied by the incoming
- // cell color, then set the occupancy and update the
- // visibility
- if (cell.getOccupancy() == Symbols.UNOCCUPIED) {
- cell.setOccupancy(srcCell.getOccupancy());
- addVisibility(row, col, cell.getOccupancy());
- }
-
- // if the input cell is water (shouldn't be) then abort
- if (srcCell.getTerrain() == 0) continue;
- int terrainDiff = srcCell.getTerrain() - cell.getTerrain();
-
- // the number of troops to transfer
- int transfer = terrainDiff + 8;
-
- // make sure our transfer is what the source can give and
- // the destination can take
- if (srcCell.getTroops() < transfer)
- transfer = srcCell.getTroops();
-
- if (transfer == 0)
- continue;
-
- // make the transfer
- if (cell.getOccupancy() == srcCell.getOccupancy()) {
- if (transfer + cell.getTroops() > Symbols.MAX_SERVER_TROOPS)
- transfer = Symbols.MAX_SERVER_TROOPS - cell.getTroops();
-
- srcCell.setTroops( srcCell.getTroops() - transfer );
- cell.setTroops( cell.getTroops() + transfer );
- }
- else {
- srcCell.setTroops(srcCell.getTroops() - transfer);
- // the transferred troops get to join the fighting
- // troops
- int attackers = transfer + srcCell.getFighters(d);
-
- // of the attackers and defenders roughly
- // defenders*attackers / (attackers+defenders) die
- int total = cell.getTroops() + attackers;
-
- double randomFactor = rand.nextGaussian();
-
- int killed = cell.getTroops() * attackers / total + 1;
- int randKill = (int)(randomFactor * killed);
-
- int defendersKilled;
- int attackersKilled;
- if (randKill < 0) {
- defendersKilled = killed - randKill;
- attackersKilled = killed;
- }
- else {
- defendersKilled = killed;
- attackersKilled = killed + randKill;
- }
-
-
- if (cell.getTroops() <= defendersKilled ||
- cell.getTroops() <= 0) {
- int oldPlayer = cell.getOccupancy();
- cell.setOccupancy(srcCell.getOccupancy());
- addVisibility(row, col, cell.getOccupancy());
- removeVisibility(row, col, oldPlayer);
- cell.clearPipes();
- int newtroops = attackers - attackersKilled;
- if (newtroops > 0)
- cell.setTroops(newtroops);
- else
- cell.setTroops(1);
- }
- else {
- cell.setTroops(cell.getTroops() - defendersKilled);
- srcCell.setFighters(d, attackers - attackersKilled);
- }
- }
- }
- }
- }
-
- /**
- * record how much time some action has taken. used to help make sure
- * that the server can consistently meet the time goal of X ms per turn.
- */
- public long howMuchTime(long oldtime, String msg) {
- long time = System.currentTimeMillis();
- // uncomment for more time info
- //logfile.log(msg + ": " + (time - oldtime));
- return time;
- }
- }
-