package  Bakadoku;

import  java.applet.Applet;
import	java.awt.*;
import  java.awt.event.*;

public class BakadokuApplet extends Applet {
    public void init() {
	Dimension sz = getSize();
	width  = sz.width;
	height = sz.height;

	problem = new Problem();

	setLayout( new BorderLayout() );

	Panel buttons = new Panel();
	buttons.setBackground( Color.white );
	
	buttons.add( probbutton( "27 clues" ) );
	buttons.add( probbutton( "20 clues" ) );
	buttons.add( probbutton( "18 clues" ) );

	cntlbtn = new Button( "   Start   " );
	started = false;
	System.out.println( "cntlbtn = " + cntlbtn );
	cntlbtn.addActionListener(
	    new ActionListener() {
		public void actionPerformed( ActionEvent e ) {
		    if( isrunning ) {
			pause();
		    } else {
			restart();
		    }
		}
	    }
	);
	buttons.add( cntlbtn );
	add( buttons, "North" );

	board = new Board( this );

	add( board, "Center" );

	datePushed( "27 clues" );
    }
 
    public void pause() {
	cntlbtn.setLabel( "  Continue  " );
	problem.setRunning( isrunning = false );
    }

    public void restart() {
	cntlbtn.setLabel( "   Stop   " );
	if( ! started ) {
	    problem.start();
	    started = true;
	}
	problem.setRunning( isrunning = true );
    }

    private Button probbutton( String str ) {
	Button btn = new Button( " "+str+" " );
	final String string = str;
	started = isrunning = false;
	btn.addActionListener(
	    new ActionListener() {
		public void actionPerformed( ActionEvent e ) {
		    datePushed( datestring = string );
	        }
	    }
	);
	return btn;
    }

    public void datePushed( String string ) {
	if( started ) {
	    reset();
	}
	problem = new Problem( string );
	problem.setBoard( board );
	board.paint( problem );
    }

    private void reset() {
	cntlbtn.setLabel( "   Start   " );
	problem.stop();
	problem = null;
	started = isrunning = false;
    }

    public void start() {
    }

    public void stop() {
    }

    public void destroy() {
    }

    public String getDatestring() {
	return datestring;
    }

    private Button cntlbtn;
    private Button btn;
    private Board board;
    private boolean started;
    private boolean isrunning = false;

    private Problem problem;
    private String  datestring;

    private int width;
    private int	height;
}

class Board extends Panel {

    public Board( BakadokuApplet bakaapp ) {
	super();
	setBackground( bgcolor );
	app = bakaapp;
    }

    public void paint( Problem pr ) {
	problem = pr;
	paint( getGraphics() );
    }

    public void paint( Graphics g ) {
	if( problem == null )
	    return;
	g.setColor( bgcolor );
	Dimension sz = getSize();
	width  = sz.width;
	height = sz.height;
	g.fillRect( 0, 0, width, height );

 	linedraw( g );
	paintDate( g );

	for( int x=0; x<9; ++x )
	    for( int y=0; y<9; ++y )
		paintCell( x, y );
    }  

    public void linedraw( Graphics g ) {
	g.setColor( linecolor );
	unit = height / 10;
	x0 = (width - unit*9)/2;
	y0 = unit/2;

	for( int i=0; i<=9; ++i ) {
	    int w = i%3==0 ? 5 : 1;
	    g.fillRect( x0, yi(i)-w/2, unit*9, w );
	    g.fillRect( xi(i)-w/2, y0, w, unit*9 );
	}
    }

    private int xi( int i ) { return x0+unit*i; }
    private int yi( int i ) { return y0+unit*i; }

    public void paintDate( Graphics g ) {
	g.setColor( Color.black );

	Font font = new Font( "Dialog", Font.BOLD, 16 );
	g.setFont(font);
	g.setColor( Color.blue );

	FontMetrics fm = getFontMetrics(font);
	String str = problem.getDate();
	int x = (width-fm.stringWidth(str))/2;
	g.drawString( str, x, y0-4 );
    }

    public void paintCell( int x, int y ) {
	Graphics g = getGraphics();
	g.setColor( bgcolor );
	g.fillRect( xi(x)+3, yi(y)+3, unit-6, unit-6 );

	int v = problem.getValue( x, y );
	if( v == 0 )
	    return;
	String str = ""+v;
	Color col = problem.getProbValue( x, y ) == 0 ? Color.blue : Color.black;

	g.setColor( col );
	Font font = new Font( "Dialog", Font.BOLD, 40 );
	g.setFont(font);
	FontMetrics fm = getFontMetrics( g.getFont());
	int xx = xi(x) + (unit - fm.stringWidth(str))/2;
	int yy = yi(y) + (unit-fm.getHeight())/2+fm.getAscent();
	g.drawString( str, xx, yy );
    }

    public void paintCounter( int cnt ) {
	Graphics g = getGraphics();
	g.setColor( bgcolor );
	g.fillRect( x0, 0, 150, y0-3 );
	Font font = new Font( "Dialog", Font.BOLD, 16 );
	g.setFont(font);
	g.setColor( Color.blue );
	g.drawString( "No. "+cnt, x0+10, y0-4 );
    }

    public BakadokuApplet getApp() {
	return app;
    }

    private Color bgcolor = new Color(0xff, 0xff, 0xcc);
    private Color linecolor = Color.black;
    private int width;
    private int	height;
    private int unit, x0, y0;	
    private Image offScreen;
    private Graphics offGraphics;

    private Problem problem;	
    private BakadokuApplet app;
}

class Problem  extends Thread {
    public Problem() {
	init();
    }

    public Problem( String dt ) {
	date = dt;
	init();
	if( dt.equals( "27 clues" ) ) {
	    problem1();		    
	} else if(  dt.equals( "20 clues" ) ) {
	    problem2();	    
	} else if(  dt.equals( "18 clues" ) ) {
	    problem3();	    
	}
	setbd();
    }

    public void run() {
	app = board.getApp();
	solve( board );
    }

    public void setRunning( boolean rn ) {
	running = rn;
    }

    private void setbd() {
	for( int i=0; i<9; ++i )
	    for( int j=0; j<9; ++j )
		bd[i][j] = prob[i][j];
    }

    private void init() {
	for( int i=0; i<9; ++i )
	    for( int j=0; j<9; ++j )
		bd[i][j] = prob[i][j] = 0;
    }
    
    private void problem1() {
	prob[0][0] = 1;
	prob[3][0] = 2;
	prob[6][0] = 4;
	prob[1][1] = 2;
	prob[4][1] = 7;
	prob[7][1] = 5;
	prob[2][2] = 3;
	prob[5][2] = 9;
	prob[8][2] = 8;
	prob[0][3] = 7;
	prob[3][3] = 4;
	prob[6][3] = 9;
	prob[1][4] = 9;
	prob[4][4] = 5;
	prob[7][4] = 2;
	prob[2][5] = 1;
	prob[5][5] = 6;
	prob[8][5] = 4;
	prob[0][6] = 4;
	prob[3][6] = 8;
	prob[6][6] = 7;
	prob[1][7] = 5;
	prob[4][7] = 4;
	prob[7][7] = 8;
	prob[2][8] = 2;
	prob[5][8] = 7;
	prob[8][8] = 9;
    }	

    private void problem2() {
	prob[1][0] = 1;
	prob[3][0] = 8;
	prob[2][1] = 4;
	prob[6][1] = 1;
	prob[8][1] = 3;
	prob[1][2] = 6;
	prob[3][2] = 5;
	prob[7][2] = 2;
	prob[6][3] = 4;
	prob[8][3] = 1;
	prob[0][5] = 2;
	prob[2][5] = 9;
	prob[1][6] = 4;
	prob[5][6] = 1;
	prob[7][6] = 7;
	prob[0][7] = 8;
	prob[2][7] = 7;
	prob[6][7] = 5;
	prob[5][8] = 3;
	prob[7][8] = 9;
    }

    private void problem3() {
	prob[7][0] = 6;
	prob[8][0] = 1;
	prob[3][1] = 2;
	prob[8][1] = 7;
	prob[3][2] = 9;
	prob[4][2] = 4;
	prob[1][3] = 8;
	prob[2][3] = 7;
	prob[2][4] = 6;
	prob[6][4] = 4;
	prob[6][5] = 2;
	prob[7][5] = 3;
	prob[4][6] = 7;
	prob[5][6] = 8;
	prob[0][7] = 2;
	prob[5][7] = 6;
	prob[0][8] = 4;
	prob[1][8] = 9;
    }

    public int getValue( int x, int y ) {
	return bd[x][y];
    } 

    public int getProbValue( int x, int y ) {
	return prob[x][y];
    } 

    public void setBoard( Board bd ) {
	board = bd;
    }

    // x, y に n を置いてみる
    public void solve( Board bd ) {
	board = bd;
	counter = 0;
	running = true;
	solve( 0, 0 );
    }

    public void solve( int x, int y ) {
	if( bd[x][y] != 0 ) {
	    int z = findNext( x, y );
	    if( z < 0 ) {
		app.pause();
		try {
		    while( running == false ) {
			Thread.sleep(100L);
		    }
		    app.restart();
		} catch (InterruptedException e ) {}
		return;	// 解答
	    }
	    x = z % 9;
	    y = z / 9;
	}

	for( int n=1; n<=9; ++n ) {
	    if( ! check( x, y, n ) )
		continue;

	    // 置く
	    bd[x][y] = n;
	    board.paintCell( x, y );
	    board.paintCounter( ++counter );
	    try {
		while( ! running ) {
		   Thread.sleep(200L);
		}
		Thread.sleep(10L);
	    } catch (InterruptedException e ) {}
	    solve( x, y );
	}

	// 外す
	bd[x][y] = 0;
	board.paintCell( x, y );
    }

    private boolean check( int x, int y, int n ) {
	for( int i=0; i<9; ++i ) {
	    if( i!=x && bd[i][y] == n )
		return false;
	    if( i!=y && bd[x][i] == n )
		return false;
	}
	for( int i=x/3*3; i<x/3*3+3; ++i ) {
	    for( int j=y/3*3; j<y/3*3+3; ++j ) {
		if( !(i==x && j==y) && bd[i][j] ==n )
		    return false;
	    }
	}
	return	true;
    }

    private int findNext( int x, int y ) {
	for( int z = y*9 + x; z<9*9 ; ++z ) {
	    int u = z % 9;	
	    int v = z / 9;	
	    if( bd[u][v] != 0 )
		continue;
	    return z;
	}
	return	-1;
    }

    public String getDate() {
	return	date;
    }

    private int bd[][]   = new int[9][9];
    private int prob[][] = new int[9][9];

    private String	date;
    private Board	board;
    private BakadokuApplet	app;
    private boolean	running;
    private int		counter;
}

