/**
 * tourhanoinr.java - jpq - 27/05/11 - 25/04/20 - 29/08/22
 * version avec pointeurs
 *
 */

import java.awt.event.*;
import java.awt.*;
import javax.swing.*;

public class tourhanoinr extends JFrame implements ActionListener, Runnable {
	static final long serialVersionUID = 220829L;

	Image img;
	Graphics gimg;
	int wimg, himg, w, h;
	int n;
	JTextField tn;
	JButton ok;
	Dessin d;
	boolean bdeplace;
	static Thread thr;

/** t[1], t[2] et t[3] contiennent les piles de disques symbolisés par des nombres 1, 2, 3...
 * wt contient les abscisses des piles
 * ptr[1], ptr [2] et ptr [3] pointent le haut de chaque pile (1er emplacement disponible
 * et donc 0 si la pile est vide)
 */
	int[][] t;
	int[] wt;
	int[] ptr;

	public tourhanoinr (String s) {
		super (s);
		setBackground (Color.WHITE);
		setFont (new Font ("sans-serif", Font.PLAIN, 10));
		setLayout (new BorderLayout ());
		JPanel p = new JPanel ();
		p.setBorder (BorderFactory.createMatteBorder (1, 1, 1, 1, Color.BLACK));
		add (p, BorderLayout.NORTH);
		p.add (new JLabel ("n ="));
		n = 4;
		p.add (tn = new JTextField ("4", 4));
		p.add (ok = new JButton ("Ok"));
		ok.addActionListener (this);
		ptr = new int [3];
		init (n);
		add (d = new Dessin (), BorderLayout.CENTER);
	}

	private void init (int n) {
		ptr[0] = n;
		ptr[1] = ptr[2] = 0;
		t = new int [3][n];
		for (int i = 0; i < n; i ++) {
			t[0][i] = n - i;
			t[1][i] = t[2][i] = 0;
		}
		bdeplace = false;							// entraîne la sortie du "deplace" puis du "run"
	}

	public void run() {
		while (bdeplace) {
				d.repaint();
				deplace ();
				bdeplace = false;
		}
	}

	private void affiche (long n) {
		d.repaint();
		try {
			Thread.sleep (n);
			}
		catch (InterruptedException ie) {}
	}
	
/*
 * fonction non récursive deplace
 *
 * l'empilement de n disques nécessite 2^n - 1 déplacements
 *
 * Si n est pair les déplacements se font plutôt dans le sens t[1] -> t[2], t[2] -> t[3], t[3] -> t[1]
 * si n est impair les déplacements se font plutôt dans le sens t[1] -> t[3], t[2] -> t[1], t[3] -> t[2].
 * on alterne le déplacement du disque 1 et le déplacement d'un autre disque.
 */
 
	private void deplace() {
		if (bdeplace && (n > 0)) {
			int dep = (n % 2 == 0) ? 1 : 2;		// déplacement du disque 1 vers la droite ou vers la gauche suivant la parité
			int pos1 = 0;						// numéro de la pile où se trouve le disque n° 1

// tant qu'on a pas appuyé sur le bouton stop et que les disques se trouvent sur t[2]
			while (bdeplace && (ptr [2] < n)) {

// on déplace le disque 1 c'est à dire le plus petit à droite ou à gauche en fonction de la parité de n
				int dest = (pos1 + dep) % 3;
				t [dest] [ptr [dest] ++] = t [pos1] [-- ptr [pos1]];
				t [pos1] [ptr [pos1]] = 0;
				pos1 = dest;
				affiche (1000L);

// puis on déplace un autre disque
				if  (bdeplace && ptr [2] < n) {
					boolean pastrouve = true;
					int org = 0;
					dest = 1;
					while (pastrouve) {

// si le disque en haut de la pile est le disque 1 ou si la pile est vide on passe à la pile suivante
						if ((org == pos1) || (ptr [org] == 0)) {
							org ++;
							dest = (org + 1) % 3;
						} else {

// on teste si la pile destinataire convient
							if ((ptr [dest] != 0) && (t [dest] [ptr [dest] - 1] < t [org] [ptr [org] - 1])) {
								dest = (dest + 1) % 3;
								if (dest == org) {
									org = (org + 1) % 3;
									dest = (org + 2) % 3;
								}
							} else {

// c'est bon on déplace le disque
								t [dest] [ptr [dest] ++] = t [org] [-- ptr [org]];
								t [org] [ptr [org]] = 0;
								pastrouve = false;
							}
						}
					}	// while (pastrouve)
					affiche (1000L);
				}
			}
		}
	}

	public void actionPerformed (ActionEvent ae) {
		if (ae.getSource() == ok) {
			try {
				n = Integer.parseInt (tn.getText ());
			}
			catch (NumberFormatException nfe) { }
			tn.setText (Integer.toString (n));
			init (n);
			d.repaint();
			try {
				Thread.sleep (1000);				// attente de 1000 ms
				bdeplace = true;
				thr = new Thread(this);
				thr.start();
			}
			catch (InterruptedException ie) {}
			
		}
	}

/**
 * classe Dessin 25/04/20
 */

protected class Dessin extends Canvas {
	static final long serialVersionUID = 200425L;

	public void update (Graphics g) {
		paint (g);
	}

	public void paint (Graphics g) {
		w = getWidth();
		h = getHeight();
		if ((img == null) || (w != wimg) || (h != himg)) {
			wimg = w;
			himg = h;
			img = createImage (w, h);
			gimg = img.getGraphics();
// initialise la position des trois piles de disque
			wt = new int [3];
			int deltaw = (w - 25) / 6;
			wt [0] = 10 + deltaw;
			wt [1] = wt [0] + 2 * deltaw;
			wt [2] = wt [1] + 2 * deltaw;
		}
		gimg.setColor (Color.WHITE);
		gimg.fillRect (0, 0, w, h);
		gimg.setColor (Color.BLACK);
		gimg.fillRect (5, h - 10, w - 10, 5);
// dessine les 3 piles
		for (int i = 0; i < 3; i ++) {
			gimg.setColor (Color.BLACK);
			gimg.fillRect (wt [i] - 1, 80, 2, h - 90);
			int k = 0;
			gimg.setColor (Color.RED);
			while ((k < n) && (t[i][k] != 0))
				gimg.fillRect (wt[i] - 5 * t[i][k], h - 20 - 10 * k, 10 * t[i][k++], 10);
		}
		g.drawImage (img, 0, 0, this);
	}
}

////////////////////////////////////////////////////////////////////////////////

	public static void main (String[] args) {
		tourhanoinr f = new tourhanoinr ("Tours de Hanoï - version non récursive");
		f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
		f.setSize (400, 200);
		f.setVisible (true);
	}

}