package mgt.risk;

import java.util.*;
import java.text.DecimalFormat;
import java.io.StringWriter;
import java.io.PrintWriter;

public class Risk {

    // for the matrix of dice
    int maxX = 25;  // defender
    int maxY = 25;  // attacker
    int simulations = 5000;
    Scenario[][] scenarios = new Scenario[25][25];

    DecimalFormat formatter = new DecimalFormat("##.##");
    Random random = new Random(new Date().getTime());

    private void start() {

        StringWriter winWriter = new StringWriter();
        StringWriter armyWriter = new StringWriter();

        PrintWriter win = new PrintWriter(winWriter);
        PrintWriter armies = new PrintWriter(armyWriter);

        // header row
        win.println(",,,,,,,,,,,,Defending Armies");
        armies.println(",,,,,,,,,,,,Defending Armies");

        win.print("                 ,,");
        armies.print("                 ,,");
        for (int i = 0; i < scenarios.length; i++) {
            win.print(i + 1);
            armies.print(i + 1);
            if (i != scenarios[i].length - 1) {
                win.print(",");
                armies.print(",");
            }
        }
        win.println("");
        armies.println("");
        //end header row

        // this 'i' loop represents attacker armies, 1 through x
        for (int i = 0; i < scenarios.length; i++) {

            if( i == 12 ){
                win.print("Attacking Armies," + (i + 1) + ",");
                armies.print("Attacking Armies," + (i + 1) + ",");
            } else {
                win.print("                ," + (i + 1) + ",");
                armies.print("                ," + (i + 1) + ",");
            }

            // this 'j' loop represents defender armies, 1 through x
            for (int j = 0; j < scenarios[i].length; j++) {

                // run the scenario a bunch of times and find average
                int total = 0;
                int armiesRemaining = 0;
                for(int z = 0; z < simulations; z++){
                    scenarios[i][j] = new Scenario(i + 1, j + 1);
                    total += scenarios[i][j].result();
                    armiesRemaining += scenarios[i][j].getEndingAttackerArmyCount();
                }
                double avg = (double) total / (double) simulations;
                double avgArmies = (double) armiesRemaining / (double) simulations;

                // the avg at this point represents the % that separates the
                // attacker and defender.  If each side has a 50/50 chance of
                // winning, then we take the % difference and give
                // half the to attacker while taking half from the defender.
                // this gives the attacker's chance to win.
                double chanceToWin = 50.0 + (avg * 100.0) / 2.0;
                win.print(formatter.format(chanceToWin));
                armies.print(formatter.format(avgArmies));
                if (j != scenarios[i].length - 1) {
                    win.print(",");
                    armies.print(",");
                }
            }
            win.println("");
            armies.println("");
        }

        System.out.println("CHANCE OF WINNING:");
        System.out.println(winWriter);
        System.out.println("");
        System.out.println("REMAINING ARMY COUNT:");
        System.out.println(armyWriter);
    }

    public static void main(String[] args) {
        new Risk().start();
    }

    private class Scenario {

        int startingAttackerArmyCount;
        int startingDefenderArmyCount;

        int endingAttackerArmyCount;
        int endingDefenderArmyCount;

        public Scenario(int attackerArmies, int defenderArmies) {
            this.startingAttackerArmyCount = attackerArmies;
            this.startingDefenderArmyCount = defenderArmies;
            fight();
        }

        public int getStartingAttackerArmyCount() {
            return startingAttackerArmyCount;
        }

        public int getStartingDefenderArmyCount() {
            return startingDefenderArmyCount;
        }

        public int getEndingAttackerArmyCount() {
            return endingAttackerArmyCount;
        }

        public int getEndingDefenderArmyCount() {
            return endingDefenderArmyCount;
        }

        /**
         * @return +1 for attacker win, -1 for defender win
         */
        public int result(){
            return endingDefenderArmyCount - endingAttackerArmyCount < 0 ? 1 : -1;
        }

        public String toString() {
            return asKey() + "=" + result();
        }

        public String asKey(){
            // the combination of armies forms a unique key in our matrix
            return startingAttackerArmyCount + "/" + startingDefenderArmyCount;
        }

        private void fight() {

            endingAttackerArmyCount = startingAttackerArmyCount;
            endingDefenderArmyCount = startingDefenderArmyCount;

            while(endingAttackerArmyCount > 0 && endingDefenderArmyCount > 0){

                int[] attackerDice = dice(endingAttackerArmyCount > 3 ? 3 : endingAttackerArmyCount);
                int[] defenderDice = dice(endingDefenderArmyCount > 2 ? 2 : endingDefenderArmyCount);

                int result = compare(attackerDice, defenderDice);

                if(/* one off each */ result == 0){
                    endingAttackerArmyCount--;
                    endingDefenderArmyCount--;
                } else if (/* defender wins */ result < 0){
                    endingAttackerArmyCount += result;  // do + because of negative number
                } else /* attacker wins */ {
                    endingDefenderArmyCount -= result;
                }
            }
        }

        /**
         * compare the dice rolls of the attacker vs. the defender
         *
         * @returns zero when tie (one off each)
         * @returns negative number when defender wins (one or two off attacker)
         * @returns positive number when attacker wins (one or two off defender)
         */
        private int compare(int[] attacker, int[] defend) {

            int tally = 0;

            for (int i = 0; i < defend.length; i++) {
                if (defend[i] < attacker[i])
                    ++tally;
                else if (defend[i] >= attacker[i])
                    --tally;

                //when attacker has 1 die against more than one defending dice
                if (attacker.length == 1)
                    break;
            }

            return tally;
        }

        private int[] dice(int count) {
            int[] dice = new int[count];
            for (int i = 0; i < count; i++)
                dice[i] = d6();
            Arrays.sort(dice);    //ascending order

            int[] reverse = new int[count];
            for (int i = (count - 1), j = 0; i >= 0; i--, j++)
                reverse[j] = dice[i];
            return reverse;    //descending order
        }

        private int d6() {
            int roll = random.nextInt(7);
            if (roll == 0)
                roll = d6();
            return roll;
        }
    }
}