package Core;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * Class that handles one iteration of the game
 * and holds all informations of the current game
 */
public class Game {
	
	/**
	 * Constants
	 */
	private final int PLAYER_RANGE = 1;
	private final int MIN_FIELD = 0;
	private final int INDEX_0 = 0;
	
	/**
	 * Instance variables
	 */
	private Playboard playboard;
	private Map<User, Player> users = new HashMap<User, Player>();
	private List<User> usersList;
	private int maxBoardIndex;
	private LinkedList<Field> starting_fields = new LinkedList<>();
	private Set<Field> explodedFields = new HashSet<>();
	private int bombCounter;
	private int explosionRadius;
	private int boardSize;
	private boolean gameOver = false;
	private int maxSteps;
	private long stepSleep;
	
	/******* Constructor and Initializer *******/
	
	/**
	 * Constructor
	 * @param usersList			List of users that are playing the game
	 * @param boardSize			Count of fields in X and Y direction of the playboard
	 * @param bombCounter		Interations needed for a bomb to explode
	 * @param explosionRadius 	Count of fields a bomb causes to explode in every direction
	 * @param maxSteps			Maximum amout of iteration till the game ends in a draw
	 * @param stepSleep			Time to wait between each iteration
	 */
	public Game(List<User> usersList, int boardSize, int bombCounter, int explosionRadius, int maxSteps, long stepSleep) {
		this.boardSize = boardSize;		
		this.maxBoardIndex = boardSize - PLAYER_RANGE;		
		this.bombCounter = bombCounter;
		this.explosionRadius = explosionRadius;
		this.maxSteps = maxSteps;
		this.usersList = usersList;		
		this.stepSleep = stepSleep;
		initializeBoard();
		initializePlayers();
	}
	
	/**
	 * Initializes the player objects for the users
	 */
	private void initializePlayers() {
		initializeStartingFields();		
		for (User user : usersList) {			
			this.users.put(user, new Player(user.getId(), starting_fields.pop()));
		}
		playboard.setPlayers(new HashSet<Player>(this.users.values()));
	}

	/**
	 * Initializes the fields where player start on
	 */
	private void initializeStartingFields() {
		Field[][] board = playboard.getBoard();
		starting_fields.add(board[MIN_FIELD][MIN_FIELD]);
		starting_fields.add(board[maxBoardIndex][maxBoardIndex]);
		starting_fields.add(board[maxBoardIndex][MIN_FIELD]);
		starting_fields.add(board[MIN_FIELD][maxBoardIndex]);		
				
	}
	
	/**
	 * Initializes the board the game will take place on
	 */
	private void initializeBoard() {
		Field[][] board = new Field[boardSize][boardSize];
		for (int i = 0; i < boardSize; i++) {
			for (int j = 0; j < boardSize; j++) {
				if (i % 2 == 1 && j % 2 == 1) {
					board[i][j] = new Field(i, j, false, false);
				} else {
					board[i][j] = new Field(i, j);
				}
			}
		}
		this.playboard = new Playboard(board, maxSteps, explosionRadius, bombCounter);
	}
	
	/******* Getter *******/
	
	/**
	 * Returns maximum amout of iteration till the game ends in a draw
	 * @return maxSteps
	 */
	public int getMaxSteps() {
		return maxSteps;
	}

	/**
	 * Returns the amount of interations needed for a bomb to explode
	 * @return bombCounter
	 */
	public int getBombCounter() {
		return bombCounter;
	}

	/**
	 * Returns the count of fields a bomb causes to explode in every direction
	 * @return explosionRadius
	 */
	public int getExplosionRadius() {
		return explosionRadius;
	}
	
	/**
	 * Returns the count of fields in X and Y direction of the playboard
	 * @return explosionRadius
	 */
	public int getBoardSize() {
		return boardSize;
	}
	
	/**
	 * Returns the list of users that are playing the game
	 * @return explosionRadius
	 */
	public List<User> getUsers(){
		List<User> usersList = new ArrayList<>();
		usersList.addAll(users.keySet());
		return usersList;
	}
	
	/**
	 * Returns the playboard with every information about the current state of the game
	 * @return playboard
	 */
	public Playboard getPlayboard() {
		return playboard;
	}	
	
	/**
	 * Returns all fields that are affected by an explosion
	 * @return explodedFields
	 */
	public Set<Field> getExplodedFields() {
		return explodedFields;
	}

	/**
	 * Is the game over?
	 * @return gameOver
	 */
	public boolean isGameOver() {
		return gameOver;
	}
	
	
	/**
	 * Returns the time the game sleeps between every iteration
	 * @return stepSleep
	 */
	public long getStepSleep() {
		return stepSleep;
	}
	
	/******* Public Methods *******/	

	/**
	 * Does a iteration of the game. Sleeps for a certain amount of time
	 * @throws InterruptedException
	 */
	public void doIteration() throws InterruptedException {
		Thread.sleep(stepSleep);
		playerActions();	
		updatePlayboard();
		checkGameOver();
	}

	/******* Private Helpers *******/		
	
