オセロゲーム書いてみました

開発言語にはC#を選びました。

その理由は、Windowsで動くゲームやアプリケーションを作れて、
開発環境が簡単に手に入って、会社をやめたときにつぶしが利く言語
にしたかったからです。
まあ別にJavaでもRubyでもなんでもよかったのですが。

開発環境と動作環境は、MicroSoft Visual C# 2005 Express Editionです。
MicroSoftのページからダウンロードできます。

つっこみどころ満載だと思います。
おいおい直していきます。

                                                                                                            • -

OthelloEngine.cs

using System;

namespace ByMonguri
{
class OthelloEngine
{
public PieceColor[,] cell;
public int nblacks, nwhites, npieces;
public int nlines, ncolumns;
public Turn turn;

public OthelloEngine(int in_ncolumns, int in_nlines)
{
cell = new PieceColor[in_ncolumns + 2, in_nlines + 2];

for (int i = 0; i < in_ncolumns + 2; i++) {
for (int j = 0; j < in_nlines + 2; j++) {
cell[i, j] = PieceColor.NONE;
}
}

cell[in_ncolumns / 2, in_nlines / 2] = PieceColor.WHITE;
cell[in_ncolumns / 2 + 1, in_nlines / 2 + 1] = PieceColor.WHITE;
cell[in_ncolumns / 2 + 1, in_nlines / 2] = PieceColor.BLACK;
cell[in_ncolumns / 2, in_nlines / 2 + 1] = PieceColor.BLACK;

nblacks = nwhites = 2;
npieces = 4;
nlines = in_nlines;
ncolumns = in_ncolumns;

turn = Turn.BLACK;
}

public bool AddPiece(int in_i, int in_j)
{
PieceColor color;
bool ok;

if (Turn.BLACK == turn) {
color = PieceColor.BLACK;
} else {
color = PieceColor.WHITE;
}

ok = CanAddPiece(in_i, in_j, color);
if (!ok) {
return false;
}

cell[in_i, in_j] = color;
npieces++;
if (PieceColor.BLACK == color) {
nblacks++;
} else {
nwhites++;
}

ReversePieces(in_i, in_j, color, Direction.UP);
ReversePieces(in_i, in_j, color, Direction.UPRIGHT);
ReversePieces(in_i, in_j, color, Direction.RIGHT);
ReversePieces(in_i, in_j, color, Direction.DOWNRIGHT);
ReversePieces(in_i, in_j, color, Direction.DOWN);
ReversePieces(in_i, in_j, color, Direction.DOWNLEFT);
ReversePieces(in_i, in_j, color, Direction.LEFT);
ReversePieces(in_i, in_j, color, Direction.UPLEFT);

ok = IsNextTurnSkip();
if (!ok) {
if (Turn.BLACK == turn) {
turn = Turn.WHITE;
} else {
turn = Turn.BLACK;
}
}

return true;
}

private bool IsNextTurnSkip()
{
bool ok;
PieceColor color;

for (int i = 1; i <= ncolumns; i++) {
for (int j = 1; j <= nlines; j++) {
if (PieceColor.NONE != cell[i, j]) {
continue;
}

if (Turn.BLACK == turn) {
color = PieceColor.WHITE;
} else {
color = PieceColor.BLACK;
}

ok = CanAddPiece(i, j, color);
if (ok) {
return false;
}
}
}

return true;
}

private bool CanAddPiece(int in_i, int in_j, PieceColor in_color)
{
int n = 0;

n = NumPinchedPieces(in_i, in_j, in_color, Direction.UP);
if (n > 0) {
return true;
}
n = NumPinchedPieces(in_i, in_j, in_color, Direction.UPRIGHT);
if (n > 0) {
return true;
}
n = NumPinchedPieces(in_i, in_j, in_color, Direction.RIGHT);
if (n > 0) {
return true;
}
n = NumPinchedPieces(in_i, in_j, in_color, Direction.DOWNRIGHT);
if (n > 0) {
return true;
}
n = NumPinchedPieces(in_i, in_j, in_color, Direction.DOWN);
if (n > 0) {
return true;
}
n = NumPinchedPieces(in_i, in_j, in_color, Direction.DOWNLEFT);
if (n > 0) {
return true;
}
n = NumPinchedPieces(in_i, in_j, in_color, Direction.LEFT);
if (n > 0) {
return true;
}
n = NumPinchedPieces(in_i, in_j, in_color, Direction.UPLEFT);
if (n > 0) {
return true;
}

return false;
}

private int NumPinchedPieces(int in_i, int in_j, PieceColor in_color, Direction in_dir)
{
int k = 0, l = 0;
int num = 0;
bool pinched = false;

switch (in_dir) {
case Direction.UP:
l = -1;
break;
case Direction.UPRIGHT:
k = 1;
l = -1;
break;
case Direction.RIGHT:
k = 1;
break;
case Direction.DOWNRIGHT:
k = 1;
l = 1;
break;
case Direction.DOWN:
l = 1;
break;
case Direction.DOWNLEFT:
k = -1;
l = 1;
break;
case Direction.LEFT:
k = -1;
break;
case Direction.UPLEFT:
k = -1;
l = -1;
break;
default:
throw new ArgumentException("Direction");
}

for (int i = in_i + k, j = in_j + l; cell[i, j] != PieceColor.NONE; i += k, j += l) {
if (cell[i, j] == in_color) {
pinched = true;
break;
} else {
num++;
}
}

if (pinched) {
return num;
} else {
return 0;
}
}

private enum Direction
{
UP, UPRIGHT, RIGHT, DOWNRIGHT, DOWN, DOWNLEFT, LEFT, UPLEFT
}

private void ReversePieces(int in_i, int in_j, PieceColor in_color, Direction in_dir)
{
int k = 0, l = 0;
bool pinched;

switch (in_dir) {
case Direction.UP:
l = -1;
break;
case Direction.UPRIGHT:
k = 1;
l = -1;
break;
case Direction.RIGHT:
k = 1;
break;
case Direction.DOWNRIGHT:
k = 1;
l = 1;
break;
case Direction.DOWN:
l = 1;
break;
case Direction.DOWNLEFT:
k = -1;
l = 1;
break;
case Direction.LEFT:
k = -1;
break;
case Direction.UPLEFT:
k = -1;
l = -1;
break;
default:
throw new ArgumentException("Direction");
}

pinched = false;
for (int i = in_i + k, j = in_j + l; cell[i, j] != PieceColor.NONE; i = i + k, j = j + l)
{
if (cell[i, j] == in_color) {
pinched = true;
break;
}
}

if (pinched)
{
int nreversed = 0;

for (int i = in_i + k, j = in_j + l; cell[i, j] != in_color; i = i + k, j = j + l) {
cell[i, j] = in_color;
nreversed++;
}

switch (in_color) {
case PieceColor.BLACK:
nblacks += nreversed;
nwhites -= nreversed;
break;
case PieceColor.WHITE:
nwhites += nreversed;
nblacks -= nreversed;
break;
default:
throw new ArgumentException("in_color");
}
}
}

public PieceColor GetColor(int in_i, int in_j)
{
return cell[in_i, in_j];
}

public Turn Turn
{
set
{
turn = value;
}
get
{
return turn;
}
}

public int Nlines
{
set
{
if (value < 0) {
throw new ArgumentOutOfRangeException("Nlines");
} else {
nlines = value;
}
}
get
{
return nlines;
}
}

public int Ncolumns
{
set
{
if (value < 0) {
throw new ArgumentOutOfRangeException("Ncolumns");
} else {
ncolumns = value;
}
}
get
{
return ncolumns;
}
}

public int Nblacks
{
set
{
if (value < 0) {
throw new ArgumentOutOfRangeException("Nblacks");
} else {
nblacks = value;
}
}
get
{
return nblacks;
}
}

public int Nwhites
{
set
{
if (value < 0) {
throw new ArgumentOutOfRangeException("Nwhites");
} else {
nwhites = value;
}
}
get
{
return nwhites;
}
}

public int Npieces
{
set
{
if (value < 0) {
throw new ArgumentOutOfRangeException("Npieces");
} else {
npieces = value;
}
}
get
{
return npieces;
}
}

}
}

                                                                                                                                          • -

OthelloApplication.cs

using System;
using System.Drawing;
using System.Windows.Forms;

namespace ByMonguri
{
public class OthelloApplication : Form
{
private OthelloEngine engine;
private Point focus;
private const int ncolumns = 8, nlines = 8;

public enum FocusDirection
{
LEFT, RIGHT, UP, DOWN
}

protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);

Graphics g = e.Graphics;
Font font = new Font(Font.Name, 16);
Brush brush = new SolidBrush(Color.Black);
string text;

for (int y = 1; y <= engine.Nlines; y++)
{
int dx = 1, dy = font.Height * y; //描画座標

for (int x = 1; x <= engine.Ncolumns; x++)
{
if (x == focus.X && y == focus.Y) {
g.DrawString("■", font, brush, dx, dy);
} else if (PieceColor.BLACK == engine.GetColor(x, y)) {
g.DrawString("●", font, brush, dx, dy);
}
else if (PieceColor.WHITE == engine.GetColor(x, y))
{
g.DrawString("○", font, brush, dx, dy);
} else {
g.DrawString("□", font, brush, dx, dy);
}
dx += font.Height;
}
}

if (Turn.BLACK == engine.Turn) {
g.DrawString("黒番", font, brush, 1, font.Height * (engine.Nlines + 2));
} else {
g.DrawString("白番", font, brush, 1, font.Height * (engine.Nlines + 2));
}

text = "黒: " + engine.Nblacks + ", 白: " + engine.Nwhites;
g.DrawString(text, font, brush, 1, font.Height * (engine.Nlines + 4));

if (0 == engine.Nwhites) {
g.DrawString("黒勝利", font, brush, 1, font.Height * (engine.Nlines + 6));
} else if (0 == engine.Nblacks) {
g.DrawString("白勝利", font, brush, 1, font.Height * (engine.Nlines + 6));
}

if (engine.Ncolumns * engine.Nlines == engine.Npieces) {
if (engine.Nblacks > engine.Nwhites) {
g.DrawString("黒勝利", font, brush, 1, font.Height * (engine.Nlines + 6));
} else if (engine.Nblacks < engine.Nwhites) {
g.DrawString("白勝利", font, brush, 1, font.Height * (engine.Nlines + 6));
} else {
g.DrawString("引き分け", font, brush, 1, font.Height * (engine.Nlines + 6));
}
}
}

