さめがめを作成する為の補助問題を考えます。補助問題は、それ自身が目的ではなく、それを考える事が他の問題(ここではさめがめを作るということ)をとくたすけになるという望みがあって考えられる問題のことです。もとの問題(さめがめを作ること)が目的であって、補助問題は目的の為の手段であるといえます。
下図のように縦に 3 つ、横に 3 つセルが並んでおり、それぞれのセルには番号が付けられています。

セルには一つだけ値を格納できます。セルに適当な数値を入れたのが下図です。

このページでは、次の点について考察します。
例えば、5 番のセルを見てください。値として 2 が格納されています。このセルに対して、
があるということを、どのように調べたらよいかを考えましょう。
上図を見れば、その番号がどのような規則で並べられているかがよく分かります。それは、左上すみのセル番号を 1 とし、右に進めば +1、下に進めば +3 だけ増加しています。だから、5 番のセルであれば、自分のセル番号 5 に対して、
とすれば求まります。が、ちょっとまってください。同じことを 4 番のセルに対しても通用するでしょうか。4 番のセルの左隣にはセルなんてありません。つまりは、「隣にはセルが存在するか」ということも調べる必要があります。
セル番号を少し変形した下の図を見てください。

どれも x+3*y という形をしています。x と y のペアは一意です。3 という数字はセルが横にいくつ並んでいるかを表しています(すなわち、横に 3 つ並んでいるということ)。
セルの左(右)隣にはセルがあるかの判定は、こんな感じのコードで表せます:
// 左隣にはセルがあるか
const
MAX_COL_COUNT = 3; // 横に並べられたセルの数
var
Num: Integer;
CellIndex: Integer;
begin
CellIndex := 4; // 知りたいセルの番号
Num := CellIndex mod MAX_COL_COUNT;
if Num = 1 then
// 左隣にはセルがない
else
// 左隣にはセルがある
end;
// 右隣にはセルがあるか
const
MAX_COL_COUNT = 3; // 横に並べられたセルの数
var
Num: Integer;
CellIndex: Integer;
begin
CellIndex := 4; // 知りたいセルの番号
Num := CellIndex mod MAX_COL_COUNT;
if Num = 0 then
// 右隣にはセルがない
else
// 右隣にはセルがある
end;
セルが上、又は下にあるかについても、左右の場合で考えたような事といっしょです。
このとき、カギとなるのは
という 2 つの条件です。この 2 つがあれば、最初に示した図のようにセル番号が並べられているのであれば、解は求まります。
実際に、これをプログラムで書いてみましょう。実行すると以下のようになります。

