package io.tafl;

import io.tafl.board.Board;
import io.tafl.board.PieceType;
import io.tafl.board.Point;
import io.tafl.board.impl.HnefataflBoard;
import io.tafl.rules.FetlarRules;
import io.tafl.rules.Rules;

import java.util.HashMap;
import java.util.stream.Collectors;

public class Game {

    private Board board;
    private Rules rules;
    private GameState state;

    Game() {
        rules = new FetlarRules();
        board = new HnefataflBoard(rules);
        state = GameState.ATTACKERS_TURN;
    }

    public boolean movePiece(Point from, Point to) {
        MovePiece movePiece = new MovePiece(rules, board);
        boolean success = false;
        switch(state) {
            case ATTACKERS_TURN:
                if(board.getAttackers().containsKey(from))
                    success = movePiece.movePiece(from, to);
                break;
            case DEFENDERS_TURN:
                if(board.getDefenders().containsKey(from))
                    success = movePiece.movePiece(from, to);
                break;
            case ATTACKERS_WIN:
            case DEFENDERS_WIN:
                return false;
        }
        if(success) {
            if(state == GameState.ATTACKERS_TURN) {
                state = GameState.DEFENDERS_TURN;
                if(!board.getDefenders().values().contains(PieceType.KING)
                        || !canSideMove(board.getDefenders())
                        || (rules.allowEncircle() && board.checkEncircle(board.getAttackers(), board.getDefenders())))
                    state = GameState.ATTACKERS_WIN;
            } else if(state == GameState.DEFENDERS_TURN) {
                state = GameState.ATTACKERS_TURN;
                if(kingEscaped()
                        || !canSideMove(board.getAttackers())
                        || (rules.allowEncircle() && board.checkEncircle(board.getDefenders(), board.getAttackers())))
                    state = GameState.DEFENDERS_WIN;
            }
        }
        return success;
    }

    /**
     * Check if the king has successfully escaped, by checking if it has reaches an
     * edge or corner piece depending on rules
     * @return true if the king escaped, false otherwise
     */
    private boolean kingEscaped() {
        return (rules.edgeEscape() && board.getEdges().contains(board.getKing()))
               || board.getCornerPoints().contains(board.getKing());
    }

    /**
     * Check if there are any available moves for a side.
     * @param pieces All of a side's pieces.
     * @return true if there are any possible moves for at least one the side's pieces, false otherwise.
     */
    private boolean canSideMove(HashMap<Point, PieceType> pieces) {
        return pieces.keySet().stream().filter(board::pieceCanMove).collect(Collectors.toSet()).isEmpty();
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        Game game = new Game();
        game.board.printBoard();
        System.out.print("\n\n");
        game.movePiece(new Point(1, 8), new Point(5, 8));
        game.movePiece(new Point(6, 4), new Point(6, 3));
        game.movePiece(new Point(11, 8), new Point(7, 8));
        game.board.printBoard();
        System.out.println("\n\n"+ (System.currentTimeMillis() - start));
    }
}

@@ -125,25 +125,23 @@
 rd.impl.
-Hnefatafl
+Brandub
 Board;%0Ai
@@ -160,22 +160,23 @@
 l.rules.
-Fetlar
+Brandub
 Rules;%0Ai
@@ -203,16 +203,37 @@
 Rules;%0A%0A
+import lombok.Data;%0A%0A
 import j
@@ -287,16 +287,22 @@
 ctors;%0A%0A
+@Data%0A
 public c
@@ -398,21 +398,69 @@
 e;%0A%0A    
-Game(
+public Game(Rules rules, Board board, GameState state
 ) %7B%0A    
@@ -467,16 +467,21 @@
     
+this.
 rules = 
 new 
@@ -480,25 +480,13 @@
 s = 
-new FetlarR
+r
 ules
-()
 ;%0A  
@@ -495,16 +495,21 @@
     
+this.
 board = 
 new 
@@ -508,33 +508,13 @@
 d = 
-new HnefataflBoard(rules)
+board
 ;%0A  
@@ -515,32 +515,37 @@
 rd;%0A        
+this.
 state = 
 GameState.AT
@@ -536,32 +536,13 @@
 e = 
-GameState.ATTACKERS_TURN
+state
 ;%0A  
@@ -2945,29 +2945,127 @@
     
-Game game = new Game(
+Rules rules = new BrandubRules();%0A        Game game = new Game(rules, new BrandubBoard(rules), GameState.ATTACKERS_TURN
 );%0A 

make public full game constructor