/* .:....1....:....2....:....3....:....4....:....5....:....6....:....7.. */ // by John Spurgeon // Contents // // Introduction // Variations on a Theme // // 0. Names and Descriptions // 1. The Main Method // 2. Solution Algorithms // 3. Utility Functions // 4. Move Data Types // 5. Moves Data Types // 6. Iterator Data Types // 7. List Data Type // // Further Reading /* * Introduction */ // The Towers of Hanoi is a puzzle consisting of disks of various // sizes that are stacked on pegs such that a smaller disk does not // appear above a larger one. The challenge is to move disks from // an initial state to a final state while obeying certain rules. // Disks must always be transferred from one peg to another peg, // and a larger disk can never be placed on top of a smaller one. // // According to the English Wikipedia's "Tower of Hanoi" article // (retrieved on 3 January 2018), // // There is a story about an Indian temple in Kashi Vishwanath // which contains a large room with three time-worn posts in it, // surrounded by 64 golden disks. Brahmin priests, acting out the // command of an ancient prophecy, have been moving these disks // in accordance with the immutable rules of Brahma since that // time. The puzzle is therefore also known as the Tower of // Brahma puzzle. According to the legend, when the last move of // the puzzle is completed, the world will end. // // The article goes on to say that it is not clear whether Édouard // Lucas, the French mathematician who invented the puzzle in 1883, // invented this legend or was inspired by it. /* * Variations on a Theme */ // 1. From Here to Anywhere // // In variation 1, a disk on top of a stack may be moved from the // peg it's on to any other peg as long as doing so does not entail // placing a larger disk on top of a smaller one. // // 2. Pegs in a Row // // In variation 2, pegs are arranged in a line, and a disk can only // be moved to a peg adjacent to the one it's on. In the three-peg // version of this variation, if the pegs are labeled 0, 1, and 2, // then peg 0 is adjacent to peg 1, and peg 1 is adjacent to peg 2; // peg 0 is not adjacent to peg 2. /* * 0. Names and Descriptions */ // Names and descriptions of frequently used variables. // // NAME DESCRIPTION // // n A non-negative integral number of disks. // // m A non-negative integral number of moves, where a move // is defined by integers that identify the peg from which // and the peg to which the move is made. // // moves A sequence of moves. // // f An integer identifying a peg from which a move is made. // // t An integer identifying a peg to which a move is made. // // d The absolute value of the difference of two peg numbers. /* * 1. The Main Method */ /** * The TowersOfHanoi class is composed of a main method and several * other static methods with names like solution1a, solution1b, * solution2a, solution2b, etc. Each of these methods produces a * a solution to a variation of the Towers of Hanoi puzzle. The * main method works as follows. * * Step 1. Use the command-line arguments (i.e. the value of the * main method's parameter, which is typically called args) * to set the values of variables that identify the puzzle * to be solved and the solution method to be used. * * a) If the number of arguments is greater than zero, then the * first argument determines which solution method to use; * otherwise, a default solution method is selected. * * b) If the number of arguments is greater than one, then the * second argument determines the number of disks to move; * otherwise, a default number of disks is selected. * * c) If the number of arguments is greater than two, then the * third argument determines the peg from which the disks are * to be moved; otherwise, a default peg number is selected. * * d) If the number of arguments is greater than three, then the * fourth argument determines the peg to which the disks are * to be moved; otherwise a default peg number is selected. * * Step 2. Use the selected solution method to obtain a solution to * to the puzzle to be solved by calling the method, passing * to the method the number of disks to move, the peg from * which to move the disks, and the peg to which the disks * are to be moved. * * Step 3. Print the solution (the value of a Moves object) that is * returned by the selected solution method. */ public class TowersOfHanoi { public static Moves solution1a(int n, int f, int t) { final Moves moves = new ListOfMoves(); return Recursively.solve1(n, f, t, moves); } public static Moves solution1b(int n, int f, int t) { final int m = Get.numMoves1(n, f, t); final Moves moves = new ArrayOfMoves(m); return Recursively.solve1(n, f, t, moves); } public static Moves solution1c(int n, int f, int t) { final Moves moves = new ListOfMoves(); return NonRecursively.solve1(n, f, t, moves); } public static Moves solution1d(int n, int f, int t) { final int m = Get.numMoves1(n, f, t); final Moves moves = new ArrayOfMoves(m); return NonRecursively.solve1(n, f, t, moves, m); } public static Moves solution2a(int n, int f, int t) { final Moves moves = new ListOfMoves(); return Recursively.solve2(n, f, t, moves); } public static Moves solution2b(int n, int f, int t) { final int m = Get.numMoves2(n, f, t); final Moves moves = new ArrayOfMoves(m); return Recursively.solve2(n, f, t, moves); } public static void main(String[] args) { final int numArgs = args.length; final String whichSolution = numArgs > 0 ? args[0] : "1"; final int n = numArgs > 1 ? Get.numDisks(args[1]) : 2, f = numArgs > 2 ? Get.pegNumber(args[2]) : 0, t = numArgs > 3 ? Get.pegNumber(args[3]) : 1; Moves moves; switch (whichSolution) { case "1": case "1a": moves = solution1a(n, f, t); break; case "1b": moves = solution1b(n, f, t); break; case "1c": moves = solution1c(n, f, t); break; case "1d": moves = solution1d(n, f, t); break; case "2": case "2a": moves = solution2a(n, f, t); break; case "2b": moves = solution2b(n, f, t); break; default: moves = new ArrayOfMoves(0); } System.out.println(moves); } } /* * 2. Solution Algorithms */ /** * The Recursively class is a collection of functions that recursively * solve variations of the Towers of Hanoi puzzle. */ class Recursively { public static Moves solve1(int n, int f, int t, Moves moves) { if (Get.distance(f, t) > 0) { if (n == 1) { moves.append(new Move1(f, t)); } else if (n > 1) { final int other = 3 - f - t; solve1(n - 1, f, other, moves); solve1(1, f, t, moves); solve1(n - 1, other, t, moves); } } return moves; } public static Moves solve2(int n, int f, int t, Moves moves) { final int other = 3 - f - t; final int d = Get.distance(f, t); if (d == 2) { solve2(n, f, other, moves); solve2(n, other, t, moves); } else if (d == 1) { if (n == 1) { moves.append(new Move2(f, t)); } else if (n > 1) { solve2(n - 1, f, other, moves); solve2(1, f, t, moves); solve2(n - 1, other, t, moves); } } return moves; } } /** * The NonRecursively class is a collection of functions that solve a * variation of the Towers of Hanoi puzzle non-recursively. */ class NonRecursively { public static Moves solve1(int n, int f, int t, Moves moves) { final int m = Get.numMoves1(n, f, t); return solve1(n, f, t, moves, m); } public static Moves solve1(int n, int f, int t, Moves moves, int m) { final Decoder mathemagician = new Decoder(n, f, t); for (int i = 1; i <= m; i++) moves.append(mathemagician.toMove1(i)); return moves; } /** * The Decoder class defines a data type. A Decoder object is * associated with a particular instance of a variation of the * Towers of Hanoi puzzle. A Decoder object has a method that * takes a move number as input and returns a Move1 object; a * solution to the puzzle associated with the Decoder object is * formed by the sequence of moves produced by calling the method * repeatedly, each time passing the next step number, beginning * with step 1 and repeating m times, where m is the minimum * number of moves required to solve the puzzle. */ private static class Decoder { final int evenTick, origin; public Decoder(int n, int f, int t) { int d = Get.distance(f, t); int magnitude = d == 0 ? 0 : 1; int sign = d == 1 ? 1 : -1; sign *= f > t ? 1 : -1; sign *= Get.isEven(n) ? 1 : -1; this.evenTick = sign * magnitude; this.origin = f; } public Move1 toMove1(int moveNumber) { int i, diskNumber = 0; for (i = moveNumber; Get.isEven(i); i = i >> 1) diskNumber++; final int tickCount = i >> 1; final int tick = Get.isEven(diskNumber) ? evenTick : -evenTick; final int f = Get.pegNumber(origin + (tick * tickCount)); final int t = Get.pegNumber(f + tick); return new Move1(f, t); } } } /* * 3. Utility Functions */ /** * The Get class is a collection of assorted utility functions that are * used by various other methods. */ class Get { public static boolean isEven(int i) { return (1 & i) == 0; } public static int numDisks(String s) { int n; try { n = Integer.parseInt(s); } catch (Exception e) { n = 0; } return n < 0 ? 0 : n; } public static int pegNumber(String s) { int i; try { i = Integer.parseInt(s); } catch (Exception e) { i = 0; } return pegNumber(i); } public static int pegNumber(int i) { return pegNumber(i, 3); } public static int pegNumber(int i, int numPegs) { return (i + numPegs) % numPegs; } public static int distance(int f, int t) { int diff = f - t; return diff < 0 ? -diff : diff; } public static int numMoves1(int n, int f, int t) { return (n < 1 || f == t) ? 0 : numMoves1(n); } public static int numMoves2(int n, int f, int t) { return numMoves2(n, distance(f, t)); } private static int numMoves1(int n) { return (n == 1) ? 1 : 1 + (numMoves1(n - 1) * 2); } private static int numMoves2(int n, int d) { if (n < 1) return 0; if (n == 1) return d; if (d == 1) return (numMoves2(n - 1, 1) * 3) + 1; return d * numMoves2(n, 1); } } /* * 4. Move Data Types */ abstract class Move { private int f, t; public Move(int f, int t) { set(f, t); if (f < 0 || t < 0) invalidate(); } public void invalidate() { set(-1, -1); } public void set(int f, int t) { this.f = f; this.t = t; } public String toString() { return toJsonObject(); } public String toCsv() { return f + "," + t; } public String toJsonObject() { return "{from:" + f + ",to:" + t + "}"; } } class Move1 extends Move { public Move1(int f, int t) { super(f, t); if (f > 2 || t > 2) invalidate(); } } class Move2 extends Move1 { public Move2(int f, int t) { super(f, t); if (Get.distance(f, t) > 1) invalidate(); } } /* * 5. Moves Data Types */ abstract class Moves { public abstract Iterator getIterator(); public abstract Moves append(Move move); public String toCsv() { String s = ""; Iterator i = this.getIterator(); if (!i.done()) { s = i.getNext().toString(); while (!i.done()) s += "," + i.getNext().toString(); } return s; } public String toJsonArray() { return "[" + toCsv() + "]"; } public String toJsonObject() { return "{moves:" + toJsonArray() + "}"; } public String toString() { return toJsonObject(); } } class ArrayOfMoves extends Moves { private final int capacity; private int nextIndex = 0; private Move[] array; public ArrayOfMoves(int capacity) { this.capacity = capacity; this.array = new Move[capacity]; } public ArrayOfMoves append(Move move) { this.array[nextIndex++] = move; return this; } public ArrayIterator getIterator() { return new ArrayIterator(this.array); } public int getCapacity() { return capacity; } } class ListOfMoves extends Moves { private List list = new List(); public ListOfMoves append(Move move) { this.list.append(new List.Node(move)); return this; } public ListIterator getIterator() { return new ListIterator(this.list); } } /* * 6. Iterator Data Types */ interface Iterator { public boolean done(); public Object getNext(); } class ArrayIterator implements Iterator { private final Object[] array; private final int last; private int i = -1; public ArrayIterator(Object[] array) { this.array = array; this.last = array.length - 1; } public boolean done() { return i >= last; } public Object getNext() { return array[++i]; } } class ListIterator implements Iterator { private final List list; private List.Node current; public ListIterator(List list) { this.list = list; this.current = new List.Node(null, list.head); } public boolean done() { return current.next == null; } public Object getNext() { current = current.next; return current.value; } } /* * 7. List Data Type */ class List { public Node head = null; public Node tail = null; public void append(Node node) { if (tail == null) head = tail = node; else tail = tail.next = node; } public static class Node { public final Object value; public Node next; public Node(Object value, Node next) { this.value = value; this.next = next; } public Node(Object value) { this(value, null); } } } /* * Further Reading */ // Computer Science: An Interdisciplinary Approach // by Robert Sedgewick and Kevin Wayne // Section 2.3 Recursion (Towers of Hanoi, p. 268) // Concrete Mathematics: A Foundation for Computer Science // by Graham, Knuth, and Patashnik // Section 1.1. The Tower of Hanoi /* .:....1....:....2....:....3....:....4....:....5....:....6....:....7.. */