選択されたセルインデックスに対して、それに隣接するセルが保持する値を TMemo に表示します。セルが存在しない場合は"なし"と表示しています。
Unit1
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Grids, Unit2;
const
MAX_COL_COUNT = 3;
MAX_ROW_COUNT = 3;
MAX_CELL_COUNT = MAX_COL_COUNT*MAX_ROW_COUNT;
DOES_NOT_EXIST_CELL = -1;
type
TForm1 = class(TForm)
StringGrid1: TStringGrid;
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure StringGrid1Click(Sender: TObject);
private
{ Private 宣言 }
FCells: array[1..MAX_CELL_COUNT] of TCell;
function GetEastIndex(x: Integer): Integer;
function GetWestIndex(x: Integer): Integer;
function GetNorthIndex(x: Integer): Integer;
function GetSouthIndex(x: Integer): Integer;
function ExistNeighborCell(x: Integer): Boolean;
procedure DisplayCell;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
begin
Randomize;
StringGrid1.ColCount := MAX_COL_COUNT;
StringGrid1.RowCount := MAX_ROW_COUNT;
for i := Low(FCells) to High(FCells) do
FCells[i] := TCell.Create(Random(100));
DisplayCell;
end;
procedure TForm1.FormDestroy(Sender: TObject);
var
i: Integer;
begin
for i := Low(FCells) to High(FCells) do
FCells[i].Free;
end;
function TForm1.GetEastIndex(x: Integer): Integer;
begin
if (x mod MAX_COL_COUNT) = 0 then
Result := DOES_NOT_EXIST_CELL
else
Result := x+1;
end;
function TForm1.GetWestIndex(x: Integer): Integer;
begin
if (x mod MAX_COL_COUNT) = 1 then
Result := DOES_NOT_EXIST_CELL
else
Result := x-1;
end;
function TForm1.GetNorthIndex(x: Integer): Integer;
begin
if (x-MAX_COL_COUNT) < 1 then
Result := DOES_NOT_EXIST_CELL
else
Result := x-MAX_COL_COUNT;
end;
function TForm1.GetSouthIndex(x: Integer): Integer;
begin
if (x+MAX_COL_COUNT) > MAX_CELL_COUNT then
Result := DOES_NOT_EXIST_CELL
else
Result := x+MAX_COL_COUNT;
end;
procedure TForm1.DisplayCell;
var
i, j, Idx: Integer;
begin
for i := 0 to MAX_ROW_COUNT-1 do
for j := 0 to MAX_COL_COUNT-1 do begin
Idx := (j+1) + MAX_COL_COUNT*i;
StringGrid1.Cells[j, i] := Format('%d: %d', [Idx, FCells[Idx].Num]);
end;
end;
procedure TForm1.StringGrid1Click(Sender: TObject);
procedure DisplayNeighborCellInfo(n, s, e, w: string);
begin
Memo1.Lines.Add('');
Memo1.Lines.Add(Format(' North: %s', [n]));
Memo1.Lines.Add(Format('West: %s East: %s', [w, e]));
Memo1.Lines.Add(Format(' South: %s', [s]));
end;
function GetNeighborCellInfo(Idx: Integer): string;
begin
if ExistNeighborCell(Idx) then
Result := IntToStr(FCells[Idx].Num)
else
Result := 'なし';
end;
var
Idx: Integer;
n, s, e, w: string;
begin
Idx := (StringGrid1.Col+1) + MAX_COL_COUNT*StringGrid1.Row;
n := GetNeighborCellInfo(GetNorthIndex(Idx));
s := GetNeighborCellInfo(GetSouthIndex(Idx));
e := GetNeighborCellInfo(GetEastIndex(Idx));
w := GetNeighborCellInfo(GetWestIndex(Idx));
Memo1.Clear;
Memo1.Lines.Add('CellIndex: '+IntToStr(Idx));
DisplayNeighborCellInfo(n, s, e, w);
end;
function TForm1.ExistNeighborCell(x: Integer): Boolean;
begin
Result := x <> DOES_NOT_EXIST_CELL;
end;
end.
Unit2
unit Unit2;
interface
type
TCell = class
private
FNum: Integer;
public
constructor Create(Num: Integer);
property Num: Integer read FNum;
end;
implementation
{ TCell }
constructor TCell.Create(Num: Integer);
begin
FNum := Num;
end;
end.
TCell クラスのインスタンスがセルを表します。
GetXXXIndex が隣り合うセルのインデックスを求めます。これと ExistNeighborCell 関数を組み合わせて、隣にセルが在るかどうかを調べます。たとえば、左隣のセルを調べる場合は、
var
Idx: Integer;
begin
Idx := 現在注目しているセルのインデックス
if ExistNeighborCell(GetWestIndex(Idx)) then
// 左隣にはセルが在る
else
// 左隣にはセルがない
end;
とします。左隣にセルが在る場合、すなわち、ExistNeighborCell 関数が true を返す場合、GetXXXIndex が返す値(隣のセルのインデックス)を使用して、そのセルが保持する値を取得するようにします。
procedure TForm1.StringGrid1Click(Sender: TObject);
procedure DisplayNeighborCellInfo(n, s, e, w: string);
begin
Memo1.Lines.Add('');
Memo1.Lines.Add(Format(' North: %s', [n]));
Memo1.Lines.Add(Format('West: %s East: %s', [w, e]));
Memo1.Lines.Add(Format(' South: %s', [s]));
end;
function GetNeighborCellInfo(Idx: Integer): string;
begin
if ExistNeighborCell(Idx) then
Result := IntToStr(FCells[Idx].Num)
else
Result := 'なし';
end;
var
Idx: Integer;
n, s, e, w: string;
begin
Idx := (StringGrid1.Col+1) + MAX_COL_COUNT*StringGrid1.Row;
n := GetNeighborCellInfo(GetNorthIndex(Idx));
s := GetNeighborCellInfo(GetSouthIndex(Idx));
e := GetNeighborCellInfo(GetEastIndex(Idx));
w := GetNeighborCellInfo(GetWestIndex(Idx));
Memo1.Clear;
Memo1.Lines.Add('CellIndex: '+IntToStr(Idx));
DisplayNeighborCellInfo(n, s, e, w);
end;
また、サンプルではサイズが縦 3、横 3 となっていますが、これは定数として定義している MAX_COL_COUNT, MAX_ROW_COUNT の値を変更しさえすれば、自由に大きさを変えることが出来る事にも注目してください。
「ある任意のセルの隣にセルが存在するかどうか」を調べ、セルが存在するなら、そのセルが保持する値を取得するプログラムを書きました。
ここで学んだ内容は、さめがめを作るに当たって、とても大切な問題解決への最初の糸口を掴んだ事になります。隣のセルが存在し、そのセルの値を取得できるなら「自分のセルと隣のセルが保持する値が等しいか」ということも調べられます。
「さめがめを作る(その 1)」の繰り返しになりますが、ここで紹介したコードは次回(その 3)へは直接持ち越しません。ページごとに、それぞれ単独のプロジェクトでプログラムを作成してください。