lovinghusbandpuzzle.LoHusbState.java Source code

Java tutorial

Introduction

Here is the source code for lovinghusbandpuzzle.LoHusbState.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package lovinghusbandpuzzle;

import cm3038.search.ActionStatePair;
import cm3038.search.State;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.math3.util.Combinations;

/**
 *
 * @author Georgi
 */
public class LoHusbState implements State {

    public char[] couplesN;//people on the north side
    public char[] couplesS;//people on the south side
    /**
     * The location of the raft as defined in the {@link RiverBank} enumerated
     * type.
     */
    public RiverBank raftLocation;

    /**
     * The state needs 3 parameters - arrays for people on the north and south,
     * and the location of the raft .
     *
     * @param couplesN people on the north side
     * @param couplesS people on the south side
     * @param raftLocation
     */
    public LoHusbState(char[] couplesN, char[] couplesS, RiverBank raftLocation) {
        this.couplesN = Arrays.copyOf(couplesN, couplesN.length);
        this.couplesS = Arrays.copyOf(couplesS, couplesS.length);
        this.raftLocation = raftLocation;
    }

    /**
     * This method returns a string representation of the state with the people
     * on both sides and the raft location.
     *
     * @return a string representation of the current state
     */
    @Override
    public String toString() {
        String result = "";
        for (int i = 0; i < couplesN.length; i++) {
            //if the array value is '0' means that it is unoccupied
            //i.e. a person can be moved there if needed
            if (couplesN[i] != '0') {
                result += couplesN[i] + "";
            }
        }
        if (raftLocation == RiverBank.NORTH) {
            result += "  Raft";
        }
        result += "\n--------\n";
        for (int i = 0; i < couplesS.length; i++) {
            if (couplesS[i] != '0') {
                result += couplesS[i] + "";
            }
        }
        if (raftLocation == RiverBank.SOUTH) {
            result += " Raft";
        }
        return result;
    }

    /**
     * This method finds all the valid successors of the current state.
     *
     * @return a list containing the valid children of the current state
     */
    @Override
    public List<ActionStatePair> successor() {
        List<ActionStatePair> result = new ArrayList<>();
        if (this.isInvalid()) //if current state is invalid
        {
            return result; //return an empty set
        }
        //an array of all of the people, that are on the side of the raft
        //therefore they will be moved and create children
        ArrayList<Character> toMove = new ArrayList<>();
        for (int j = 0; j < couplesN.length; j++) {
            if (raftLocation == RiverBank.NORTH) {//if the raft is on the north
                if (couplesN[j] != '0') {//people from the north are added to the arraylist
                    toMove.add(couplesN[j]);
                }
            } else {
                if (couplesS[j] != '0') {//if the raft is on the south
                    toMove.add(couplesS[j]);//people from the north are added to the arraylist
                }
            }
        }
        //start with moving a number of people as the raft size
        //until it gets to 1(moving 1 person)
        for (int i = LovingHusbandProblem.raftSize; i > 0; i--) {
            if (toMove.size() < i) {
                //if the people that need to be moved are less than the raft size
                //then i starts with the size of the arraylist
                i = toMove.size();
            }
            //make a new combination every time
            Combinations comb = new Combinations(toMove.size(), i);
            Iterator<int[]> iterator = comb.iterator();
            while (iterator.hasNext()) //while there is a combination left
            {
                int[] selected = iterator.next();//get the next one
                char[] temp = new char[i];//make a temporary array
                int count = 0;
                for (int index : selected) //Go through elements in the "selected" array. Each one is an index into the original array.
                {
                    temp[count] = toMove.get(index);//fill the temp array with combination people
                    count++;
                }
                LoHusbAction action = new LoHusbAction(temp, oppositeBank(this.raftLocation));
                if (action.peopleMoved.length > 0) {
                    LoHusbState nextState = this.applyAction(action);
                    if (!nextState.isInvalid()) {
                        result.add(new ActionStatePair(action, nextState));
                    }
                }

            }
        }
        return result;
    }

    /**
     *
     * @param current position of the raft
     * @return opposite side of the river raft
     */
    private RiverBank oppositeBank(RiverBank current) {
        if (current == RiverBank.NORTH) {
            return RiverBank.SOUTH;
        }
        return RiverBank.NORTH;
    }

