package ee.ttu.cs.iti0011.iabb104231.k1.Board;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import ee.ttu.cs.iti0011.iabb104231.k1.Board.StateStorage.AbstractStateStorage;
import ee.ttu.cs.iti0011.iabb104231.k1.Board.StateStorage.BitSetStateStorage;
import ee.ttu.cs.iti0011.iabb104231.k1.Exceptions.NotYourMove;
import ee.ttu.cs.iti0011.iabb104231.k1.Exceptions.NumberOutOfRange;
import ee.ttu.cs.iti0011.iabb104231.k1.Exceptions.PlayerCantMoveTwice;
import ee.ttu.cs.iti0011.iabb104231.k1.Exceptions.PlayerNotSet;
import ee.ttu.cs.iti0011.iabb104231.k1.Exceptions.PositionIsAlreadyTaken;
import ee.ttu.cs.iti0011.iabb104231.k1.Player.AbstractPlayer;
import ee.ttu.cs.iti0011.iabb104231.k1.Renderer.AbstractRenderer;
public class Board
{
private AbstractPlayer lastMove;
private ArrayList<AbstractPlayer> players = new ArrayList<>();
private AbstractPlayer startsFirst;
private boolean gameStarted = false;
private boolean finished = true;
private AbstractRenderer renderer;
private Map<AbstractPlayer, AbstractStateStorage> storages = new HashMap<AbstractPlayer, AbstractStateStorage>();
private AbstractStateStorage storage;
public Board()
{
// this(new IntegersMapStateStorage());
this(new BitSetStateStorage());
}
public Board(AbstractStateStorage storage)
{
setStorage(storage);
}
public void output(String message)
{
System.out.println(message);
}
public void clear(){
try{
Runtime.getRuntime().exec("cls");
Runtime.getRuntime().exec("clear");
} catch(Exception e){
for(int j = 0; j < 30; j++) {
System.out.println();
}
}
}
public AbstractPlayer occupiedBy(Integer cell)
{
return getStorage().occupiedBy(cell);
}
private AbstractStateStorage getStorage() {
return storage;
}
private void setStorage(AbstractStateStorage storage) {
this.storage = storage;
}
/**
* Add new player.
* @param player
*/
public void addPlayer(AbstractPlayer player)
{
player.setBoard(this);
players.add(player);
setPlayerStorage(player, player.getStorage());
}
/**
* Get the list of players.
* @return
*/
public ArrayList<AbstractPlayer> getPlayers()
{
return players;
}
/**
* Set player storage.
* @param player
* @param storage
* @return
*/
public Board setPlayerStorage(AbstractPlayer player, AbstractStateStorage storage)
{
storages.put(player, storage);
return this;
}
public AbstractStateStorage getPlayerStorage(AbstractPlayer player)
{
return storages.get(player);
}
/**
* Get all opened for move cells
* @return
*/
public ArrayList<Integer> getOpenCells()
{
return getStorage().getFreeCells();
}
public boolean isFinished()
{
return finished;
}
private void setLastMoved(AbstractPlayer player){
lastMove = player;
}
/**
* Starts the game
*/
public void startTheGame()
{
finished = false;
// Game is already running.
if (gameStarted == true) {
output("game is already started");
System.exit(1);
}
output("Starting new game");
gameStarted = true;
Integer i = 0;
while(!isFinished() && i < 10)
{
getRenderer().render();
AbstractPlayer nextPlayer = whoIsNext();
try{
makeMove(nextPlayer, nextPlayer.yourNextMove());
} catch(Exception e){
output("ERROR !!!");
output(e.getMessage());
output(e.toString());
e.printStackTrace();
}
// clear();
i++;
}
getRenderer().render();
getRenderer().renderTheWinner(getStorage().getWinner());
}
public AbstractPlayer whoStartsFirst()
{
if (startsFirst == null) {
return players.get(0);
}
return startsFirst;
}
/**
* Define, which player moves first.
* By default, when game is started and no one is defined, the first added will start the game.
* @param player
* @throws PlayerNotSet
*/
public void movesFirst(AbstractPlayer player) throws PlayerNotSet
{
if (!players.contains(player)) {
throw new PlayerNotSet(player);
}
}
/**
* Get next player.
* @todo - actually it's with bug - if more then two players will
* play, it will not work :).... But anyway, when did you see more than
* two players were playing this game ????
* @return
*/
public AbstractPlayer whoIsNext()
{
if (lastMove == null) {
return whoStartsFirst();
}
for(AbstractPlayer player: players)
{
if (!player.equals(lastMove)) return player;
}
return null;
}
/**
* Make player move.
* @param player
* @param cell
*/
public void makeMove(AbstractPlayer player, Integer cell) throws PositionIsAlreadyTaken, PlayerCantMoveTwice, NumberOutOfRange, NotYourMove, Exception
{
output("Player: " + player.getName() + " moves to " + cell);
// There is no cell. end game
if (getOpenCells().size() == 0) {
finished = true;
System.out.println("Game finished.");
return;
}
// player can't move twice
if (lastMove != null && player.equals(lastMove)) {
throw new PlayerCantMoveTwice(player);
}
// It's not your turn
if (!player.equals(whoIsNext())) {
throw new NotYourMove(player);
}
// It's already taken
if (!getStorage().cellIsEmpty(cell)){
throw new PositionIsAlreadyTaken();
}
if (cell < 1 || cell > 9){
throw new NumberOutOfRange();
}
getStorage().registerPlayerMove(player, cell);
for(Map.Entry<AbstractPlayer, AbstractStateStorage> entry: storages.entrySet())
{
entry.getValue().registerPlayerMove(player, cell);
}
setLastMoved(player);
if (getStorage().getWinner() != null) {
finished = true;
}
if (getOpenCells().size() == 0) {
finished = true;
}
return;
}
public AbstractRenderer getRenderer() {
return renderer;
}
public void setRenderer(AbstractRenderer renderer) {
renderer.setBoard(this);
this.renderer = renderer;
}
public AbstractPlayer findWinner()
{
return getStorage().getWinner();
}
}