	/**
	 * Checks if the game is over
	 */
	private void checkGameOver() {
		List<User> playersAlive = new ArrayList<>();
		for (Entry<User, Player> entry : users.entrySet()) {
			if (entry.getValue().isAlive()) {
				playersAlive.add(entry.getKey());
			}
		}
		if (playersAlive.size() == 1 && !gameOver) {
			User player = playersAlive.get(INDEX_0);
			player.won();
			player.gameOver(true, playboard);	
			
			//gameOver(false) to everyone, who lost			
			for(Entry<User, Player> entry : users.entrySet()) {
			    if(entry.getKey() != player) {
			        entry.getKey().gameOver(false, playboard);
			    }
			}
			gameOver = true;
		} else if (playersAlive.size() == 0 && !gameOver || playboard.getStepsLeft() == 0) {
			for (User user : usersList) {
				user.gameOver(false, playboard);
			}
			gameOver = true;
		}
		playboard.decreaseStepsLeft();
	}

	/**
	 * Executes all player actions
	 */
	private void playerActions() {
	    //Get the playboard only once
	    Playboard currentBoard = playboard.clone();
	    
		for (Entry<User, Player> entry : users.entrySet()) {
			Player player = entry.getValue();
			User user = entry.getKey();
			switch (user.getAction(currentBoard)) {
			case 1:
				if (player.getY() - PLAYER_RANGE >= MIN_FIELD) {
					setPlayerPosition(player.getX(), player.getY() - PLAYER_RANGE, player);
				}				
				break;
			case 2:
				if (player.getY() + PLAYER_RANGE <= maxBoardIndex) {
					setPlayerPosition(player.getX(), player.getY() + PLAYER_RANGE, player);
				}
				break;
			case 3:
				if (player.getX() - PLAYER_RANGE >= MIN_FIELD) {
					setPlayerPosition(player.getX() - PLAYER_RANGE, player.getY(), player);
				}
				break;
			case 4:
				if (player.getX() + PLAYER_RANGE <= maxBoardIndex) {
					setPlayerPosition(player.getX() + PLAYER_RANGE, player.getY(), player);
				}
				break;
			case 5:
				playboard.addBomb(bombCounter, player.getX(), player.getY(), explosionRadius, player.getId());
				break;
			default:
				break;
			}
			user.resetMove();
		}
	}
	
	/**
	 * Sets the position of a certain player to the coordinates x and y
	 * @param x
	 * @param y
	 * @param player
	 */
	private void setPlayerPosition(int x, int y, Player player) {
		Field destination = playboard.getBoard()[x][y];
		if (destination.isPassable()) {
			player.setField(destination);
		}
	}
	
	/**
	 * Updates the player isAlive status if a player is in an explosion
	 */
	private void updatePlayboard() {
		explodedFields = explodingFields();
		for (Player player : playboard.getPlayers()) {			
			for (Field field : explodedFields) {
				if (player.getField().equals(field)) {
					player.setAlive(false);
				}
			}			
		}
	}


	/**
	 * Calculates all fields that are exploding in this iteration and 
	 * sets the counter of every bomb that is not exploding minus one
	 * @return
	 */
	private Set<Field> explodingFields() {
		Set<Field> explodedFields = new HashSet<>();
		Set<Bomb> bombs = playboard.getBombs();
		explodedFields = chainExplosions(explodedFields);
		Set<Bomb> bombsToRemove = new HashSet<>();
		for (Bomb bomb : bombs) {
			if (bomb.isExploded()) {
				bomb.getField().setPassable(true);
				bombsToRemove.add(bomb);
			} else {
				bomb.countDown();
			}
		}
		bombs.removeAll(bombsToRemove);
		playboard.setBombs(bombs);
		return explodedFields;
	}
	
	/**
	 * Helper to calculate a chain explosion of a bomb
	 * @param explodedFields
	 * @return exploding fields from chainexplosion
	 */
	private Set<Field> chainExplosions(Set<Field> explodedFields) {
		for (Bomb bomb : playboard.getBombs()) {
			if (bomb.shouldExplode() || explodedFields.contains(bomb.getField()) && !bomb.isExploded()) {
				explodedFields.addAll(bomb.explode(playboard.getBoard()));	
				return chainExplosions(explodedFields);					
			}			
		}
		return explodedFields;
	}
	
}
最近下载更多
adap12345  LV5 2023年6月8日
hkxyyz  LV6 2023年3月2日
总有人间一两风  LV8 2022年12月12日
liangge2115  LV27 2022年11月7日
微信网友_6004879537377280  LV3 2022年6月22日
jytjyt  LV1 2021年12月16日
ssssqweq  LV1 2021年11月1日
此次东方  LV1 2021年10月20日
qliu0130  LV1 2021年10月10日
zpy123  LV1 2021年7月18日
最近浏览更多
ClydeSon  LV5 2023年12月27日
bangyiyang  LV2 2023年12月21日
1112WHQ  LV7 2023年11月3日
微信网友_6672184532766720  LV3 2023年10月6日
srrrrrr 2023年9月19日
暂无贡献等级
adap12345  LV5 2023年6月8日
laa94698 2023年4月21日
暂无贡献等级
hkxyyz  LV6 2023年3月2日
k麝神k  LV10 2022年12月21日
wwfl02  LV3 2022年12月16日
顶部 客服 微信二维码 底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友