You are on page 1of 10

mushi-pas-prg.

txt
{ $r+}
program mushi;
uses dos, crt;
{*******************************************************************
OS-9 虫食い算パズルの解法 <かけ算>
------------------------------------------------------------by A. Udagawa
OS9/Basic09
ver 2.0 1988.7
OS9 -> MS.DOS/Quick Basic
1989.12
Quick Basic -> Turbo Pascal
1990.2 coded H.Takahashi
ver 2.2 1992.12
long Integer(9桁まで) version
*******************************************************************}
const
MAXMOJISU = 9;
XHABA = MAXMOJISU * 2;
YHABA = 3 + MAXMOJISU;
XA = XHABA - 1;
YA = YHABA - 1;

{ 最大文字列数(被乗数ならびに乗数)}
{ 格納用配列の横幅
}
{
の縦幅
}
{ 配列用横サイズ = 17
}
{
縦サイズ = 11
}

type
_gyou = record
{ 1行の各種情報
keta: integer;
{ 桁数
head: integer;
{ 有効文字列の左端の位置
tail: integer;
{
の右端の位置
relcnt: integer;
{ リレーションが設定されている文字の数
dfp: integer;
{ 最初に現れるリレーション文字位置
vmin: longint;
{ この行の有効文字列がとりうる最小数値
vmax: longint
{
最大数値
end;
_inta10 = array[0..9] of boolean;
_intaxa = array[0..XA] of integer;

}
}
}
}
}
}
}
}

var
s: array[0..YA, 0..XA] of string[2];
{ キーボード入力された文字を正序化して格納 }
v: array[0..YA, 0..XA] of integer;
{ 対応する文字の値を格納する配列 }
relationtb: array[0..YA, 0..XA, 0..1] of integer;
{ 文字相互間の親子関係を(x,y)表示 }
gyotbl: array[0..YA] of _gyou;
{ 各行の情報を格納 }
ytail: integer;
zsum: integer;
zerocounter:array[0..XA] of integer; { 乗数行に含まれる初期値ゼロを記録 }
lvlid: integer;
{ 入力時に指定する経過表示レベル
}
hour, min, sec, sec100: word;
adtabs: _intaxa;
usednumtb: _inta10;
kanjsw: boolean;

{ 数字が使用済か否かを登録 }
{ 全角入力スイッチ }

procedure chrchk( valu, zeroid:integer;
hijyosu: longint;
x: integer;
var result: integer;
var linked: boolean;
var adtabs: _intaxa;
var usednumtb: _inta10);
{ =======================================================
数字の妥当性をチェック
------------------------------------------------------hijyosu : 被乗数の値
valu
: チェックする数値(この数値が妥当かどうか?)
x
: チェックする乗数行の文字位置
adtabs: 合計行の桁上がり数値保存テーブル
usednumtb:
result : このルーチンが返す値 0=チェクok
linked :
zeroid :
======================================================= }
ページ(1)

