package com.gregstoll.cluesolver.client;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.ImageBundle;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SourcesTabEvents;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.user.client.ui.TabListener;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Tree;
import com.google.gwt.user.client.ui.TreeItem;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONValue;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Entry point classes define onModuleLoad().
*/
public class ClueSolver implements EntryPoint {
public static final String[][] internalNames = {{"ProfessorPlum", "ColonelMustard", "MrGreen", "MissScarlet", "MsWhite", "MrsPeacock"},
{"Knife", "Candlestick", "Revolver", "LeadPipe", "Rope", "Wrench"},
{"Hall", "Conservatory", "DiningRoom", "Kitchen", "Study", "Library", "Ballroom", "Lounge", "BilliardRoom"}};
public static final String[][] externalNames = {{"Professor Plum", "Colonel Mustard", "Mr. Green", "Miss Scarlet", "Ms. White", "Mrs. Peacock"},
{"Knife", "Candlestick", "Revolver", "Lead Pipe", "Rope", "Wrench"},
{"Hall", "Conservatory", "Dining Room", "Kitchen", "Study", "Library", "Ballroom", "Lounge", "Billiard Room"}};
public ArrayList playerNames = new ArrayList();
public VerticalPanel namesPanel = null;
public HashMap internalNameToClueStateWidgetMap = new HashMap();
public boolean infoAdded = false;
private ArrayList playerListBoxes = new ArrayList();
private ArrayList numPlayersButtons = new ArrayList();
public static final String scriptName = "clue.cgi";
public String curSessionString = null;
public Tree clauseInfoTree = new Tree();
public Label warningLabel = null;
private boolean validNumberOfCards = true;
private ListBox refutingCard = null;
private ArrayList workingLabels = new ArrayList();
private ArrayList actionButtons = new ArrayList();
private ListBox undoHistoryBox = null;
private Grid simulationTable = null;
private Label gameStateLabel = null;
private TextBox loadGameBox = null;
private ArrayList playerRadioButtons = null;
public Label consistentLabel = null;
/**
* Aggregate the images
*/
public interface Images extends ClueStateWidget.Images {
AbstractImagePrototype rainbowpalette();
}
private Images images = (Images) GWT.create(Images.class);
/*private static class TestPopup extends PopupPanel {
public TestPopup(String s) {
super(true);
HTML contents = new HTML(s);
contents.setWidth("128px");
setWidget(contents);
setStyleName("ks-popups-Popup");
}
}*/
public void addActionToHistory(String description, String state) {
undoHistoryBox.addItem(description, description + "|" + state);
}
private String internalToExternalName(String internalName) {
for (int i = 0; i < internalNames.length; ++i) {
for (int j = 0; j < internalNames[i].length; ++j) {
if (internalName.equals(internalNames[i][j])) {
return externalNames[i][j];
}
}
}
if (internalName == "None") {
return "None/Unknown";
}
return "???(" + internalName + ")";
}
private String getPlayerName(int index) {
if (index >= 0 && index < playerNames.size()) {
return (String) playerNames.get(index);
}
if (index == -1) {
return "None";
}
if (index == playerNames.size()) {
return "Solution (case file)";
}
return "???";
}
private static class ConfirmDialog extends DialogBox implements ClickListener {
private ClueSolver solver;
private ConfirmHandler handler;
public ConfirmDialog(String title, String text, ClueSolver _solver, ConfirmHandler _handler) {
solver = _solver;
handler = _handler;
Button okButton = new Button("OK", this);
Button cancelButton = new Button("Cancel", this);
setText(title);
DockPanel dock = new DockPanel();
HorizontalPanel buttonPanel = new HorizontalPanel();
buttonPanel.add(okButton);
buttonPanel.add(cancelButton);
dock.add(buttonPanel, DockPanel.SOUTH);
dock.add(new HTML(text), DockPanel.NORTH);
setWidget(dock);
}
public void onClick(Widget sender) {
String text = ((Button) sender).getText();
if (text.equals("OK")) {
handler.doAction(solver);
}
hide();
}
}
public void handleNewInfo(String body) {
if (infoAdded == false) {
// We have real info now, so don't change the number of players!
infoAdded = true;
for (int i = 0; i < numPlayersButtons.size(); ++i) {
RadioButton button = ((RadioButton) numPlayersButtons.get(i));
button.setEnabled(false);
}
// disable changing number of cards
for (int i = 0; i < playerNames.size(); ++i) {
((NameSuggestPanel) namesPanel.getWidget(i)).setNumCardsEnabled(false);
}
}
// Reset the refuting card to be None to try to prevent errors.
refutingCard.setSelectedIndex(0);
JSONObject response = JSONParser.parse(body).isObject();
double errorStatus = response.get("errorStatus").isNumber().getValue();
if (errorStatus != 0.0) {
Window.alert("Internal error - error returned from script - " + response.get("errorText").isString().toString());
} else {
setGameState(response.get("session").isString().stringValue());
JSONArray newInfos = response.get("newInfo").isArray();
int numElements = newInfos.size();
for (int i = 0; i < numElements; ++i) {
JSONObject curInfo = newInfos.get(i).isObject();
String card = curInfo.get("card").isString().stringValue();
int status = (int) curInfo.get("status").isNumber().getValue();
JSONArray ownerArray = curInfo.get("owner").isArray();
int[] owners = new int[ownerArray.size()];
for (int j = 0; j < owners.length; ++j) {
owners[j] = (int) ownerArray.get(j).isNumber().getValue();
}
getStateWidget(card).setState(status, owners);
}
clauseInfoTree.removeItems();
if (response.containsKey("clauseInfo")) {
JSONObject clauseInfoObj = response.get("clauseInfo").isObject();
for (int i = 0; i < playerNames.size(); ++i) {
if (clauseInfoObj.containsKey(new Integer(i).toString())) {
JSONArray playerClauseArray = clauseInfoObj.get(new Integer(i).toString()).isArray();
TreeItem playerClauseInfo = new TreeItem(playerNames.get(i) + " has:");
for (int j = 0; j < playerClauseArray.size(); ++j) {
JSONArray curClause = playerClauseArray.get(j).isArray();
StringBuffer clauseBuffer = new StringBuffer();
for (int k = 0; k < curClause.size(); ++k) {
clauseBuffer.append(internalToExternalName(curClause.get(k).isString().stringValue()));
// FFV - order these nicely?
if (k != curClause.size() - 1) {
clauseBuffer.append(" or ");
}
}
playerClauseInfo.addItem(clauseBuffer.toString());
}
clauseInfoTree.addItem(playerClauseInfo);
playerClauseInfo.setState(true);
}
}
}
if (response.containsKey("isConsistent")) {
boolean isConsistent = response.get("isConsistent").isBoolean().booleanValue();
setIsConsistent(isConsistent);
}
}
}
CgiResponseHandler newInfoHandler = new CgiResponseHandler() {
public void onSuccess(String body) {
handleNewInfo(body);
setWorking(false);
}
public void onError(Throwable ex) {
Window.alert("Internal error - unable to contact backend - " + ex.getMessage());
setWorking(false);
}
};
CgiResponseHandler loadHandler = new CgiResponseHandler() {
public void onSuccess(String body) {
// If we got here than the session is valid.
// Set the number of players and their number of cards.
JSONObject response = JSONParser.parse(body).isObject();
int numPlayers = (int) response.get("numPlayers").isNumber().getValue();
setNumberOfPlayers(numPlayers, false);
JSONArray numCards = response.get("numCards").isArray();
for (int i = 0; i < numPlayers; ++i) {
((NameSuggestPanel) namesPanel.getWidget(i)).setDefaultNumCards((int) numCards.get(i).isNumber().getValue());
}
handleNewInfo(body);
// We can't undo a load so clear the history.
undoHistoryBox.clear();
setWorking(false);
}
public void onError(Throwable ex) {
Window.alert("Internal error - unable to contact backend - " + ex.getMessage());
setWorking(false);
}
};
CgiResponseHandler simulateInfoHandler = new CgiResponseHandler() {
public void onSuccess(String body) {
JSONObject response = JSONParser.parse(body).isObject();
double errorStatus = response.get("errorStatus").isNumber().getValue();
if (errorStatus != 0.0) {
Window.alert("Internal error - error returned from script - " + response.get("errorText").isString().toString());
} else {
JSONObject simulateData = response.get("simData").isObject();
int[] offsets = {2, 9, 16};
for (int i = 0; i < offsets.length; ++i) {
for (int j = 0; j < internalNames[i].length; ++j) {
JSONArray dataArray = simulateData.get(internalNames[i][j]).isArray();
double total = 0.0;
for (int k = 0; k < dataArray.size(); ++k) {
total += dataArray.get(k).isNumber().getValue();
}
for (int k = 0; k < dataArray.size(); ++k) {
double percent;
if (total > 0.0) {
percent = (dataArray.get(k).isNumber().getValue() * 100) / total;
} else {
percent = 0.0;
}
percent = ((int) (percent * 10)) / 10.0;
double percentTo255 = percent * (255.0/100.0);
int red = 0;
int green = 0;
int blue = 0;
if (percentTo255 <= 64) {
green = (int) (percentTo255 * (255.0/64.0));
blue = 255;
} else if (percentTo255 <= 128) {
green = 255;
blue = (int) ((128 - percentTo255) * (255.0/64.0));
} else if (percentTo255 <= 192) {
red = (int) ((percentTo255 - 128) * (255.0/64.0));
green = 255;
} else {
red = 255;
blue = (int) ((255 - percentTo255) * (255.0/64.0));
}
simulationTable.setHTML(offsets[i] + j, k + 1, "" + Double.toString(percent) + "%");
}
}
}
}
setWorking(false);
}
public void onError(Throwable ex) {
Window.alert("Internal error - unable to contact backend - " + ex.getMessage());
setWorking(false);
}
};
public void setValidNumberOfCards(boolean valid) {
validNumberOfCards = valid;
warningLabel.setVisible(!valid);
}
public void setIsConsistent(boolean isConsistent) {
consistentLabel.setVisible(!isConsistent);
// TODO - do more?
}
public void setWorking(boolean working) {
if (!working) {
for (int i = 0; i < workingLabels.size(); ++i) {
((Label) workingLabels.get(i)).setVisible(false);
}
} else {
for (int i = 0; i < workingLabels.size(); ++i) {
Label workingLabel = (Label) workingLabels.get(i);
workingLabel.setVisible(true);
workingLabel.setText("Working...");
}
}
// Enable or disable the buttons.
for (int i = 0; i < actionButtons.size(); ++i) {
Button curButton = (Button) actionButtons.get(i);
curButton.setEnabled(!working);
}
}
/**
* This is the entry point method.
*/
public void onModuleLoad() {
ClueStateWidget.solver = this;
playerNames.add("Player 1");
playerNames.add("Player 2");
playerNames.add("Player 3");
playerNames.add("Player 4");
playerNames.add("Player 5");
playerNames.add("Player 6");
VerticalPanel playerInfoPanel = new VerticalPanel();
playerInfoPanel.setHorizontalAlignment(VerticalPanel.ALIGN_LEFT);
playerInfoPanel.setVerticalAlignment(HorizontalPanel.ALIGN_TOP);
playerInfoPanel.add(new HTML("Number of players:"));
FlowPanel radioPanel = new FlowPanel();
playerRadioButtons = new ArrayList();
for (int i = 3; i <= 6; ++i) {
RadioButton cur = new RadioButton("numPlayers", new Integer(i).toString());
final int iFinal = i;
cur.addClickListener(new ClickListener() {
public void onClick(Widget sender) {
setNumberOfPlayers(iFinal, true);
}
});
if (i == 6) {
cur.setChecked(true);
}
playerRadioButtons.add(cur);
radioPanel.add(cur);
numPlayersButtons.add(cur);
}
playerInfoPanel.add(radioPanel);
namesPanel = new VerticalPanel();
namesPanel.setHorizontalAlignment(VerticalPanel.ALIGN_LEFT);
namesPanel.setVerticalAlignment(HorizontalPanel.ALIGN_TOP);
for (int i = 0; i < playerNames.size(); ++i) {
NameSuggestPanel nsp = new NameSuggestPanel((String) playerNames.get(i), i, this);
namesPanel.add(nsp);
}
playerInfoPanel.add(namesPanel);
warningLabel = new Label("Total number of cards must total 18!");
warningLabel.setStylePrimaryName("warning");
warningLabel.setVisible(false);
playerInfoPanel.add(warningLabel);
final ClueSolver clueSolver = this;
Button newGameButton = new Button("New game", new ClickListener() {
public void onClick(Widget sender) {
ConfirmDialog dialog = new ConfirmDialog("Confirm new game", "Are you sure you want to start a new game and wipe out all progress?
", clueSolver, new ConfirmHandler() {
public void doAction(ClueSolver solver) {
solver.startNewGame();
}
});
dialog.center();
}
});
actionButtons.add(newGameButton);
playerInfoPanel.add(newGameButton);
HorizontalPanel gameStateLabelPanel = new HorizontalPanel();
gameStateLabelPanel.add(new Label("Current game state (for loading later): "));
gameStateLabel = new Label();
gameStateLabelPanel.add(gameStateLabel);
playerInfoPanel.add(gameStateLabelPanel);
HorizontalPanel loadGamePanel = new HorizontalPanel();
loadGamePanel.add(new Label("Load game state: "));
loadGameBox = new TextBox();
loadGamePanel.add(loadGameBox);
playerInfoPanel.add(loadGamePanel);
Button loadGameButton = new Button("Load game", new ClickListener() {
public void onClick(Widget sender) {
final String gameToLoad = loadGameBox.getText().trim();
ConfirmDialog dialog = new ConfirmDialog("Confirm load", "Are you sure you want to load a game and wipe out all progress?
", clueSolver, new ConfirmHandler() {
public void doAction(ClueSolver solver) {
setWorking(true);
CgiHelper.doRequest(RequestBuilder.GET, scriptName, "sess=" + gameToLoad + "&action=fullInfo", loadHandler);
}
});
dialog.center();
}
});
actionButtons.add(loadGameButton);
playerInfoPanel.add(loadGameButton);
VerticalPanel gameInfoPanel = new VerticalPanel();
HorizontalPanel gameInfoMainPanel = new HorizontalPanel();
gameInfoMainPanel.setHorizontalAlignment(VerticalPanel.ALIGN_LEFT);
gameInfoMainPanel.setVerticalAlignment(HorizontalPanel.ALIGN_TOP);
Tree infoTree = new Tree();
TreeItem suspectTree = new TreeItem("Suspects");
for (int i = 0; i < internalNames[0].length; ++i) {
suspectTree.addItem(new ClueStateWidget(images, internalNames[0][i], externalNames[0][i]));
}
TreeItem weaponTree = new TreeItem("Weapons");
for (int i = 0; i < internalNames[1].length; ++i) {
weaponTree.addItem(new ClueStateWidget(images, internalNames[1][i], externalNames[1][i]));
}
TreeItem roomTree = new TreeItem("Rooms");
for (int i = 0; i < internalNames[2].length; ++i) {
roomTree.addItem(new ClueStateWidget(images, internalNames[2][i], externalNames[2][i]));
}
infoTree.addItem(suspectTree);
infoTree.addItem(weaponTree);
infoTree.addItem(roomTree);
suspectTree.setState(true);
weaponTree.setState(true);
roomTree.setState(true);
gameInfoMainPanel.add(infoTree);
VerticalPanel enterInfoPanel = new VerticalPanel();
enterInfoPanel.add(new HTML("Enter new info:"));
TabPanel enterInfoTabs = new TabPanel();
VerticalPanel whoOwnsCardPanel = new VerticalPanel();
HorizontalPanel tempPanel1 = new HorizontalPanel();
tempPanel1.add(new HTML("Card: "));
final ListBox whichCardOwned = makeNewCardListBox(-1, false);
tempPanel1.add(whichCardOwned);
whoOwnsCardPanel.add(tempPanel1);
tempPanel1 = new HorizontalPanel();
tempPanel1.add(new HTML("Owned by: "));
final ListBox ownerOwned = makeNewPlayerListBox(false, true);
tempPanel1.add(ownerOwned);
whoOwnsCardPanel.add(tempPanel1);
Button whoOwnsSubmitButton = new Button("Add info", new ClickListener() {
public void onClick(Widget sender) {
setWorking(true);
addActionToHistory(internalToExternalName(listBoxValue(whichCardOwned)) + " owned by " + getPlayerName(Integer.parseInt(listBoxValue(ownerOwned))), curSessionString);
CgiHelper.doRequest(RequestBuilder.POST, scriptName, "sess=" + curSessionString + "&action=whoOwns&owner=" + listBoxValue(ownerOwned) + "&card=" + listBoxValue(whichCardOwned), newInfoHandler);
}
});
actionButtons.add(whoOwnsSubmitButton);
whoOwnsCardPanel.add(whoOwnsSubmitButton);
enterInfoTabs.add(whoOwnsCardPanel, "Who owns a card");
VerticalPanel suggestionMadePanel = new VerticalPanel();
tempPanel1 = new HorizontalPanel();
tempPanel1.add(new HTML("Made by: "));
final ListBox suggestingPlayer = makeNewPlayerListBox(false, false);
tempPanel1.add(suggestingPlayer);
suggestionMadePanel.add(tempPanel1);
tempPanel1 = new HorizontalPanel();
tempPanel1.add(new HTML("Suspect: "));
final ListBox card1 = makeNewCardListBox(0, false);
tempPanel1.add(card1);
suggestionMadePanel.add(tempPanel1);
tempPanel1 = new HorizontalPanel();
tempPanel1.add(new HTML("Weapon: "));
final ListBox card2 = makeNewCardListBox(1, false);
tempPanel1.add(card2);
suggestionMadePanel.add(tempPanel1);
tempPanel1 = new HorizontalPanel();
tempPanel1.add(new HTML("Room: "));
final ListBox card3 = makeNewCardListBox(2, false);
tempPanel1.add(card3);
suggestionMadePanel.add(tempPanel1);
tempPanel1 = new HorizontalPanel();
tempPanel1.add(new HTML("Refuted by: "));
final ListBox refutingPlayer = makeNewPlayerListBox(true, false);
tempPanel1.add(refutingPlayer);
suggestionMadePanel.add(tempPanel1);
tempPanel1 = new HorizontalPanel();
tempPanel1.add(new HTML("Refuting card: "));
refutingCard = makeNewCardListBox(-1, true);
tempPanel1.add(refutingCard);
ChangeListener updateRefutingCardListener = new ChangeListener() {
public void onChange(Widget widget) {
int originalSelectedIndex = refutingCard.getSelectedIndex();
// Clear out everything except for None/Unknown
while (refutingCard.getItemCount() > 1) {
refutingCard.removeItem(1);
}
// Add the possibilities to this listbox.
String internalCard1 = listBoxValue(card1);
refutingCard.addItem(internalToExternalName(internalCard1), internalCard1);
String internalCard2 = listBoxValue(card2);
refutingCard.addItem(internalToExternalName(internalCard2), internalCard2);
String internalCard3 = listBoxValue(card3);
refutingCard.addItem(internalToExternalName(internalCard3), internalCard3);
refutingCard.setSelectedIndex(originalSelectedIndex);
}
};
card1.addChangeListener(updateRefutingCardListener);
card2.addChangeListener(updateRefutingCardListener);
card3.addChangeListener(updateRefutingCardListener);
// Trigger a change to make the refuting card show up correctly at the
// beginning.
updateRefutingCardListener.onChange(null);
suggestionMadePanel.add(tempPanel1);
Button suggestionSubmitButton = new Button("Add info", new ClickListener() {
public void onClick(Widget sender) {
setWorking(true);
addActionToHistory(getPlayerName(Integer.parseInt(listBoxValue(suggestingPlayer))) + " suggested " + internalToExternalName(listBoxValue(card1)) + ", " + internalToExternalName(listBoxValue(card2)) + ", " + internalToExternalName(listBoxValue(card3)) + " - refuted by " + getPlayerName(Integer.parseInt(listBoxValue(refutingPlayer))) + " with card " + internalToExternalName(listBoxValue(refutingCard)), curSessionString);
CgiHelper.doRequest(RequestBuilder.POST, scriptName, "sess=" + curSessionString + "&action=suggestion&suggestingPlayer=" + listBoxValue(suggestingPlayer) + "&card1=" + listBoxValue(card1) + "&card2=" + listBoxValue(card2) + "&card3=" + listBoxValue(card3) + "&refutingPlayer=" + listBoxValue(refutingPlayer) + "&refutingCard=" + listBoxValue(refutingCard), newInfoHandler);
}
});
actionButtons.add(suggestionSubmitButton);
suggestionMadePanel.add(suggestionSubmitButton);
enterInfoTabs.add(suggestionMadePanel, "Suggestion made");
enterInfoTabs.selectTab(0);
enterInfoPanel.add(enterInfoTabs);
Label workingLabel = new Label();
workingLabel.setVisible(false);
workingLabels.add(workingLabel);
enterInfoPanel.add(workingLabel);
gameInfoMainPanel.add(enterInfoPanel);
DisclosurePanel clauseInfoPanel = new DisclosurePanel("Additional information", false);
clauseInfoPanel.add(clauseInfoTree);
consistentLabel = new Label("Game is no longer consistent!");
consistentLabel.setStylePrimaryName("warning");
consistentLabel.setVisible(false);
gameInfoPanel.add(consistentLabel);
gameInfoPanel.add(gameInfoMainPanel);
gameInfoPanel.add(clauseInfoPanel);
VerticalPanel undoHistoryPanel = new VerticalPanel();
Label undoHistoryLabel = new Label("List of information:");
undoHistoryPanel.add(undoHistoryLabel);
undoHistoryBox = new ListBox();
undoHistoryBox.setVisibleItemCount(15);
undoHistoryBox.addChangeListener(new ChangeListener() {
public void onChange(Widget widget) {
// Don't let them select anything but the bottom one, lest they
// think they can undo other things.
undoHistoryBox.setSelectedIndex(undoHistoryBox.getItemCount() - 1);
}
});
undoHistoryPanel.add(undoHistoryBox);
Button undoButton = new Button("Undo latest information", new ClickListener() {
public void onClick(Widget widget) {
// Get the new status
String descriptionAndState = undoHistoryBox.getValue(undoHistoryBox.getItemCount() - 1);
// There could be a | in the name but not in the description, so
// look for the last one.
setGameState(descriptionAndState.substring(descriptionAndState.lastIndexOf('|') + 1));
// Remove the last info from the listbox and get new status.
undoHistoryBox.removeItem(undoHistoryBox.getItemCount() - 1);
setWorking(true);
CgiHelper.doRequest(RequestBuilder.GET, scriptName, "sess=" + curSessionString + "&action=fullInfo", newInfoHandler);
}
});
actionButtons.add(undoButton);
undoHistoryPanel.add(undoButton);
VerticalPanel simulationPanel = new VerticalPanel();
Button simulateButton = new Button("Simulate", new ClickListener() {
public void onClick(Widget widget) {
setWorking(true);
CgiHelper.doRequest(RequestBuilder.GET, scriptName, "sess=" + curSessionString + "&action=simulate", simulateInfoHandler);
}
});
actionButtons.add(simulateButton);
simulationPanel.add(simulateButton);
Label workingLabel2 = new Label();
workingLabel2.setVisible(false);
workingLabels.add(workingLabel2);
simulationPanel.add(workingLabel2);
simulationPanel.add(new Label("Note that this will take 1-2 minutes."));
createSimulationTable();
HorizontalPanel tableAndImage = new HorizontalPanel();
tableAndImage.add(simulationTable);
tableAndImage.add(images.rainbowpalette().createImage());
simulationPanel.add(tableAndImage);
TabPanel tabs = new TabPanel();
tabs.add(playerInfoPanel, "Game Setup");
tabs.add(gameInfoPanel, "Game Info");
tabs.add(undoHistoryPanel, "Undo and History");
tabs.add(simulationPanel, "Simulation");
tabs.addTabListener(new TabListener() {
public boolean onBeforeTabSelected(SourcesTabEvents sender, int tabIndex) {
// Don't allow a switch if we're in an invalid state.
if (!validNumberOfCards) {
return false;
}
return true;
}
public void onTabSelected(SourcesTabEvents sender, int tabIndex) {
}
});
tabs.selectTab(0);
RootPanel.get("main").add(tabs);
// Get the state of the game.
setNumberOfPlayers(6, true);
}
public void createSimulationTable() {
if (simulationTable == null) {
simulationTable = new Grid(25, playerNames.size() + 2);
} else {
simulationTable.resize(25, playerNames.size() + 2);
}
simulationTable.setHTML(1, 0, "Suspects");
for (int i = 0; i < externalNames[0].length; ++i) {
simulationTable.setText(2+i, 0, externalNames[0][i]);
}
simulationTable.setHTML(8, 0, "Weapons");
for (int i = 0; i < externalNames[1].length; ++i) {
simulationTable.setText(9+i, 0, externalNames[1][i]);
}
simulationTable.setHTML(15, 0, "Rooms");
for (int i = 0; i < externalNames[2].length; ++i) {
simulationTable.setText(16+i, 0, externalNames[2][i]);
}
for (int i = 0; i < playerNames.size(); ++i) {
simulationTable.setText(0, 1+i, (String) playerNames.get(i));
}
simulationTable.setText(0, playerNames.size() + 1, "Solution");
}
public void setNumberOfPlayers(int numP, boolean startNewGame) {
// Fix the radio button if it needs fixing.
int numPAccordingToRadioButtons = -1;
for (int i = 0; i < playerRadioButtons.size(); ++i) {
if (((RadioButton) playerRadioButtons.get(i)).isChecked()) {
numPAccordingToRadioButtons = 3 + i;
}
}
if (numPAccordingToRadioButtons != numP) {
((RadioButton) playerRadioButtons.get(numP - 3)).setChecked(true);
}
int curNumP = playerNames.size();
int deltaNumP = numP - curNumP;
if (curNumP > numP) {
while (curNumP > numP) {
namesPanel.remove(namesPanel.getWidgetCount() - 1);
playerNames.remove(playerNames.size() - 1);
--curNumP;
}
} else if (curNumP < numP) {
while (curNumP < numP) {
playerNames.add("Player " + new Integer(curNumP + 1).toString());
namesPanel.add(new NameSuggestPanel((String) playerNames.get(curNumP), curNumP, this));
++curNumP;
}
}
// Update the number of cards
int[] numCards = new int[numP];
// There are 18 cards among the players.
int baseNumCards = 18 / numP;
int leftovers = 18 % numP;
for (int i = 0; i < numP; ++i) {
numCards[i] = (i < leftovers) ? (baseNumCards + 1) : baseNumCards;
}
for (int i = 0; i < numP; ++i) {
((NameSuggestPanel) namesPanel.getWidget(i)).setDefaultNumCards(numCards[i]);
}
// Update the list boxes.
for (int i = 0; i < playerListBoxes.size(); ++i) {
ListBox listBox = (ListBox) playerListBoxes.get(i);
int endCorrection = 0;
int startIndex = 0;
// See if we start with an extra item.
if (listBox.getValue(0).equals("-1")) {
startIndex = 1;
}
// See if we end with an extra item.
if (listBox.getValue(listBox.getItemCount() - 1).equals(new Integer(numP - deltaNumP).toString())) {
endCorrection = 1;
}
int currentNumInListBox = listBox.getItemCount() - startIndex;
if (deltaNumP > 0) {
for (int j = 0; j < deltaNumP; ++j) {
listBox.insertItem((String) playerNames.get(j+(currentNumInListBox-1)), new Integer(j+currentNumInListBox).toString(), (listBox.getItemCount() - 1) - endCorrection);
}
} else if (deltaNumP < 0) {
for (int j = 0; j > deltaNumP; --j) {
listBox.removeItem(listBox.getItemCount() - 1 - endCorrection);
}
}
// Fix the extra item (solution) value at the end, if there is one.
if (endCorrection == 1) {
listBox.setValue(listBox.getItemCount() - 1, new Integer(numP).toString());
}
}
// Update the simulation table
createSimulationTable();
if (startNewGame) {
doNewGameRequest();
}
}
public void checkTotalNumCards() {
int totalNumCards = 0;
for (int i = 0; i < playerNames.size(); ++i) {
totalNumCards += ((NameSuggestPanel) namesPanel.getWidget(i)).getNumCards();
}
if (totalNumCards != 18) {
setValidNumberOfCards(false);
} else {
setValidNumberOfCards(true);
doNewGameRequest();
}
}
public void doNewGameRequest() {
StringBuffer requestStringBuffer = new StringBuffer();
requestStringBuffer.append("action=new&players=" + playerNames.size());
for (int i = 0; i < playerNames.size(); ++i) {
int numCards = ((NameSuggestPanel) namesPanel.getWidget(i)).getNumCards();
requestStringBuffer.append("&numCards" + new Integer(i).toString() + "=" + numCards);
}
CgiHelper.doRequest(RequestBuilder.POST, scriptName, requestStringBuffer.toString(), new CgiResponseHandler() {
public void onSuccess(String body) {
JSONObject response = JSONParser.parse(body).isObject();
double errorStatus = response.get("errorStatus").isNumber().getValue();
if (errorStatus != 0.0) {
Window.alert("Internal error - error returned from script - " + response.get("errorText").isString().toString());
} else {
setGameState(response.get("session").isString().stringValue());
}
}
public void onError(Throwable ex) {
Window.alert("Internal error - unable to contact backend for new session - " + ex.getMessage());
}
});
}
public void startNewGame() {
undoHistoryBox.clear();
if (infoAdded == true) {
infoAdded = false;
for (int i = 0; i < numPlayersButtons.size(); ++i) {
RadioButton button = ((RadioButton) numPlayersButtons.get(i));
button.setEnabled(true);
}
// enable changing number of cards
for (int i = 0; i < playerNames.size(); ++i) {
((NameSuggestPanel) namesPanel.getWidget(i)).setNumCardsEnabled(true);
}
}
setNumberOfPlayers(playerNames.size(), true);
// Reset the widgets
Set stateWidgetKeys = internalNameToClueStateWidgetMap.entrySet();
for (Iterator it = stateWidgetKeys.iterator(); it.hasNext();) {
Map.Entry curEntry = (Map.Entry) it.next();
ClueStateWidget curWidget = (ClueStateWidget) curEntry.getValue();
curWidget.setState(ClueStateWidget.STATE_UNKNOWN, null);
}
}
public ClueStateWidget getStateWidget(String id) {
return (ClueStateWidget) internalNameToClueStateWidgetMap.get(id);
}
public static String listBoxValue(ListBox lb) {
return lb.getValue(lb.getSelectedIndex());
}
public ListBox makeNewCardListBox(int index, boolean includeNoneUnknown) {
ListBox toReturn = new ListBox();
if (includeNoneUnknown) {
toReturn.addItem("None/Unknown", "None");
}
if (index == -1) {
for (int i = 0; i < externalNames.length; ++i) {
for (int j = 0; j < externalNames[i].length; ++j) {
toReturn.addItem(externalNames[i][j], internalNames[i][j]);
}
}
} else {
for (int i = 0; i < externalNames[index].length; ++i) {
toReturn.addItem(externalNames[index][i], internalNames[index][i]);
}
}
return toReturn;
}
public ListBox makeNewPlayerListBox(boolean includeNone, boolean includeSolution) {
ListBox toReturn = new ListBox();
if (includeNone) {
toReturn.addItem("None", "-1");
}
for (int i = 0; i < playerNames.size(); ++i) {
toReturn.addItem((String) playerNames.get(i), new Integer(i).toString());
}
if (includeSolution) {
toReturn.addItem("Solution (case file)", new Integer(playerNames.size()).toString());
}
playerListBoxes.add(toReturn);
return toReturn;
}
public void changePlayerName(int index, String newName) {
playerNames.set(index, newName);
for (int i = 0; i < playerListBoxes.size(); ++i) {
ListBox listBox = ((ListBox) playerListBoxes.get(i));
// See if we start with an extra item.
int curIndex = index;
if (listBox.getValue(0).equals("-1")) {
++curIndex;
}
listBox.setItemText(curIndex, newName);
}
// Update the tooltips on the images in the ClueStateWidgets
Set stateWidgetKeys = internalNameToClueStateWidgetMap.entrySet();
for (Iterator it = stateWidgetKeys.iterator(); it.hasNext();) {
Map.Entry curEntry = (Map.Entry) it.next();
ClueStateWidget curWidget = (ClueStateWidget) curEntry.getValue();
curWidget.setImage();
}
// Update the simulation table.
simulationTable.setText(0, 1+index, newName);
}
public void setGameState(String sessionString) {
curSessionString = sessionString;
gameStateLabel.setText(sessionString);
}
}