package MV3500Cohort2024JulySeptember.homework3.Smith; import edu.nps.moves.dis7.enumerations.VariableRecordType; import edu.nps.moves.dis7.pdus.*; import edu.nps.moves.dis7.utilities.DisChannel; import edu.nps.moves.dis7.utilities.PduFactory; import java.util.logging.Level; import java.util.logging.Logger; /** * The purpose of this inheritable class is to provide an easily modifiable * example simulation program that includes DIS-capable entities performing * tasks of interest, and then reporting activity via PDUs to the network. * Default program initialization includes PDU recording turned on by default. */ public class ExampleSimulationProgram { // Constants for the Battleship game private static final int GRID_SIZE = 10; // 10x10 grid for Battleship private static final int MAX_TURNS = 200; // Limit turns to avoid infinite games // Grids for each player private char[][] player1Grid = new char[GRID_SIZE][GRID_SIZE]; private char[][] player2Grid = new char[GRID_SIZE][GRID_SIZE]; // To track whose turn it is private boolean isPlayer1Turn = true; // Ship positions (simple example positions for each player's ships) private final int[][] player1Ships = {{0, 0}, {0, 1}, {0, 2}}; // Positions for player 1's ships private final int[][] player2Ships = {{5, 5}, {5, 6}, {5, 7}}; // Positions for player 2's ships // DIS utilities private String descriptor = this.getClass().getSimpleName(); protected DisChannel disChannel; protected PduFactory pduFactory; protected CommentPdu gridStatusPdu; protected FirePdu firePduPlayer1; protected FirePdu firePduPlayer2; protected MunitionDescriptor munitionDescriptor1 = new MunitionDescriptor(); // Simulation time settings private double simulationTimeStepDuration = 1.0; // seconds private double simulationTimeSeconds = 0.0; // Tracking indices for Player 1's systematic search private int player1SearchRow = 0; private int player1SearchCol = 0; /** * Constructor to create an instance of this class. Design goal: additional * built-in initialization conveniences can go here to keep your efforts * focused on the runSimulation() method. */ public ExampleSimulationProgram() { initialize(); } /** * Initialize the simulation program, setting up the DIS channel, PDUs, and * the Battleship game grids. */ private void initialize() { initializeDisChannel(); initializeSimulationEntities(); setupGrids(); disChannel.join(); } /** * Initialize the DIS channel for communication, setting verbose output for * debugging and creating the PduFactory. */ private void initializeDisChannel() { disChannel = new DisChannel(); pduFactory = disChannel.getPduFactory(); disChannel.setDescriptor(this.getClass().getSimpleName()); disChannel.setUpNetworkInterface(); disChannel.getDisNetworkInterface().setVerbose(true); // sending and receipt disChannel.getPduRecorder().setVerbose(true); } /** * Initialize simulation entities by creating PDUs for firing events. */ private void initializeSimulationEntities() { firePduPlayer1 = pduFactory.makeFirePdu(); firePduPlayer2 = pduFactory.makeFirePdu(); gridStatusPdu = pduFactory.makeCommentPdu(); } /** * Set up the game grids for each player, marking empty water and ship * positions. */ private void setupGrids() { // Initialize grids with empty water '~' for (int i = 0; i < GRID_SIZE; i++) { for (int j = 0; j < GRID_SIZE; j++) { player1Grid[i][j] = '~'; player2Grid[i][j] = '~'; } } // Place ships on the grids ('P' denotes a ship) for (int[] ship : player1Ships) { player1Grid[ship[0]][ship[1]] = 'P'; } for (int[] ship : player2Ships) { player2Grid[ship[0]][ship[1]] = 'P'; } } /** * This runSimulationLoops() method is for you, a customizable * programmer-modifiable code block for defining and running a new * simulation of interest. */ public void runSimulationLoops() { int turnCount = 0; while (turnCount < MAX_TURNS) { if (isPlayer1Turn) { // Player 1 systematically searches Player 2's grid int[] target = selectNextTargetForPlayer1(); handleFire(firePduPlayer1, player2Grid, target, "Player 1"); } else { // Player 2 fires at Player 1 randomly int[] target = selectRandomTarget(); handleFire(firePduPlayer2, player1Grid, target, "Player 2"); } // Switch turns and increment turn counter isPlayer1Turn = !isPlayer1Turn; turnCount++; // Check for win conditions if (checkWinCondition(player1Grid)) { System.out.println("Player 2 wins!"); break; } else if (checkWinCondition(player2Grid)) { System.out.println("Player 1 wins!"); break; } // Wait for the next simulation step try { Thread.sleep((long) (simulationTimeStepDuration * 1000)); // units of seconds * (1000 msec/sec) = milliseconds } catch (InterruptedException iex) { Logger.getLogger(ExampleSimulationProgram.class.getSimpleName()).log(Level.SEVERE, null, iex); } // Increment simulation time simulationTimeSeconds += simulationTimeStepDuration; } } /** * Systematically select the next target for Player 1 in a row-by-row * manner. * * @return the coordinates of the next target cell. */ private int[] selectNextTargetForPlayer1() { // Systematically select the next target in a row-by-row manner int[] target = {player1SearchRow, player1SearchCol}; // Move to the next column player1SearchCol++; // If end of row is reached, move to the next row and reset column if (player1SearchCol >= GRID_SIZE) { player1SearchCol = 0; player1SearchRow++; } // Ensure search stays within grid bounds if (player1SearchRow >= GRID_SIZE) { player1SearchRow = 0; // Reset search if bounds are exceeded } return target; } /** * Randomly select a target on the grid for Player 2's turn. * * @return the coordinates of the randomly selected target cell. */ private int[] selectRandomTarget() { int x = (int) (Math.random() * GRID_SIZE); int y = (int) (Math.random() * GRID_SIZE); return new int[]{x, y}; } /** * Handles the firing action, setting relevant data in the FirePdu, checking * if the target is a hit or miss, updating the grid, and sending the * appropriate PDU. * * @param firePdu the FirePdu object to use for the shot. * @param grid the grid of the player being fired at. * @param target the coordinates of the target cell. * @param player the name of the player firing the shot. */ private void handleFire(FirePdu firePdu, char[][] grid, int[] target, String player) { int x = target[0]; int y = target[1]; // Set relevant details in the FirePdu firePdu.setFiringEntityID(new EntityID().setEntityID(player.equals("Player 1") ? 1 : 2)); firePdu.setTargetEntityID(new EntityID().setEntityID(player.equals("Player 1") ? 1 : 2)); Vector3Double fireLoc = new Vector3Double(); //annoying that there isn't a Vector3Double(x, y, z) constructor... fireLoc.setX(x); fireLoc.setY(y); fireLoc.setZ(0.0); firePdu.setLocationInWorldCoordinates(fireLoc); firePdu.setDescriptor(munitionDescriptor1); // Not needed I guess firePdu.setRange(100.0f);// Not needed I gueess // Print the firing action System.out.println(player + " fires at (" + x + ", " + y + ")."); // Determine hit or miss and update the game grid if (grid[x][y] == 'P') { grid[x][y] = 'H'; // Mark as Hit firePdu.setFireMissionIndex(1); // Indicate that this was a successful hit with an index of 1 System.out.println(player + " hits a ship at (" + x + ", " + y + ")!"); } else if (grid[x][y] == '~') { grid[x][y] = 'M'; // Mark as Miss firePdu.setFireMissionIndex(0); // Indicate that this was a miss with an index of 0 System.out.println(player + " misses at (" + x + ", " + y + ")."); } else { System.out.println(player + " fires at (" + x + ", " + y + ") but it's already hit/missed."); } // Send the FirePdu to represent the shot disChannel.sendSinglePdu(simulationTimeSeconds, firePdu); // Send the updated grid status of both players sendGridStatus(); } /** * Checks the win condition by verifying if all ships on the grid have been * hit. * * @param grid the grid to check. * @return true if all ships are hit; false otherwise. */ private boolean checkWinCondition(char[][] grid) { // Check if all ships have been hit for (int i = 0; i < GRID_SIZE; i++) { for (int j = 0; j < GRID_SIZE; j++) { if (grid[i][j] == 'P') { return false; // There are still ships left } } } return true; // All ships have been hit } /** * Sends the current grid status of both players as a CommentPdu. */ private void sendGridStatus() { // Construct a string representation of both grids StringBuilder gridStatus = new StringBuilder(); gridStatus.append("Player 1 Grid:\n"); appendGridToString(gridStatus, player1Grid); gridStatus.append("Player 2 Grid:\n"); appendGridToString(gridStatus, player2Grid); // Set the grid status in the CommentPdu gridStatusPdu.getVariableDatums().clear(); // Clear previous comments gridStatusPdu.getVariableDatums().add(new VariableDatum() .setVariableDatumID(VariableRecordType.OTHER) .setVariableDatumValue(gridStatus.toString().getBytes()) .setVariableDatumLengthInBytes(gridStatus.toString().getBytes().length)); // Send the CommentPdu containing the grid status disChannel.sendSinglePdu(simulationTimeSeconds, gridStatusPdu); } /** * Appends the current grid status to a StringBuilder. * * @param sb the StringBuilder to append to. * @param grid the grid to represent. */ private void appendGridToString(StringBuilder sb, char[][] grid) { for (char[] row : grid) { for (char cell : row) { sb.append(cell).append(' '); } sb.append('\n'); } } /** * Main method is first executed when a program instance is loaded. * * @param args command-line parameters: network address and port. * Command-line arguments are an array of optional String parameters that * are passed from execution environment during invocation */ public static void main(String[] args) { ExampleSimulationProgram game = new ExampleSimulationProgram(); game.runSimulationLoops(); System.out.println("Game Over"); } }