mushi-pas-prg.txt
label
10, 20, 90, 99;
var
cyukan: array[0..XA] of integer;
temp: longint;
ws: string;
py, i, j: integer;
dx, dy: integer;
sum, amari: integer;
ch: char;
st: string[1];
begin
result := 99; { 初期値はエラー状態にしておく }
py := XA - x + 2 - zerocounter[x]; { 中間行の y 位置を割り出す }
if zeroid > 0 then goto 10; { 乗数桁 X がゼロでないので縦計チェックのみ }
temp := hijyosu * valu; { 中間行の値 }
{ 1. 桁数チェック }
if (temp < gyotbl[py].vmin) or (temp > gyotbl[py].vmax) then begin
goto 99 { 桁エラー }
end;
str(temp, ws); { 中間行 temp の数値をセット }
j:= 1;
if gyotbl[py].relcnt = 0 then { この行に,親へのリンクの設定がない場合は }
for i := gyotbl[py].head to gyotbl[py].tail do begin
st := copy(ws, j, 1);
v[py, i] := ord(st[1]) - 48;
j := j + 1
end
else begin
{ そうでなく}
{ 2. 中間行に定義文字がある場合の処理 }
for i := gyotbl[py].head to gyotbl[py].tail do begin
{ 中間行の数値を cyukan にセット }
st := copy(ws, j, 1);
cyukan[i] := ord(st[1]) - 48; { 中間行の数値を cyukan にセット }
j := j + 1
end;
for i := gyotbl[py].dfp to gyotbl[py].tail do begin
if relationtb[py, i, 0] < 90 then begin { 親が設定されているなら }
dy := relationtb[py, i, 0]; { 親の座標を取り出し }
dx := relationtb[py, i, 1];
if (dy = py) and (dx <> i) then begin { 同じ行の他の文字へリンク }
if cyukan[dx] <> cyukan[i] then begin
goto 99 { エラー }
end
end else
if v[dy, dx] <> cyukan[i] then begin
goto 99
end
end
else begin
if relationtb[py, i, 0] = 90 then begin { 覆面文字のルートなら }
if usednumtb[cyukan[i]] then begin
goto 99
end;
usednumtb[cyukan[i]] := TRUE
end
end;
if gyotbl[py].relcnt = 1 then goto 20
end; {for}
20:
for i := gyotbl[py].head to gyotbl[py].tail do begin
v[py, i] := cyukan[i]
end
end;
10:
sum := adtabs[x];
ページ(2)

mushi-pas-prg.txt
if py >= 2 then
for i := 2 to py do begin
sum := sum + v[i, x]
end;
amari := sum mod 10;
adtabs[x - 1] := sum div 10;

{ 桁上がり数をセットする }

