import java.io.*; import java.util.Vector; public class MastermindSolve { public static void main( String[] args ) throws IOException { BufferedReader rdr = new BufferedReader( new InputStreamReader( System.in )); String str = null; System.out.print( "Enter puzzle: " ); str = rdr.readLine(); Board b = new Board( str.toCharArray() ); Solver s = new Solver( b ); s.solve(); } } class Board { public static final int NUM_CHOICES = 6; public static final int NUM_SLOTS = 4; private char[] answerAttr = new char[NUM_SLOTS]; private int[] answerCharsAttr = new int[NUM_CHOICES]; private int[] inputChars = new int[NUM_CHOICES]; public Board() { for (int i=0; i < NUM_SLOTS; i++) answerAttr[i] = (char) ('a' + (int) (Math.random()*1000) % NUM_CHOICES); for (int i=0; i < NUM_SLOTS; i++) answerCharsAttr[ answerAttr[i] - 'a' ]++; } public Board( char[] puzzle ) { answerAttr = puzzle; for (int i=0; i < NUM_SLOTS; i++) answerCharsAttr[ answerAttr[i] - 'a' ]++; } public void evaluate( char[] guess, int[] response ) { evaluate( answerAttr, answerCharsAttr, guess, response ); } public void evaluate( char[] answer, int[] answerChars, char[] guess, int[] response ) { for (int i=0; i < NUM_CHOICES; i++) inputChars[i] = 0; response[0] = response[1] = 0; for (int i=0; i < NUM_SLOTS; i++) inputChars[ guess[i] - 'a' ]++; for (int i=0; i < NUM_SLOTS; i++) if (guess[i] == answer[i]) response[0]++; for (int i=0; i < NUM_CHOICES; i++) response[1] += (inputChars[i] < answerChars[i] ? inputChars[i] : answerChars[i]); response[1] = response[1] - response[0]; } } class OutParameterStruct { char next; boolean carry; } class Slot { private StringBuffer choices = new StringBuffer(); private int next; public Slot() { for (int i=0; i < Board.NUM_CHOICES; i++) choices.append( (char)('a'+i) ); } public char reset() { next = 0; return choices.charAt( next ); } public void getNext( OutParameterStruct s ) { s.carry = false; if (++next == choices.length()) { next = 0; s.carry = true; } s.next = choices.charAt(next); } public void remove( char ch ) { int i; for (i=0; i < choices.length(); i++) if (choices.charAt(i) == ch) break; if (i < choices.length()) choices.deleteCharAt( i ); } public void report() { for (int i=0; i < choices.length(); i++) System.out.print( choices.charAt(i) ); System.out.print( " " ); } public char get() { return choices.charAt(next); } } class TurnStruct { public char[] guess; public int black, white; public TurnStruct( char[] g, int[] ans ) { guess = new char[Board.NUM_SLOTS]; for (int i=0; i < Board.NUM_SLOTS; i++) guess[i] = g[i]; black = ans[0]; white = ans[1]; } } class Solver { private Board b; private Slot[] slots = new Slot[Board.NUM_SLOTS]; private Vector history = new Vector(); private State current; private int[] answerChars = new int[Board.NUM_CHOICES]; private int[] response = new int[2]; private char[] next = new char[Board.NUM_SLOTS]; private OutParameterStruct s = new OutParameterStruct(); private int notConsistent = 0; public Solver( Board bored ) { b = bored; for (int i=0; i < Board.NUM_SLOTS; i++) slots[i] = new Slot(); current = GuessOne.inst(); } public void setCurrent( State s ) { current = s; } public void solve() { while ( ! current.solve( this )); } public boolean evaluate( char[] guess, int[] response ) { b.evaluate( guess, response ); for (int i=0; i < guess.length; i++) System.out.print( guess[i] ); System.out.println( " " + response[0] + " " + response[1] ); history.add( new TurnStruct( guess, response ) ); return response[0] == Board.NUM_SLOTS; } public void eliminate( char gone ) { System.out.println( " eliminating " + gone ); for (int j=0; j < Board.NUM_SLOTS; j++) slots[j].remove( gone ); } public boolean applyRules( char[] guess, int[] response ) { if ( ! ruleOne( guess, response )) ruleTwo( guess, response ); return ruleThree( guess, response ); } public boolean ruleOne( char[] guess, int[] response ) { if (response[0] + response[1] == 0) { for (int i=0; i < guess.length; i++) eliminate( guess[i] ); return true; } else return false; } public boolean ruleTwo( char[] guess, int[] response ) { if (response[0] == 0) { for (int i=0; i < guess.length; i++) { System.out.println( " removing " + guess[i] + " from position " + (i+1) ); slots[i].remove( guess[i] ); } return true; } else return false; } public boolean ruleThree( char[] guess, int[] response ) { if (response[0] + response[1] == Board.NUM_SLOTS) { GuessPermute.inst().setState( guess ); current = GuessPermute.inst(); return true; } else return false; } public boolean isConsistent( char[] a ) { for (int j=0; j < Board.NUM_CHOICES; j++) answerChars[j] = 0; for (int j=0; j < Board.NUM_SLOTS; j++) answerChars[ a[j] - 'a' ]++; for (int i=0; i < history.size(); i++) { TurnStruct t = (TurnStruct) history.elementAt(i); b.evaluate( a, answerChars, t.guess, response ); if (response[0] != t.black || response[1] != t.white) { notConsistent++; return false; } } return true; } public void reportNotConsistent() { System.out.println( " possible guesses eliminated were " + notConsistent ); notConsistent = 0; } public void reportSlots() { System.out.print( "reportSlots - " ); for (int i=0; i < Board.NUM_SLOTS; i++) System.out.print(slots[i].get()); System.out.println(); } public char[] first_count_string() { for (int i=0; i < Board.NUM_SLOTS; i++) next[i] = slots[i].reset(); return next; } public char[] next_count_string() { int x = 0; slots[x].getNext( s ); next[x] = s.next; while (s.carry) { slots[++x].getNext( s ); next[x] = s.next; if (x == Board.NUM_SLOTS-1 && s.carry) return null; } return next; } } interface State { boolean solve( Solver wrapper ); } class GuessOne implements State { private static GuessOne me = new GuessOne(); public static GuessOne inst() { return me; } public boolean solve( Solver wrapper ) { char first = (char)('a' + ((int)(Math.random() * 29)) % Board.NUM_CHOICES); // char first = 'b'; char second = (char)('a' + (first+1-'a') % Board.NUM_CHOICES); char[] guess = new char[Board.NUM_SLOTS]; int i; for (i=0; i < Board.NUM_SLOTS / 2; i++) guess[i] = first; for ( ; i < Board.NUM_SLOTS; i++) guess[i] = second; int[] answer = new int[2]; if (wrapper.evaluate( guess, answer )) return true; // If rule 3 sets the current state to GuessPermute, then don't set the // current state to GuessTwo, but do return false to indicate that the // puzzle has not yet been solved if (wrapper.applyRules( guess, answer )) return false; // Pass on to the next state: the next random letter, and the total of black // plus white GuessTwo.inst().setState( (char)('a' + (second+1-'a') % Board.NUM_CHOICES), answer[0] + answer[1] ); wrapper.setCurrent( GuessTwo.inst() ); return false; } } class GuessTwo implements State { private char first; private int firstTotal; private static GuessTwo me = new GuessTwo(); public static GuessTwo inst() { return me; } public void setState( char n, int total ) { first = n; firstTotal = total; } public boolean solve( Solver wrapper ) { char second = (char)('a' + (first+1-'a') % Board.NUM_CHOICES); char[] guess = new char[Board.NUM_SLOTS]; int i; for (i=0; i < Board.NUM_SLOTS / 2; i++) guess[i] = first; for ( ; i < Board.NUM_SLOTS; i++) guess[i] = second; int[] answer = new int[2]; if (wrapper.evaluate( guess, answer )) return true; if (wrapper.applyRules( guess, answer )) return false; if (firstTotal + answer[0] + answer[1] == Board.NUM_SLOTS) { for (int j=0; j < Board.NUM_CHOICES - 4; j++) wrapper.eliminate( (char)('a' + (second+1+j-'a') % Board.NUM_CHOICES) ); } wrapper.setCurrent( GuessCompute.inst() ); return false; } } class GuessCompute implements State { private static GuessCompute me = new GuessCompute(); public static GuessCompute inst() { return me; } public boolean solve( Solver wrapper ) { char[] a; int[] answer = new int[2]; a = wrapper.first_count_string(); while (true) { if (a == null) return true; if (wrapper.isConsistent( a )) { wrapper.reportNotConsistent(); if (wrapper.evaluate( a, answer )) return true; if (wrapper.ruleOne( a, answer ) || wrapper.ruleTwo( a, answer )) { a = wrapper.first_count_string(); continue; } if (wrapper.ruleThree( a, answer )) return false; } a = wrapper.next_count_string(); } } } class GuessPermute implements State { private int[] answer = new int[2]; private char[] a; private static GuessPermute me = new GuessPermute(); public static GuessPermute inst() { return me; } public void setState( char[] ans ) { a = ans; } public boolean solve( Solver wrapper ) { sort(); while (true) { if (wrapper.isConsistent( a )) { wrapper.reportNotConsistent(); if (wrapper.evaluate( a, answer )) return true; } next_permutation_string(); } } private boolean next_permutation_string() { char t; if (a.length < 2) return false; for (int i=a.length-1, ii, j; ; ) { ii = i--; if (a[i] < a[ii]) { j = a.length; while ( ! (a[i] < a[--j])) { } t=a[i]; a[i] = a[j]; a[j] = t; reverse( ii, a.length-1 ); return true; } if (i == 0) { reverse( 0, a.length-1 ); return false; } } } private void sort() { // Shell sort int n = a.length; char t; for (int g = n/2; g > 0; g /= 2) for (int i = g; i < n; i++) for (int j = i-g; j >= 0; j -= g) if (a[j] > a[j+g]) { t = a[j]; a[j] = a[j+g]; a[j+g] = t; } } private void reverse( int b, int e ) { int m = (b + e + 1) / 2; char t; for (int i=b, j=e; i < m; i++, j--) { t = a[i]; a[i] = a[j]; a[j] = t; } } }