オセロゲームに焦点を合わせたので、細部に入る前にこれを概観することにしました。リンクを貼っていますが、よく分かる様にコードを載せ、コメント(オリジナルのコメントは青)します。
【Othello.txt】
// ■オセロゲームプログラム
// ソースプログラムをコピー&ペーストしたあと、Form1を選択状態にした状態で、
// イベントハンドラを指定できるようにして、
// (プロパティウィンドウでイベントアイコンをクリック)
// ① Form1のMuseClickイベントハンドラをForm1_MouseClickに設定してください。
// ② Form1のLoadイベントハンドラをForm1_Loadに設定してください。
// ③ timer1コントロールをFormに貼り付け、timer1_Tickイベントハンドラ
// を指定してください。
// ④ label1コントロールを貼り付けてください。
// ⑤ radioButton1(Text="先攻"), radioButton2(Text="後"), button1(Text="開始"),
// button2(Text="パス")をForm1の上方に配置し, ボタンにはClickイベントハンドラ
// を指定してください。
// 解説:Formとロードイベントにマウスクリックイベント、TimerとTickイベント、Label、RadioButton x 2(Enabledの初期状態は不明)、Button(数不明)が必要とされることが分かりました。これらは自分で実装する必要があります。
//
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq; // C# 2005以前の場合,Linqsを外すこと。
using System.Text; //Encoding.GetEncodingを使用するのに必要(オリジナルのままでは必要なし。)
// 解説:Visual Studioによるコンパイルの為にいくつかのDLLを使用しますが、MSConpAssでは不要です。なお、以下は機能拡張のために追加しました。
using System.Windows.Forms;
using System.Reflection; //Assemblyを使う為
using System.IO; //StreamReader/Writerを使用するのに必要
namespace Othello
{
public partial class Form1 : Form // 解説:Formから派生させているので、ここでフォームが作れます。
{
public Image image = new Bitmap(1000, 1000);// 表示用ビットマップ
public static Graphics g; // 表示用グラフィックス
// 解説:↑が描画用のフィールドです。
byte[,] dTab = new byte[8, 8]; // マス目のデータ(解説:後でわかりますが、これが「オセロ盤」です。)
int[,] Eval = // 評価用テーブル(解説:8 x 8のオセロ盤と同じだと分かります。)
{{200, 6, 70, 30, 30, 70, 6,200}, // 解説:この配列[8, 8]を(y, x)座標で読むことが後で重要になります。
{ 6, 5, 7, 6, 6, 7, 5, 6},
{ 70, 7, 40, 30, 30, 40, 7, 70},
{ 30, 6, 30, 1, 1, 30, 6, 30},
{ 30, 6, 30, 1, 1, 30, 6, 30},
{ 70, 7, 40, 30, 30, 40, 7, 70},
{ 6, 5, 7, 6, 6, 7, 5, 6},
{200, 6, 70, 30, 30, 70, 6,200}};
// 解説:この8 x 8の盤は「どこに打つと戦略的に有利か」という評価を表すものだということが後でわかります。
int[,] procTable // チェック方向増分表
= { { -1, -1 }, { -1, 0 }, { -1, 1 }, { 0, -1 },
{ 0, 1 }, { 1, -1 }, { 1, 0 }, { 1, 1 }};
// 解説:この0 - 7の2次配列は「盤上の位置の周囲8方向を(y, x)の増減」で表していることが後でわかります。
Boolean gameStart; // ゲーム開始フラグ(解説:これがあとで問題となりす。)
Brush bD = new SolidBrush(Color.DarkGreen); // ブラシ 暗い緑色
Brush bW = new SolidBrush(Color.White); // 白色
Brush bB = new SolidBrush(Color.Black); // 黒色
Brush bG = new SolidBrush(Color.Gray); // 灰色
Pen pB = new Pen(Color.Black, 1); // ペン 黒色
Pen pW = new Pen(Color.White, 1); // 白色
Pen pG = new Pen(Color.DarkGray, 1); // 暗い灰色
Pen p1 = new Pen(Color.White, 2); // 白色(太さ2)
Pen p2 = new Pen(Color.White, 4); // 白色(太さ4)
// 解説:↑はオセロの石を描画する為に定義されています。
public Form1() // 解説:コンストラクターですね。
{
InitializeComponent(); //解説:これがVisual Studioのフォーム、リソース管理用メソッドです。
//解説:MSCompAssではForm1_Loadでコントロール等を配置しようと思います。
}
protected override void OnPaint(PaintEventArgs e) // 描画
// 解説:OnPaintイベント処理ですが、ハンドラー処理でなく、オーバーライド処理をとっています。
{
base.OnPaint(e); display();
// 解説:分かりづらいですが、先ず標準のOnPaintメソッド、次にオセロ盤を描画するdisplay()メソッドを実行します。
e.Graphics.DrawImage(image, 10, 50);
// 解説:Formのクライアントエリア(10, 50)の座標にimageビットマップを貼り付けます。
}
private void Form1_Load(object sender, EventArgs e) // 開始処理
//解説:WM_CREATEに相当するLoadイベント処理です。
{
g = Graphics.FromImage(image); g.Clear(this.BackColor);
// 解説:分かりづらいですが、imageというビットマップのグラフィックハンドルを取り、ウィンドウ背景色で初期化しています。本プログラムではこのimageビットマップに描画して、Formの描画処理(OnPaint)でそれを貼り付けます。
initDtab(); this.Invalidate();
// 解説:分かりづらいですが、オセロ盤の初期化(imageへの描画あり)をして、再描画(Invalidate)しています。
label1.Text = "先攻,後攻を選択して,開始ボタンをクリックして下さい。";
}
private void wait() // 待ち処理
{
timer1.Enabled = true; while (!timer1.Enabled) ;
// 解説:分かりづらいですが、タイマーを有効にし(て、実際に有効になるまで待機し)ます。
// 解説:(補足)タイマーが1/1000秒で呼び出すtimer1_Tickイベントではtimer1.Enabledを偽(false)にするので、一回のイベントでタイマーは無効になります。ひょっとすると別途タイマーのTickインターバルをデフォルトの1/1000秒から変更しているのかもしれません。
}
private void button1_Click(object sender, EventArgs e)
{ // 開始ボタン処理
Initialize(); if (radioButton2.Checked) computer();
// 解説:分かりづらいですが、初期設定(gameStartフラグを偽、オセロ盤の初期化、再描画)を行っています。(初回はForm1_Loadの処理と被ります。)その後、人間が後手ならコンピューターの処理に移ります。
label1.Text = ""; gameStart = true;
// 解説:Form1_Loadの「先攻,後攻を選択して,開始ボタンをクリックして下さい。」を消し、gameStartフラグを偽にしています。
}
public void computer() // コンピュータ側判定処理
{
if (judgeEnd()) return ; // ゲーム終了時なら処理なし(解説:JudgeEndが勝敗判定メソッドのようです。)
label1.Text = ""; gameStart = false; int num;
// 解説:またここで「先攻,後攻を選択して,開始ボタンをクリックして下さい。」を消し、gameStartフラグを偽にして、作業用整数変数を宣言しています。被ってますね。
byte TB = 1; if (radioButton1.Checked) TB = 2;
// 解説:分かりづらいですが、TBが「先攻(1)または後攻(2)」を意味し、初期値は「先攻」で、人間が「先攻(先攻ラジオボタンがチェック済)なら「後攻(2)」にしています。
int MaxPr = -1, MaxI = -1, MaxJ = -1, MaxNum = 0;
// 解説:(後で分かったのですが)MaxPr、MaxI、MaxJ、MaxNumは夫々(評価テーブルの)「最大値」、その位置のY座標とX座標、及び裏返すことが出来る石の数の最大値で、あり得ない値(偽)で初期化しています。
for (int ii = 0; ii < 8; ii++) for (int jj = 0; jj < 8; jj++)
// 解説:面白い書き方ですが、for文のネスト(i==y座標、j==x座標)ですね。(このように書いたり、i, jを多用することと言い、この作者はBasic派じゃなかったのかと思います。)
{ // 解説:以下がオセロ盤のすべての枠(8 x 8)をネストしたforループでチェックする内容です。
int numTotal = 0; // 解説:以下のforループのnumの合計用です。
for (int i = 0; i < 8; i++) // セル(ii, jj)に置いたとき裏返す石の数カウント
// 解説:コメントはこう書いていますが、チェックする枠についてforループで8方向をチェックします。
if ((num = numberCount(ii, jj, TB, i)) > 0) numTotal += num;
// 解説:numberCountメソッドで石を幾つ裏返せるか(num)をチェックし、1つ以上だと加算して行きます。
// 解説:ここで8方向チェックを抜け、評価処理に移ります。
if (numTotal > 0 &&( MaxPr < 0 || MaxPr < Eval[ii, jj] ||
(MaxPr == Eval[ii, jj]&& MaxNum < numTotal))) // 同一優先順位のとき
{ // 裏返すことができる中で最優先順位,同一優先順位のとき裏返す石が多い方
// 解説:コメントが分かりづらいですが、条件式は、
// ①8方向の裏返せる石の数の合計が1以上の場合で、且つ
// ②(ア)評価の最大値が0未満または評価の最大値がその枠の評価テーブルの値より低い、または
// (補足)「その枠の評価値」は必ず1以上(評価テーブル参照)なので赤字の条件式は不要ですね。
// ②(イ)その枠の評価値が評価の最大値と同じで且つ裏返せる石の数が多い場合
// 要すれば、「今までの評価最大値を上回るか、同じでも裏返せる石の数が多い場合」ということです。
MaxPr = Eval[ii, jj];
MaxI = ii; MaxJ = jj; MaxNum = numTotal;
// 解説:評価用のMaxPr、MaxI、MaxJ、MaxNumの値を優越する枠の値で更新します。
}
}
if (MaxPr < 0) MessageBox.Show("打つ場所がありません。パスします");
// 解説:MaxProが-1のままであればすべての枠が対象外になるので。
else replacePlane(MaxI, MaxJ, TB); // 石を裏返す
// 解説:そうでなければこの枠の石を裏返します。(メソッド名が「面を置き換える」で違和感があります。)
this.Invalidate(); gameStart = true;
// 解説:再描画して、gameSmtart(何に使っているのだか?)フラグを真にします。
}
public void person(int ip, int jp) // 対戦者の処理
{
if (dTab[ip, jp] != 0) return; // 既に石が置いてあるところは無視(解説:「無視」というよりも「何もしない」ですね。)
gameStart = false; // 解説:gameStartフラグを偽にします。何故でしょう?
byte TB = 2; if (radioButton1.Checked) TB = 1; // 対戦者の石の種類
// 解説:コンピューターの時と同様、初期値は「後攻(2)」で「先攻ラジオボタンがチェック済なら「先攻(1)」にします。
if (canPlace(ip, jp, TB)) // 石を置くことができるとき,
{ // 裏返し処理
replacePlane(ip, jp, TB); this.Invalidate(); wait();
// 解説:分かりずらいですが、指定座標(ip-Y座標、jp-X座標)の石を裏返し、再描画し、タイマーを有効にします。(↑のwaitメソッドの通り、デフォルトでは1/1000秒の待機でしかない。)
computer(); // コンピュータ側処理
}
else MessageBox.Show("そこには打てません。");
if (judgeEnd()) // ゲーム終了の場合
// 解説:以下に勝敗が付いた時の処理が書かれていますが、これはcomputerメソッドでも同じことであり、むしろjudgeEndメソッドに纏めるべきではないでしょうか?
{
int TB1 = 0; int TB2 = 0; // 両者の石をカウントする(解説:TB1が「先攻」、TB2が「後攻」ですね。)
for (int ii = 0; ii < 8; ii++) for (int jj = 0; jj < 8; jj++)
// 解説:ここでもfor文のネスト(i==y座標、j==x座標)をこのように書いています。
{
if (dTab[ii, jj] == 1) TB1++;
else if (dTab[ii, jj] == 2) TB2++;
}
if (TB1 == TB2) MessageBox.Show("引き分けです。"); // 石の数で勝負判定
else if ((TB1 > TB2 && radioButton1.Checked) || (TB2 > TB1 && radioButton2.Checked))
MessageBox.Show("貴方の勝ちです。黒 = " + TB1 + ", 白 = " + TB2);
else MessageBox.Show("私の勝ちです。黒 = " + TB1 + ", 白 = " + TB2);
// 解説:実際に動かすと、「人間」と「PC」のいずれもが「貴方」か「私」と言っても問題がないので、一瞬「ん?」となります。正確には「先攻の勝ち、後攻の勝ち」という表示にすべきでしょう。
label1.Text = "先攻,後攻を選択して,開始ボタンをクリックして下さい。";
// 解説:ここで消していた(同じ)メッセージを復活させます。
}
gameStart = true; this.Invalidate();
// 解説:GameStartフラグを真にして再描画していますが、このフラグで何をするのでしょうか?
}
private void Form1_MouseClick(object sender, MouseEventArgs e) // 画面をクリックしたら
// 解説:マウスクリックのイベント処理です。イベント処理メソッドがユーザーメソッドと混ざっています。
{ // 対戦者の処理
int x = (e.X - 20) / 50, y=(e.Y-60)/50; if(x>7 || y<0) return;
// 解説:マウス座標を外れていた場合は何もしない、という意味でしょう。
// マウス座標(e.Xとe.Y→共にフォームのクライアント領域の座標)
// から、オセロ盤の描画始点(20, 60)をオフセットし、オセロ盤の
// 枠のサイズ(50 x 50)で(整数)除算したものをオセロ盤の枠座
// 標(0 - 7, 0 - 7)として使うつもりだったんだと思います。
// しかし、クライアント座標がマイナス(例:e.X==10でe.Y==60)
// の場合、x(-0.2)が0となり、条件式("if(x(0)>7 || y(0)<0)")
// を充足してしまいます。また何故正確に
// "if((x < 0 || x > 7) || (y < 0 || y > 7))"
// としなかったのかも不明です。
person(y,x);
// 解説:「画面をクリックしたら対戦者の処理」はその通りなのですが、
// ゲームを開始する前からこのイベント処理が有効となってしまって
// います。ひょっとしてgameStartフラグはこの為にあったのかもし
// れませんが、不明です。
}
private void button2_Click(object sender, EventArgs e) // 「パス」ボタンが押されたら
// 解説:ボタンのイベント処理です。イベント処理メソッドがユーザーメソッドと混ざっています。
{ // 石を置ける場所があるかどうか判定し、置ける場合はパス解消。ない場合にパス。
int TB = 2; if (radioButton1.Checked) TB = 1;
// 解説:コンピューターの時と同様、初期値は「後攻(2)」で「先攻ラジオボタンがチェック済なら「先攻(1)」にします。
if (canPlace(TB)) MessageBox.Show("置ける場所がありますのでパスできません");
else computer();
}
private void Initialize() // 初期設定
{
gameStart = false; initDtab(); this.Invalidate();
// 解説:gameStartフラグを偽にし、オセロ盤を初期化し、再描画する初期設定処理です。
}
private void initDtab() // 石の配置初期設定
{
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) dTab[i, j] = 0;
dTab[3, 3] = dTab[4, 4] = 1; dTab[4, 3] = dTab[3, 4] = 2;
// 解説:オセロ盤を初期化する処理です。黒と白の石を交差して二つづつ置いています。
}
public int numberCount(int ii, int jj, int TB, int i)
// 解説:オセロ盤上の(ii(y)、jj(z))座標にある空の枠に、先手(TB==1)、後手(TB==2)
// が石を置いた場合、いくつの敵石を裏返せるか、というメソッド。
// 引数はy座標、x座標、先手(1)・後手(2)、方向(0 - 7)。
// 戻り値はおけない場合-1、おける場合は裏返せる石の合計数(含0)。
{ // 石を置いたとき裏返す石をカウント。なければ -1 を返却する。
if (dTab[ii,jj] != 0) return -1; // 既に石が配置されていれば置けない。
// 解説:指定座標に石が置かれている場合、「置けない場合(-1)」を返す。
int RB = 3 - TB, ip = procTable[i, 0], jp = procTable[i, 1]; // チェック方向設定
// 解説:RBはTBの敵(1または2)を表す。ip、jpは方向iに関わるx,y座標の増減差分
int IIP = ii + ip, JJP = jj + jp; // 1 個先の配列添字設定。
// 解説:IIP(y)、JJP(x)は指定座標の方向iで差分差だけ隣接する座標
if (IIP < 0 || IIP > 7 || JJP < 0 || JJP > 7) return -1;// 1 個先が(解説:オセロ盤の)範囲外なら置けない。
if (dTab[IIP, JJP] != RB) return -1; // 1 個先が対戦者の石でない(解説:自分の石か空)とき
int numStone = 1; // 置けない(自分の石/石がない)。 // 解説:これは上の行のコメントの続きであろうと思われる。
// 解説:numStoneは裏返せる石の数を意味する。1を代入しているのは隣接するIIP、JJPの石がRBだから。
while (dTab[IIP, JJP] == RB) // 対戦者の石が続くときカウント。
// 解説:既にカウントしたIIP、JJPからループさせる。
{
IIP = IIP + ip; JJP = JJP + jp;
// 解説:IIP、JJPに更に差分を加え、次にチェックする隣接枠にする。
if (IIP < 0 || IIP > 7 || JJP < 0 || JJP > 7) return -1;
// 解説:オセロ盤外であれば自石で挟めず、「置けない場合(-1)」を返す。
numStone++;
// 解説:おける場合は裏返せる石の数を増やす。
}
if (dTab[IIP, JJP] == TB) return numStone--; // 対戦者の石が途切れ箇所が
else return -1; // 自分の石のとき石を置ける。
// 解説:指定した「空枠」に隣接する「敵石」の数を数え、「自石」にたどり着けた場合、
// 既に1をカウントした始点から更にカウントしている為、-1して調整する。
// 【例示】
// (指定座標-空)
// (指定座標-空)●(敵石●があれば、その座標を記録し、カウントを1)
// (指定座標-空)●(ループはその座標からなので、カウントが+1)
// (指定座標-空)●〇(自石に辿り着いた時、カウントは2になっている。)
// 自石に辿り着けなければ(空の場合)、「置けない場合(-1)」を返す。
}
// 解説:「置けない場合(-1)」が定義され、computerメソッドで使われるが、この
// メソッドが使われる3か所の条件式がすべて"> 0"なので、戻り値を0にしても
// 差し支えない。
public Boolean canPlace(int ii, int jj, int TB) // セル(ii, jj)に石を置くこと
{ // ができるかを判定。
for (int i = 0; i < 8; i++)
if (numberCount(ii, jj, TB, i) > 0) return true;
// 解説:オセロ盤の(ii、jj)座標の周囲8方向を、先手(TB==1)または後手
// (TB==2)について、おける場所がないかチェックし、一つでもあれ
// ば真、なければ偽を返します。
return false;
}
public Boolean canPlace(int TB) // 石を置く場所があるかを判定。
{
for (int ii = 0; ii < 8; ii++) for (int jj = 0; jj < 8; jj++)
if (canPlace(ii, jj, TB)) return true;
// 解説:先手(TB==1)または後手(TB==2)について、オセロ盤上でおける
// i場所がないか、ii(y)、jj(x)のネストfor文にすべての枠をチェックし、
// 一つでもあれば真、なければ偽を返します。
return false;
}
public Boolean judgeEnd() // 終了判定。
{
for (int TB = 1; TB < 3; TB++) if (canPlace(TB)) return false;
// 解説:先手(TB==1)、後手(TB==2)共における場所がないかチェックし、
// 一つでもあれば偽、なければ真を返します。
return true;
}
public void replacePlane(int ii, int jj, byte TB) // 石の裏返し。
{ // 解説:「面の置き換え」は違和感があります。FlipStoneではないでしょうか?
byte [, ]Temp=new byte[8, 8];// 石の状態を作業領域に移して
for(int i=0;i<8;i++)for(int j=0;j<8;j++)Temp[i, j]=dTab[i, j];
// 解説:「石の状態を作業領域に移」す、とはオセロ盤をTempにコピーすることです。
for (int i = 0; i < 8; i++) // 作業領域を判定して(解説:正確には「オセロ盤を判定して」です。)
{
int num = numberCount(ii, jj, TB, i),ip = ii, jp = jj;
int di = procTable[i, 0], dj = procTable[i, 1];
if (num > 0) // 石を裏返す(解説:「(作業用盤の)石を裏返す」です。)
for (int j = 0; j <= num; j++, ip += di, jp += dj) Temp[ip, jp] = TB;
}
// 解説:本来のオセロ盤のデータから裏返す(データを書き換える)石(のある枠)
// のデータを読み、データ書き込みはTempを使っています。これは処理に
// よりオセロ盤のデータが書き換わるので、それで誤った判定をしないよう
// にするためです。
for(int i=0;i<8;i++)for(int j=0;j<8;j++)dTab[i, j]=Temp[i, j];
// 解説:最後に書き換えたTempのデータでオセロ盤を更新します。
}
private void dspStone(int i, int j, Brush b, Pen p, float R)// 石の表示
{ // 解説:disp(lay)Stone"の略ですね。
float X = j * 50 + 20, X1 = X + 1;
float Y = i * 50 + 20, Y1 = Y + 1;
g.FillEllipse(bG, X1, Y1, R, R); // 石に厚さがあるような形で表示
g.FillEllipse(b , X, Y, R, R);
g.DrawEllipse(p , X, Y, R, R);
// 解説:見事な描画処理ですね。
}
public void display() // 解説:”display what?"と訊き返したくなりますね。
{
g.FillRectangle(bD, 10, 10, 400, 400); // マス目の表示
for (float X = 60; X < 390; X += 50) g.DrawLine(p1, X, 10, X, 410);
for (float Y = 60; Y < 390; Y += 50) g.DrawLine(p1, 10, Y, 410, Y);
g.DrawRectangle(p2, 10, 10, 400, 400);
// 解説:先ず背景を塗り、縦横の線を描き、外枠線を描いています。
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) // 白い石は大きく見えるので若干小さく表示
if (dTab[i, j] == 1) dspStone(i, j, bB, pG, 30);
else if (dTab[i, j] == 2) dspStone(i, j, bW, pB, 29);
// 解説:各桝に1(先攻-黒)または2(後攻-白)のデータがあれば、石を描画します。
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Enabled = false;
// 解説:Timerが1/1000秒毎に呼ぶメソッドで、呼ばれたらすぐにタイマーを無効にしています。
}
}
}
いかがでしょうか?
取り敢えず、プログラムを動かせるように、
public class App
{
[STAThread]
public static void Main()
{
OthelloForm of = new OthelloForm();
Application.Run(of);
}
}
を追加し、コンストラクター
//コンストラクター
public OthelloForm() //解説:クラス名はForm1からOthelloFormに名称変更しました。
{
//プログラムアイコンをフォームにつける
Assembly myOwn = Assembly.GetEntryAssembly();
this.Icon = Icon.ExtractAssociatedIcon(myOwn.Location);
this.Size = new Size(560, 528);
this.MinimumSize = new Size(530, 508);
this.Text = "Othello";
this.Load += Form_Load;
this.MouseClick += MainForm_MouseClick;
}
とForm1_Load(Form_Loadに名称変更しています)でコントロールを適当に配置しました。なお、変数名をより分かり易くするよう変更したものは赤字にしています。
//Timerの設定
GameTimer = new Timer(); //解説:旧名称timer1
GameTimer.Tick += GameTimer_Tick;
//Labelの設定
label = new Label(); //解説:旧名称label1
label.Location = new Point(10, 10);
label.Width = 400; //Othello盤面幅(400)
label.Text = "先攻、後攻を選択して、開始ボタンをクリックして下さい。";
this.Controls.Add(label);
//RadioButtonsの設定
radioButton1 = new RadioButton();
radioButton1.Location = new Point(10, label.Height + 10);
radioButton1.Text = "先攻";
this.Controls.Add(radioButton1);
radioButton2 = new RadioButton();
radioButton2.Location = new Point(radioButton1.Width + 10, label.Height + 10);
radioButton2.Text = "後攻";
this.Controls.Add(radioButton2);
//Buttonsの設定
startButton = new Button(); //解説:旧名称button1
startButton.Location = new Point(ClientSize.Width - startButton.Width - 10, 20);
startButton.Text = "開始";
startButton.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
startButton.Click += startButton_Click;
this.Controls.Add(startButton);
passButton = new Button(); //解説:旧名称button2
passButton.Location = new Point(ClientSize.Width - passButton.Width - 10, startButton.Height + 30);
passButton.Text = "パス";
passButton.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
passButton.Click += passButton_Click;
this.Controls.Add(passButton);
exitButton = new Button(); //解説:旧名称button3
exitButton.Location = new Point(ClientSize.Width - exitButton.Width - 10, startButton.Height + passButton.Height + 40);
exitButton.Text = "終了";
exitButton.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
exitButton.Click += exitButton_Click;
this.Controls.Add(exitButton);
//ビットマップdisp_imageのGraphicsを取得(解説:旧名称image)
gr_image = Graphics.FromImage(disp_image); //解説:旧名称g
gr_image.Clear(this.BackColor);
this.Invalidate(); //フォームの再描画
//盤の初期化と石の初期配置
InitTable(); //解説:旧名称initDtab
等の変更を加え、一応オリジナルで動くようにはしました。
が、
↑の解説:で書いたようにタイマーをつける意味がない、使われないフラグ(gameStart)がある、開始ボタンを押さないでもオセロ盤をクリックするとperson()メソッドが動いてしまう等の不明点があり、この段階で、
えいやっ...と
自分流に処理の変更やコードをいじくらせていただきました。
最終形
のコードはまたご披露いたします。