あみだくじを作ってみよう8
スレッド(並行動作する別のプロセス)と、他の処理との調停には、
スレッド動作中を示すフラグを使うのが簡単です。
毎度、管理人イガジーです。
具体的には、スレッドの入り口でフラグを立て(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; の値を大きくするとゆっくりになります。
ご意見、ご質問などは、お気軽にメールフォームからご連絡ください。
コメントも歓迎いたします。
この記事へのコメントはこちら