解答例 - j1.lesson09.Hanoi

package j1.lesson09;

import java.io.*;

/**
 * 課題0904 - 解答例.
 @author s.arakawa
 @version $Id: Hanoi_java.rps,v 1.1 2006/03/06 12:56:15 java2005 Exp $
 */
public class Hanoi {

    /**
     * コンソールから1以上の整数を入力させ、
     * 入力された値だけ円盤を持つ「ハノイの塔」の問題を解く流れを表示するプログラム。
     * 「ハノイの塔」というのは、次のような問題である。
     <p>
     * 3つの柱がある。そのうちの1つの柱に穴のあいた円盤を何枚か通して積み重ねてある。
     * それらの円盤は下のものほど大きく、上のものほど小さい。
     * そのすべての円盤を以下の規則に従って動かし、指定した柱に円盤を全て移動させる。
     </p>
     <ol>
     <li>円盤は、一度に一つだけ移動させることができる</li>
     <li>円盤は、それより小さな円盤の上に積むことはできない</li>
     </ol>
     @param args 無視される
     @throws IOException 入力時に例外が発生した場合
     */
    public static void main(String[] argsthrows IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        System.out.print("円盤の数を入力:");
        int disks = Integer.parseInt(reader.readLine());
        
        if (disks < 1) {
            System.out.println("1以上の整数を入力してください");
        }
        else {
            hanoi(disks);
        }
    }
    
    /**
     * 引数に指定された値を円盤の数として、その円盤の数を持つハノイの塔を解き
     * 手順を以下のように表示する。
     <pre> 円盤の番号: 移動元の塔の番号 -&gt; 移動先の塔の番号 </pre>
     * ただし、円盤の番号は一番小さい円盤を 1 とし、小さい順に 1, 2, 3,.. と割り振られる。
     * また、塔の番号は次のように割り振られる。
     <ol>
     <li> 最初に円盤が置いてある場所 </li>
     <li> 円盤を移動させる際に経由させる場所 </li>
     <li> 円盤を移動させる先 </li>
     </ol>
     <code>n</code> が0以下の場合は考慮しない。
     @param n 円盤の数 
     */
    public static void hanoi(int n) {
        
        // n 個のハノイの塔を表示する
        // ただし、それぞれの塔に番号をつける
        //  start: 最初に円盤が置いてある塔 = 1
        //   temp: 円盤を移動させる際に経由させる塔 = 2
        // target: 円盤を移動させる先の塔 = 3
        hanoiView(n, 123);
    }
    
    /**
     <b>このメソッドは課題の仕様に含まれていない</b>
     * 引数で指定した塔の番号 <code>start</code> から <code>target</code> へ <code>n</code> 個の円盤を
     * 移動する手順を表示する。
     <code>temp</code> は円盤を移動させる際に経由させる塔の番号を表す。
     @param n 移動させる円盤の個数
     @param start 移動元の塔の番号
     @param temp 移動させる際に経由させる塔の番号
     @param target 移動先の塔の番号
     */
    public static void hanoiView(int n, int start, int temp, int target) {
        
        // まず、ハノイの塔を解くに当たり、作業を3つに分ける
        // 1. start から temp へ n - 1 個の円盤を移す
        // 2. start には一番大きな円盤が残るので、それを target へ移す
        // 3. temp から target へ n - 1 個の円盤を移す
        //
        // このように考えると、1, 3 はn-1個の円盤を移すハノイの塔であると
        // 考えることができ、2 は単純に円盤を一つ移すだけの作業になる。
        
        // ただし、円盤 0 個のハノイの塔 ということは、何もしないということである
        if (n == 0) {
            return;
        }
        
        // start 上にある n - 1 個の円盤を temp へ移す
        // つまり、
        // start から temp へ、 target を経由して
        // n - 1 個の円盤を持つハノイの塔を実行する
        // よって、以下のように塔の番号を入れ替えて実行する
        //  start -> start
        //   temp -> target
        // target -> temp
        hanoiView(n - 1, start, target, temp);
        
        // 上記の作業が終わると、各塔にある円盤の数は以下のようになる
        //  start = 1 個
        //   temp = N - 1 個
        // target = 0 個
        // start に残っている円盤は最も大きな円盤であるので、
        // target に移動する
        // 実際に円盤を移す作業なので、これを表示する
        System.out.println(n + ": " + start + " -> " + target);
        
        // temp 上にある n - 1 個の円盤を target へ移す
        // つまり、
        // temp から target へ、 start を経由して
        // n - 1 個の円盤を持つハノイの塔を実行する
        // よって、以下のように塔の番号を入れ替えて実行する
        //  start -> temp
        //   temp -> start
        // target -> target
        hanoiView(n - 1, temp, start, target);
    }
}