This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package towerofhanoi; | |
import java.awt.Color; | |
import java.util.Random; | |
import CS2114.Shape; | |
/** | |
* A disc representation | |
* | |
* @author Anon | |
* @version 23.10.2017 | |
* | |
*/ | |
public class Disc extends Shape implements Comparable<Disc> { | |
private int size = 0; | |
// ~ Constructor ........................................................... | |
/** | |
* Creates a new Disc of a specified width and a random color | |
* | |
* @param width | |
* the disc's width | |
*/ | |
public Disc(int width) { | |
super(0, 0, width, GameWindow.DISC_HEIGHT); | |
this.size = width; | |
int [] color = this.randomRGB(); | |
Color randomColor = new Color(color[0], color[1], color[2]); | |
this.setBackgroundColor(randomColor); | |
} | |
// ~ Methods ............................................................... | |
/** | |
* Compares this disc to another disc | |
* | |
* @param otherDisc | |
* the other disc | |
* @return a negative number if this disc is smaller, 0 if they are equal, | |
* and a positive number if this disc is larger | |
*/ | |
public int compareTo(Disc otherDisc) throws IllegalArgumentException { | |
if (otherDisc == null) { | |
throw new IllegalArgumentException("No disc"); | |
} | |
return this.size - otherDisc.size; | |
} | |
/** | |
* Compares this disc to another object | |
* | |
* @param obj | |
* the object to compare to | |
* @return true if both are discs of the same size, false otherwise | |
*/ | |
public boolean equals(Object obj) { | |
if (!(obj instanceof Disc)) { | |
return false; | |
} | |
return this.compareTo((Disc)obj) == 0; | |
} | |
/** | |
* Returns the width of this disc as a string | |
* | |
* @return the width of the disc as a string | |
*/ | |
public String toString() { | |
return (new Integer(this.size)).toString(); | |
} | |
/** | |
* Returns a random RGB value | |
* | |
* @return a random RGB value | |
*/ | |
private int[] randomRGB() { | |
Random random = new Random(); | |
int[] randomRGB = new int[3]; | |
for (int i = 0; i < 3; i++) { | |
randomRGB[i] = random.nextInt(255); | |
} | |
return randomRGB; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package towerofhanoi; | |
import java.awt.Color; | |
import java.util.Observable; | |
import java.util.Observer; | |
import CS2114.Button; | |
import CS2114.Shape; | |
import CS2114.Window; | |
import CS2114.WindowSide; | |
public class GameWindow implements Observer { | |
private HanoiSolver game; | |
private Shape left, middle, right; | |
private Window window; | |
/** | |
* The size of the gap between individual discs which are on top of each | |
* other | |
*/ | |
protected final int DISC_GAP = 5; | |
/** | |
* The thickness of a disc | |
*/ | |
protected static final int DISC_HEIGHT = 18; | |
public GameWindow(HanoiSolver game) { | |
this.game = game; | |
this.window = new Window("Tower of Hanoi"); | |
int padd = window.getHeight() - window.getGraphPanelHeight(); | |
int width = window.getGraphPanelWidth(); | |
int height = window.getGraphPanelHeight(); | |
int xDist = width / 4; | |
int yDist = height - (200 + padd); | |
this.left = new Shape(xDist - 0, yDist, 10, 200, Color.RED); | |
this.middle = new Shape((xDist * 2) - 5, yDist, 10, 200, Color.RED); | |
this.right = new Shape((xDist * 3) - 10, yDist, 10, 200, Color.RED); | |
game.addObserver((Observer)this); | |
for (int i = game.discs(); i > 0; i--) { | |
Disc tempDisc = new Disc(((int)Math.pow(i, 1.5) * 7) + 20); | |
game.getTower(Position.LEFT).push(tempDisc); | |
this.window.addShape(tempDisc); | |
this.moveDisc(Position.LEFT); | |
} | |
System.out.println(); | |
this.window.addShape(this.left); | |
this.window.addShape(this.right); | |
this.window.addShape(this.middle); | |
Button button = new Button("Solve"); | |
button.onClick(this, "clickedSolve"); | |
this.window.addButton(button, WindowSide.NORTH); | |
} | |
public void update(Observable o, Object arg) { | |
if (arg.getClass().equals(Position.class)) { | |
Position pos = (Position) arg; | |
this.moveDisc(pos); | |
} | |
this.sleep(); | |
} | |
public Window getWindow() { | |
return this.window; | |
} | |
public void clickedSolve(Button button) { | |
button.disable(); | |
new Thread() { | |
public void run() { | |
game.solve(); | |
} | |
}.start(); | |
} | |
private void moveDisc(Position pos) { | |
Shape currentPole = null; | |
Tower currentTower = this.game.getTower(pos); | |
Disc currentDisc = (Disc)currentTower.peek(); | |
switch (pos) { | |
case OTHER: | |
case LEFT: | |
currentPole = left; | |
break; | |
case RIGHT: | |
currentPole = right; | |
break; | |
case MIDDLE: | |
currentPole = middle; | |
break; | |
} | |
int y = currentPole.getY() + 190; | |
int x = currentPole.getX() - (currentDisc.getWidth() / 2) + (currentPole.getWidth() / 2); | |
if (currentTower.size() > 0) { | |
y -= (currentTower.size() * (DISC_HEIGHT + DISC_GAP)); | |
} | |
currentDisc.moveTo(x, y); | |
} | |
private void sleep() { | |
try { | |
Thread.sleep(500); | |
} | |
catch (Exception e) { | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package towerofhanoi; | |
import java.util.Observable; | |
/** | |
* | |
* @author Anon | |
* @version 23.10.2017 | |
* | |
*/ | |
public class HanoiSolver extends Observable { | |
// ~ Fields ................................................................ | |
private Tower left, middle, right; | |
private int numDiscs; | |
public HanoiSolver(int numDiscs) { | |
this.numDiscs = numDiscs; | |
this.left = new Tower(Position.LEFT); | |
this.right = new Tower(Position.RIGHT); | |
this.middle = new Tower(Position.MIDDLE); | |
} | |
public Tower getTower(Position pos) { | |
switch(pos) { | |
default: | |
case OTHER: | |
case LEFT: | |
return this.left; | |
case RIGHT: | |
return this.right; | |
case MIDDLE: | |
return this.middle; | |
} | |
} | |
public int discs() { | |
return this.numDiscs; | |
} | |
public void solve() { | |
this.solveTowers(this.discs(), this.left, this.middle, this.right); | |
} | |
public String toString() { | |
return this.left.toString() + this.middle.toString() + this.right.toString(); | |
} | |
private void solveTowers(int currentDiscs, | |
Tower startPole, | |
Tower tempPole, | |
Tower endPole) { | |
boolean baseCase = (currentDiscs == 1); | |
if (baseCase) { | |
this.move(startPole, endPole); | |
return; | |
} | |
this.solveTowers(currentDiscs - 1, startPole, endPole, tempPole); | |
this.move(startPole, endPole); | |
this.solveTowers(currentDiscs - 1, tempPole, startPole, endPole); | |
} | |
private void move(Tower source, Tower destination) { | |
Disc discToMove = (Disc) source.pop(); | |
destination.push(discToMove); | |
System.out.println(this.toString()); | |
this.setChanged(); | |
notifyObservers(destination.position()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package towerofhanoi; | |
import java.util.EmptyStackException; | |
import stack.StackInterface; | |
/** | |
* A linked-stack implementation | |
* | |
* @author Anon | |
* @version 23.10.2017 | |
* | |
* @param <T> A generic type | |
* | |
*/ | |
public class LinkedStack<T> implements StackInterface<T> { | |
// ~ Fields ................................................................ | |
private Node<T> topNode; | |
private int size; | |
// ~ Constructor ........................................................... | |
/** | |
* Creates a new empty linked stack | |
*/ | |
public LinkedStack() { | |
this.topNode = null; | |
this.size = 0; | |
} | |
// ~ Methods ............................................................... | |
/** | |
* Pushes the given entry to the stack | |
* | |
* @param newEntry the entry to be pushed | |
*/ | |
public void push(T newEntry) { | |
Node<T> newNode = new Node<T>(newEntry); | |
if (!this.isEmpty()) { | |
newNode.setNextNode(this.topNode); | |
} | |
this.topNode = newNode; | |
size++; | |
System.out.println("pushed: " + newEntry.toString()); | |
} | |
/** | |
* Pops an entry from the stack | |
* | |
* @return the popped entry | |
* @throws EmptyStackException if the stack is empty before the operation | |
*/ | |
public T pop() throws EmptyStackException { | |
if (this.isEmpty()) { | |
throw new EmptyStackException(); | |
} | |
Node<T> popped = this.topNode; | |
this.topNode = popped.getNextNode(); | |
popped.setNextNode(null); | |
size--; | |
System.out.println("popped: " + popped.getData().toString()); | |
return popped.getData(); | |
} | |
/** | |
* Returns the entry at the top of the stack | |
* | |
* @return the entry at the top of the stack | |
* @throws EmptyStackException if the stack is empty before the operation | |
*/ | |
public T peek() throws EmptyStackException { | |
if (this.isEmpty()) { | |
throw new EmptyStackException(); | |
} | |
return this.topNode.getData(); | |
} | |
public int size() { | |
return this.size; | |
} | |
/** | |
* Checks whethert the stack is empty | |
* | |
* @return whether the stack is empty | |
*/ | |
public boolean isEmpty() { | |
return (this.size == 0); | |
} | |
/** | |
* Clears the stack of all entries | |
*/ | |
public void clear() { | |
this.topNode = null; | |
this.size = 0; | |
} | |
/** | |
* Returns a string representation of this stack. A stack's string | |
* representation is written as a comma-separated list of its contents (in | |
* top-to-bottom order) surrounded by square brackets, such as: | |
* | |
* "[lastPush, secondPush, firstPush]" | |
* | |
* or "[]" for an empty stack | |
* | |
* @return a string representation of the stack | |
*/ | |
public String toString() { | |
if (this.isEmpty()) { | |
return "[]"; | |
} | |
StringBuilder s = new StringBuilder(); | |
s.append("["); | |
Node<T> cur = this.topNode; | |
for (int i = 0; i < this.size - 1; i++) { | |
s.append(cur.getData().toString() + ", "); | |
cur = cur.getNextNode(); | |
} | |
s.append(cur.getData().toString() + "]"); | |
return s.toString(); | |
} | |
// ~ Nested Classes ........................................................ | |
/** | |
* A Single Linked Node implementation | |
* | |
* @author Lila Pustovoyt (lila) | |
* @version 23.10.2017 | |
* | |
* @param <T> a generic type | |
* | |
*/ | |
class Node<T> { | |
// ~ Fields ............................................................ | |
private Node<T> next; | |
private T data; | |
// ~ Constructors ...................................................... | |
/** | |
* Creates a new node with the given data | |
* | |
* @param datum the data to be stored in the node | |
*/ | |
public Node(T datum) { | |
this.data = datum; | |
this.next = null; | |
} | |
/** | |
* Creates a new node with the given data and next node reference | |
* | |
* @param datum the data to be stored in the node | |
* @param next the next node reference | |
*/ | |
public Node(T datum, Node<T> next) { | |
this.data = datum; | |
this.next = next; | |
} | |
// ~ Methods ........................................................... | |
/** | |
* Returns this node's next node reference | |
* | |
* @return the next node | |
*/ | |
public Node<T> getNextNode() { | |
return this.next; | |
} | |
/** | |
* Returns this node's data | |
* | |
* @return the node's data | |
*/ | |
public T getData() { | |
return this.data; | |
} | |
/** | |
* Sets this node's next node reference | |
* | |
* @param next the next node reference | |
*/ | |
public void setNextNode(Node<T> next) { | |
this.next = next; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package towerofhanoi; | |
public enum Position { | |
LEFT, RIGHT, MIDDLE, OTHER | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package towerofhanoi; | |
public class ProjectRunner { | |
public static void main(String[] args) { | |
// TODO Auto-generated method stub | |
int disks = 5; | |
if (args.length > 0) { | |
disks = Integer.parseInt(args[0]); | |
} | |
HanoiSolver game = new HanoiSolver(disks); | |
GameWindow gameWindow = new GameWindow(game); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package towerofhanoi; | |
/** | |
* A representation of the towers on which the discs are stored | |
* | |
* @author Anon | |
* @version 23.10.2017 | |
* | |
*/ | |
public class Tower extends LinkedStack <Disc>{ | |
// ~ Fields ................................................................ | |
private Position position; | |
// ~ Constructors .......................................................... | |
/** | |
* Creates a new tower at the specified position | |
* | |
* @param position the tower's position | |
*/ | |
public Tower(Position position) { | |
super(); | |
this.position = position; | |
} | |
/** | |
* Returns this tower's position | |
* | |
* @return the tower's position | |
*/ | |
public Position position() { | |
return this.position; | |
} | |
/** | |
* Places a disc on the towers | |
* | |
* @param disc the disc to be placed | |
*/ | |
@Override | |
public void push(Disc disc) throws IllegalStateException, | |
IllegalArgumentException { | |
if (disc == null){ | |
throw new IllegalArgumentException("Disc cannnot be null"); | |
} | |
if (this.size() > 0 && this.peek().compareTo(disc) < 0){ | |
throw new IllegalStateException("Cannot add a bigger disk"); | |
} | |
super.push(disc); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment