add QTableFrame and clickable states that display a gui
- remove org.javaTuple in favour of org.apache.common for tuples and circleQueue - remove ViewListener from non-GUI Controller - stateActionTable saves the last 10 states that changed. They will get displayed in QTable Frame in JTextAreas
This commit is contained in:
@@ -26,24 +26,25 @@ public class LearningInfoPanel extends JPanel {
|
||||
private JCheckBox drawEnvironmentCheckbox;
|
||||
private JTextField learnMoreEpisodesInput;
|
||||
private JButton learnMoreEpisodesButton;
|
||||
private JButton showQTableButton;
|
||||
|
||||
public LearningInfoPanel(Learning learning, ViewListener viewListener){
|
||||
public LearningInfoPanel(Learning learning, ViewListener viewListener) {
|
||||
this.learning = learning;
|
||||
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
||||
policyLabel = new JLabel();
|
||||
discountLabel = new JLabel();
|
||||
delayLabel = new JLabel();
|
||||
if(learning instanceof Episodic){
|
||||
if(learning instanceof Episodic) {
|
||||
episodeLabel = new JLabel();
|
||||
add(episodeLabel);
|
||||
}
|
||||
delaySlider = new JSlider(0,1000, learning.getDelay());
|
||||
delaySlider = new JSlider(0, 1000, learning.getDelay());
|
||||
delaySlider.addChangeListener(e -> viewListener.onDelayChange(delaySlider.getValue()));
|
||||
add(policyLabel);
|
||||
add(discountLabel);
|
||||
if(learning.getPolicy() instanceof EpsilonPolicy){
|
||||
if(learning.getPolicy() instanceof EpsilonPolicy) {
|
||||
epsilonLabel = new JLabel();
|
||||
epsilonSlider = new JSlider(0, 100, (int)((EpsilonPolicy)learning.getPolicy()).getEpsilon() * 100);
|
||||
epsilonSlider = new JSlider(0, 100, (int) ((EpsilonPolicy) learning.getPolicy()).getEpsilon() * 100);
|
||||
epsilonSlider.addChangeListener(e -> viewListener.onEpsilonChange(epsilonSlider.getValue() / 100f));
|
||||
add(epsilonLabel);
|
||||
add(epsilonSlider);
|
||||
@@ -51,7 +52,7 @@ public class LearningInfoPanel extends JPanel {
|
||||
|
||||
toggleFastLearningButton = new JButton("Enable fast-learn");
|
||||
fastLearning = false;
|
||||
toggleFastLearningButton.addActionListener(e->{
|
||||
toggleFastLearningButton.addActionListener(e -> {
|
||||
fastLearning = !fastLearning;
|
||||
delaySlider.setEnabled(!fastLearning);
|
||||
epsilonSlider.setEnabled(!fastLearning);
|
||||
@@ -71,10 +72,10 @@ public class LearningInfoPanel extends JPanel {
|
||||
|
||||
if(learning instanceof EpisodicLearning) {
|
||||
learnMoreEpisodesInput = new JTextField();
|
||||
learnMoreEpisodesInput.setMaximumSize(new Dimension(200,20));
|
||||
learnMoreEpisodesInput.setMaximumSize(new Dimension(200, 20));
|
||||
learnMoreEpisodesButton = new JButton("Learn More Episodes");
|
||||
learnMoreEpisodesButton.addActionListener(e -> {
|
||||
if (Util.isNumeric(learnMoreEpisodesInput.getText())) {
|
||||
if(Util.isNumeric(learnMoreEpisodesInput.getText())) {
|
||||
viewListener.onLearnMoreEpisodes(Integer.parseInt(learnMoreEpisodesInput.getText()));
|
||||
} else {
|
||||
learnMoreEpisodesInput.setText("");
|
||||
@@ -83,9 +84,14 @@ public class LearningInfoPanel extends JPanel {
|
||||
add(learnMoreEpisodesInput);
|
||||
add(learnMoreEpisodesButton);
|
||||
}
|
||||
showQTableButton = new JButton("Show Q-Table");
|
||||
showQTableButton.addActionListener(e -> {
|
||||
viewListener.onShowQTable();
|
||||
});
|
||||
add(drawEnvironmentCheckbox);
|
||||
add(smoothGraphCheckbox);
|
||||
add(last100Checkbox);
|
||||
add(showQTableButton);
|
||||
refreshLabels();
|
||||
setVisible(true);
|
||||
}
|
||||
@@ -93,17 +99,17 @@ public class LearningInfoPanel extends JPanel {
|
||||
public void refreshLabels() {
|
||||
policyLabel.setText("Policy: " + learning.getPolicy().getClass());
|
||||
discountLabel.setText("Discount factor: " + learning.getDiscountFactor());
|
||||
if(learning instanceof Episodic){
|
||||
episodeLabel.setText("Episode: " + ((Episodic)(learning)).getCurrentEpisode() +
|
||||
"\t Episodes to go: " + ((Episodic)(learning)).getEpisodesToGo() +
|
||||
"\t Eps/Sec: " + ((Episodic)(learning)).getEpisodesPerSecond());
|
||||
if(learning instanceof Episodic) {
|
||||
episodeLabel.setText("Episode: " + ((Episodic) (learning)).getCurrentEpisode() +
|
||||
"\t Episodes to go: " + ((Episodic) (learning)).getEpisodesToGo() +
|
||||
"\t Eps/Sec: " + ((Episodic) (learning)).getEpisodesPerSecond());
|
||||
}
|
||||
if (learning.getPolicy() instanceof EpsilonPolicy) {
|
||||
if(learning.getPolicy() instanceof EpsilonPolicy) {
|
||||
epsilonLabel.setText("Exploration (Epsilon): " + ((EpsilonPolicy) learning.getPolicy()).getEpsilon());
|
||||
epsilonSlider.setValue((int)(((EpsilonPolicy) learning.getPolicy()).getEpsilon() * 100));
|
||||
epsilonSlider.setValue((int) (((EpsilonPolicy) learning.getPolicy()).getEpsilon() * 100));
|
||||
}
|
||||
delayLabel.setText("Delay (ms): " + learning.getDelay());
|
||||
if(delaySlider.isEnabled()){
|
||||
if(delaySlider.isEnabled()) {
|
||||
delaySlider.setValue(learning.getDelay());
|
||||
}
|
||||
toggleFastLearningButton.setText(fastLearning ? "Disable fast-learning" : "Enable fast-learning");
|
||||
@@ -112,11 +118,12 @@ public class LearningInfoPanel extends JPanel {
|
||||
protected boolean isSmoothenGraphSelected() {
|
||||
return smoothGraphCheckbox.isSelected();
|
||||
}
|
||||
protected boolean isLast100Selected(){
|
||||
|
||||
protected boolean isLast100Selected() {
|
||||
return last100Checkbox.isSelected();
|
||||
}
|
||||
|
||||
protected boolean isDrawEnvironmentSelected(){
|
||||
protected boolean isDrawEnvironmentSelected() {
|
||||
return drawEnvironmentCheckbox.isSelected();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,5 +8,7 @@ import java.util.List;
|
||||
public interface LearningView {
|
||||
void repaintEnvironment();
|
||||
void updateLearningInfoPanel();
|
||||
void updateQTable();
|
||||
void updateRewardGraph(final List<Double> rewardHistory);
|
||||
void showQTableFrame();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package core.gui;
|
||||
|
||||
import core.State;
|
||||
import core.StateActionTable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class QTableFrame<A extends Enum> extends JFrame {
|
||||
private JLabel stateCountLabel;
|
||||
private StateActionTable<A> stateActionTable;
|
||||
private List<StateActionRow<A>> rows;
|
||||
private JPanel areaWrapper;
|
||||
|
||||
public QTableFrame(StateActionTable<A> stateActionTable) {
|
||||
super("Q-Table");
|
||||
this.stateActionTable = stateActionTable;
|
||||
rows = new ArrayList<>(10);
|
||||
setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
|
||||
setLayout(new BorderLayout());
|
||||
setPreferredSize(new Dimension(500, 500));
|
||||
stateCountLabel = new JLabel();
|
||||
add(BorderLayout.NORTH, stateCountLabel);
|
||||
areaWrapper = new JPanel();
|
||||
areaWrapper.setLayout(new BoxLayout(areaWrapper, BoxLayout.Y_AXIS));
|
||||
for(int i = 0; i < 10; ++i) {
|
||||
StateActionRow<A> a = new StateActionRow<>();
|
||||
rows.add(a);
|
||||
areaWrapper.add(a);
|
||||
}
|
||||
add(BorderLayout.CENTER, areaWrapper);
|
||||
setVisible(false);
|
||||
pack();
|
||||
}
|
||||
|
||||
private void refreshAllTextAreas(){
|
||||
for(StateActionRow<A> row : rows){
|
||||
row.refreshLabels();
|
||||
}
|
||||
}
|
||||
protected void refreshQTable() {
|
||||
System.out.println("ref");
|
||||
int stateCount = stateActionTable.getStateCount();
|
||||
stateCountLabel.setText("Total states: " + stateCount);
|
||||
int idx = -1;
|
||||
for(Map.Entry<State, Map<A, Double>> entry : stateActionTable.getFirstStateEntriesForView()) {
|
||||
if(++idx > rows.size() -1) break;
|
||||
StateActionRow<A> row = rows.get(idx);
|
||||
row.setState(entry.getKey());
|
||||
row.setActionValues(entry.getValue());
|
||||
}
|
||||
refreshAllTextAreas();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package core.gui;
|
||||
|
||||
import core.State;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Map;
|
||||
|
||||
@Setter
|
||||
public class StateActionRow<A extends Enum> extends JTextArea {
|
||||
private State state;
|
||||
private Map<A, Double> actionValues;
|
||||
|
||||
public StateActionRow(){
|
||||
this.state = null;
|
||||
this.actionValues = null;
|
||||
setMaximumSize(new Dimension(600, 100));
|
||||
setEditable(false);
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
super.mousePressed(e);
|
||||
showState();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void refreshLabels(){
|
||||
if(state == null || actionValues == null) return;
|
||||
System.out.println("refreshing");
|
||||
StringBuilder sb = new StringBuilder(state.toString()).append("\n");
|
||||
for(Map.Entry<A, Double> actionValue: actionValues.entrySet()){
|
||||
sb.append("\t").append(actionValue.getKey()).append("\t").append(actionValue.getValue()).append("\n");
|
||||
}
|
||||
setText(sb.toString());
|
||||
}
|
||||
|
||||
private void showState() {
|
||||
if(state != null && state instanceof Visualizable){
|
||||
new JFrame() {
|
||||
{
|
||||
JComponent stateComponent = ((Visualizable)state).visualize();
|
||||
setPreferredSize(stateComponent.getPreferredSize());
|
||||
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
add(stateComponent);
|
||||
pack();
|
||||
setVisible(true);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,8 @@ import core.Environment;
|
||||
import core.algo.Learning;
|
||||
import core.listener.ViewListener;
|
||||
import lombok.Getter;
|
||||
import org.javatuples.Pair;
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.knowm.xchart.QuickChart;
|
||||
import org.knowm.xchart.XChartPanel;
|
||||
import org.knowm.xchart.XYChart;
|
||||
@@ -16,7 +17,7 @@ import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class View<A extends Enum> implements LearningView{
|
||||
public class View<A extends Enum> implements LearningView {
|
||||
private Learning<A> learning;
|
||||
private Environment<A> environment;
|
||||
@Getter
|
||||
@@ -26,6 +27,7 @@ public class View<A extends Enum> implements LearningView{
|
||||
@Getter
|
||||
private JFrame mainFrame;
|
||||
private JFrame environmentFrame;
|
||||
private QTableFrame<A> qTableFrame;
|
||||
private XChartPanel<XYChart> rewardChartPanel;
|
||||
private ViewListener viewListener;
|
||||
private JMenuBar menuBar;
|
||||
@@ -36,11 +38,12 @@ public class View<A extends Enum> implements LearningView{
|
||||
this.environment = environment;
|
||||
this.viewListener = viewListener;
|
||||
initMainFrame();
|
||||
initQTableFrame();
|
||||
}
|
||||
|
||||
private void initMainFrame() {
|
||||
mainFrame = new JFrame();
|
||||
mainFrame.setPreferredSize(new Dimension(1280, 720));
|
||||
mainFrame.setPreferredSize(new Dimension(1000, 400));
|
||||
mainFrame.setLayout(new BorderLayout());
|
||||
menuBar = new JMenuBar();
|
||||
fileMenu = new JMenu("File");
|
||||
@@ -52,7 +55,7 @@ public class View<A extends Enum> implements LearningView{
|
||||
fc.setCurrentDirectory(new File(System.getProperty("user.dir")));
|
||||
int returnVal = fc.showOpenDialog(mainFrame);
|
||||
|
||||
if (returnVal == JFileChooser.APPROVE_OPTION) {
|
||||
if(returnVal == JFileChooser.APPROVE_OPTION) {
|
||||
viewListener.onLoadState(fc.getSelectedFile().toString());
|
||||
}
|
||||
}
|
||||
@@ -62,7 +65,7 @@ public class View<A extends Enum> implements LearningView{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String fileName = JOptionPane.showInputDialog("Enter file name", "path/to/file");
|
||||
if(fileName != null){
|
||||
if(fileName != null) {
|
||||
viewListener.onSaveState(fileName);
|
||||
}
|
||||
}
|
||||
@@ -78,7 +81,7 @@ public class View<A extends Enum> implements LearningView{
|
||||
mainFrame.pack();
|
||||
mainFrame.setVisible(true);
|
||||
|
||||
if (environment instanceof Visualizable) {
|
||||
if(environment instanceof Visualizable) {
|
||||
environmentFrame = new JFrame() {
|
||||
{
|
||||
add(((Visualizable) environment).visualize());
|
||||
@@ -86,9 +89,21 @@ public class View<A extends Enum> implements LearningView{
|
||||
setVisible(true);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
private void initQTableFrame(){
|
||||
qTableFrame = new QTableFrame<>(learning.getStateActionTable());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateQTable() {
|
||||
qTableFrame.refreshQTable();
|
||||
}
|
||||
|
||||
public void showQTableFrame(){
|
||||
updateQTable();
|
||||
qTableFrame.setVisible(true);
|
||||
}
|
||||
|
||||
private void initLearningInfoPanel() {
|
||||
learningInfoPanel = new LearningInfoPanel(learning, viewListener);
|
||||
@@ -109,32 +124,21 @@ public class View<A extends Enum> implements LearningView{
|
||||
rewardChartPanel.setPreferredSize(new Dimension(300, 300));
|
||||
}
|
||||
|
||||
public void showState(Visualizable state) {
|
||||
new JFrame() {
|
||||
{
|
||||
JComponent stateComponent = state.visualize();
|
||||
setPreferredSize(new Dimension(stateComponent.getWidth(), stateComponent.getHeight()));
|
||||
add(stateComponent);
|
||||
setVisible(true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void updateRewardGraph(final List<Double> rewardHistory) {
|
||||
List<Integer> xValues;
|
||||
List<Double> yValues;
|
||||
if(learningInfoPanel.isLast100Selected()){
|
||||
if(learningInfoPanel.isLast100Selected()) {
|
||||
yValues = new CopyOnWriteArrayList<>(rewardHistory.subList(rewardHistory.size() - Math.min(rewardHistory.size(), 100), rewardHistory.size()));
|
||||
xValues = new CopyOnWriteArrayList<>();
|
||||
for(int i = rewardHistory.size() - Math.min(rewardHistory.size(), 100); i <rewardHistory.size(); ++i){
|
||||
for(int i = rewardHistory.size() - Math.min(rewardHistory.size(), 100); i < rewardHistory.size(); ++i) {
|
||||
xValues.add(i);
|
||||
}
|
||||
}else{
|
||||
if(learningInfoPanel.isSmoothenGraphSelected()){
|
||||
} else {
|
||||
if(learningInfoPanel.isSmoothenGraphSelected()) {
|
||||
Pair<List<Integer>, List<Double>> XYvalues = smoothenGraph(rewardHistory);
|
||||
xValues = XYvalues.getValue0();
|
||||
yValues = XYvalues.getValue1();
|
||||
}else{
|
||||
xValues = XYvalues.getKey();
|
||||
yValues = XYvalues.getValue();
|
||||
} else {
|
||||
xValues = null;
|
||||
yValues = rewardHistory;
|
||||
}
|
||||
@@ -145,37 +149,37 @@ public class View<A extends Enum> implements LearningView{
|
||||
rewardChartPanel.repaint();
|
||||
}
|
||||
|
||||
private Pair<List<Integer>, List<Double>> smoothenGraph(List<Double> original){
|
||||
private Pair<List<Integer>, List<Double>> smoothenGraph(List<Double> original) {
|
||||
int totalXPoints = 100;
|
||||
|
||||
List<Integer> xValues = new CopyOnWriteArrayList<>();
|
||||
List<Double> tmp = new CopyOnWriteArrayList<>();
|
||||
int meanBatch = original.size() / totalXPoints;
|
||||
if(meanBatch < 1){
|
||||
if(meanBatch < 1) {
|
||||
meanBatch = 1;
|
||||
}
|
||||
|
||||
int idx = 0;
|
||||
int batchIdx = 0;
|
||||
double batchSum = 0;
|
||||
for(Double x: original) {
|
||||
for(Double x : original) {
|
||||
++idx;
|
||||
batchSum += x;
|
||||
if (idx == 1 || ++batchIdx % meanBatch == 0) {
|
||||
if(idx == 1 || ++batchIdx % meanBatch == 0) {
|
||||
tmp.add(batchSum / meanBatch);
|
||||
xValues.add(idx);
|
||||
batchSum = 0;
|
||||
}
|
||||
}
|
||||
return new Pair<>(xValues, tmp);
|
||||
return new ImmutablePair<>(xValues, tmp);
|
||||
}
|
||||
|
||||
public void updateLearningInfoPanel() {
|
||||
this.learningInfoPanel.refreshLabels();
|
||||
}
|
||||
|
||||
public void repaintEnvironment(){
|
||||
if (environmentFrame != null && learningInfoPanel.isDrawEnvironmentSelected()) {
|
||||
public void repaintEnvironment() {
|
||||
if(environmentFrame != null && learningInfoPanel.isDrawEnvironmentSelected()) {
|
||||
environmentFrame.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user