以前、オセロをつくりました。
このときは対戦相手であるコンピュータは着手可能な手を適当に選んでいただけなので、お話にならないくらいに弱いです。これに改良を加えます(もっともワンパターンな行動しかできないコンピュータを叩くのがゲームの面白いところなのかもしれませんが・・・)。
前回同様にピクチャーボックスを8行8列に敷き詰めて盤面をつくります。ただ前回はStoneというPictureBoxを継承したクラスを石がある位置の管理にも使っていましたが、石の描画と石の位置の管理は別のクラスにします。コンピュータに次の一手を考えさせるときに、石の描画と石の位置の管理が同じクラスではちょっと困ったことがおきるからです。
石の描画は前回同様にPictureBoxを継承したクラスを使います。ただしクラスの名前をCellに変えます。Cellクラスがどのようなものかを示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
public class Cell : PictureBox { public int Colum { private set; get; } public int Row { private set; get; } public Cell(int row, int colum) { Colum = colum; Row = row; Click += Cell_Click; } // クリックされたときイベント処理ができるようにする public delegate void CellClickHandler(int x, int y); public event CellClickHandler CellClick; private void Cell_Click(object sender, EventArgs e) { CellClick?.Invoke(Colum, Row); } public StoneColor StoneColor { set { SizeMode = PictureBoxSizeMode.StretchImage; if(value == StoneColor.Black) Image = Properties.Resources.black; if(value == StoneColor.White) Image = Properties.Resources.white; if(value == StoneColor.None) Image = null; } } } |
StoneColorプロパティでProperties.Resources.black;とかProperties.Resources.white;とありますが、以下のようなリソースをつくって追加しておきます。
こちらは石の位置と色を管理するクラスです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public class StonePosition { public StonePosition(int x, int y, StoneColor color) { PositionX = x; PositionY = y; Color = color; } public int PositionX { private set; get; } public int PositionY { private set; get; } public StoneColor Color { set; get; } } |
あと石の色をあらわす列挙型を示します。黒か白か石は置かれていないかのどれかです。
1 2 3 4 5 6 |
public enum StoneColor { None, Black, White, } |
ではフォーム部分をみていきましょう。
コンストラクタ内で自作メソッド CreateCells()でセルを作成します。CreateCells()とGameStart()メソッドはこのあと示します。
1 2 3 4 5 6 7 8 9 10 11 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); CreateCells(); GameStart(); } } |
CreateCells()メソッドを示します。フォームの座標(30, 30)を起点に8×8のピクチャーボックスをセットします。フォーム上にセットしたピクチャーボックスはフィールド変数 Cells に格納して必要なときにアクセスできるようにします。同様にピクチャーボックスの位置もフィールド変数 StonePositions に格納します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public partial class Form1 : Form { Point leftTopPoint = new Point(30, 30); List<StonePosition> StonePositions = new List<StonePosition>(); List<Cell> Cells = new List<Cell>(); const int ColumMax = 8; const int RowMax = 8; void CreateCells() { for(int row=0; row< RowMax; row++) { for(int colum = 0; colum < ColumMax; colum++) { Cell cell = new Cell(row, colum); cell.Parent = this; cell.Size = new Size(40, 40); cell.BorderStyle = BorderStyle.FixedSingle; cell.Location = new Point(leftTopPoint.X +colum * 40, leftTopPoint.Y + row * 40); Cells.Add(cell); cell.CellClick += OnCellClick; StonePositions.Add(new StonePosition(colum, row, StoneColor.None)); cell.BackColor = Color.Green; } } } } |
ゲームが開始したら4つの石を中央に配置します。配置したらCellのプロパティとStonePositionのプロパティを変更します。そのためにSetCellColorメソッドを作成しました。
プレイヤーが黒なら先手、白なら後手です。後手の場合は最初の一手をコンピュータに考えさせます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
public partial class Form1 : Form { bool isYour = false; // プレイヤーの手番かどうか? StoneColor YourColor = StoneColor.Black; StoneColor EnemyColor = StoneColor.White; void GameStart() { foreach (StonePosition pos in StonePositions) SetCellColor(pos.PositionX, pos.PositionY, StoneColor.None); SetCellColor(3, 3, StoneColor.Black); SetCellColor(4, 4, StoneColor.Black); SetCellColor(3, 4, StoneColor.White); SetCellColor(4, 3, StoneColor.White); if (YourColor == StoneColor.Black) { isYour = true; toolStripStatusLabel1.Text = "あなたの手番です。"; } if (YourColor == StoneColor.White) { isYour = false; toolStripStatusLabel1.Text = "コンピュータが考えています"; EnemyThink(); } } void SetCellColor(int posX, int posY, StoneColor color) { Cell cell = Cells.First(x => x.Row == posY && x.Colum == posX); cell.StoneColor = color; StonePosition pos = StonePositions.First(x => x.PositionX == posX && x.PositionY == posY); pos.Color = color; } } |
自分の手番のときにマスをクリックしたら着手できます。ただし石を置くことで相手の石をひっくり返せる場所でなければなりません。
CreateCells()メソッドを実行したときにCellオブジェクトにはイベントハンドラが追加されているのでOnCellClickメソッドが呼び出されます。自分の手番か確認し、着手可能な場所か調べて問題がなければ着手が成立します。その後、コンピュータの手番となります。
また本当に着手可能な場所かどうかは自作メソッド GetRevarseStones(StonePositions, posX, posY, YourColor)で調べます。これはひっくり返される石に対応するStonePositionオブジェクトのリストを返します。リストが空でなければ着手可能な場所と判断できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public partial class Form1 : Form { private void OnCellClick(int posX, int posY) { // 自分の手番か確認する if (!isYour) { toolStripStatusLabel1.Text = "あなたの手番ではありません。"; return; } // 着手可能な場所か調べる List<StonePosition> reversedPositions = GetRevarseStones(StonePositions, posX, posY, YourColor); if (reversedPositions.Count != 0) { SetCellColor(posX, posY, YourColor); foreach (StonePosition pos in reversedPositions) { SetCellColor(pos.PositionX, pos.PositionY, YourColor); } isYour = false; toolStripStatusLabel1.Text = "コンピュータが考えています"; EnemyThink(); } else toolStripStatusLabel1.Text = "ここには打てません"; } } |
ひっくり返される石に対応するStonePositionオブジェクトのリストを返すGetRevarseStonesメソッドを示します。
一度に調べるのは大変なので、上方向にひっくり返せる石があるか?下方向は?と全部で8方向について調べています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class Form1 : Form { List<StonePosition> GetRevarseStones(List<StonePosition> stonePositions, int posX, int posY, StoneColor stoneColor) { List<StonePosition> stones = new List<StonePosition>(); stones.AddRange(GetReverseUp(stonePositions, posX, posY, stoneColor)); stones.AddRange(GetReverseDown(stonePositions, posX, posY, stoneColor)); stones.AddRange(GetReverseLeft(stonePositions, posX, posY, stoneColor)); stones.AddRange(GetReverseRight(stonePositions, posX, posY, stoneColor)); stones.AddRange(GetReverseLeftUp(stonePositions, posX, posY, stoneColor)); stones.AddRange(GetReverseLeftDown(stonePositions, posX, posY, stoneColor)); stones.AddRange(GetReverseRightUp(stonePositions, posX, posY, stoneColor)); stones.AddRange(GetReverseRightDown(stonePositions, posX, posY, stoneColor)); return stones; } } |
着手しようとしている場所の上方向にひっくり返せる石があるか調べる処理を示します。その場所のひとつ上を調べて盤の端であれば対象の石は存在しないことになります。ひとつ上にある石が相手の石の場合は、さらにその上を調べます。
これを繰り返しひとつ以上相手の石が続いて、そのあと自分の石があればその間にある石は挟まれていることになります。それ以外の場合は挟めていないと判断します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
public partial class Form1 : Form { List<StonePosition> GetReverseUp(List<StonePosition> stonePositions, int posX, int posY, StoneColor color) { List<StonePosition> retPos = new List<StonePosition>(); StoneColor enemyColor; if (color == StoneColor.Black) enemyColor = StoneColor.White; else enemyColor = StoneColor.Black; // 盤の端なので石が存在しない if (posY - 1 < 0) return retPos; // となりの石は存在するが自分の石、または石が存在しない StonePosition pos = stonePositions.First(x => x.PositionX == posX && x.PositionY == posY - 1); if (pos.Color == color || pos.Color == StoneColor.None) return retPos; for (int i = 0; ; i++) { // もう片方が存在しない。実は挟めていなかったので空のリストを返す if (posY - 1 - i < 0) return new List<StonePosition>(); // 連続して敵の石の場合、挟めているかもしれないのでリストに追加する StonePosition nextPos = stonePositions.First(x => x.PositionX == posX && x.PositionY == posY - 1 - i); if (nextPos.Color == enemyColor) { retPos.Add(nextPos); continue; } // もう片方が自分の石であるならリストのなかにある敵の石は挟めているということなので結果を返す if (nextPos.Color == color) return retPos; // もう片方が存在しない。実は挟めていなかったので空のリストを返す if (nextPos.Color == StoneColor.None) return new List<StonePosition>(); } } } |
他の7方向も同様に調べます。やっていることはほとんど同じなので一気に示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
public partial class Form1 : Form { List<StonePosition> GetReverseDown(List<StonePosition> stonePositions, int posX, int posY, StoneColor color) { List<StonePosition> retPos = new List<StonePosition>(); StoneColor enemyColor; if (color == StoneColor.Black) enemyColor = StoneColor.White; else enemyColor = StoneColor.Black; if (posY + 1 >= RowMax) return retPos; StonePosition pos = stonePositions.First(x => x.PositionX == posX && x.PositionY == posY + 1); if (pos.Color == color || pos.Color == StoneColor.None) return retPos; for (int i = 0; ; i++) { if (posY + 1 + i >= RowMax) return new List<StonePosition>(); StonePosition nextPos = stonePositions.First(x => x.PositionX == posX && x.PositionY == posY + 1 + i); if (nextPos.Color == enemyColor) { retPos.Add(nextPos); continue; } if (nextPos.Color == color) return retPos; if (nextPos.Color == StoneColor.None) return new List<StonePosition>(); } } List<StonePosition> GetReverseLeft(List<StonePosition> stonePositions, int posX, int posY, StoneColor color) { List<StonePosition> retPos = new List<StonePosition>(); StoneColor enemyColor; if (color == StoneColor.Black) enemyColor = StoneColor.White; else enemyColor = StoneColor.Black; if (posX - 1 < 0) return retPos; StonePosition pos = stonePositions.First(x => x.PositionX == posX - 1 && x.PositionY == posY); if (pos.Color == color || pos.Color == StoneColor.None) return retPos; for (int i = 0; ; i++) { if (posX - 1 - i < 0) return new List<StonePosition>(); StonePosition nextPos = stonePositions.First(x => x.PositionX == posX - 1 - i && x.PositionY == posY); if (nextPos.Color == enemyColor) { retPos.Add(nextPos); continue; } if (nextPos.Color == color) return retPos; if (nextPos.Color == StoneColor.None) return new List<StonePosition>(); } } List<StonePosition> GetReverseRight(List<StonePosition> stonePositions, int posX, int posY, StoneColor color) { List<StonePosition> retPos = new List<StonePosition>(); StoneColor enemyColor; if (color == StoneColor.Black) enemyColor = StoneColor.White; else enemyColor = StoneColor.Black; if (posX + 1 >= ColumMax) return retPos; StonePosition pos = stonePositions.First(x => x.PositionX == posX + 1 && x.PositionY == posY); if (pos.Color == color || pos.Color == StoneColor.None) return retPos; for (int i = 0; ; i++) { if (posX + 1 + i >= ColumMax) return new List<StonePosition>(); StonePosition nextPos = stonePositions.First(x => x.PositionX == posX + 1 + i && x.PositionY == posY); if (nextPos.Color == enemyColor) { retPos.Add(nextPos); continue; } if (nextPos.Color == color) return retPos; if (nextPos.Color == StoneColor.None) return new List<StonePosition>(); } } List<StonePosition> GetReverseLeftUp(List<StonePosition> stonePositions, int posX, int posY, StoneColor color) { List<StonePosition> retPos = new List<StonePosition>(); StoneColor enemyColor; if (color == StoneColor.Black) enemyColor = StoneColor.White; else enemyColor = StoneColor.Black; if (posX - 1 < 0 || posY - 1 < 0) return retPos; StonePosition pos = stonePositions.First(x => x.PositionX == posX - 1 && x.PositionY == posY - 1); if (pos.Color == color || pos.Color == StoneColor.None) return retPos; for (int i = 0; ; i++) { if (posX - 1 - i < 0 || posY - 1 - i < 0) return new List<StonePosition>(); StonePosition nextPos = stonePositions.First(x => x.PositionX == posX - 1 - i && x.PositionY == posY - 1 - i); if (nextPos.Color == enemyColor) { retPos.Add(nextPos); continue; } if (nextPos.Color == color) return retPos; if (nextPos.Color == StoneColor.None) return new List<StonePosition>(); } } List<StonePosition> GetReverseRightDown(List<StonePosition> stonePositions, int posX, int posY, StoneColor color) { List<StonePosition> retPos = new List<StonePosition>(); StoneColor enemyColor; if (color == StoneColor.Black) enemyColor = StoneColor.White; else enemyColor = StoneColor.Black; if (posX + 1 >= ColumMax || posY + 1 >= RowMax) return retPos; StonePosition pos = stonePositions.First(x => x.PositionX == posX + 1 && x.PositionY == posY + 1); if (pos.Color == color || pos.Color == StoneColor.None) return retPos; for (int i = 0; ; i++) { if (posX + 1 + i >= ColumMax || posY + 1 + i >= RowMax) return new List<StonePosition>(); StonePosition nextPos = stonePositions.First(x => x.PositionX == posX + 1 + i && x.PositionY == posY + 1 + i); if (nextPos.Color == enemyColor) { retPos.Add(nextPos); continue; } if (nextPos.Color == color) return retPos; if (nextPos.Color == StoneColor.None) return new List<StonePosition>(); } } List<StonePosition> GetReverseRightUp(List<StonePosition> stonePositions, int posX, int posY, StoneColor color) { List<StonePosition> retPos = new List<StonePosition>(); StoneColor enemyColor; if (color == StoneColor.Black) enemyColor = StoneColor.White; else enemyColor = StoneColor.Black; if (posX + 1 >= ColumMax || posY - 1 < 0) return retPos; StonePosition pos = stonePositions.First(x => x.PositionX == posX + 1 && x.PositionY == posY - 1); if (pos.Color == color || pos.Color == StoneColor.None) return retPos; for (int i = 0; ; i++) { if (posX + 1 + i >= ColumMax || posY - 1 - i < 0) return new List<StonePosition>(); StonePosition nextPos = stonePositions.First(x => x.PositionX == posX + 1 + i && x.PositionY == posY - 1 - i); if (nextPos.Color == enemyColor) { retPos.Add(nextPos); continue; } if (nextPos.Color == color) return retPos; if (nextPos.Color == StoneColor.None) return new List<StonePosition>(); } } List<StonePosition> GetReverseLeftDown(List<StonePosition> stonePositions, int posX, int posY, StoneColor color) { List<StonePosition> retPos = new List<StonePosition>(); StoneColor enemyColor; if (color == StoneColor.Black) enemyColor = StoneColor.White; else enemyColor = StoneColor.Black; if (posX - 1 < 0 || posY + 1 >= RowMax) return retPos; StonePosition pos = stonePositions.First(x => x.PositionX == posX - 1 && x.PositionY == posY + 1); if (pos.Color == color || pos.Color == StoneColor.None) return retPos; for (int i = 0; ; i++) { if (posX - 1 - i < 0 || posY + 1 + i >= RowMax) return new List<StonePosition>(); StonePosition nextPos = stonePositions.First(x => x.PositionX == posX - 1 - i && x.PositionY == posY + 1 + i); if (nextPos.Color == enemyColor) { retPos.Add(nextPos); continue; } if (nextPos.Color == color) return retPos; if (nextPos.Color == StoneColor.None) return new List<StonePosition>(); } } } |
次にコンピュータの手番の処理を考えたいのですが、そのまえにそもそも手自体が存在するのかを調べるメソッドを作成しておきます。
取得したいのは、石が置かれていない場所で相手の石を挟むことができる場所です。第一引数は盤面の全部のマスです。これを総当りで調べています。そして結果をStonePositionのリストで返します。
1 2 3 4 5 6 7 8 9 10 11 |
public partial class Form1 : Form { // 石が置かれていない場所で挟むことができる場所を探す。 List<StonePosition> GetRevarsePlace(List<StonePosition> stonePositions, StoneColor color) { return stonePositions.Where( x => x.Color == StoneColor.None && GetRevarseStones(stonePositions, x.PositionX, x.PositionY, color).Count > 0 ).ToList(); } } |
ではコンピュータの手番の処理を考えます。
まず1秒間の間をおきます。これは特に意味があるわけではなく、自分が着手したあと一瞬でまた手番が回ってくるのは違和感があるので間をおくだけです。
プレイヤーの石を挟むことができる場所を探すのですが、複数見つかったら適当に選ぶのではなくちょっと考えさせます。
まず角を安易に取らせてはいけません。見つけ出した候補手のなかから次にプレイヤーに角を取られる手はいったん候補から外します。そのうえで候補手が複数みつかったらプレイヤーの次の手が一番少なくなるものを選びます。
もし候補がない場合は角を取られるような場所でも仕方がないので適当に選びます。また着手可能な場所がひとつも存在しないのであれば、その場合はパスをします。
コンピュータの着手後、プレイヤーに手番が回ってくるのですが、この場合、プレイヤーはパスをするしかない場合も考えられます。そのときはひきつづきコンピュータの手番となります。もしコンピュータもパスするしかない場合はすべてのマス目が埋まっていなくても試合終了となります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
public partial class Form1 : Form { async void EnemyThink() { await Task.Delay(1000); bool isComPassed = false; // プレイヤーの石を挟むことができる場所を探す。 List<StonePosition> enemyHands = GetRevarsePlace(StonePositions, EnemyColor); List<NextCandidate> nextCandidates = new List<NextCandidate>(); foreach (StonePosition hand in enemyHands) { // ためしに石を置いてひっくり返す // StonePositionsのコピーをつくる List<StonePosition> copiedPositions = new List<StonePosition>(); foreach (StonePosition pos in StonePositions) copiedPositions.Add(new StonePosition(pos.PositionX, pos.PositionY, pos.Color)); var enemyPos = copiedPositions.First(x => x.PositionX == hand.PositionX && x.PositionY == hand.PositionY); enemyPos.Color = EnemyColor; List<StonePosition> reversedPositions = GetRevarseStones(copiedPositions, hand.PositionX, hand.PositionY, EnemyColor); foreach (StonePosition pos in reversedPositions) pos.Color = EnemyColor; List<StonePosition> yourHands = GetRevarsePlace(copiedPositions, YourColor); // 角を奪われるような手は候補から外す bool isDeprivedConer = yourHands.Any(x => (x.PositionX == 0 && x.PositionY == 0) || (x.PositionX == 0 && x.PositionY == RowMax - 1) || (x.PositionX == ColumMax - 1 && x.PositionY == 0) || (x.PositionX == ColumMax - 1 && x.PositionY == RowMax - 1) ); if (!isDeprivedConer) nextCandidates.Add(new NextCandidate(hand, yourHands.Count)); } // 敵はできるだけプレイヤーの次の手が少なくなるような手を選ぶ StonePosition nextHand = null; if (nextCandidates.Count > 0) { int min = nextCandidates.Min(x => x.HandsCount); nextHand = nextCandidates.First(x => x.HandsCount == min).StonePosition; } else { // 候補手がない場合は、角を奪われても仕方がないので適当に選ぶしかない int count = enemyHands.Count; if (count > 0) { Random random = new Random(); int r = random.Next(count); nextHand = enemyHands[r]; } else { // 次の手がまったく存在しない場合はパス isComPassed = true; } } if (nextHand != null) { SetCellColor(nextHand.PositionX, nextHand.PositionY, EnemyColor); List<StonePosition> reversedPositions2 = GetRevarseStones(StonePositions, nextHand.PositionX, nextHand.PositionY, EnemyColor); foreach (StonePosition pos in reversedPositions2) SetCellColor(pos.PositionX, pos.PositionY, EnemyColor); } // プレイヤーの手番になったとき、次の手は存在するのか? List<StonePosition> yourNextHands = GetRevarsePlace(StonePositions, YourColor); if (yourNextHands.Count > 0) { isYour = true; if (!isComPassed) toolStripStatusLabel1.Text = "あなたの手番です。"; else toolStripStatusLabel1.Text = "コンピュータはパスしました。あなたの手番です。"; } else { if (!isComPassed) { toolStripStatusLabel1.Text = "あなたの手番ですがパスするしかありません。"; EnemyThink(); } else End(); } } // 次の手の候補の情報を管理するクラス public class NextCandidate { public NextCandidate(StonePosition pos, int count) { HandsCount = count; StonePosition = pos; } // 次にプレイヤーに手番が回ってきたときの候補手の数 public int HandsCount { get; private set; } // コンピュータが着手する位置情報 public StonePosition StonePosition { get; private set; } } } |
すべてのマス目が埋まってしまった場合、双方がパスをするしかない場合は試合終了です。お互いの石を数えてどちらが勝ったのかを判定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class Form1 : Form { void End() { int black = StonePositions.Where(x => x.Color == StoneColor.Black).Count(); int white = StonePositions.Where(x => x.Color == StoneColor.White).Count(); string winner; if(black > white) winner = "黒の勝ちです。"; else if (black < white) winner = "白の勝ちです。"; else winner = "引き分けです。"; string mes = String.Format("終了しました。{0} 対 {1} で{2}", black, white, winner); toolStripStatusLabel1.Text = mes; } } |