package apps.tiltyard;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import apps.common.GameSelector;
import apps.common.NativeUI;
import player.gamer.Gamer;
import util.game.Game;
import util.match.Match;
import util.reflection.ProjectSearcher;
import util.statemachine.Role;
import util.statemachine.StateMachine;
import util.statemachine.implementation.prover.ProverStateMachine;
/**
* Tiltyard is an application which allows you to quickly run a large number
* of matches of a single game between multiple players on the local machine.
* This can be used to understand which players are stronger than which other
* players on a particular game, and is a great tool for performing automated
* experiments on players you're developing.
*
* Since both players will be running on the same machine, they may run into
* resource contention issues: for example, both may attempt to use all of the
* available memory and processor cycles. Tiltyard does not prevent this: for
* best performance, ensure that at least one of the two players is a simple
* player (like RandomGamer, LegalGamer, or SimpleSearchLightGamer) that will
* not attempt to use the majority of the machine's resources. Ensuring that
* resource contention between multiple resource-intensive players is resolved
* fairly is well beyond the scope of Tiltyard.
*
* @author Sam Schreiber
*/
@SuppressWarnings("serial")
public final class Tiltyard extends JPanel implements ActionListener {
private static void createAndShowGUI(Tiltyard playerPanel) {
JFrame frame = new JFrame("General Gaming Tiltyard");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(1024, 768));
frame.getContentPane().add(playerPanel);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) throws IOException {
NativeUI.setNativeUI();
final Tiltyard tiltyardPanel = new Tiltyard();
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI(tiltyardPanel);
}
});
}
private final JButton runButton;
private final JTextField playClockTextField;
private final JTextField startClockTextField;
private final JTextField numRepsTextField;
private List<Class<?>> gamers = ProjectSearcher.getAllClassesThatAre(Gamer.class);
private List<JComboBox> playerBoxes;
private final JPanel playerBoxesPanel;
private final TiltyardEventsPanel eventsPanel;
private final GameSelector gameSelector;
private JComboBox getFreshPlayerComboBox() {
JComboBox newBox = new JComboBox();
List<Class<?>> gamersCopy = new ArrayList<Class<?>>(gamers);
for (Class<?> gamer : gamersCopy) {
Gamer g;
try {
g = (Gamer) gamer.newInstance();
// TODO: Come up with a more elegant way to exclude
// the HumanPlayer, which doesn't fit the Tiltyard model.
if(g.getName().equals("Human")) throw new RuntimeException();
newBox.addItem(g.getName());
} catch (Exception ex) {
gamers.remove(gamer);
}
}
newBox.setSelectedItem("Random");
return newBox;
}
public Tiltyard() {
super(new GridBagLayout());
// Create the game-selection controls
startClockTextField = new JTextField("30");
playClockTextField = new JTextField("15");
numRepsTextField = new JTextField("100");
// Create the player-selection controls
playerBoxes = new ArrayList<JComboBox>();
playerBoxesPanel = new JPanel(new GridBagLayout());
// Create the panel at the bottom with the buttons
JPanel buttonPanel = new JPanel(new FlowLayout());
runButton = new JButton(runButtonMethod());
runButton.setEnabled(false);
buttonPanel.add(runButton);
// Create the panel that shows the actual events
eventsPanel = new TiltyardEventsPanel();
gameSelector = new GameSelector();
int nGridRow = 0;
JPanel managerPanel = new JPanel(new GridBagLayout());
managerPanel.setBorder(new TitledBorder("Manager"));
managerPanel.add(new JLabel("Repository:"), new GridBagConstraints(0, nGridRow, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 5, 5));
managerPanel.add(gameSelector.getRepositoryList(), new GridBagConstraints(1, nGridRow++, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 5, 5));
managerPanel.add(new JLabel("Game:"), new GridBagConstraints(0, nGridRow, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 5, 5));
managerPanel.add(gameSelector.getGameList(), new GridBagConstraints(1, nGridRow++, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 5, 5));
managerPanel.add(new JSeparator(), new GridBagConstraints(0, nGridRow++, 2, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 5, 5));
managerPanel.add(new JLabel("Start Clock:"), new GridBagConstraints(0, nGridRow, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 5, 5));
managerPanel.add(startClockTextField, new GridBagConstraints(1, nGridRow++, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 5, 5));
managerPanel.add(new JLabel("Play Clock:"), new GridBagConstraints(0, nGridRow, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 5, 5));
managerPanel.add(playClockTextField, new GridBagConstraints(1, nGridRow++, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 5, 5));
managerPanel.add(new JLabel("Repetitions:"), new GridBagConstraints(0, nGridRow, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 5, 5));
managerPanel.add(numRepsTextField, new GridBagConstraints(1, nGridRow++, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 5, 5));
managerPanel.add(new JSeparator(), new GridBagConstraints(0, nGridRow++, 2, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 5, 5));
managerPanel.add(playerBoxesPanel, new GridBagConstraints(0, nGridRow++, 2, 1, 1.0, 1.0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 0, 0));
managerPanel.add(buttonPanel, new GridBagConstraints(1, nGridRow++, 1, 1, 1.0, 1.0, GridBagConstraints.SOUTH, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 0, 0));
JPanel gamesPanel = new JPanel(new GridBagLayout());
gamesPanel.setBorder(new TitledBorder("Tiltyard Games"));
gamesPanel.add(eventsPanel, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 5, 5));
this.add(managerPanel, new GridBagConstraints(0, 0, 1, 2, 0.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 5, 5));
this.add(gamesPanel, new GridBagConstraints(1, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 5, 5));
validate();
gameSelector.getGameList().addActionListener(this);
gameSelector.repopulateGameList();
}
private void runTiltyard() {
try {
List<Class<?>> thePlayers = new ArrayList<Class<?>>();
for(int i = 0; i < playerBoxes.size(); i++) {
thePlayers.add(gamers.get(playerBoxes.get(i).getSelectedIndex()));
}
int playClock = Integer.parseInt(playClockTextField.getText());
int startClock = Integer.parseInt(startClockTextField.getText());
int numReps = Integer.parseInt(numRepsTextField.getText());
Match theMatchModel = new Match("MatchID", startClock, playClock, theGame);
TiltyardManager theManager = new TiltyardManager(thePlayers, theMatchModel, gameName, numReps, eventsPanel);
theManager.start();
} catch (Exception e) {
e.printStackTrace();
}
}
private AbstractAction runButtonMethod() {
return new AbstractAction("Run Tiltyard") {
public void actionPerformed(ActionEvent evt) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
runTiltyard();
}
});
}
};
}
private String gameName;
private Game theGame;
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == gameSelector.getGameList()) {
theGame = gameSelector.getSelectedGame();
if (theGame == null) {
runButton.setEnabled(false);
return;
}
StateMachine stateMachine = new ProverStateMachine();
stateMachine.initialize(theGame.getRules());
List<Role> roles = stateMachine.getRoles();
int nRoles = roles.size();
while(playerBoxes.size() > nRoles) {
playerBoxes.remove(playerBoxes.size()-1);
}
while(playerBoxes.size() < nRoles) {
playerBoxes.add(getFreshPlayerComboBox());
}
List<Integer> currentSelections = new ArrayList<Integer>();
for(int i = 0; i < playerBoxes.size(); i++) {
currentSelections.add(playerBoxes.get(i).getSelectedIndex());
}
playerBoxesPanel.removeAll();
for(int i = 0; i < roles.size(); i++) {
playerBoxesPanel.add(new JLabel("Player " + (i+1) + " Type:"), new GridBagConstraints(0, i, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0));
playerBoxesPanel.add(playerBoxes.get(i), new GridBagConstraints(1, i, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
}
for(int i = 0; i < playerBoxes.size(); i++) {
playerBoxes.get(i).setSelectedIndex(currentSelections.get(i));
}
playerBoxesPanel.validate();
validate();
runButton.setEnabled(true);
}
}
}