動作イメージ

Untitled

ソースコード (Processing)

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);
}

参考