Skip to content

Instantly share code, notes, and snippets.

@fajarzuhrihadiyanto
Created June 8, 2021 17:25
Show Gist options
  • Save fajarzuhrihadiyanto/389b4c18720d5b99cc83955d74d51105 to your computer and use it in GitHub Desktop.
Save fajarzuhrihadiyanto/389b4c18720d5b99cc83955d74d51105 to your computer and use it in GitHub Desktop.
Tower of Hanoi - Controllers
package assignment_08.controllers;
import assignment_08.models.Model;
import assignment_08.views.View;
/**
* This class represents set of all necessary controllers in the application
*
* @since June 8th 2021
* @author Fajar Zuhri Hadiyanto
* @version 1.0
* */
public class Controller {
/** instance of main controller */
private final MainController mainController;
/** instance of tower of hanoi controller */
private final TowerOfHanoiController towerOfHanoiController;
/**
* This constructor will create new main controller and tower of hanoi controller with a given model and view
*
* @param model model used by this controller
* @param view view used by this controller
* */
public Controller(Model model, View view) {
this.mainController = new MainController(model, view);
this.towerOfHanoiController = new TowerOfHanoiController(model, view);
}
}
package assignment_08.controllers;
import assignment_08.models.Model;
import assignment_08.views.View;
import javax.swing.*;
import java.util.Stack;
/**
* This class represents controller of model and view related to the main application
*
* @since June 8th 2021
* @author Fajar Zuhri Hadiyanto
* @version 1.0
* */
public class MainController {
/** Model used by this controller */
private final Model model;
/** View used by this controller */
private final View view;
/**
* This constructor will create new controller with a given model and view
*
* @param model model used by this controller
* @param view view used by this controller
* */
public MainController(Model model, View view) {
this.model = model;
this.view = view;
this.initView();
this.initController();
}
/**
* This method is used to initialize the view that need data from the model
* */
public void initView() {
this.view.getMainView().getDiskSpinner().setModel(new SpinnerNumberModel(this.model.getStateModel().getnDisk(), 3, 8, 1));
this.view.getMainView().getMoveState().setText(String.valueOf(this.model.getStateModel().getTotalMoves()));
}
/**
* This method is used to initialize the controller by setting event listener for every control
* related to the main app
* */
public void initController() {
// EVENT LISTENER OF THE SPINNER
this.view.getMainView().getDiskSpinner().addChangeListener(e -> {
int num = (int) ((JSpinner)e.getSource()).getValue();
this.model.getStateModel().setnDisk(num);
this.initGame();
});
// EVENT LISTENER OF THE RESTART BUTTON
this.view.getMainView().getRestartButton().addActionListener(e -> {
this.initGame();
});
// EVENT LISTENER OF THE LOG BUTTON
this.view.getMainView().getLogButton().addActionListener(e -> {
this.view.getLogView().displayFrame();
});
}
/**
* This method is used to initialize the game
* */
private void initGame() {
try {
// REINITIALIZE THE ROD MODEL
this.model.getRodsModel().reinitiate(this.model.getStateModel().getnDisk());
this.view.getMainView().getTowers()[0].updateDisk(new Stack<>());
for (int i = this.model.getStateModel().getnDisk(); i > 0; i--) {
this.view.getMainView().getTowers()[0].addDisk(i);
}
this.view.getMainView().getTowers()[1].updateDisk(new Stack<>());
this.view.getMainView().getTowers()[2].updateDisk(new Stack<>());
// RESET THE MOVE STATE
this.model.getStateModel().setTotalMoves(0);
this.view.getMainView().getMoveState().setText(String.valueOf(0));
// CLEAR THE LOGS
this.model.getLogsModel().getLogs().clear();
this.view.getLogView().getList().clear();
// ENABLE THE START GAME BUTTON
this.view.getMainView().getSolveButton().setEnabled(true);
this.view.getMainView().getStepButton().setEnabled(true);
// DISABLE THE NEXT AND PREVIOUS BUTTON
this.view.getMainView().getPrevButton().setEnabled(false);
this.view.getMainView().getNextButton().setEnabled(false);
// STOP THE RUNNING THREAD WITH NAME "toh" IF ITS EXIST
for (Thread t : Thread.getAllStackTraces().keySet()) {
if (t.getName().equals("toh")) {
t.stop();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package assignment_08.controllers;
import assignment_08.models.LogModel;
import assignment_08.models.Model;
import assignment_08.views.View;
import java.util.ArrayList;
import java.util.ListIterator;
/**
* This class represents controller of model and view related to the main game
*
* @since June 8th 2021
* @author Fajar Zuhri Hadiyanto
* @version 1.0
* */
public class TowerOfHanoiController implements Runnable {
/** Model used by this controller */
private final Model model;
/** View used by this controller */
private final View view;
/** field to contain integer iterator to do step by step solution */
private ListIterator<Integer> moveIt;
/**
* This method override from {@link Runnable} interface
* */
@Override
public void run() {
try {
this.solve(this.model.getStateModel().getnDisk(), 0, 2, 1);
} catch (Exception exception) {
exception.printStackTrace();
}
}
/**
* This constructor will create new controller with a given model and view
*
* @param model model used by this controller
* @param view view used by this controller
* */
public TowerOfHanoiController(Model model, View view) {
this.model = model;
this.view = view;
initController();
}
/**
* This method is used to initialize the controller by setting event listener for every control
* related to the main game
* */
public void initController() {
// EVENT LISTENER OF THE SOLVE BUTTON
this.view.getMainView().getSolveButton().addActionListener(e -> {
this.view.getMainView().getSolveButton().setEnabled(false);
this.view.getMainView().getStepButton().setEnabled(false);
this.getT().start();
});
// EVENT LISTENER OF THE STEP BY STEP BUTTON
this.view.getMainView().getStepButton().addActionListener(e -> {
this.view.getMainView().getSolveButton().setEnabled(false);
this.view.getMainView().getStepButton().setEnabled(false);
ArrayList<Integer> list = new ArrayList<>();
for (int i = 1; i < Math.pow(2, this.model.getStateModel().getnDisk()); i++) {
list.add(i);
}
this.moveIt = list.listIterator();
if (this.moveIt.hasNext()) this.view.getMainView().getNextButton().setEnabled(true);
});
// EVENT LISTENER OF THE PREVIOUS BUTTON
this.view.getMainView().getPrevButton().addActionListener(e -> {
try {
this.stepSolution(false);
this.view.getMainView().getNextButton().setEnabled(true);
if (!this.moveIt.hasPrevious()) this.view.getMainView().getPrevButton().setEnabled(false);
} catch (Exception ignored) {
}
});
// EVENT LISTENER OF THE NEXT BUTTON
this.view.getMainView().getNextButton().addActionListener(e -> {
try {
this.stepSolution(true);
this.view.getMainView().getPrevButton().setEnabled(true);
if (!this.moveIt.hasNext()) this.view.getMainView().getNextButton().setEnabled(false);
} catch (Exception ignored) {
}
});
}
/**
* This method is used to do one step solution, either forward or backward
*
* @param forward flag parameter used to indicated if its forward or backward
* */
private void stepSolution(boolean forward) throws Exception {
// GET CURRENT ITERATOR
int num = forward ? this.moveIt.next() % 3 : this.moveIt.previous() % 3;
// GET THE LEFT, MIDDLE, AND THE RIGHT INDEX AND ROD DATA
int leftDisk = this.view.getMainView().getTowers()[0].getTopDisk(), middleDisk, rightDisk, middleIndex, rightIndex;
if (this.model.getStateModel().getnDisk() % 2 == 1) {
middleIndex = 1;
rightIndex = 2;
} else {
middleIndex = 2;
rightIndex = 1;
}
middleDisk = this.view.getMainView().getTowers()[middleIndex].getTopDisk();
rightDisk = this.view.getMainView().getTowers()[rightIndex].getTopDisk();
// MOVEMENT CONSIDERATION
switch (num) {
case 1:
if (leftDisk == 0) this.moveDisk(rightIndex, 0, forward);
else if (rightDisk == 0) this.moveDisk(0, rightIndex, forward);
else if (leftDisk > rightDisk) this.moveDisk(rightIndex, 0, forward);
else this.moveDisk(0, rightIndex, forward);
break;
case 2:
if (leftDisk == 0) this.moveDisk(middleIndex,0, forward);
else if (middleDisk == 0) this.moveDisk(0, middleIndex, forward);
else if (leftDisk > middleDisk) this.moveDisk(middleIndex, 0, forward);
else this.moveDisk(0, middleIndex, forward);
break;
case 0:
if (rightDisk == 0) this.moveDisk(middleIndex,rightIndex, forward);
else if (middleDisk == 0) this.moveDisk(rightIndex, middleIndex, forward);
else if (rightDisk > middleDisk) this.moveDisk(middleIndex, rightIndex, forward);
else this.moveDisk(rightIndex, middleIndex, forward);
break;
default:
break;
}
}
/**
* This method is used to solve the game automatically
*
* @param num number of the disk
* @param from index of the from rod
* @param to index of the to rod
* @param aux index of the auc rod
* */
private void solve(int num, int from, int to, int aux) throws Exception {
// BASE CASE, WHEN THE NUM IS 1
if (num == 1) {
moveDisk(from, to, true);
Thread.sleep(500);
return;
}
solve(num - 1, from, aux, to);
moveDisk(from, to, true);
Thread.sleep(500);
solve(num - 1, aux, to, from);
}
/**
* This method is used to move a disk from one rod to another rod
*
* @param from index of the from rod
* @param to index of the to rod
* @param forward flag that indicate if the movement is forward or backward
* */
private void moveDisk(int from, int to, boolean forward) throws Exception {
this.model.getRodsModel().moveDisk(from, to);
if (forward) {
this.model.getStateModel().incrementTotalMoves();
this.model.getLogsModel().getLogs().add(new LogModel(from, to, this.view.getMainView().getTowers()[from].getTopDisk()));
this.view.getLogView().getList().addElement(new LogModel(from, to, this.view.getMainView().getTowers()[from].getTopDisk()));
} else {
this.model.getStateModel().decrementTotalMoves();
this.model.getLogsModel().getLogs().pop();
this.view.getLogView().getList().remove(this.view.getLogView().getList().getSize() - 1);
}
this.view.getMainView().getTowers()[to].addDisk(this.view.getMainView().getTowers()[from].getTopDisk());
this.view.getMainView().getTowers()[from].removeDisk();
this.view.getMainView().getMoveState().setText(String.valueOf(this.model.getStateModel().getTotalMoves()));
}
/**
* This method is used to create new Thread for game solving
*
* @return new Thread with name "toh"
* */
public Thread getT() {
return new Thread(this, "toh");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment