あみだくじを作ってみよう8

   2012/08/07

スレッド(並行動作する別のプロセス)と、他の処理との調停には、
スレッド動作中を示すフラグを使うのが簡単です。

毎度、管理人イガジーです。

具体的には、スレッドの入り口でフラグを立て(running=ture;)
出口で解除する(running=false;)ようにすると、
そのフラグ(running)が立っている時(trueの時)には、
(衝突するような)処理をしない(if (running) return;)ようにできます。
※ 例えば、迷路をたどっている時に、迷路「作成」ボタンを押しても
動作しないようにしておくわけです。
という訳で、あみだくじのプログラム例(最終型)を以下に示します。

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Label;
import java.awt.Panel;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.Random;

public class Amida5 implements Runnable {
	int nn=6;  // 縦棒の数
	int vn=9;   // 横棒の最大本数
	int yh=26;
	int vd=vn-2; // 横棒の密度
	int x0=40; // 左余白
	int y0=50; // 上余白
	int xlen=210/(nn-1);   // 横棒の長さ
	int ylen=yh*(vn+1);  // 縦棒の長さ
	int y1=y0+ylen;  // 縦棒の下端座標
//	int yh=(ylen-20)/vn; // 横棒の間隔
	int yh2=yh/2; // 隣の横棒をずらす量

	Amida5 ami;
	Frame f;
	MyCanvas cv;
	Graphics g;
	Random rnd=new Random();
	boolean [][] hb=new boolean[10][vn];
	boolean gened=false;
	String []gs=new String[10];

	Button gen,drw;
	TextField an=new TextField("6",2);  // 棒数の初期値(6本)

	int wtt=160;  // たどるアニメのスピード
	int gn;
	boolean up;
	boolean running=false;

	public Amida5() {
		ami=this;
		f=new Frame("Amida");
		f.addWindowListener(new WindowAdapter(){
			public void windowClosing(WindowEvent ev) {
				System.exit(0);
			}
		});
		cv=new MyCanvas(400,500);
		g=cv.b.getGraphics();
		f.add(cv,BorderLayout.CENTER);

		Panel p0=new Panel();
		p0.setLayout(new FlowLayout(FlowLayout.LEFT));
		gen=new Button("生成");
		gen.addActionListener(new genAmida());
		an.addActionListener(new genAmida());

		drw=new Button("描画");
		drw.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				if (gened) {
					drawAmida(nn-1);
					cv.repaint();
				}
			}
		});
		p0.add(new Label("クジ数:"));
		p0.add(an);
		p0.add(gen);
		p0.add(drw);
		f.add(p0,BorderLayout.NORTH);

		f.setSize(300,420);
		f.setVisible(true);
	} // constructor

	void drawAmida(int n) { // n=縦棒の数
		g.setColor(Color.BLACK);
		g.fillRect(0, 0, cv.b.getWidth(), cv.b.getHeight());
		g.setColor(Color.WHITE);
		for (int i=0;i<nn;++i) { // 縦棒を描画
			int x=xlen*i+x0;
			g.drawLine(x, y0, x, y1);
			g.drawString(Character.toString((char) ('A'+i)), x-3, y0-6); // 棒の上のラベル
			g.drawString(gs[i], x-3, y1+16); // 棒の下のラベル
		}

		for (int i=0;i<n;++i) {
			for (int v=0;v<vn;++v) {
				if (hb[i][v]) {
					int x=xlen*i+x0;
					int y2=v*yh+y0+yh2+(i&1)*yh2;
					g.drawLine(x, y2, x+xlen, y2);
				}
			}
		}
	}

	// Runnable
	@Override
	public void run() { // あみだをたどるアニメ描画
	//trace(int gn,boolean up) {
		if (running) return;
		running=true;
		int y2,x,yt,j,ye;
		int i=gn;
		if (i>=nn) i=nn-1;
		y2=x=0;
		g.setColor(Color.CYAN);
		yt=y0;ye=y1;
		if (up) {
			yt=y1;ye=y0;
			g.setColor(Color.YELLOW);
		}
		for (int j2=0;j2<vn*2;++j2) {
			j=j2;
			if (up) {
				j=vn*2-j2-1;
			}
			y2=j*yh2+y0+yh2;
			x=i*xlen+x0;
			if (up) {
				g.fillRect(x-1, y2, 3, yt-y2);
			}else{
				g.fillRect(x-1, yt, 3, yh2);
			}
			repaintwait(wtt);
			yt=y2;
			if (((j^i)&1)==0){  // 偶数上段 or 奇数下段
				// 右を見る
				if (i<(nn-1)){ // 右端でなければ
					if (hb[i][j/2]) { // 右に横棒あり?
						g.fillRect(x, yt-1, xlen+2, 3);
						repaintwait(wtt);
						++i;
					}
				}
			}else{ // 偶数下段 or 奇数上段
				// 左を見る
				if (i>0) { // 左端でなければ
					if (hb[i-1][j/2]) { // 左に棒あり?
						g.fillRect(x-xlen, yt-1, xlen+2, 3);
						repaintwait(wtt);
						--i;
					}
				}
			}
		}// for j
		x=i*xlen+x0;
		if (up) g.fillRect(x-1,ye,3,yh2);
		else g.fillRect(x-1,yt,3,ye-yt);
		repaintwait(wtt);
		running=false;
	}

	void repaintwait(int wt) {
		cv.repaint();
		try {
			Thread.sleep(wt);
		} catch (InterruptedException e) {
			System.out.println("sleep Excepton!");
		}
	}

	class genAmida implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {
			if (running) return;
			try {
				nn=Integer.parseInt(an.getText());
			} catch (NumberFormatException ex) {
				nn=6;
			}
			if (nn<2) nn=2;
			if (nn>10) nn=10;
			an.setText(Integer.toString(nn));
			xlen=210/(nn-1);   // 横棒の長さ

			// ゴールの数値セット
			for (int i=0;i<nn;++i) gs[i]=Integer.toString(i);
			for (int i=0;i<nn;++i) { // シャッフル
				int j=rnd.nextInt(nn);
				int k=rnd.nextInt(nn);
				String t=gs[j];
				gs[j]=gs[k]; gs[k]=t;
			}

			for (int rx=0;rx<nn;++rx) { // 横棒の有無をクリア
				Arrays.fill(hb[rx], false);
			}
			for (int i=0;i<(nn-1);++i) {  // 乱数で横棒をセット
				for (int j=0;j<vd;++j) {
					int r=rnd.nextInt(vn);
					hb[i][r]=true;
				}
			}
			// 縦棒のみ描画
			g.setColor(Color.BLACK);
			g.fillRect(0, 0, cv.b.getWidth(), cv.b.getHeight());
			g.setColor(Color.WHITE);
			for (int i=0;i<nn;++i) { // 縦棒を描画
				int x=xlen*i+x0;
				g.drawLine(x, y0, x, y1);
				g.drawString(Character.toString((char) ('A'+i)), x-3, y0-6); // 棒の上のラベル
				g.drawString(gs[i], x-3, y1+16); // 棒の下のラベル
			}
			cv.repaint();
			gened=true;
		}
	}
	public static void main(String[] args) {
		new Amida5();
	}

	public class MyCanvas extends Canvas{
		private static final long serialVersionUID = 1L;
		public BufferedImage b;

		public MyCanvas(int ww,int hh) {
			b=new BufferedImage(ww,hh,BufferedImage.TYPE_INT_RGB);
			this.addMouseListener(new ClickHand());
		}
		public void paint(Graphics g0) {
			g0.drawImage(b,0,0,null);
		}
		public void update(Graphics g0) {
			paint(g0);
		}
	}
	class ClickHand extends MouseAdapter {
		@Override
		public void mouseClicked(MouseEvent me) {
			if (!gened) return;
			if (running) return;
			int cky=-1;
			int ckx=-1;

			int y=me.getY();
			if ((y>(y0-20))&&(y<y0)) cky=0; // 棒の上(アルファベット)
			else if ((y>y1)&&(y<(y1+20))) cky=1; // 棒の下(数字)

			if (cky>=0) {
				int x=me.getX();
				int x2=(x-x0+8)/xlen;
				int x3=x2*xlen+8+x0;
				if ((x>(x0-8))&&(x<x3)) ckx=x2;
				if (ckx>=0) {
					gn=ckx;
					up=(cky==1);
					drawAmida(nn-1);
					new Thread(ami).start();
				}
			}
		}
	}
}

昨日までは、ylen(縦棒の長さ)から横棒の間隔(yh)を
求めていましたが、vnを増やした時に表示誤差が出てしまうので
yhからylenを算出するように変更しました。
あみだをたどるスピードは、wtt=80; の値を大きくするとゆっくりになります。

ご意見、ご質問などは、お気軽にメールフォームからご連絡ください。
コメントも歓迎いたします。

この記事へのコメントはこちら

メールアドレスは公開されませんのでご安心ください。
また、* が付いている欄は必須項目となりますので、必ずご記入をお願いします。

内容に問題なければ、下記の「コメント送信」ボタンを押してください。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)