protected override void OnKeyDown(KeyEventArgs e)
{
bool ok = false;

base.OnKeyDown(e);
if (e.KeyCode == Keys.Left) ok = MoveFocus(FocusDirection.LEFT);
else if (e.KeyCode == Keys.Right) ok = MoveFocus(FocusDirection.RIGHT);
else if (e.KeyCode == Keys.Up) ok = MoveFocus(FocusDirection.UP);
else if (e.KeyCode == Keys.Down) ok = MoveFocus(FocusDirection.DOWN);
else if (e.KeyCode == Keys.Enter) {
ok = engine.AddPiece(focus.X, focus.Y);
if (!ok) {
throw new ArgumentException("NG");
}
} else
throw new ArgumentException("Key not accepted");

if (ok) {
Invalidate();
}
}

public bool MoveFocus(FocusDirection fd)
{
if (fd == FocusDirection.LEFT) {
if (focus.X > 1) {
focus.X--;
return true;
}
} else if (fd == FocusDirection.RIGHT) {
if (focus.X < 8) {
focus.X++;
return true;
}
} else if (fd == FocusDirection.UP) {
if (focus.Y > 1) {
focus.Y--;
return true;
}
} else {
if (focus.Y < 8) {
focus.Y++;
return true;
}
}

return false;
}

public OthelloApplication()
{
engine = new OthelloEngine(ncolumns, nlines);
focus = new Point(ncolumns / 2, nlines / 2);
}

static void Main()
{
Form form = new OthelloApplication();
form.Width = 400;
form.Height = 600;
Application.Run(form);
}
}
}

                                                                                                                                                    • -

Turn.cs

namespace ByMonguri
{
public enum Turn
{
BLACK, WHITE
}
}

                                                                                                                                            • -

PieceColor.cs

namespace ByMonguri
{
public enum PieceColor
{
BLACK, WHITE, NONE
}
}

                                                                                                                                            • -