    /**
     * This method basically replaces the '0' on one side with the values from
     * the action. Then it finds the people that it has already added on the
     * opposite side and replaces them with '0'.
     *
     * @param action
     * @return the new state after the applied action
     */
    public LoHusbState applyAction(LoHusbAction action) {
        int count = 0;//use count to iterate through the people from the action
        LoHusbState newState = new LoHusbState(couplesN, couplesS, raftLocation);
        if (action.toBank == RiverBank.NORTH) {//if we are moving to the north
            newState.raftLocation = RiverBank.NORTH;//new raft loc is north
            for (int i = 0; i < newState.couplesN.length; i++) {
                if (newState.couplesN[i] == '0' && (count < action.peopleMoved.length)) {
                    //going through the peopleMoved from action and putting them
                    //in their new place
                    newState.couplesN[i] = action.peopleMoved[count];
                    count++;
                }
            }
            count = 0;//now replace the added values in the north, with '0'
            //in the south(because they still exist there)
            for (int i = 0; i < newState.couplesS.length; i++) {
                if (count < action.peopleMoved.length) {
                    if (newState.couplesS[i] == action.peopleMoved[count]) {
                        newState.couplesS[i] = '0';
                        count++;
                    }
                }
            }
            count = 0;
        } else {
            //do exactly the same thing if we are moving to the south
            newState.raftLocation = RiverBank.SOUTH;
            for (int i = 0; i < newState.couplesS.length; i++) {
                if (newState.couplesS[i] == '0' && count < action.peopleMoved.length) {
                    newState.couplesS[i] = action.peopleMoved[count];
                    count++;
                }
            }
            count = 0;//now replace them with '0'
            for (int i = 0; i < newState.couplesN.length; i++) {
                if (count < action.peopleMoved.length) {
                    if ((newState.couplesN[i] == action.peopleMoved[count])) {
                        newState.couplesN[i] = '0';
                        count++;
                    }
                }
            }
            count = 0;
        }
        return newState;
        //On first look the method may seem long and complicated, but it is 
        //actually simple, it does almost the same things a few times.
        //Depending on the collection chosen to represent the problem it could
        //be shorter i.e. if using arraylist, there are easier method instead of loops
    }

    /**
     * This method checks if 2 states are the same. It sorts the arrays first,
     * because Arrays.sort does take in mind order of the elements inside.
     *
     * @param state other state
     * @return true/false
     */
    @Override
    public boolean equals(Object state) {
        if (!(state instanceof LoHusbState)) //if the given parameter is not a McState
        {
            return false; //it must be false
        }
        LoHusbState other = (LoHusbState) state;
        Arrays.sort(this.couplesN);
        Arrays.sort(this.couplesS);
        Arrays.sort(other.couplesN);
        Arrays.sort(other.couplesS);
        return Arrays.equals(this.couplesN, other.couplesN) && Arrays.equals(this.couplesS, other.couplesS)
                && this.raftLocation == other.raftLocation;
    }

    /**
     * The method goes through the couples and finds on which side they are on.
     * If they are on the same side - all OK, if not then check if there is a
     * man on the same side as the wife, if there is then it is invalid. If it
     * passes all of the checks, then the state is okay.
     *
     * @return true/false
     */
    public boolean isInvalid() {
        for (char c : LovingHusbandPuzzle.marriages.keySet()) {//use marriage map to find all of the people
            char temp = LovingHusbandPuzzle.marriages.get(c);//get every woman - c is a man and temp woman
            boolean inNorthH = false, inNorthW = false;//the side of a husband and wife
            for (int i = 0; i < couplesN.length; i++) {
                if (couplesN[i] == c) {//find side of the husband
                    inNorthH = true;
                }
            }
            for (int i = 0; i < couplesN.length; i++) {
                if (couplesN[i] == temp) {//find side of the wife
                    inNorthW = true;
                }
            }
            if (inNorthW != inNorthH) {
                //if the sides are different then check the side of the wife
                //to see if there are any other men there, if there are then it's invalid
                if (inNorthW == true) {
                    for (char husb : LovingHusbandPuzzle.marriages.keySet()) {
                        for (int i = 0; i < couplesN.length; i++) {
                            if (couplesN[i] == husb) {//a husband is found
                                return true;
                            }
                        }
                    }
                } else {
                    for (char husb : LovingHusbandPuzzle.marriages.keySet()) {
                        for (int i = 0; i < couplesS.length; i++) {
                            if (couplesS[i] == husb) {
                                return true;
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    /**
     * Computes the hash code based on the ASCII value of the letters(people)
     * The people's values on the north are multiplied by 5 to have bigger
     * difference between the buckets.
     *
     * @return
     */
    @Override
    public int hashCode() {
        int hash = 0;
        for (int i = 0; i < this.couplesN.length; i++) {
            if (couplesN[i] != '0') {
                hash += ((int) couplesN[i]) * 5;
            }
        }
        for (int i = 0; i < this.couplesS.length; i++) {
            if (couplesS[i] != '0') {
                hash += ((int) couplesS[i]);
            }
        }
        if (this.raftLocation == RiverBank.NORTH) {
            hash += 1;
        }
        return hash;
    }

}