CSCI1130 Assignment 3 2015 Fall (Work even with variable number of peg per guess)

DO NOT COPY THIS SOLUTION! 

Only take it as a reference while doing your homework!

This solution make use of recursion to allow variable number of for loop:

package game;

import java.util.ArrayList;
import java.util.List;

/**
 * MasterMind solution searching, with a given game state/ log. Students should
 * work on this file. The input game state filename is fixed already. We shall
 * make use of another given complete class MasterMindGameState. Remark: Colors
 * are expressed by MasterMindGameState.pegColors.
 * <br>
 * Student Name: Ling Leong
 * <br>
 * Student ID : 1155062557
 * <br>
 * Date : 06-10-2015
 * <br>
 * Declaration : I declare that the assignment here submitted is original except
 * for the source material explicitly acknowledged, and that the same or closely
 * related material has not been previously submitted for another course. I also
 * acknowledge that I am aware of University policy and regulations on honesty
 * in academic work, and of the disciplinary guidelines and procedures
 * applicable to breaches of such policy and regulations, as contained in the
 * website.
 *
 * University Guideline on Academic Honesty:
 * http://www.cuhk.edu.hk/policy/academichonesty/ Faulty of Engineering
 * Guidelines to Academic Honesty:
 * http://ww.erg.cuhk.edu.hk/erg-intra/upload/documents/ENGG_Discipline.pdf
 * <br>
 *
 * @author Michael FUNG, CSE TA's and Ling Leong
 * @since Oct 2015
 */
public class MasterMind {

    /**
     * Suggested method for checking if a permutation is consistent with an
     * attempt
     */
    /**
     * Print MasterMind game state
     *
     * @param aGame is a MasterMindGameState object
     */
    public static void printGameState(MasterMindGameState aGame) {
        System.out.println("There are " + aGame.getNumberOfGuesses() + " attempts in the game:");
        for (int i = 0; i < aGame.getNumberOfGuesses(); i++) {
            System.out.print("Attempt " + i + ": [" + aGame.getAttempt(i) + "]");
            System.out.print("  -");
            for (int peg = 0; peg < aGame.numberOfPegsPerGuess; peg++) {
                System.out.print("  " + aGame.getPeg(i, peg));
            }
            System.out.println("   with " + aGame.getNumberOfBlackPins(i) + " Bingos");
        }
    }

    /**
     * Main method, starting point of the program
     *
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        MasterMindGameState mmg = new MasterMindGameState("MasterMindGame1.txt");

        printGameState(mmg);

        mmg.display();

        System.out.println("Searching for compatible solution(s)...");

        //init the number of solution to the maximum possible number of solution
       // int maxSolutionAmount = (int) Math.pow(mmg.getNumberOfPossiblePegColors(), MasterMindGameState.numberOfPegsPerGuess);

        int numberOfSolutions = 0;

        List<PegPattern> possibleSolutionList = new ArrayList<PegPattern>(); //
        List<PegPattern> solutionList = new ArrayList<PegPattern>();
        List<PegPattern> attemptList = new ArrayList<PegPattern>();

        /*for (int i = 0; i < mmg.getNumberOfPossiblePegColors(); i++) {
         for (int j = 0; j < mmg.getNumberOfPossiblePegColors(); j++) {
         for (int k = 0; k < mmg.getNumberOfPossiblePegColors(); k++) {
         for (int l = 0; l < mmg.getNumberOfPossiblePegColors(); l++) {
         possibleSolutionList.add(new PegPattern(i, j, k, l));
         }
         }

         }
         }*/
        
        //generate all possible combination
        ArrayList<Integer> indexes = new ArrayList<>();
        int numberOfLoop = MasterMindGameState.numberOfPegsPerGuess;
        int currentLoop = 0;
        generateCombination(possibleSolutionList, mmg, indexes, numberOfLoop, currentLoop);

        //add each guess to an list
        for (int i = 0; i < mmg.getNumberOfGuesses(); i++) {
            attemptList.add(new PegPattern(mmg.getAttempt(i), mmg.getNumberOfBlackPins(i)));

        }

        //test each combination to see if it is a possible combination
        for (PegPattern possibleSolution : possibleSolutionList) {
            if (verifyPegPattern(possibleSolution, attemptList) == true) {
                solutionList.add(possibleSolution);
                System.out.println(possibleSolution);
                numberOfSolutions++;
            }
        }

