marunomaruno-memo

marunomaruno-memo

[leJOS][NXT] Bluetooth を使った送受信

2012年05月11日 | LEGO
[leJOS][NXT] Bluetooth を使った送受信
================================================================================

※ このサンプルは leJOS 0.9.1 で作成している。

Bluetooth を使って、データを送受信する。
プログラムは以下の 3 つを作った。

Sender11      リモートデバイス側。NXT のセンサーから値を読んで、Bluetooth を使っ
              て相手(Receiver11)に送信する

Receiver11    ローカルデバイス側。Sender11 から送られてきたデータを受信して、画
              面に表示する。

SensorValues  エンティティ。送受信時に使う NXT のセンサーの値を保持する。

Bluetooth では、データを送受信するときに、リモートデバイス側とローカルデバイス側
とで通信をする。このとき、リモートデバイス側のプログラム(Sender11)を先に起動して
おく。


■ リモートデバイス側

NXT の Esacape ボタン以外を押下したときに、各センサーの値を読み取って、相手の 
NXT に送信する。Esacape ボタンを押下したときは、プログラムを終了する。


□ Sender11.java
---
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import lejos.nxt.Button;
import lejos.nxt.LCD;
import lejos.nxt.LightSensor;
import lejos.nxt.Motor;
import lejos.nxt.SensorPort;
import lejos.nxt.SoundSensor;
import lejos.nxt.TouchSensor;
import lejos.nxt.UltrasonicSensor;
import lejos.nxt.comm.Bluetooth;
import lejos.nxt.comm.NXTConnection;

/**
 * NXTのセンサーからデータを取得し、Bluetoothを使って値を送信する。
 * リモートデバイス側(先にこちらのプログラムを起動しておく)
 * leJOS 0.9.1
 * @author marunomaruno
 * @version 1.0, 2012/03/14
 * @since 1.0
 */
public class Sender11 {
    public static void main(String[] args) throws Exception {
        // センサーを設定する
        TouchSensor touchSensor = new TouchSensor(SensorPort.S1);
        SoundSensor soundSensor = new SoundSensor(SensorPort.S2);
        LightSensor lightSensor = new LightSensor(SensorPort.S3);
        UltrasonicSensor ultrasonicSensor = new UltrasonicSensor(SensorPort.S4);

        // センサーが安定するまで待つ
        Thread.sleep(200);

        // ローカルデバイスとの通信を待つ
        LCD.drawString("Waiting...", 0, 0);
        NXTConnection connection = Bluetooth.waitForConnection();    // (1)

        // 接続できない場合、プログラムを終了する
        if (connection == null) {    // (2)
            LCD.clear();
            LCD.drawString("Connect fail", 0, 0);
            Button.waitForAnyPress();
            System.exit(1);
        }

        // ローカルにもログをとっておく
        File file = new File("values.dat");
        file.delete();
        file.createNewFile();
        DataOutputStream log = new DataOutputStream(new FileOutputStream(
                file));

        // Bluetooth 用のデータストリームを取得する
        DataOutputStream out = connection.openDataOutputStream();    // (3)

        // センサー値を保持するオブジェクトを確保する
        SensorValues values = new SensorValues();    // (4)

        // ESCが押されたら終わり
        while (Button.waitForAnyPress() != Button.ID_ESCAPE) {
            try {
                // IDをカウントアップする
                values.id++;

                // センサーから値を取得する
                values.buttonId = Button.readButtons();
                values.touch = touchSensor.isPressed() ? 1 : 0;
                values.sound = soundSensor.readValue();
                values.light = lightSensor.readValue();
                values.ultrasonic = ultrasonicSensor.getDistance();

                values.tachoCountA = Motor.A.getTachoCount();
                values.tachoCountB = Motor.B.getTachoCount();
                values.tachoCountC = Motor.C.getTachoCount();

                // データを画面に表示する
                LCD.clear();
                values.print(System.out);

                // ログファイルに出力する
                values.write(log);

                // Bluetoothに出力する
                values.write(out);    // (5)

            } catch (IOException e) {
                System.out.println(" write error " + e);
            }
        }

        // ログファイルを閉じる
        try {
            log.close();

        } catch (IOException e) {
            LCD.drawString("Close Exception", 0, 0);
            LCD.drawString(e.getMessage(), 0, 1);
            Button.waitForAnyPress();
        }

        // 通信を閉じる
        try {
            LCD.clear();
            LCD.drawString("Closing... ", 0, 0);
            out.close();    // (6)
            connection.close();    // (7)

        } catch (IOException e) {
            LCD.drawString("Close Exception", 0, 0);
            LCD.drawString(e.getMessage(), 0, 1);
            Button.waitForAnyPress();
        }

        // 終了メッセージを表示する
        LCD.clear();
        LCD.drawString("Finished", 0, 0);
        Button.waitForAnyPress();
    }
}
----

