パーセプトロンへの入力:相手の過去の手(を学習する)
パーセプトロンの出力:相手の次の手(を予測する)
int N = 5; //何手前まで学習するか
int i, pred, m;
int[] v = new int[3]; //出力
int[] x = new int[3*N+1]; //入力
int[] w = new int[9*N+3]; //重み
int[] fw = new int[3]; //買った回数
String[] msg = new String[4]; // じゃんけんの手
void setup() {
size(600, 300);
textSize(20);
msg[1] = "gu";
msg[2] = "choki";
msg[3] = "pa";
// 初期化
for (i=0; i<3; i++) {
v[i] = 0; // 予測入力
fw[i] = 0; // 勝敗累積
}
for (i=0; i<3*N; i++)x[i] = 0; // 内部状態
x[3*N]=-1; // しきい値
for (i=0; i<9*N+3; i++)w[i] = 0; // 重み
m = 2; //最初の手
}
void draw() {
background(0);
fill(255);
// じゃんけん結果の表示
text("["+frameCount+"] press key: 1(gu), 2(choki), 3(pa)", 10, 30);
fill(0, 255, 0);
text(" You: "+msg[m], 10, 80);
fill(255, 0, 0);
text(" PC: "+msg[winJanken(pred)], 150, 80);
fill(255);
// これ以降は勝ち負け表示
// プレイヤーがマシンの予測通りの手を打ったのでマシンの勝ち
if (pred==m) {
text("-> PC wins!", 300, 80);
fw[2]++;
// 同じ手なので引き分け
} else if ((winJanken(pred) == m)) {
text("-> Draw", 300, 80);
fw[1]++;
// プレーヤの勝ち
} else {
text("-> You win!", 300, 80);
fw[0]++;
}
// 通算成績を表示
text("Number of wins:", 10, 130);
fill(0, 255, 0);
rect(10, 160, fw[0]*10, 10); //棒グラフ
text(fw[0], fw[0]*10+15, 170); //回数
fill(255, 0, 0);
rect(10, 200, fw[2]*10, 10);
text(fw[2], fw[2]*10+15, 210);
fill(255);
rect(10, 240, fw[1]*10, 10);
text(fw[1], fw[1]*10+15, 250);
noLoop(); // 繰返し処理を止める
}
//キーが押された時の処理
void keyTyped() {
// パーセプトロンの予測した手
pred = perceptron(m, x, w, v); //predは予測手(1,2,3)
// プレーヤの出した手
switch(key) {
case '1':
m = 1;
break; //グー
case '2':
m = 2;
break; //チョキ
case '3':
m = 3;
break; //パー
}
}
//キーが話された時の処理
void keyReleased() {
loop(); //繰返し処理を進める
}
/* 相手の手(x)に対して勝つ手を返す関数
x=1(グー) → 3(パー)
x=2(チョキ) → 1(グー)
x=3(パー) → 2(チョキ) */
int winJanken(int x) {
int y = (x+1)%3+1;
return(y);
}
int perceptron(int m, int x[], int w[], int v[]) {
int i, j, k, kmax, vmax;
int[] prec = new int[3];
/* 前回のプレイヤーの手 m=1,2,3 の表現:
グー (m=1):prec={+1,-1,-1}
チョキ(m=2):prec={-1,+1,-1}
パー (m=3):prec={-1,-1,+1} */
for (k=0; k<3; k++) prec[k] = -1;
prec[m-1] = +1;
/* 各予測ユニットの入力と相手の新しい手が
一致していない場合に誤り訂正学習を行う */
for (k=0; k<3; k++) {
if (prec[k]*v[k] <= 0) {
for (j=0; j<3*N+1; j++) w[(3*N+1)*k+j] += prec[k]*x[j];
}
}
// x[0]からx[3*N-1]を3ビット分右に移動
for (i=0; i<3*N-3; i++) x[3*N-1-i]=x[3*N-4-i];
/* 前回の相手プレイヤーの手
{prec[0], prec[1], prec[2]} を
入力スロット最前列{x[0],x[1],x[2]}に挿入*/
for (i=0; i<3; i++) x[i]=prec[i];
// 予測ユニットへの入力信号の算定
for (k=0; k<3; k++) v[k]=0;
for (k=0; k<3; k++) {
for (j=0; j<3*N+1; j++) {
v[k] += w[(3*N+1)*k+j]*x[j];
}
}
// 最大入力を受けたユニットの番号を返す
vmax = -1000000; //ダミーの小さな値
kmax = 0;
for (k=0; k<3; k++) {
if (v[k] >= vmax) {
vmax=v[k];
kmax=k;
}
}
return(kmax+1);
}