// Arup Guha
// 2/24/2026
// Game class to manange my "Pacman" game.
// Edited on 3/8/2026 for Abstract class lecture, this is probably the biggest edit.

import java.util.*;

public class PacmanGame {

	// For my fruits!
	final public static String[] FRUITNAMES = {"strawberry", "banana", "orange", "kiwi"};
	final public static String[] COLORS = {"red", "yellow", "orange", "green"};
	final public static int[] POINTS = {100, 50, 150, 300};
	
	// Just to start stuff off.
	public static Random rndObj = new Random();
	
	// Parts of my PacmanGame object.
	private int numX;
	private int numY;
	private Pacman player;
	private int numFoodObj;
	private Piece[] food;
	private int timeStep;
	private int dotsLeft;
	
	public PacmanGame(String playerName, int sizeX, int sizeY, int numFruit, int numGhosts) {
		
		// For board printing.
		numX = sizeX;
		numY = sizeY;
		
		// I start at the origin. Yes, the universe centers around PacMan!
		player = new Pacman(0, 0, playerName);
		
		// Allocate all items.
		int numDots = (2*numX+1)*(2*numY+1)-1;
		numFoodObj = numDots+numFruit+numGhosts;
		food = new Piece[numFoodObj];
		int foodIdx = 0;
		
		// Place my dots everywhere except the middle.
		for (int i=-numX; i<=numX; i++) {
			for (int j=-numY; j<=numY; j++) {
				if (i==0 && j==0) continue;
				food[foodIdx++] = new Dot(i,j);
			}
		}
		dotsLeft = (2*numX+1)*(2*numY+1)-1;
		
		// Keeps track of how many moves were made.
		timeStep = 0;
		
		// Create random Fruit objects.
		for (int i=0; i<numFruit; i++) {
			int x = -numX+rndObj.nextInt(2*numX+1);
			int y = -numY+rndObj.nextInt(2*numY+1);
			int which = rndObj.nextInt(FRUITNAMES.length);
			food[foodIdx++] = new Fruit(new Position(x,y), getRndDir(), FRUITNAMES[which], COLORS[which], POINTS[which]);
		}
		
		// And Ghosts!
		for (int i=0; i<numGhosts; i++) {
			int x = -numX+rndObj.nextInt(2*numX+1);
			int y = -numY+rndObj.nextInt(2*numY+1);
			Position dir = getRndDir();
			food[foodIdx++] = new Ghost(new Position(x,y), dir);
		}
	}
	
	// Gets a random direction of movement.
	public static Position getRndDir() {
		int dx = 0, dy = 0;
		if (rndObj.nextInt(2) == 1)
			dx = -1 + 2*rndObj.nextInt(2);
		else
			dy = -1 + 2*rndObj.nextInt(2);
		return new Position(dx, dy);
	}
	
	// Returns a String representation of this PacmanGame.
	public String toString() {
		
		// Make an empty grid.
		char[][] grid = new char[2*numX+1][2*numY+1];
		for (int i=0; i<grid.length; i++)
			Arrays.fill(grid[i], '_');
		
		// Add all of the pieces.
		for (Piece p: food) 
			if (p.inbounds(-numX, numX, -numY, numY))
				grid[p.loc.getX()+numX][p.loc.getY()+numY] = p.getCode();
			
		// Now the player.
		if (player.inbounds(-numX, numX, -numY, numY))
			grid[player.loc.getX()+numX][player.loc.getY()+numY] = player.getCode();
		
		// Build and return the string.
		/*** Faster to use the class StringBuffer which is mutable ***/
		String res = "";
		for (int i=0; i<grid.length; i++)
			res = res + new String(grid[i])+"\n";
		res = res + player + "\n";
		res = res + "Dots Left = "+ dotsLeft + "\n";
		return res;
	}
	
	// We win if there's no dots left.
	public boolean win() {
		return dotsLeft == 0;
	}
	
	// Returns the number of time steps.
	public int numTimeSteps() {
		return timeStep;
	}
	
	// Valid moves are 'U', 'D', 'L', 'R' and 'S' (default)
	public boolean move(char myMove) {
		
		// Update movement vector of player.
		if (myMove == 'U') player.decY();
		if (myMove == 'D') player.incY();
		if (myMove == 'L') player.decX();
		if (myMove == 'R') player.incX();
		
		// Move the player, as long as it stays in the grid.
		if (player.wouldStayInBounds(-numX, numX, -numY, numY))
			player.move();
		
		// Reflect food if necessary.
		for (int i=0; i<numFoodObj; i++) 
			if (!food[i].wouldStayInBounds(-numX, numX, -numY, numY))
				food[i].negateDir();
	
		// I move the other pieces about half the time.
		if (rndObj.nextInt(2) == 1) {
		
			// Move the food.
			for (int i=0; i<numFoodObj; i++)
				food[i].move();
		}
		
		// Figure out how many are eaten.
		int lose = numEaten();
		int newSize = numFoodObj - lose;
		
		// Copy surviving pieces to a temporary array.
		boolean okay = true;
		Piece[] survived = new Piece[newSize];
		for (int i=0,j=0; i<numFoodObj; i++) {
			
			// Eat me!
			if (player.getLoc().equals(food[i].getLoc())) {
				okay = okay && player.eat(food[i]);
				if (food[i] instanceof Dot)
					dotsLeft--;
			}
			
			// Copy into survived food.
			else {
				survived[j++] = food[i];
			}
		}
				
		// Update all of this.
		numFoodObj = newSize;
		food = survived;
		timeStep++;
		return okay;
	}
	
	// Returns the number of pieces eaten.
	private int numEaten() {
		int numEaten = 0;
		for (Piece p: food) {
			if (p instanceof Ghost) continue;
			if (player.getLoc().equals(p.getLoc()))
				numEaten++;
		}
		return numEaten;
	}
	
	public static void main(String[] args) {
		
		// Get player name.
		Scanner stdin = new Scanner(System.in);
		System.out.println("What is your name?");
		String name = stdin.next();
		
		// Create a game...change the four paramters at the end as needed!
		PacmanGame thisGame = new PacmanGame(name, 3, 4, 5, 3);
		boolean status = true;
		while (!thisGame.win()) {
			
			// Print the game.
			System.out.println(thisGame);
			
			// Get move and execute it.
			System.out.println("What move?(U,D,L,R,S)");
			char myMove = stdin.next().charAt(0);
			status = thisGame.move(myMove);
			
			// Ghost ate you case!
			if (!status) {
				System.out.println("Sorry a ghost ate you. You lost.");
				System.out.println("You lasted "+thisGame.numTimeSteps()+" moves.");
				break;
			}
		}
		
		// Winner winner chicken dinner!
		if (thisGame.win() && status) {
			System.out.println("Great job you won by eating all the dots!");
			System.out.println("It took you "+thisGame.numTimeSteps()+" moves to win!");
		}
	}
}