{ 3. 合計行の x 位置には数字の定義があるか? }
if relationtb[ytail, x, 0] < 90 then begin
dy := relationtb[ytail, x, 0];
dx := relationtb[ytail, x, 1];
if amari <> v[dy, dx] then begin
goto 99 { 桁の合計が定義値と不一致 }
end
end else
if relationtb[ytail, x, 0] = 90 then begin { 覆面文字ルート }
if usednumtb[amari] then begin
goto 99
end;
usednumtb[amari] := TRUE
end;
v[ytail, x] := amari;
if zeroid > 0 then goto 90 ;
{ 4. 最終行の場合は合計値の妥当性をチェック }
if py = ytail - 1 then begin
for i := x - 1 downto gyotbl[ytail].head do begin { 左半分のみチェック }
sum := adtabs[i];
for j := 2 to ytail - 1 do sum := sum + v[j, i]; { 桁合計 }
if i > 0 then
adtabs[i - 1] := sum div 10; { 桁上がり数値をセット }
amari := sum mod 10;
if relationtb[ytail, i, 0] < 90 then begin
dy := relationtb[ytail, i, 0];
dx := relationtb[ytail, i, 1];
if v[dy, dx] <> amari then goto 99
end else
if relationtb[ytail, i, 0] = 90 then begin { 覆面文字ルート }
if usednumtb[amari] then goto 99;
usednumtb[amari] := TRUE
end;
v[ytail, i] := amari
end; {for i}
if v[ytail, gyotbl[ytail].head] = 0 then goto 99
end;
90:
result := 0;
99:
end;
procedure fdisply (id: integer);
{ =================================================
表 示 ル ー チ ン
id = 0 処理結果出力
id <>0 入力式出力
================================================ }
var
i,j,k: integer;
begin
if id = 0 then begin
gettime(hour, min, sec, sec100);
writeln('>> 解発見
', hour:2, ':', min:2, ':',sec:2)
end;
for i := 0 to ytail do begin
for k := 0 to XA do
if (k < gyotbl[i].head) or (k > gyotbl[i].tail) then write('
')
else if id = 0 then write(v[i, k]:4) else write(' ', s[i, k]:2);
writeln;
if (i = 1) or (i = ytail - 1) then begin
{ 線を引く }
for j := 1 to gyotbl[ytail].head do write('
');
for j := gyotbl[ytail].head to XA do write('----');
writeln
ページ(3)

mushi-pas-prg.txt
end
end {for i}
end;
procedure initset ( var adtabs: _intaxa; var usednumtb: _inta10);
{ ==================================================
計算前に各種テーブルをセット
================================================== }
label
5, 10, 11;
var
indata: string;
v1: longint;
v2: longint;
i,j,k, l,m: integer;
x,y: integer;
zerocnt, zerop: integer;
code, digit: integer;
inchar: char;
st, s2: string[2];
procedure init_sub( k, m: integer );
var
x, y: integer;
begin
if (relationtb[k, m, 0] = 99) and ((s[k, m] <> '* ') and (s[k, m] <> '*')) then begin
{ *以外は覆面文字とする }
relationtb[k, m, 0] := 90;
relationtb[k, m, 1] := 90;
{ 同一覆面文字のルート=90 }
for y := 0 to ytail do
for x := gyotbl[y].head to gyotbl[y].tail do
if (relationtb[y, x, 0] = 99) and (s[y, x] = s[k, m])
and ((y <> k) or (x <> m)) then begin
relationtb[y, x, 0] := k;
relationtb[y, x, 1] := m
end
end {if}
end;
begin
{ テーブル初期化 }
for i := 0 to 9 do { usednumtbの配列要素 0--9 は 0 から 9 までの数字を代表し}
usednumtb[i] := FALSE; { その数字が既に使用されている場合は true 値をとる}
for i := 0 to YA do begin { 行情報テーブルを初期化 }
gyotbl[i].relcnt := 0;
gyotbl[i].keta := 0;
{ 桁数
}
gyotbl[i].head := 0;
{ 有効文字列の左端の位置
}
gyotbl[i].tail := 0;
{
の右端の位置
}
gyotbl[i].relcnt := 0; { リレーションが設定されている文字の数 }
gyotbl[i].dfp := 0;
{ 最初に現れるリレーション文字位置
}
gyotbl[i].vmin := 0;
{ この行の有効文字列がとりうる最小数値 }
gyotbl[i].vmax := 0
{
最大数値 }
end;
{ メイン処理 }
writeln;
writeln('=> 文字列を入力してください.',
'( 虫食い文字は*です.終了はcrのみ )');
repeat
for y := 0 to YA do
{ テーブルの初期化 }
for x := 0 to XA do begin
s[y, x] := ' ';
{ シンボル(ソースストリング)テーブル }
v[y, x] := 0;
{ 値テーブル }
relationtb[y, x, 0] := 99; { 同一文字間の親子関係(位置)記録テーブル }
relationtb[y, x, 1] := 99
end;
for i := 0 to XA do begin
adtabs[i] := 0;
zerocounter[i] := 0
ページ(4)

mushi-pas-prg.txt
end;
x := 0;
zerocnt := 0;
zerop := XA + 1;
zsum := 0;
kanjsw := false;
(*

writeln;

*)

for y := 0 to YA do begin
{ 一行分の入力データの取り込み }
write('> '); readln(indata);
if y=0 then begin
if ord(indata[1]) < $20 then begin
l := length(indata) -1;
indata := copy(indata, 2, l)
end;
if (ord(indata[1]) >= $80) and (ord(indata[1]) <= $9F) then kanjsw := TRUE;
if (ord(indata[1]) >= $E0) and (ord(indata[1]) <= $FF) then kanjsw := TRUE;
end;
if indata = '' then begin
{ CR 入力なら }
if y=0 then halt;
if y - 1 <> ytail then begin
write('==> 入力式の行数があいません.');
goto 11
end else begin
ytail := y - 1
end;
goto 10
end;
{ 作業テーブルにパラメーターをセット }
if not kanjsw then
l := length(indata)
else
l := length(indata) div 2;

{ 半角の場合は }
{ 全角の場合は }

if (l > MAXMOJISU) and (y < 2 ) then begin
write('==> 文字数が多すぎます.');
goto 11
end;
gyotbl[y].keta := l; { 行テーブルに桁数登録 }
v1 := 1;
v2 := 9;
for i := 2 to l do begin { 桁数に該当する有効数字を登録 }
v1 := v1 * 10;
v2 := v2 * 10 + 9
end;
gyotbl[y].vmin := v1; { 桁数がとる最小値 }
gyotbl[y].vmax := v2; { 桁数がとる最大値 }
if (y < 2) or (y = ytail) then { 行位置が被乗数行,乗数行,積行の場合は }
i := 0
{ 桁末尾の空白文字数はゼロ }
else begin
{ その他の場合は }
i := y - 2;
{ 次に,乗数行にゼロがある場合について行位置の補正を行う }
repeat
zerop := zerop - 1;
if adtabs[zerop] = 0 then goto 5;
zerocnt := zerocnt + 1
until zerop < gyotbl[1].head;
5:
i := i + zerocnt
end;
gyotbl[y].head := XA - l + 1 - i;
gyotbl[y].tail := gyotbl[y].head + l - 1;

{ 有効文字列の左端位置 }
{ 有効文字列の右端位置 }

{ データをシンボルテーブルに右詰めに移す.}
for x := 0 to l - 1 do
if not kanjsw then
ページ(5)

mushi-pas-prg.txt
s[y, gyotbl[y].head + x] := copy(indata, x + 1, 1) + ' '
else
{ 全角の場合は }
s[y, gyotbl[y].head + x] := copy(indata, x*2+1, 2);
if y = 1 then begin
{ 乗数行の場合のみゼロがあるかチェック? }
for i := gyotbl[1].tail downto gyotbl[1].head do begin
if (s[1, i] = '0 ') or (s[1, i] = '0') then begin
adtabs[i] := 99;
zsum := zsum + 1
end else
{ 乗数行の該当桁は,右から数えて既に何個のゼロが }
zerocounter[i] := zsum
{ 出現しているか,を記録 }
end;
ytail := l + 2 - zsum
{ 入力最終行の位置ポインターを決定する}
end
end; {for y}
10:
fdisply(1);
11:
write('データOK? (Y/N/E) >');
readln(indata);
if (indata = 'E') or (indata = 'e') or (indata = 'E') or (indata = 'e') then halt
until (indata = 'Y') or (indata = 'y') or (indata = 'Y') or (indata = 'y');
lvlid := 0;
write('被乗数行の途中経過表示をする場合は,表示桁(1--) を入力 >');
read(inchar);
if indata <> '' then begin
lvlid := ord(inchar) - 48;
read(inchar)
end;
writeln;
{ 後処理 }
for i := 0 to XA do adtabs[i] := 0;
{シンボルテーブルの数字(0-9)を定義テーブル(relationtb)に登録(親子関係なし)}
for y := 0 to ytail do begin
for x := gyotbl[y].head to gyotbl[y].tail do begin
if not kanjsw then begin
{ 半角数字処理 }
if (s[y, x] >= '0 ') and (s[y, x] <= '9 ') then begin
s2 := copy(s[y,x], 1,1);
val(s2, digit, code);
v[y, x] := digit;
relationtb[y, x, 0] := y;
relationtb[y, x, 1] := x
{ 変更なし用に自分自身を定義する }
end;
end else begin
{ 全角数字処理 }
if (s[y, x] >= '0') and (s[y, x] <= '9') then begin
if s[y, x] = '1' then v[y, x] := 1
else if s[y, x] = '2' then v[y, x] := 2
else if s[y, x] = '3' then v[y, x] := 3
else if s[y, x] = '4' then v[y, x] := 4
else if s[y, x] = '5' then v[y, x] := 5
else if s[y, x] = '6' then v[y, x] := 6
else if s[y, x] = '7' then v[y, x] := 7
else if s[y, x] = '8' then v[y, x] := 8
else if s[y, x] = '9' then v[y, x] := 9
else if s[y, x] = '0' then v[y, x] := 0;
relationtb[y, x, 0] := y;
relationtb[y, x, 1] := x
{ 変更なし用に自分自身を定義する }
end;
end
end{for x}
end; {for y}
{ 同一覆面文字をサーチして,その親子関係を設定する }
for i := gyotbl[0].head to gyotbl[0].tail do init_sub( 0, i );
for i := gyotbl[1].tail downto gyotbl[1].head do begin
init_sub( 1, i );
l := XA - i + 2 - zerocounter[i];
for j := gyotbl[l].tail downto gyotbl[l].head do init_sub( l, j );
ページ(6)

mushi-pas-prg.txt
init_sub( ytail, i )
end;
for i := gyotbl[ytail-1].tail-1 downto gyotbl[ytail].head do
init_sub( ytail, i );
{ 各行の定義文字情報を作成 }
for y := 0 to ytail do begin
for x := gyotbl[y].tail downto gyotbl[y].head do begin
if relationtb[y, x, 0] < 99 then begin
{ この行に含まれるリレーション文字数合計 }
gyotbl[y].relcnt := gyotbl[y].relcnt + 1;
gyotbl[y].dfp := x { 一番始め(左端)のリレーション文字位置 }
end
end
end
end;
procedure solut2( hijyosu: longint; x2: integer;
adtabs: _intaxa; usednumtb: _inta10 );
{=========================================================================
2行目の乗数の値を(右から左方向に)決定するルーチン.
-----------------------------------------------------------------------hijyosu: 被乗数値
x2: 解を決定すべき乗数の左端からの位置(この値を変えて再帰呼出しする)
adtabs: chrchk で使用する合計行の桁上がり数値保存テーブル
usednumtb:
=========================================================================}
label
10, 20, 90;
var
l: integer;
fukws: _inta10;

{ 解の値(0から9までの数字)}

fukroot, linked: boolean;
i: integer;
result: integer;
dx, dy: integer;
begin
{ 初期値設定 }
fukroot := FALSE;
linked := FALSE;
{ 乗数行のX 位置の数値がゼロなら,位置をさらに左に移行 }
while s[1, x2] = '0' do begin
chrchk(0, 1, hijyosu, x2, result, linked, adtabs, usednumtb);
if result <> 0 then goto 90;
x2 := x2 - 1
end;
{ x 位置の乗数の初期値を 1 にセット }
if gyotbl[XA - x2 + 2 - zsum].keta > gyotbl[0].keta then l := 2
else l := 1;
if relationtb[1, x2, 0] < 90 then begin
dx := relationtb[1, x2, 1];
dy := relationtb[1, x2, 0];
{ リンク先 }
l := v[dy, dx];
linked := TRUE
{ リンク先に値をセット }
end else begin
if relationtb[1, x2, 0] = 90 then
{ 覆面文字ルート }
while ( true ) do begin
if not usednumtb[l] then begin
{ まだ使用していない }
usednumtb[l] := TRUE;
fukroot := TRUE;
goto 10
end;
l := l + 1;
if l > 9 then goto 90
end;
10:
end;
ページ(7)

mushi-pas-prg.txt
{ メインルーチン }
repeat
{ --- 初期値をセット }
for i := 0 to 9 do fukws[i] := usednumtb[i];
v[1, x2] := l;
{ 乗数に値をセット }
{ --- メイン処理 }
chrchk(l, 0, hijyosu, x2, result, linked, adtabs, usednumtb);
if result = 0 then
{ 処理位置の文字 s(1,x) の値は1でok }
if x2 = gyotbl[1].head then
{ 全文字へ割当が終了 }
fdisply(0)
else
{ 次の位置の文字処理へ進む }
solut2(hijyosu, x2 - 1, adtabs, usednumtb);
{ --- 後処理 }
if linked then goto 90;
for i := 0 to 9 do usednumtb[i] := fukws[i];
if fukroot then begin
usednumtb[l] := FALSE;
while( true ) do begin
l := l + 1;
if l > 9 then goto 90;
if not usednumtb[l] then begin { まだ使用していない }
usednumtb[l] := TRUE;
goto 20
end
end;
20:;
end else l := l + 1
until l > 9;
90:
end;
procedure solution( x, level: integer;
adtabs: _intaxa; usednumtb: _inta10 );
{===========================================================================
1行目の被乗数の値を(左から右方向順に)決定するルーチン.
--------------------------------------------------------------------------x: 解を決定すべき被乗数の左端からの位置(この値を変えて再帰呼出する)
adtabs: 合計行データ(chrchkで使用)値
usednumtb:
level: 途中経過表示をする際の表示レベル(乗数行の左端より1,2.)
===========================================================================}
label
30, 40, 90;
var
linked, fukroot: boolean;
l: integer;
{ 解の値(0から9の数字)}
hijyosu: longint;
i: integer;
dx, dy: integer;
nx: integer;
procedure milestone; { 途中経過表示 }
var
i: integer;
begin
if level <= lvlid then begin
for i := 1 to level do write('
');
write('位置-',level:2);
write('=', l);
gettime(hour, min, sec, sec100);
writeln(' ', hour:2, ':', min:2, ':',sec:2)
end
end;
begin
ページ(8)

mushi-pas-prg.txt
{ 初期値セット}
linked := FALSE;
fukroot := FALSE;
{ x位置の被乗数の解の初期値をセット }
if x = gyotbl[0].head then l := 1 { 左端文字の初期値は1 }
else l := 0; { それ以外は0 }
if relationtb[0, x, 0] < 90 then begin { この位置の文字は既出なので親を捜す }
dx := relationtb[0, x, 1];
dy := relationtb[0, x, 0];
{ リンク先(親の位置) }
l := v[dy, dx];
{ 初期値には親の値を採用する }
linked := TRUE
{ この文字はリンク値を採用したというリンクサインをセット }
end else
{ 新しい文字なので新しい値を採用する }
if relationtb[0, x, 0] = 90 then begin { 覆面文字のルートの場合は }
while( true ) do begin
if not usednumtb[l] then begin { この値はまだ使用されていないので }
usednumtb[l] := TRUE;
{ 使用済みにし,}
fukroot := TRUE;
{ 覆面文字のルートサインをセットする }
goto 30
end;
l := l + 1;
if l > 9 then goto 90
{ 全ての数字が使われており,新しい数字が得られない }
end;
30:
end;
{ メインルーチン }
repeat
v[0, x] := l;
milestone;

{ 初期値を値テーブルにセット }
{(採用値の)途中経過表示 }

{ --- メイン処理 }
if x <> gyotbl[0].tail then { 被乗数行の右端まで達していないなら }
solution(x + 1, level + 1, adtabs, usednumtb) { 次の位置で再帰呼出 }
else begin
{ 被乗数行は終了したので }
hijyosu := v[0, gyotbl[0].head];
{ 被乗数値を作成して }
for i := gyotbl[0].head + 1 to gyotbl[0].tail do
hijyosu := hijyosu * 10 + v[0, i];
nx := gyotbl[1].tail;
{ 乗数行の解析文字位置(右端から左へ)をセットして }
solut2(hijyosu, nx, adtabs, usednumtb) { 乗数行の値を決定に行く }
end;
{ 後処理:リカーシブコールで値をさらに決定しに行ったが失敗に終わった.
ということは,このルーチンで採用した値が妥当ではなかったわけで
設定した値を戻し,このルーチンを呼んだ親に戻る }
if linked then goto 90; { 子であるので値を他の文字にする必要がない }
if fukroot then begin
{ 覆面文字ルートなら }
usednumtb[l] := FALSE;
while( true ) do begin
l := l + 1;
if l > 9 then goto 90;
if not usednumtb[l] then begin { まだ未使用 }
usednumtb[l] := TRUE;
goto 40
end
end;
40:
end else
l := l + 1
until l > 9;
90:
end;

{ x位置の値を次の数字(+1)にして再トライする }

{ -------------------------------------------メインルーチン
-------------------------------------------- }
begin
clrscr;
ページ(9)

mushi-pas-prg.txt
writeln('
writeln('
writeln('
writeln('
writeln('

+++
+++
+++
+++
+++

虫食い算パズルの解法(かけ算)
a. udagawa : ver 2.2
Long Integer (9 桁まで) version

repeat
initset( adtabs, usednumtb );
gettime(hour, min, sec, sec100);
writeln('解析スタート
', hour:2, ':', min:2, ':',sec:2);
solution( gyotbl[0].head, 1, adtabs, usednumtb );
gettime(hour, min, sec, sec100);
writeln('解析終了
', hour:2, ':', min:2, ':',sec:2)
until hour <0;
end.

ページ(10)

+++');
+++');
+++');
+++');
+++');