(1) ローカルデバイスとの通信を待つ

Bluetooth クラス の waitForConnection() メソッドを使って、ローカルデバイスとの通
信を待つ。通信があれば、待ち状態は解除され、BTConnection オブジェクトが返る。
なお、NXTConnection は、BTConnection のスーパークラス。

    NXTConnection connection = Bluetooth.waitForConnection();    // (1)

---
static BTConnection waitForConnection() 
          現在のデフォルトの PIN を使って取得する。
---

PIN は、ペアリングするときに使用する 4 桁の数字。デフォルトだと、「0000」または
「1234」。


□ Bluetooth クラス

java.lang.Object
  lejos.nxt.comm.NXTCommDevice
      lejos.nxt.comm.Bluetooth

Bluetooth 通信をあらわすクラス。

メソッド
---
static BTConnection connect(RemoteDevice remoteDevice) 
static BTConnection connect(String target, int mode) 
static BTConnection connect(String target, int mode, byte[] pin) 

static RemoteDevice getKnownDevice(String fName) 
static ArrayList<RemoteDevice> getKnownDevicesList() 

static BTConnection waitForConnection() 
static BTConnection waitForConnection(int timeout, int mode) 
static BTConnection waitForConnection(int timeout, int mode, byte[] pin) 
---


□ BTConnection クラス

java.lang.Object
  extended by lejos.nxt.comm.NXTConnection
      extended by lejos.nxt.comm.BTConnection

Bluetooth の接続をあらわすクラス。つぎに示す NXTConnection クラスを継承している。

メソッド
---
void  closeStream()
int   getSignalStrength()
void  openStream()
void  setActiveMode(int mode)
---


□ NXTConnection クラス

java.lang.Object
  lejos.nxt.comm.NXTConnection

一般的な NXT leJOS の接続を表すクラス。


メソッド
---
void             close() 
DataInputStream  openDataInputStream() 
DataOutputStream openDataOutputStream() 
InputStream      openInputStream() 
OutputStream     openOutputStream() 
---


(2) 接続できない場合、プログラムを終了する

Bluetooth.waitForConnection() メソッドで、接続ができない場合は、null が返るので、
これをチェックする。

    if (connection == null) {    // (2)


(3) Bluetooth 用のデータストリームを取得する

このプログラムは、センサーから読み取ったデータを相手に送信する側なので、出力スト
リームを開く。

    DataOutputStream out = connection.openDataOutputStream();    // (3)


(4) センサー値を保持するオブジェクトを確保する

NXT のセンサーからの値は、SensorValues オブジェクトとして生成する。

    SensorValues values = new SensorValues();    // (4)


(5) Bluetooth に出力する

SensorValues オブジェクトの write() メソッドに、Bluetooth の出力ストリーム・オブ
ジェクトを渡して書き込む。

    values.write(out);    // (5)


(6)(7) 通信を閉じる

通信が終わったら、出力ストリーム、接続ともに閉じる。

    out.close();    // (6)
    connection.close();    // (7)


■ ローカルデバイス側

Bluetooth を使って送られてきたデータを受信して、その値を表示するプログラム。
リモートデバイス側の名前を指定して、相手を特定する。


□ Receiver11.java
---
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.bluetooth.RemoteDevice;

import lejos.nxt.Button;
import lejos.nxt.LCD;
import lejos.nxt.comm.Bluetooth;
import lejos.nxt.comm.NXTConnection;

/**
 * Bluetoothを使って送られてきたデータを受信して、その値を表示する。
 * ローカルデバイス側(先に、リモートデバイス側を起動しておく)
 * leJOS 0.9.1
 * @author marunomaruno
 * @version 1.0, 2011/08/14
 * @since 1.0
 */
public class Receiver11 {
    public static void main(String[] args) throws Exception {

        // リモートデバイスと接続する
        LCD.drawString("Connecting...", 0, 0);

        // リモートデバイスのNXT名を設定する
        RemoteDevice device = Bluetooth.getKnownDevice("NXT名");    // (1)

        // リモートデバイスが見つからない場合、プログラムを終了する
        if (device == null) {
            LCD.clear();
            LCD.drawString("No such device", 0, 0);
            Button.waitForAnyPress();
            System.exit(1);
        }

        // 接続する
        NXTConnection connection = Bluetooth.connect(device);    // (2)

        // 接続できない場合、プログラムを終了する
        if (connection == null) {
            LCD.clear();
            LCD.drawString("Connect fail", 0, 0);
            Button.waitForAnyPress();
            System.exit(1);
        }

        // データストリームを開く
        DataInputStream in = connection.openDataInputStream();    // (3)

        // ローカルにもログをとっておく
        File file = new File("values.dat");
        file.delete();
        file.createNewFile();
        DataOutputStream log = new DataOutputStream(new FileOutputStream(file));

        SensorValues values = new SensorValues();

        // メッセージを受け取る
        try {
            while (Button.ESCAPE.isDown() != true) {
                // ストリームから値を読み込む
                values.read(in);    // (4)

                // ログファイルに出力する
                values.write(log);

                // データを画面に表示する
                LCD.clear();
                values.print(System.out);

                // 待つ
                Thread.sleep(200);
            }

        } catch (IOException e) {
            System.out.println(" write error " + e);
        }

        // ログファイルを閉じる
        try {
            log.close();

        } catch (IOException e) {
            LCD.drawString("Close Exception", 0, 0);
            LCD.drawString(e.getMessage(), 0, 1);
            Button.waitForAnyPress();
        }

        // 通信を閉じる
        try {
            LCD.clear();
            LCD.drawString("Closing...", 0, 0);
            in.close();
            connection.close();

        } catch (IOException e) {
            LCD.drawString("Close Exception", 0, 0);
            LCD.drawString(e.getMessage(), 0, 1);
            Button.waitForAnyPress();
        }

        LCD.clear();
        LCD.drawString("Finished", 0, 0);
        Button.waitForAnyPress();
    }

}
---

(1) リモートデバイスのNXT名を設定する

Bluetooth クラスの getKnownDevice() メソッドを使って、相手のデバイス名を指定して、
接続先のデバイスを取得する。

    RemoteDevice device = Bluetooth.getKnownDevice("NXT名");    // (1)

リモートデバイスが見つからない場合は、null が返る。


Bluetooth クラスのメソッド
---
static RemoteDevice getKnownDevice(String fName) 
static ArrayList<RemoteDevice> getKnownDevicesList() 
---


□ RemoteDevice クラス

java.lang.Object
  javax.bluetooth.RemoteDevice

リモートデバイスをあらわすクラス。


主なメソッド
---
 String  getBluetoothAddress() 
 String  getDeviceAddr() 
 int     getDeviceClass() 
 String  getFriendlyName(boolean alwaysAsk) 
static RemoteDevice getRemoteDevice(Connection conn) 
 boolean isAuthenticated() 
 boolean isEncrypted() 
 void    setDeviceAddr(String deviceAddr) 
 void    setFriendlyName(String fName) 
---


(2) 接続する

    NXTConnection connection = Bluetooth.connect(device);    // (2)

接続できない場合は、null が返る。


Bluetooth クラスのメソッド
---
static BTConnection connect(RemoteDevice remoteDevice) 
static BTConnection connect(String target, int mode) 
static BTConnection connect(String target, int mode, byte[] pin) 
---


(3) データストリームを開く

このプログラムは、Bluetooth から受信した値を画面に表示するので、入力ストリームを
開く。

    DataInputStream in = connection.openDataInputStream();    // (3)


(4) ストリームから値を読み込む

SensorValues オブジェクトの read() メソッドに、Bluetooth の入力ストリーム・オブ
ジェクトを渡して書き込む。

    values.read(in);    // (4)


■ エンティティ

NXT のセンサーの値を管理するエンティティ・クラス。
入力ストリームや出力ストリームのオブジェクトを受け取ることで、そのストリームに対
する入出力も行う。
なお、データの値は int 値として送受信している。

管理している値はつぎのとおり。
    ------------- ------------------------
    フィールド名  意味
    ------------- ------------------------
    id            ID
    buttonId      ボタンのID
    touch         タッチセンサーの値
    sound         音センサーの値
    light         光センサーの値
    ultrasonic    超音波センサーの値
    tachoCountA   モーターAのタコカウント
    tachoCountB   モーターBのタコカウント
    tachoCountC   モーターCのタコカウント
    ------------- ------------------------


□ SensorValues.java
---
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintStream;

/**
 * NXTのセンサーの値を保持するエンティティ
 * leJOS 0.9.1, Android 2.2
 * @author marunomaruno
 * @version 1.0, 2012/03/14
 * @since 1.0
 */
public class SensorValues {

    int id;    // ID

    int buttonId;    // ボタンのID
    int touch;    // タッチセンサーの値
    int sound;    // 音センサーの値
    int light;    // 光センサーの値
    int ultrasonic;    // 超音波センサーの値

    int tachoCountA;    // モーターAのタコカウント
    int tachoCountB;    // モーターBのタコカウント
    int tachoCountC;    // モーターCのタコカウント

    /**
     * 値を表示する。
     * @param out 表示するストリーム
     */
    public void print(PrintStream out) {
        out.println(id);
        out.println(toSensorString());
        out.println(toMotorString());
    }

    /**
     * データを入力する。
     * @param in 入力ストリーム
     * @throws IOException
     */
    public void read(DataInputStream in) throws IOException {
        id = in.readInt();

        buttonId = in.readInt();
        touch = in.readInt();
        sound = in.readInt();
        light = in.readInt();
        ultrasonic = in.readInt();

        tachoCountA = in.readInt();
        tachoCountB = in.readInt();
        tachoCountC = in.readInt();
    }

    /**
     * データを出力する。
     * @param out 出力ストリーム
     * @throws IOException
     */
    public void write(DataOutputStream out) throws IOException {
        out.writeInt(id);

        out.writeInt(buttonId);
        out.writeInt(touch);
        out.writeInt(sound);
        out.writeInt(light);
        out.writeInt(ultrasonic);

        out.writeInt(tachoCountA);
        out.writeInt(tachoCountB);
        out.writeInt(tachoCountC);

        out.flush();
    }

    /**
     * すべてのセンサーの値からなる文字列を組み立てる。
     * @return 組み立てた文字列
     */
    public String toSensorString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        sb.append(buttonId);
        sb.append(", ");
        sb.append(touch);
        sb.append(", ");
        sb.append(sound);
        sb.append(", ");
        sb.append(light);
        sb.append(", ");
        sb.append(ultrasonic);
        sb.append("]");

        return sb.toString();
    }

    /**
     * すべてのモーターの値からなる文字列を組み立てる。
     * @return 組み立てた文字列
     */
    public String toMotorString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        sb.append(toMotorString(new int[]{tachoCountA}));
        sb.append(", ");
        sb.append(toMotorString(new int[]{tachoCountB}));
        sb.append(", ");
        sb.append(toMotorString(new int[]{tachoCountC}));
        sb.append("]");

        return sb.toString();
    }

    /**
     * 指定されたモーター値の配列からなる文字列を組み立てる。
     * @param values 値の配列
     * @return 組み立てた文字列
     */
    public String toMotorString(int[] values) {
        if (values.length == 0) {
            return "[]";
        }

        StringBuilder sb = new StringBuilder();
        sb.append("[");
        sb.append(values[0]);
        for (int i = 1; i < values.length; i++) {
            sb.append(", ");
            sb.append(values[i]);
        }
        sb.append("]");

        return sb.toString();
    }

    /*
     * (非 Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(id);

        sb.append("[");
        sb.append(toSensorString());
        sb.append(", ");
        sb.append(toMotorString());
        sb.append("]");

        return sb.toString();
    }
}
---

                                                                            以上


[Android] 丸が自動的に動く

2012年05月10日 | Android
[Android] 丸が自動的に動く
================================================================================

画面上を○が自動的に動くサンプル。
View クラスをオーバーライドしている以外は、比較的単純なサンプル。


▲ レイアウト

View クラスを継承した CanvasView クラスを使っているので、完全限定クラス名(FQCN)
で要素名を記述している。


□ res/layout/main.xml
---
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <jp.marunomaruno.android.movingcircle.CanvasView
        android:id="@+id/canvasView"
        android:layout_width="match_parent"
        android:layout_height="400px" />

</LinearLayout>
---


▲ アクティビティ

Eclipse が自動生成するものと変わりなし。

□ MovingCircle01Activity.java
---
package jp.marunomaruno.android.movingcircle;

import android.app.Activity;
import android.os.Bundle;

public class MovingCircle01Activity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}
---


▲ ビュー

View クラスを継承したクラスとして作る。
描画する部分は onDraw() メソッドをオーバーライドして作る。
Thread.sleep() メソッドを利用することで、丸の動きの速さを制御している。
これには、1 ~ 10までの乱数を使う。

□ CanvasView.java
---
package jp.marunomaruno.android.movingcircle;

import java.util.Random;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

/**
 * 丸を自動的に動かす。
 *
 * @author marunomaruno
 * @version 1.0, 2012-05-06
 */
public class CanvasView extends View {

    private int cx;
    private int cy;
    private int radius;

    private int orientationX;
    private int orientationY;

    private Paint paint;

    public CanvasView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public CanvasView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CanvasView(Context context) {
        super(context);
        init();
    }

    /**
     * すべてのコンストラクターで共通の処理。
     */
    private void init() {
        paint = new Paint();
        paint.setColor(Color.YELLOW);
        paint.setAntiAlias(true);

        radius = 30;
        cx = radius;
        cy = radius;

        orientationX = randomInt();
        orientationY = randomInt();
    }

    /**
     * 1~10 までの乱数を返す。
     * @return 1~10 までの乱数
     */
    private int randomInt() {
        return new Random().nextInt(10) + 1;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 丸を表示する
        canvas.drawCircle(cx, cy, radius, paint);

        // 丸を指定方向に移動する
        cx += orientationX;
        cy += orientationY;

        // 左右の端にぶつかったら反転する
        if (isBorderX()) {
            orientationX *= -1;
        }

        // 上下の端にぶつかったら反転する
        if (isBorderY()) {
            orientationY *= -1;
        }

        // 指定ミリ秒分ウエイトする
        try {
            Thread.sleep(randomInt());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        invalidate();
    }

    /**
     * 横の端かどうかを判断する。
     * @return 端のときは true
     */
    private boolean isBorderX() {
        return isBorder(cx, getWidth());
    }

    /**
     * たての端かどうかを判断する。
     * @return 端のときは true
     */
    private boolean isBorderY() {
        return isBorder(cy, getHeight());
    }

    /**
     * 端かどうかを判断する。
     * @return 端のときは true
     */
    private boolean isBorder(int v, int border) {
        return (v < radius) || (v > border - radius);
    }
}
---

                                                                            以上


[Android] ○×ゲーム

2012年05月04日 | Android
[Android] ○×ゲーム
================================================================================

○×ゲーム。
LinearLayout、TextView、Button だけでできるサンプル。
コードの解説はとくにない。


▲ レイアウト

LinearLayout、TextView、Button だけでで構成。
3x3 は、LinearLayout をネストさせている。
ボタンは、1~9 までの番号をつける。

□ res/layout/main.xml
---
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/guide"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/button1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onClickButtonHandler"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <Button
            android:id="@+id/button2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onClickButtonHandler"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <Button
            android:id="@+id/button3"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onClickButtonHandler"
            android:textAppearance="?android:attr/textAppearanceLarge" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/linearLayout2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/button4"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onClickButtonHandler"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <Button
            android:id="@+id/button5"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onClickButtonHandler"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <Button
            android:id="@+id/button6"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onClickButtonHandler"
            android:textAppearance="?android:attr/textAppearanceLarge" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/linearLayout3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/button7"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onClickButtonHandler"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <Button
            android:id="@+id/button8"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onClickButtonHandler"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <Button
            android:id="@+id/button9"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onClickButtonHandler"
            android:textAppearance="?android:attr/textAppearanceLarge" />
    </LinearLayout>

    <TextView
        android:id="@+id/textResult"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""
        android:textAppearance="?android:attr/textAppearanceLarge" />

</LinearLayout>
---


▲ アクティビティ

ゲームは、ボードの 3x3 のボタンの配置が
    1 2 3
    4 5 6
    7 8 9
のとき、
    1-2-3, 4-5-6, 7-8-9,
    1-4-7, 2-5-8, 3-6-9,
    1-5-9, 3-5-7 
でがそろった方の勝ち。力技でチェックしている。


□ MaruBatsu01Activity.java
---
package jp.marunomaruno.android.marubatsu;

import java.util.Random;
import jp.marunomaruno.android.marubatsu.R;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

/**
 * ○×ゲーム
 * @author marunomaruno
 *
 */
public class MaruBatsu01Activity extends Activity {
    public static int SIZE = 3;    // ボードのサイズ