        System.out.println(numberOfSolutions + " solution(s) found.");
    }

    /**
     * Generate all possible combination, given the number of peg per guess and the possible colors
     * 
     * p.s. This is implemented with recursion, to be able to have a variable number of nested for loop , in case of the number of peg per guess is a variable
     * 
     * @param possibleSolutionList a list to contain the possible solution
     * @param mmg MasterMindGameState object for knowing number of peg per guess and the possible colors
     * @param indexes a list of for loop indexes
     * @param numberOfLoop number of nested for loop to run
     * @param currentLoop indicate the current loop level
     */
    public static void generateCombination(List<PegPattern> possibleSolutionList, MasterMindGameState mmg, ArrayList<Integer> indexes, int numberOfLoop, int currentLoop) {

        //terminal condition
        if (currentLoop >= numberOfLoop) {
            return;
        } else {
            //if the indexes list if empty, fill it with zero
            if (indexes.isEmpty()) {
                for (int i = 0; i < numberOfLoop; i++) {
                    indexes.add(0);
                }
            }
            // start the looping
            for (int i = 0; i < mmg.getNumberOfPossiblePegColors(); i++) {
                //update the for loop params accordingly
                indexes.set(currentLoop, i);
                //if the current loop is the deepest loop, generate the pattern
                if (currentLoop == numberOfLoop - 1) {
                    PegPattern peg = new PegPattern((ArrayList<Integer>) indexes.clone());
                    possibleSolutionList.add(peg);

                } else {
                    //else start a new nest loop
                    generateCombination(possibleSolutionList, mmg, indexes, numberOfLoop, currentLoop + 1);
                }
            }
        }
    }

    /**
     * Assume the pattern is the solution and check it against a list of attempt, check whether the pattern is a possible solution
     * 
     * @param pattern
     * @param attemptList
     * @return true if it is a possible solution, false if it is not
     */
    public static boolean verifyPegPattern(PegPattern pattern, List<PegPattern> attemptList) {
        boolean isSolution = true;

        for (PegPattern attempt : attemptList) {

            if (checkAttempt(attempt, pattern) != attempt.getBlackPinAmount()) {
                isSolution = false;
                return isSolution;
            }

        }

        return isSolution;
    }
    
    /**
    * Compare two PegPattern, check how many black pin the attempt will get 
    * 
    * @param attempt
    * @param solution
    * @return 
    */
    public static int checkAttempt(PegPattern attempt, PegPattern solution) {
        int blackPinAmount = 0;

        for (int i = 0; i < attempt.getPegChars().size(); i++) {
            if (attempt.getPegChars().get(i).equals(solution.getPegChars().get(i))) {
                blackPinAmount++;
            }
        }

        return blackPinAmount;

    }

}

/**
 * A simple data class for storing Peg's pattern
 * 
 * @author Ling Leong
 */
 class PegPattern {

    private List<Integer> pegs = new ArrayList<Integer>();
    private List<Character> pegChars = new ArrayList<>();

    private int blackPinAmount = 0;
    private boolean isAttempt = false;

    /**
     * Create a PegPattern with a list of int, where each int indicates the location of the char in MasterMindGameState.pegColors
     * Use this for generated pattern
     * 
     * @param pegs 
     */
    PegPattern(List<Integer> pegs) {
        final String colors = MasterMindGameState.pegColors;
        this.pegs = pegs;
        for (int peg : pegs) {
            pegChars.add(colors.charAt(peg));
        }
    }

    /**
     * Create a PegPattern with a string of pattern and the number of black pin
     * Use this for attempt
     * @param pegs pegs pattern in string
     * @param blackPinAmount number of black pin
     */
    PegPattern(String pegs, int blackPinAmount) {

        for (int i = 0; i < pegs.length(); i++) {
            pegChars.add(pegs.charAt(i));
        }
        isAttempt = true;
        this.blackPinAmount = blackPinAmount;

    }

    /**
     * Convert the peg pattern to string
     * 
     * @return the pattern in string
     */
    @Override
    public String toString() {

        final String colors = MasterMindGameState.pegColors;
        String target = "";
        for (int peg : pegs) {
            target += Character.toString(colors.charAt(peg));
        }
        return target;

    }

    /**
     * @return number of black pin
     */
    public int getBlackPinAmount() {
        return blackPinAmount;
    }

    /**
     * @param blackPinAmount 
     */
    public void setBlackPinAmount(int blackPinAmount) {
        this.blackPinAmount = blackPinAmount;
    }

    /**
     * @return peg pattern as a list of characters
     */
    public List<Character> getPegChars() {
        return pegChars;
    }

}