package MV3500Cohort2024JulySeptember.homework3.Smith; 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 = 100; // 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 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(); } /** * 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, 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]; // Fire at the target position firePdu.setDescriptor(munitionDescriptor1).setRange(100.0f); // Example fire properties if (grid[x][y] == 'P') { grid[x][y] = 'H'; // Hit System.out.println(player + " hits a ship at (" + x + ", " + y + ")!"); } else if (grid[x][y] == '~') { grid[x][y] = 'M'; // Miss 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 fire PDU to reflect the shot disChannel.sendSinglePdu(simulationTimeSeconds, firePdu); } /** * 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 } /** * 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"); } }