// GameFlow.java // // smart panel governing the movements of the game // ---------------------------------------------------------------------- // part of ArgumentGame (simulation of a rational argument) // game written for/designed by Prof. Ron Loui, loui@ai.cs.wustl.edu // based on the paper "An Argument Game" by Ronald Loui and Wiliam Chen // based on the game "LMNOP" // (see Loui-Norman-Stiefvater-Merrill-Olson-Costello [92] // code by Nina Kang, nkang@husc.harvard.edu, 8/96 import java.awt.*; import java.util.*; public class GameFlow extends Panel { // structures belonging to the Game, for convenience' sake static ArgumentGame game; static CardDeck deck; static PanelCollection panels; static Contract contract; Brain brain; // the intelligence behind the panel ArgumentChoice myArgument; // both players' arguments // layout devices TextArea instructions; // instructions from computer TextArea input; // input from user Panel bPanel; // displays buttons (challenge, argue, done) ResourcesLayout rPanel; // displays resources, burden OptionsLayout oPanel; // displays options (ordered evidence, etc) EvidenceLayout fPanel; // displays evidence ClaimLayout cPanel; // displays claim String Vtext; // the text of Vladimir's argument String Etext; // ditto for Estragon GameFlow (ArgumentGame g) { game = g; deck = g.deck; panels = g.panels; setLayout (new GridLayout(2, 1)); Panel a; add (a = new Panel()); a.setLayout(new FlowLayout(FlowLayout.LEFT)); instructions = new TextArea ("Contract decided.\n", 4, 30); instructions.appendText ("Click \"Done\" to start play."); instructions.setEditable(false); a.add (instructions); rPanel = new ResourcesLayout (game); a.add (rPanel); oPanel = new OptionsLayout (game); a.add (oPanel); Panel b; add (b = new Panel()); b.setLayout (new FlowLayout(FlowLayout.LEFT)); input = new TextArea ("Type input here", 4, 23); input.setEditable (true); input.selectAll(); b.add (input); cPanel = new ClaimLayout (game); b.add (cPanel); fPanel = new EvidenceLayout (game); b.add (fPanel); bPanel = new Panel(); bPanel.setLayout (new GridLayout(3, 1)); bPanel.add (new Button ("Argue")); bPanel.add (new Button ("Challenge")); bPanel.add (new Button ("Done")); b.add (bPanel); } public void paint (Graphics g) { panels.bgRect (bounds(), g, Color.white); } public void getContract(Contract c) { contract = c; fPanel.getContract (c); rPanel.getContract (c); cPanel.getContract (c); oPanel.getContract (c); brain = new Brain(game); myArgument = new ArgumentChoice (game); game.bottom.add ("Choice", myArgument); } public void addToResources (int increment) { contract.addToResources(increment); rPanel.repaint(); } public void subtractFromResources (int i) { contract.subtractFromResources (i); rPanel.repaint(); } public Dimension minimumSize() { return new Dimension (488, 170); } public Dimension preferredSize() { return minimumSize(); } // HANDLE CARD // preliminary error-checking before passing information to brain public void handleCard (Case stack, String partOfStack) { if (!brain.startPlay) return; if ((stack != null) && (!stack.exhausted) && (((partOfStack.equals ("Optional")) && (stack.numOptional < stack.optional.length)) || ((partOfStack.equals ("Excluding") && (stack.excluding != null) && (stack.numExcluding < stack.excluding.length))))) { if ((brain.currMove == brain.V_ARGUE) || (brain.currMove == brain.E_ARGUE)) brain.A_handleCard (stack, partOfStack); else if ((brain.currMove == brain.V_CHALLENGE) || (brain.currMove == brain.E_CHALLENGE)) brain.C_handleCard (stack, partOfStack); } else instructions.setText ("Exhausted stack or part of stack."); } // ACTION-- pass button-clicks on to the Brain public boolean action (Event evt, Object arg) { if ((evt != null) && (evt.target instanceof Button)) { Case.allCasesExhausted(); // just check this every time if ("Done".equals (arg)) { if (!brain.startPlay) brain.initGame(); else if ((brain.currMove == brain.V_ARGUE) || (brain.currMove == brain.E_ARGUE)) brain.endArgueTurn(); else if ((brain.currMove == brain.V_CHALLENGE) || (brain.currMove == brain.E_CHALLENGE)) brain.endChallengeTurn(); else if (brain.currMove == brain.NO_MOVE) brain.nextMove(); } else if (!brain.startPlay) return false; else if ("Argue".equals (arg)) brain.handleArgue(); else if ("Challenge".equals (arg)) brain.handleChallenge(); } return true; } // GET ARGUMENT-- first step in parsing a player's argument public boolean getArgument (int gameState) { if ((gameState != brain.E_ARGUE) && (gameState != brain.V_ARGUE)) throw new IllegalArgumentException(); boolean success = true; char[][] argument; int argumentIndex; char[] inputString; int lastNewline; char[] temp; char[][] bigTemp; argument = new char[10][]; argumentIndex = 0; if (gameState == brain.E_ARGUE) // save text for typing Etext = input.getText(); else Vtext = input.getText(); inputString = input.getText().toCharArray(); int lastNewLine = 0; int i = 0; for (i = 0; i < inputString.length; i++) { if ((i > lastNewLine) && (inputString[i] == '\n')) { // break argument up into lines temp = new char[i-lastNewLine]; System.arraycopy (inputString, lastNewLine, temp, 0, i-lastNewLine); lastNewLine = i + 1; // reallocate new array of lines, if necessary if (argumentIndex >= argument.length) { bigTemp = argument; argument = new char[bigTemp.length*2][]; System.arraycopy (bigTemp, 0, argument, 0, bigTemp.length); } // add new line to array of lines argument[argumentIndex] = temp; argumentIndex++; } } if (i >= lastNewLine) { // step back, grab last line temp = new char[i-lastNewLine]; System.arraycopy (inputString, lastNewLine, temp, 0, i-lastNewLine); // reallocate new array of lines, if necessary if (argumentIndex >= argument.length) { bigTemp = argument; argument = new char[bigTemp.length*2][]; System.arraycopy (bigTemp, 0, argument, 0, bigTemp.length); } // add new line to array of lines argument[argumentIndex] = temp; argumentIndex++; } // trim array down to size bigTemp = argument; argument = new char[argumentIndex][]; System.arraycopy (bigTemp, 0, argument, 0, argumentIndex); // make a tree of the argument try { if (gameState == brain.E_ARGUE) { myArgument.makeRoot (argument, game.contract.Estragon()); } else { myArgument.makeRoot (argument, game.contract.Vladimir()); } } catch (IllegalArgumentException e) { success = false; if (gameState == brain.E_ARGUE) myArgument.Eroot = null; else myArgument.Vroot = null; myArgument.errorMsg = e.getMessage(); instructions.setText (myArgument.errorMsg + "\nTry again."); } return (success); } } // ------------------ CLASS: BRAIN --------------------------------------- // contains the state of the game as it is played // class Brain { static ArgumentGame game; // the game and some important structures static Case[] cases; static Contract contract; static GameFlow summary; // game state boolean startPlay; // has the game started? Case caseInQuestion; // what case is being challenged? Case lastBadCase; int decisionInQuestion; // the decision-value of said case (Case.decision) int resourcePot; // amt of resources in jeopardy boolean hitLiveCard; // has a live card been turned over in this turn int lastMove; // the last move made int currMove; // move in progress // possible moves static final int V_CHALLENGE = 0; // can move up or down one in this array static final int E_ARGUE = 1; // (0 - 3) for a valid next move. static final int V_ARGUE = 2; static final int E_CHALLENGE = 3; static final int WAIT_JUDGE = 4; // game in stasis static final int NO_MOVE = 5; // next move not yet selected by player Brain (ArgumentGame g) { game = g; cases = g.cases; contract = g.contract; summary = g.flow; startPlay = false; lastMove = NO_MOVE; currMove = NO_MOVE; decisionInQuestion = -1; caseInQuestion = null; resourcePot = 0; hitLiveCard = false; } // HANDLE DONE ---------------------------------------------------------- // functions handling the click of the "done" button void initGame() { startPlay = true; decisionInQuestion = contract.establish; summary.input.setText(""); summary.instructions.setText (contract.Vladimir() + " must argue "+ CardDeck.textCard(decisionInQuestion)+".\n"+ "Click \"Argue\", select cards, enter\n" + "argument below, then click \"Done\"."); } void nextMove() { // if player clicks "done" twice in a row, prompt the next player to move. if ((lastMove == V_ARGUE) || (lastMove == V_CHALLENGE)) summary.instructions.setText (contract.Estragon()); else // lastMove == E_ARGUE, E_CHALLENGE, NO_MOVE summary.instructions.setText (contract.Vladimir()); summary.instructions.appendText (", you must click \"Argue\"\n"); if ((lastMove == E_ARGUE) || (lastMove == V_ARGUE)) summary.instructions.appendText ("or \"Challenge\""); summary.instructions.appendText (" to start play."); } // // ArgumentGame section 2.5: // // A sufficient response for the declarer consists of... // being able to cite an undefeated argument for the claim of the contract. // // A sufficient response for the defender consists of... // being able to cite an undefeated counterargument to // the declarer's argument for the claim of the contract. void endArgueTurn() { // if it is the end of an arguing turn... boolean validArgument = summary.getArgument (currMove); if (!validArgument) return; // screen out all failed moves // if the last player failed to come up with a good rebuttal, // the opponent wins. if ((currMove == V_ARGUE) && (!summary.myArgument.sufficientDeclarerResponse())) game.noRebuttal (contract.Estragon()); if ((currMove == E_ARGUE) && (!summary.myArgument.sufficientDefenderResponse())) game.noRebuttal (contract.Vladimir()); game.cl2.show (game.bottom, "Choice"); lastMove = currMove; currMove = NO_MOVE; // fiddle with resources if (resourcePot != 0) { if (hitLiveCard) { // free search, with results-- do nothing } else if (currMove == E_ARGUE) { summary.addToResources(resourcePot); } else if (currMove == V_ARGUE) { summary.subtractFromResources(resourcePot); } resourcePot = 0; } decisionInQuestion = -1; // prompt next player if (lastMove == E_ARGUE) summary.instructions.setText (contract.Vladimir()); else summary.instructions.setText (contract.Estragon()); summary.instructions.appendText (", your turn \n(argue/challenge)."); } // // ArgumentGame section 2.5: // // A sufficient response could also consist of... // challenging a challengeable card used in one of the opponent's // counterarguments. void endChallengeTurn() { // if it's a valid challenge, pass it on. if ((caseInQuestion != null) && (summary.myArgument.challengeIsValid (caseInQuestion.decision, currMove))){ lastMove = currMove; currMove = NO_MOVE; decisionInQuestion = caseInQuestion.decision; if (lastMove== E_CHALLENGE) { summary.instructions.setText(contract.Vladimir()); } else { summary.instructions.setText(contract.Estragon()); } summary.instructions.appendText (", you must argue " +CardDeck.textCard(decisionInQuestion) + "\nor back down and pay for search." + "\n(click \"Argue\")."); } else { // bad challenge-- instigator loses if (lastMove == E_CHALLENGE) game.badChallenge (contract.Estragon()); else game.badChallenge (contract.Vladimir()); } } // HANDLE CARD---------------------------------------------------------- // when the user has just clicked on a stack of cards // Argument Game section 2.5 says // * Flipping in response to a challenge must stop as soon as a sufficient // response can be made. // handle the card when it is an argument void A_handleCard (Case stack, String partOfStack) { if ((currMove == V_ARGUE) && (contract.resources <= resourcePot + 1)) { game.noResources(); return; } boolean meetingChallenge = false; if ((decisionInQuestion != -1) && (CardDeck.equivalent (decisionInQuestion, stack.decision))) meetingChallenge = true; if (meetingChallenge) { resourcePot++; } else if (currMove == V_ARGUE) { summary.subtractFromResources(1); } else if (currMove == E_ARGUE) { summary.addToResources(1); } stack.drawNewCard(partOfStack); if (stack.lastCardLive(partOfStack)) { hitLiveCard = true; // if the flipping was in response to a challenge if (meetingChallenge) summary.instructions.setText ("You have met your challenge, and\n"+ "technically, should enter your\n"+ "argument and click \"Done\" now."); } } // SOMEONE IS CHALLENGING // print info on card void C_handleCard (Case stack, String partOfStack) { summary.instructions.setText (CardDeck.textCard(stack.decision)); // if this is a valid challenge, save stack if (summary.myArgument.challengeIsValid (stack.decision, currMove)) { caseInQuestion = stack; summary.instructions.appendText (" is being challenged"); } else if ((lastBadCase != null) && (lastBadCase == stack)) { summary.instructions.appendText (" is not valid,\n" +"but it's your funeral."); } else { // does not appear summary.instructions.appendText (" is not a valid challenge."); } } // HANDLE CHALLENGE ----------------------------------------------- void handleChallenge () { if (!startPlay) return; // does nothing until game has begun if (currMove != NO_MOVE) { summary.instructions.setText ("Click \"Done\" to finish turn"); // on the first turn, vladimir must argue } else if (lastMove == NO_MOVE) { summary.instructions.setText (contract.Vladimir() + "must argue claim."); // if the last move was a challenge, cannot rechallenge. } else if ((lastMove == V_CHALLENGE) || (lastMove == E_CHALLENGE)) { summary.instructions.setText ("Cannot challenge a challenge."); // if the last move was vladimir, it must be estragon who challenges. } else if (lastMove == V_ARGUE) { if (canChallenge (summary.myArgument.Vroot, E_CHALLENGE)) { caseInQuestion = null; decisionInQuestion = -1; currMove = E_CHALLENGE; summary.instructions.setText(game.contract.Estragon()+":\n"+ "click the card you'll challenge,\n"+ "then \"Done\"."); game.cl2.show (game.bottom, "Cases"); } else { summary.instructions.setText(game.contract.Estragon()+": Sorry,"+ game.contract.Vladimir()+ "'s tree can't\nbe challenged.\n"+ "Click \"Argue\" to argue."); } // if estragon argued last, it must be vladimir who challenges. } else if (lastMove == E_ARGUE) { if (canChallenge (summary.myArgument.Eroot, V_CHALLENGE)){ caseInQuestion = null; decisionInQuestion = -1; currMove = V_CHALLENGE; summary.instructions.setText(game.contract.Vladimir()+":\n"); summary.instructions.appendText("click the card you'll challenge,\n"); summary.instructions.appendText("then \"Done\"."); game.cl2.show (game.bottom, "Cases"); } else { summary.instructions.setText(game.contract.Vladimir()+": Sorry,"+ game.contract.Estragon()+ "'s tree can't\nbe challenged.\n"+ "Click \"Argue\" to argue."); } } } boolean canChallenge (CaseNode root, int move) { // checks to see if a decision is challengeable if ((root.myCase != null) && (summary.myArgument.challengeIsValid (root.myCase.decision, move))) { return true; } else if (root.children == null) { return false; } else { for (int i = 0; i < root.children.length; i++) { if (canChallenge (root.children[i], move)) return true; } } return false; } // HANDLE ARGUMENT ---------------------------------------------------- void handleArgue() { if (!startPlay) return; if (currMove != NO_MOVE) { if ((currMove == E_ARGUE) || (currMove == E_CHALLENGE)) summary.instructions.setText (contract.Estragon()); else summary.instructions.setText (contract.Vladimir()); summary.instructions.appendText(",\nclick \"Done\" to finish your turn"); // if the last player was vladimir, the new player is estragon. } else if ((lastMove == V_CHALLENGE) || (lastMove == V_ARGUE)) { game.cl2.show (game.bottom, "Cases"); hitLiveCard = false; currMove = E_ARGUE; summary.instructions.setText (contract.Estragon()+": "); if (lastMove == V_CHALLENGE) { summary.instructions.appendText ("("+ CardDeck.textCard (decisionInQuestion) +") "); } summary.instructions.appendText ("uncover cards,\n"); summary.instructions.appendText ("enter argument, click \"Done\"."); if (summary.Etext != null) summary.input.setText (summary.Etext); else summary.input.setText(""); // if the last player was estragon, the new player is vladimir. } else if ((lastMove == E_CHALLENGE) || (lastMove == E_ARGUE) || (lastMove == NO_MOVE)) { // first move-- v by default game.cl2.show (game.bottom, "Cases"); hitLiveCard = false; currMove = V_ARGUE; summary.instructions.setText (contract.Vladimir()+":\n"); if (lastMove == E_CHALLENGE) { summary.instructions.appendText ("("+ CardDeck.textCard (decisionInQuestion) +") "); } summary.instructions.appendText ("click cards to form argument,\n"); summary.instructions.appendText ("type in argument, click \"Done\"."); if (summary.Vtext != null) summary.input.setText (summary.Vtext); else summary.input.setText(""); } } }