    private static final int[] BOARD = {
        R.id.button1,
        R.id.button2,
        R.id.button3,
        R.id.button4,
        R.id.button5,
        R.id.button6,
        R.id.button7,
        R.id.button8,
        R.id.button9,
    };

    private boolean isFinish = false;    // ゲーム終了ならtrue

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        initialize();
    }

    /**
     * ボードを初期化する。
     */
    private void initialize() {
        for (int id : BOARD) {
            Button button = (Button) findViewById(id);
            button.setText(R.string.blank);
        }
    }

    public void onClickButtonHandler(View view) {
        // ゲーム終了のときは何もしない
        if (isFinish) {
            return;
        }

        Button button = (Button) view;
        if (button.getText().equals(getString(R.string.blank))) {
            button.setText(R.string.maru);
            if (!complete()) {
                setBatsu();
                complete();
            }
        }
    }

    /**
     * ゲームが完了したかどうかを判断する。
     * @return 完了したらtrue
     */
    private boolean complete() {
        TextView textResult = (TextView) findViewById(R.id.textResult);

        // 1-2-3, 4-5-6, 7-8-9,
        // 1-4-7, 2-5-8, 3-6-9,
        // 1-5-9, 3-5-7 のいずれかが同じなら勝ち
        final int[][] PATTERN = {
                {0, 4, 8},
                {2, 4, 6},
                {0, 1, 2},
                {3, 4, 5},
                {6, 7, 8},
                {0, 3, 6},
                {1, 4, 7},
                {2, 5, 8},
        };

        String winner = null;

        for (int[] pattern : PATTERN) {
            winner = judge(pattern);
            if (winner != null) {
                textResult.setText(winner + getString(R.string.win));
                isFinish = true;
                return true;
            }
        }

        if (isDraw()) {
            textResult.setText(R.string.draw);
            isFinish = true;
        }

        return false;
    }

    /**
     * 引き分けかどうかを判定する。
     * @return 引き分けのときtrue
     */
    private boolean isDraw() {
        int count = 0;
        for (int id : BOARD) {
            Button button = (Button) findViewById(id);
            if (button.getText().equals(getString(R.string.blank))) {
                count++;
            }
        }
        // 空白ブロックが1個以下のときは引き分け
        return count <= 1;
    }

    /**
     * 「×」をランダムに配置する。
     */
    private void setBatsu() {
        int index = 0;
        Random random = new Random();

        do {
            index = random.nextInt(SIZE * SIZE);
        } while (!isBlank(index));
        Button button = (Button) findViewById(BOARD[index]);
        button.setText(R.string.batsu);
    }

    /**
     * 指定されたインデックスのボタンが空白かどうか判断する。
     * @param index インデックス
     * @return 空白のときtrue
     */
    private boolean isBlank(int index) {
        Button button = (Button) findViewById(BOARD[index]);
        return button.getText().equals(getString(R.string.blank));
    }

    /**
     * 指定された組のボタンで、誰が勝ちかを判定する。
     * @param pattern 勝ちパターンのインデックスの組
     * @return 勝ちの人の名前。勝ちがないときはnull
     */
    private String judge(int[] pattern) {
        assert pattern.length == SIZE;
        Button buttonA = (Button) findViewById(BOARD[pattern[0]]);
        Button buttonB = (Button) findViewById(BOARD[pattern[1]]);
        Button buttonC = (Button) findViewById(BOARD[pattern[2]]);
        if (buttonA.getText().equals(buttonB.getText()) &&
                buttonB.getText().equals(buttonC.getText())) {
            if (buttonA.getText().equals(getString(R.string.maru))) {
                return getString(R.string.you);
            } else if (buttonA.getText().equals(getString(R.string.batsu))) {
                return getString(R.string.computer);
            }
        }

        return null;
    }
}
---


▲ リソース

□ res/values/strings.xml
---
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World, MaruBatsu01Activity!</string>
    <string name="app_name">○×(まるばつ)</string>
    <string name="guide">あなたが○です。空いているところをクリックしてください。</string>
    <string name="win">の勝ち</string>
    <string name="draw">引き分け</string>
    <string name="you">あなた</string>
    <string name="computer">アンドロイド君</string>
    <string name="maru">○</string>
    <string name="batsu">×</string>
    <string name="blank"></string>
</resources>
---

                                                                            以上