import java.awt.*; import java.io.FileWriter; import java.io.IOException; import java.util.*; import javax.swing.*; import javax.swing.Timer; public class MazeManager extends JPanel { //Agens and queue private Queue agentQueue; private Agent agent1; private Agent agent2; //variables private final int cols, rows; private final int cellSize = 20; private final MazeTile[][] tiles; private final boolean[][][] walls; private final Random random = new Random(); private final int trapChance; private final int powerUpChance; //Game Mechanic private boolean gameOver = false; private int currRount = 0; private Timer timer; public MazeManager(int cols, int rows, int trapChance, int powerUpChance) { //Create ways in maze this.cols = cols; this.rows = rows; this.trapChance = trapChance; this.powerUpChance = powerUpChance; this.tiles = new MazeTile[cols][rows]; this.walls = new boolean[cols][rows][4]; for (int x = 0; x < cols; x++) { for (int y = 0; y < rows; y++) { tiles[x][y] = new MazeTile(); for (int d = 0; d < 4; d++) walls[x][y][d] = true; } } boolean[][] visited = new boolean[cols][rows]; generateMaze(0, 0, visited); placeEntities(); setPreferredSize(new Dimension(cols * cellSize, rows * cellSize)); agentQueue = new Queue(10); agent1 = new Agent(0, 0, this, Color.BLUE, "Blue Agent",1); agent2 = new Agent(cols - 1, rows - 1, this, Color.ORANGE, "Orange Agent",2); agentQueue.enqueue(agent1); agentQueue.enqueue(agent2); timer = new Timer(300, e -> { if (gameOver || agentQueue.isEmpty()) return; if (currRount > 0 && currRount % 5 == 0) rotateCorridor(); repaint(); Agent currentAgent = agentQueue.dequeue(); currentAgent.moveTowardsGoal(); if (tiles[currentAgent.getX()][currentAgent.getY()].getEntity() == MazeTile.EntityType.GOAL) { gameOver = true; timer.stop(); Agent loser = currentAgent == agent1 ? agent2 : agent1; GameLog(currentAgent, loser); JOptionPane.showMessageDialog(this, finalReport(agent1, agent2, currentAgent), "Game over!", JOptionPane.INFORMATION_MESSAGE); return; } agentQueue.enqueue(currentAgent); currRount++; }); timer.start(); } private void generateMaze(int x, int y, boolean[][] visited) { visited[x][y] = true; Integer[] dirs = {0, 1, 2, 3}; Collections.shuffle(Arrays.asList(dirs)); for (int dir : dirs) { int nx = x, ny = y; switch (dir) { case 0: ny--; break; case 1: nx++; break; case 2: ny++; break; case 3: nx--; break; } if (isInMaze(nx, ny) && !visited[nx][ny]) { removeWall(x, y, dir); generateMaze(nx, ny, visited); } } } public boolean hasWall(int x, int y, int dir) { return walls[x][y][dir]; } public void removeWall(int x, int y, int dir) { walls[x][y][dir] = false; int nx = x, ny = y; int opp = (dir + 2) % 4; switch (dir) { case 0: ny--; break; case 1: nx++; break; case 2: ny++; break; case 3: nx--; break; } if (isInMaze(nx, ny)) walls[nx][ny][opp] = false; } //-WİP- private void placeEntities() { int goalX = cols / 2; int goalY = rows / 2; tiles[goalX][goalY].setEntity(MazeTile.EntityType.GOAL); int total = cols * rows; int trapCount = total * trapChance / 100; int powerCount = total * powerUpChance / 100; for (int i = 0; i < trapCount; i++) { int x, y; do { x = random.nextInt(cols); y = random.nextInt(rows); } while (tiles[x][y].getEntity() != MazeTile.EntityType.EMPTY || (x == goalX && y == goalY)); tiles[x][y].setEntity(MazeTile.EntityType.TRAP); } for (int i = 0; i < powerCount; i++) { int x, y; do { x = random.nextInt(cols); y = random.nextInt(rows); } while (tiles[x][y].getEntity() != MazeTile.EntityType.EMPTY || (x == goalX && y == goalY)); tiles[x][y].setEntity(MazeTile.EntityType.POWER_UP); } } private void rotateCorridor() { boolean rotateRow = random.nextBoolean(); int index = rotateRow ? random.nextInt(rows) : random.nextInt(cols); //1=row, 2=col int goalX = cols / 2, goalY = rows / 2; //coordinats of goal ///////////////////////////////////////////// if (rotateRow && index == goalY) { return; } if (!rotateRow && index == goalX) { return; } if (rotateRow && index == agent1.getY()) { return; } //for not touching goal,agent1, agent2 if (!rotateRow && index == agent1.getX()) { return; } if (!rotateRow && index == agent2.getX()) { return; } if (rotateRow && index == agent2.getY()) { return; } ///////////////////////////////////////////// if (rotateRow) { CircularLinkedList entityList = new CircularLinkedList<>(); CircularLinkedList activePowerUp = new CircularLinkedList<>(); // taking data of every tile( entity type, is trap active, is powerup active) //taking them to same circular linked list CircularLinkedList activeTrap = new CircularLinkedList<>(); //rewrite every tiles for (int x = 0; x < cols; x++) { MazeTile tile = tiles[x][index]; entityList.add(tile.getEntity()); activeTrap.add( tile.isTrapActive()); activePowerUp.add(tile.isPowerUpActive()); } entityList.rotateOneStep(1); activeTrap.rotateOneStep(1); activePowerUp.rotateOneStep(1); for (int x = 0; x < cols; x++) { tiles[x][index].setPowerUpActive(activePowerUp.get(x)); tiles[x][index].setEntity(entityList.get(x)); tiles[x][index].setTrapActive(activeTrap.get(x)); } } else { CircularLinkedList trapActiveList = new CircularLinkedList<>(); CircularLinkedList powerUpActiveList = new CircularLinkedList<>(); CircularLinkedList entityList = new CircularLinkedList<>(); for (int y = 0; y < rows; y++) { MazeTile tile = tiles[index][y]; entityList.add(tile.getEntity()); trapActiveList.add(tile.isTrapActive()); powerUpActiveList.add(tile.isPowerUpActive()); } entityList.rotateOneStep(1); trapActiveList.rotateOneStep(1); powerUpActiveList.rotateOneStep(1); for (int y = 0; y < rows; y++) { tiles[index][y].setEntity(entityList.get(y)); tiles[index][y].setTrapActive(trapActiveList.get(y)); tiles[index][y].setPowerUpActive(powerUpActiveList.get(y)); } } } public boolean isInMaze(int x, int y) { return x >= 0 && y >= 0 && x < cols && y < rows; } public MazeTile getTile(int x, int y) { return tiles[x][y]; } public int getCols() { return cols; } public int getRows() { return rows; } private String finalReport(Agent a1, Agent a2, Agent winner) { StringBuilder sb = new StringBuilder(); sb.append(winner.getName()).append(" reached the target!\nnumber of rount played: ").append(currRount).append("\n\n"); for (Agent agent : new Agent[]{a1, a2}) { sb.append(agent.getName()).append(":\n"); Stack stack = agent.getMoveHistory(); for (int i = 0; i < stack.getSize(); i++) { sb.append(stack.getElements()[i]).append(""); } sb.append("\n"); } return sb.toString(); } private void GameLog(Agent winner, Agent loser) { // preparing console screen before printing results System.out.print("\033[H\033[2J"); System.out.flush(); //----------- System.out.println("\n=== Game Over ==="); System.out.println("Winner Agent: " + winner.getName()); System.out.println("Number of rount played: " + currRount); System.out.println("----------------------"); System.out.println(winner.getName() + " Statistics:"); System.out.println("- Step number: " + winner.getStepCount()); System.out.println("- Trap number: " + winner.getTrapHits()); System.out.println("- Power-Up usage: " + winner.getPowerUpUses()); System.out.println(); System.out.println(loser.getName() + " Statistics:"); System.out.println("- Step number: " + loser.getStepCount()); System.out.println("- Trap number: " + loser.getTrapHits()); System.out.println("- Power-Up usage: " + loser.getPowerUpUses()); try (FileWriter writer = new FileWriter("game_log.txt", true)) { writer.write("=== Game Over ===\n"); writer.write("Winner Agent: " + winner.getName() + " (ID: " + winner.getId() + ")\n"); writer.write("Number of rount played: " + currRount + "\n\n"); for (Agent agent : new Agent[]{winner, loser}) { writer.write(agent.getName() + " (ID: " + agent.getId() + ")\n"); writer.write("- Step number: " + agent.getStepCount() + "\n"); writer.write("- Trap number: " + agent.getTrapHits() + "\n"); writer.write("- Power-Up usage: " + agent.getPowerUpUses() + "\n"); writer.write("- Moves: "); Stack stack = agent.getMoveHistory(); for (int i = 0; i < stack.getSize(); i++) { Object move = stack.getElements()[i]; writer.write(String.valueOf(move)); } writer.write("\n--------------------------\n"); } } catch (IOException e) { System.out.println("Log file error: "); } } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.BLACK); for (int x = 0; x < cols; x++) { for (int y = 0; y < rows; y++) { int px = x * cellSize; int py = y * cellSize; if (walls[x][y][3]) g.drawLine(px, py, px, py + cellSize); if (walls[x][y][1]) g.drawLine(px + cellSize, py, px + cellSize, py + cellSize); if (walls[x][y][0]) g.drawLine(px, py, px + cellSize, py); if (walls[x][y][2]) g.drawLine(px, py + cellSize, px + cellSize, py + cellSize); switch (tiles[x][y].getEntity()) { case GOAL -> { g.setColor(Color.GREEN); g.fillRect(px + 4, py + 4, cellSize - 8, cellSize - 8); g.setColor(Color.BLACK); } case POWER_UP -> { g.setColor(tiles[x][y].isPowerUpActive() ? Color.YELLOW : Color.GRAY); g.fillRect(px + 4, py + 4, cellSize - 8, cellSize - 8); g.setColor(Color.BLACK); } case TRAP -> { g.setColor(tiles[x][y].isTrapActive() ? Color.RED : Color.GRAY); g.fillRect(px + 4, py + 4, cellSize - 8, cellSize - 8); g.setColor(Color.BLACK); } } } } for (Agent agent : new Agent[]{agent1, agent2}) { g.setColor(agent.passedWallThisTurn() ? Color.MAGENTA : agent.getColor()); g.fillOval(agent.getX() * cellSize + 5, agent.getY() * cellSize + 5, cellSize - 10, cellSize - 10); } g.setColor(Color.BLACK); g.drawString("Curr Round: " + currRount, 10, 15); } }