// $Id: unicluec.java 53 2008-05-01 05:50:20Z edsko $ // $Source: /home/skochine/RCS/unicluec.java,v $ import java.io.*; class ClueSheet { private static String card2Index="S1S2S3S4S5S6W1W2W3W4W5W6R1R2R3R4R5R6R7R8R9"; private final int TOTAL_CARDS=21; // card status public final int csUNKNOWN=0; public final int csPRESENT=1; public final int csNOT_PRESENT=-1; int envelopeRoom; int envelopeSuspect; int envelopeWeapon; boolean envelopeContentsKnown=false; int P; int [][] scoreCard; public ClueSheet(int players) { P=players; scoreCard=new int[TOTAL_CARDS][P]; for (int c=0; c < TOTAL_CARDS; c++) { for (int p=0; p < P; p++) { scoreCard[c][p]=csUNKNOWN; } } envelopeRoom=-1; envelopeSuspect=-1; envelopeWeapon=-1; } int CardsPerPlayer(int player) { int C=0; switch (P) { case 3: C=6; break; case 4: switch (player) { case 1: case 2: C=5; break; default: C=4; break; } break; case 5: switch (player) { case 1: case 2: case 3: C=4; break; default: C=3; } break; case 6: C=3; break; } return C; } public static String GetCardName(int i) { return card2Index.substring(i*2,i*2 + 2); } public static int GetCardId(String c) { return card2Index.indexOf(c)/2; } public void DisplaySheet() { for (int c=0; c < TOTAL_CARDS; c++) { System.out.print(GetCardName(c) + " "); for (int p=0; p < P; p++) { switch (scoreCard[c][p]) { case csUNKNOWN: System.out.print(' '); break; case csPRESENT: System.out.print('O'); break; case csNOT_PRESENT: System.out.print('x'); break; default: } } System.out.println(); } System.out.println("(" + envelopeSuspect + "," + envelopeWeapon + "," + envelopeRoom + ")"); } public boolean GetCardFound(int card) { boolean found=false; for (int p=0; p < P; p++) { if (scoreCard[card][p] == csPRESENT) { found=true; break; // for (int p } } return found; } public void SetCardFound(int card, int player) { for (int p=0; p < P; p++) { scoreCard[card][p]=csNOT_PRESENT; } scoreCard[card][player - 1]=csPRESENT; } public void SetCardNotPresent(int card, int player) { scoreCard[card][player - 1]=csNOT_PRESENT; } public void LocateCardViaProcessOfElimination() { int cardsLocated; if (envelopeSuspect < 0) { cardsLocated=0; for (int c=0; c < 6; c++) { cardsLocated=GetCardFound(c) ? cardsLocated + 1 : cardsLocated; } if (cardsLocated == 5) { // the unlocated card must be in the envelope for (int c=0; c < 6; c++) { if (!GetCardFound(c)) { for (int p=1; p <= P; p++) { SetCardNotPresent(c,p); } envelopeSuspect=c + 1; /*D*/ // System.out.println("Suspect Located via Elimination: " + envelopeSuspect); break; // for (int c } } } } if (envelopeWeapon < 0) { cardsLocated=0; for (int c=6; c < 12; c++) { cardsLocated=GetCardFound(c) ? cardsLocated + 1 : cardsLocated; } if (cardsLocated == 5) { // the unlocated card must be in the envelope for (int c=6; c < 12; c++) { if (!GetCardFound(c)) { for (int p=1; p <= P; p++) { SetCardNotPresent(c,p); } envelopeWeapon=c - 5; /*D*/ // System.out.println("Weapon Located via Elimination: " + envelopeWeapon); break; // for (int c } } } } if (envelopeRoom < 0) { cardsLocated=0; for (int c=12; c < TOTAL_CARDS; c++) { cardsLocated=GetCardFound(c) ? cardsLocated + 1 : cardsLocated; } if (cardsLocated == 8) { // the unlocated card must be in the envelope for (int c=12; c < TOTAL_CARDS; c++) { if (!GetCardFound(c)) { for (int p=1; p <= P; p++) { SetCardNotPresent(c,p); } envelopeRoom=c - 11; /*D*/ // System.out.println("Room Located via Elimination: " + envelopeRoom); break; // for (int c } } } } } public void LocateCardViaNotPresent() { if (envelopeSuspect < 0) { for (int c=0; c < 6; c++) { boolean notPresentForAllPlayers=true; for (int p=0; p < P; p++) { notPresentForAllPlayers=(notPresentForAllPlayers && (scoreCard[c][p]==csNOT_PRESENT)); } if (notPresentForAllPlayers) { envelopeSuspect=c + 1; break; // for (int c } } } if (envelopeWeapon < 0) { for (int c=6; c < 12; c++) { boolean notPresentForAllPlayers=true; for (int p=0; p < P; p++) { notPresentForAllPlayers=(notPresentForAllPlayers && (scoreCard[c][p]==csNOT_PRESENT)); } if (notPresentForAllPlayers) { envelopeWeapon=c - 5; break; // for (int c } } } if (envelopeRoom < 0) { for (int c=12; c < TOTAL_CARDS; c++) { boolean notPresentForAllPlayers=true; for (int p=0; p < P; p++) { notPresentForAllPlayers=(notPresentForAllPlayers && (scoreCard[c][p]==csNOT_PRESENT)); } if (notPresentForAllPlayers) { envelopeRoom=c - 11; break; // for (int c } } } } public void SatisfyPlayer(int player) { int C; int notPresent; int present; int unknown; C=CardsPerPlayer(player); unknown=0; present=0; notPresent=0; for (int c=0; c < TOTAL_CARDS; c++) { switch (scoreCard[c][player - 1]) { case csUNKNOWN: unknown++; break; case csPRESENT: present++; break; case csNOT_PRESENT: notPresent++; break; } } // assert(unknown + present + notPresent == TOTAL_CARDS); // see if we can deduce the cards not present in players's hand if ((present == C) && (unknown > 0)) { // we can mark all other cards for this player as not present for (int c=0; c < TOTAL_CARDS; c++) { if (scoreCard[c][player - 1] == csUNKNOWN) { scoreCard[c][player - 1]=csNOT_PRESENT; } } notPresent=notPresent + unknown; unknown=0; } // see if we can deduce the cards present in player's hand if (unknown == (C - present)) { for (int c=0; c < TOTAL_CARDS; c++) { if (scoreCard[c][player - 1] == csUNKNOWN) { scoreCard[c][player - 1]=csPRESENT; } } present=present + unknown; unknown=0; } } public int GetCardHolder(int card) { int player=-1; for (int p=0; p < P; p++) { if (scoreCard[card][p] == csPRESENT) { player=p; break; // for (int p } } return player + 1; } public boolean GetEnvelopeContentsKnown() { return (envelopeRoom > -1) && (envelopeSuspect > -1) && (envelopeWeapon > -1); } public int GetLeastKnownCard(int c1, int c2, int stopAtPlayer) { int card=c1; int unknownCells=2*P; for (int c=c1; c <= c2; c++) { int n=0; for (int p=0; p < stopAtPlayer; p++) { switch (scoreCard[c][p]) { case csNOT_PRESENT: n++; break; case csPRESENT: n+=P; break; default: } } if (n < unknownCells) { card=c; unknownCells=n; } } return card; } public int AnyCardInHand(int player, int c1, int c2) { for (int c=c1; c <= c2; c++) { if (scoreCard[c][player - 1] == csPRESENT) { return c; } } return -1; } public int[] FormQuery(int room) { int card; int cardHolder=GetCardHolder(room + 11); int[] sw=new int[2]; sw[0]=(int)(Math.random()*6) + 1; sw[1]=(int)(Math.random()*6) + 1; // room card location known (in envelope); choose SW cards wisely to gain new info if (envelopeRoom == room) { sw[0]=GetLeastKnownCard(0,5,P) + 1; sw[1]=GetLeastKnownCard(6,11,P) - 5; } else if (cardHolder > 0) { // I hold this card; choose SW cards wisely to gain new info if (cardHolder == 1) { sw[0]=GetLeastKnownCard(0,5,P) + 1; sw[1]=GetLeastKnownCard(6,11,P) - 5; } // someone else holds the card, but I might be able to gain new information from a previous player else { sw[0]=GetLeastKnownCard(0,5,cardHolder) + 1; sw[1]=GetLeastKnownCard(6,11,cardHolder) - 5; } } // Don't know where the room card is, try to form a query that forces the location card out else { // Query for envelopeSuspect to guarantee no suspect alibis. // Failing that, query for a suspect card in my hand to guarantee no suspect alibis. // Otherwise, just try to get some information if (envelopeSuspect > 0) { sw[0]=envelopeSuspect; } else if ((card=AnyCardInHand(1,0,5)) >= 0) { sw[0]=card + 1; } else { sw[0]=GetLeastKnownCard(0,5,P) + 1; } // Query for envelopeWeapon to guarantee no weapon alibis. // Failing that, query for a weapon card in my hand to guarantee no weapon alibis. // Otherwise, just try to get some information if (envelopeWeapon > 0) { sw[1]=envelopeWeapon; } else if ((card=AnyCardInHand(1,6,11)) >= 0) { sw[1]=card - 5; } else { sw[1]=GetLeastKnownCard(6,11,P) - 5; } } return sw; } public int[] FormAccusation() { int[] sw=new int[3]; sw[0]=envelopeSuspect; sw[1]=envelopeWeapon; sw[2]=envelopeRoom; return sw; } public void ProcessResponse(int suspect,int weapon,int room,String s) { int p; p=s.charAt(0) - '0'; if (s.charAt(1) == '-') { SetCardNotPresent(suspect-1,p); SetCardNotPresent(weapon + 5,p); SetCardNotPresent(room + 11,p); } else { int c=GetCardId(s.substring(1)); SetCardFound(c,p); } SatisfyPlayer(p); /*D*/ //DisplaySheet(); } } class aout { public static void main(String[] args) throws IOException { ClueSheet clueSheet; int P; int room=-1; String s; int suspect=-1; String [] sa; BufferedReader stdin=new BufferedReader(new InputStreamReader(System.in)); int weapon=-1; // read configuration System.out.println('C'); System.out.flush(); s=stdin.readLine(); P=Integer.parseInt(s); clueSheet=new ClueSheet(P); s=stdin.readLine(); sa=s.split(" "); for (int i=0; i < sa.length; i++) { int c=ClueSheet.GetCardId(sa[i]); clueSheet.SetCardFound(c,1); // self is player 1 } clueSheet.SatisfyPlayer(1); /*D*/ //clueSheet.DisplaySheet(); // issue queries until envelope contents are known s=stdin.readLine(); do { int[] sw; room=Integer.parseInt(s); sw=clueSheet.FormQuery(room); System.out.println("Q" + sw[0] + sw[1] + room); System.out.flush(); // process server response s=stdin.readLine(); while (s.length() == 3) { clueSheet.ProcessResponse(sw[0],sw[1],room,s); s=stdin.readLine(); } clueSheet.LocateCardViaNotPresent(); clueSheet.LocateCardViaProcessOfElimination(); /*D*/ // clueSheet.DisplaySheet(); } while (!clueSheet.GetEnvelopeContentsKnown()); int[] swr; swr=clueSheet.FormAccusation(); System.out.println("A" + swr[0] + swr[1] + swr[2]); s=stdin.readLine(); } }