;******************************************************************
; 爆弾捜しゲーム：ｂｓ
;	アイディアは違うけど、プログラムは
;		Copyright (C) 1993-95 by AIG-SOFT
;******************************************************************
; これが最初に作ったサンプル
; かなりおっきい。主にスピードのテストと総合的な動作チェックをした。

onbrk ONBREAK

; 定数定義 
MX	VAR 24 2 / =		; マップ横幅
MY	VAR 10 =		; マップ縦幅

BOMB	VAR '*' =		; 爆弾コード：0x2a <= 0x3f
ERR	VAR 9 =			; 判定間違い
BMBIT	VAR 7 BIT =		; 爆弾在るはずビット
CHBIT	VAR 6 BIT =		; わかんないビット
OPBIT	VAR BMBIT CHBIT | =	; オープン状態を示すビット
NOPBIT	VAR OPBIT ~ LOW.B =	; キャラクターのみを取り出すビット

; 画面表示ホーム座標
SX	VAR 2 =		; sx>=2 & sx+(MX+1)*2<画面ｘ幅
SY	VAR 4 =		; sy>=4 & sy+MY+1<画面ｙ幅

; キーコード
ESC	VAR $1b =
BRK	VAR '!' =

; グローバル変数 
( MX MY ). map  ARY.W ,	; 現在マップ
( MX MY ). map0 ARY.W ,	; 表示用仮想画面

( wx wy ). VARS		; マップ幅
( seed bomb ). VARS	; 全爆弾数,爆弾ありそう数
all VAR	,		; 開けるべき全ますの数
( ts dt ). VARS		; 開始時間、経過時間
;
; メインルーチン
;
[N
ret VAR ,
	wx MX =
	wy MY =
	seed wx wy * 8 / = /* 全体の1/8にする */
	REPEAT
		; 爆弾配置
		all seed BombSeed. =
		bomb 0 =
		BombCount.	; 各ますごとの周りの爆弾数数え
		dt 0 = 		; 経過時間
		InitScreen.	; 初期画面表示
		
		; キー操作
		ret CursorMove. =.
		. -2 == IF. BREAK, ; 強制終了
		SWITCH
			-1 case ; やり直し
				1 MapPrintAll. ; 全表示
				BREAK
			0 case ; Game clear
				GameClear.
				BREAK
			1 case ; Game over
				1 MapPrintAll. ; 全表示
				GameOver.
				BREAK
		SEND
		; 再開処理
		20 2 LOCATE "《もう一度しますか？：Ｙ／Ｎ》" PRINTS 
		ret GETCH TOUPPER =
		ret 'N' ==
		ret ESC == ||
		ret BRK == ||
	UNTIL
0 ]N
end

;****************************************************************

CursorMove:
; カーソル移動＆操作
{
( x y c t pt fend ). VARS
	; 初期座標 : 爆弾が無い所に置く
	fend 0 =
	0 wx FOR.
		0 wy FOR.
			; if ((map[y][x]&NOPBIT)!=BOMB) goto CM1;
			( J? I? ). map @ -> NOPBIT &
			BOMB != IF
				x J? =
				y I? =
				fend 1 =
				BREAK
			ENDIF
		NEXT
		fend IF. BREAK
	NEXT
	
	fend 0 =
	ts 1 CLOCK = ; 開始時間
	REPEAT
		( x y ) Locate2.
		WHILE ; キー入力の無い間
			kbhit IF. BREAK
			t 1 CLOCK =
			t ts - dt > IF ; 秒が変った
				dt . ++ =
				PrintTime.
				( x y ) Locate2.
			ENDIF
		WEND
		c GETCH =.
		SWITCH
			ESC case ; やり直し
				fend 1 =
				-1
				BREAK
			
			BRK case ; 強制終了
				fend 1 =
				-2
				BREAK
			
			; カーソルは画面端でロールする
			'2' case ; ↓
				; if (y<wy-1) y++;
				y wy -- <
				IF
					y . ++ =
				ELSE
					y 0 =
				ENDIF
				BREAK
			'4' case ; ←
				; if (x>0) x--;
				x 0 >
				IF
					x . -- =
				ELSE
					x wx -- =
				ENDIF
				BREAK
			'6' case ; →
				; if (x<wx-1) x++;
				x wx -- <
				IF
					x . ++ =
				ELSE
					x 0 =
				ENDIF
				BREAK
			'8' case ; ↑
				; if (y>0) y--;
				y 0 >
				IF
					y . -- =
				ELSE
					y wy -- =
				ENDIF
				BREAK
			'5' case ; Check
				( x y ) CheckMapPoint.
				MapPrint. ,
				; チェックでは全開きにはならない
				BREAK
			
			'0' case
			' ' case ; Open
				( x y ) OpenMapPoint.
				if
					fend 1 =
					1 ; リターン値
				ELSE
					; 表示
					MapPrint.
					IF
						fend 1 =
						0 ; リターン値
					ENDIF
				ENDIF
				BREAK
		SEND
	fend UNTIL
}
RETURN

;****************************************************************

MapPrint:
; マップを表示する＆仮想画面変更
; リターン値：0=まだ全ては開いていない , 1=全部開いた
{
c VAR ,
all0 VAR 0 =
	0 wy FOR.
		0 wx FOR.
			c ( I? J? ). map @ -> = ; c=map[y][x];
			( I? J? ). map0 @ -> c != IF ;; if (map0[y][x]!=c) {
				; 画面に変更がある
				( I? J? ) Locate2.
				c PrintChr.
				; map0[y][x]=c ; 仮想画面も変更
				( I? J? ). map0 @ c <-
			ENDIF
			; 開いているポイントを数える
			; if ((c&OPBIT)==OPBIT) all0++;
			c OPBIT & OPBIT == IF
				all0 . ++ =
			ENDIF
		NEXT
	NEXT
	all0 all ==
}
RETURN

;****************************************************************

CheckMapPoint:
; y(1) x(0) → マップ上のポイントをチェックする
{
( x y ). VARS=
c VAR ,
	c ( x y ). map @ -> = ; c=map[y][x];
	c OPBIT & SWITCH
		0 case ; 閉じている → 爆弾ありそう
			; map[y][x]|=BMBIT;
			( x y ). map @ . -> BMBIT | <-
			bomb . ++ = ; bomb++;
			PrintBombCount.
			BREAK
		BMBIT case ; 爆弾ありそう → チェックしてね
			; map[y][x]=(c&NOPBIT)|CHBIT;
			( x y ). map @ c NOPBIT & CHBIT | <-
			BREAK
		CHBIT case ; チェックしてね → 閉じる
			; map[y][x]&=NOPBIT;
			( x y ). map @ . -> NOPBIT & <-
			bomb . -- =
			PrintBombCount.
			BREAK
		OPBIT case ; すでに開いている
			; 開いている所を調べることは不用
			BEEP
			BREAK
	SEND
}
RETURN

;****************************************************************

OpenMapPoint:
; x(0) y(1) → マップ上のポイントを開く
{
( x y ). VARS=
c VAR ,
	c ( x y ). map @ -> = ; c=map[y][x];
	; if ((c&OPBIT)==OPBIT) { ; すでに開いている
	c OPBIT & OPBIT == IF
		; 開いているところを再度開くことは不用
		BEEP
		0 ; リターン値
		OMP goto ; 警告以外は何もしない
	ENDIF
	; if ((c&NOPBIT)==BOMB) { ; 爆弾を開けてしまった
	c NOPBIT & BOMB == IF
		BEEP BEEP
		1 ; リターン値
		OMP goto ; game over
	ENDIF
	; そこはまだ開いていない所なので開ける
	c ELIF	; if (c==0) {
		; 周りに爆弾がない所ならペイント型で開く
		( x y ) PaintMap.
	ELSE	; 周りに１つでも爆弾がある時は１ポイントだけ開ける
		; map[y][x]|=OPBIT ; 開けた印
		( x y ). map @ . -> OPBIT | <-
	ENDIF
	0
OMP:
}
RETURN

;****************************************************************

PaintMap:
; x(0) y(1) → マップの(x,y)から０の点を開く
; 基本的にはペイントと同じルーチンになるが、境界まで開くのが違う
{
( x y ). VARS=
( c xs xe ). VARS
	; if (map[y][x]!=0) return ; 最初の点が０でなければ終わり
	( x y ). map @ -> IF PM goto ENDIF
	
	; 0 ← x : 左サーチ
	; xe=x+1 ; 右へ捜し始めるポイント
	xe x ++ =
	WHILE
		x 0 >= EL. BREAK
		c ( x y ). map @ -> = ; c=map[y][x];
		; if ((c&OPBIT)==0) map[y][x]|=OPBIT;
		c OPBIT & ELIF
			( x y ). map @ . -> OPBIT | <-
		ENDIF
		; if (c!=0) BREAK; ; 境界
		c IF. BREAK
		/* 境界も開かれる
			境界は爆弾ではない。
			なぜなら爆弾の周りには１〜８が必ずあるから。
		*/
		x . -- = ; 左へ 
	WEND
	xs x =
	x 0 < IF xs 0 = ENDIF ;; xs=(x<0? 0:x);
	; xs=線分左端（境界も含む）
	
	; x → wx-1 : 右サーチ
	WHILE
		xe wx -- <= EL. BREAK
		c ( xe y ). map @ -> = ; c=map[y][xe];
		; if ((c&OPBIT)==0) map[y][xe]|=OPBIT;
		c OPBIT & ELIF
			( xe y ). map @ . -> OPBIT | <-
		ENDIF
		; if (c!=0) BREAK; ; 境界
		c IF. BREAK
		xe . ++ = ; 右へ
	WEND
	; if (xe==wx) xe--;
	xe wx == IF xe . -- = ENDIF
	; xe=線分右端（境界を含む）
	
	; if (y>0)    PaintMapSub(xs,xe,y-1) ; 上チェック
	y 0 >     IF ( xs xe y -- ) PaintMapSub. ENDIF
	; if (y<wy-1) PaintMapSub(xs,xe,y+1) ; 下チェック
	y wy -- < IF ( xs xe y ++ ) PaintMapSub. ENDIF
PM:
}
RETURN

;****************************************************************

PaintMapSub:
; xs(0) xe(1) yy(2) → PaintMapサブルーチン：上下チェック用
{
( xs xe yy ). VARS=
c VAR ,
	xs xe FOR ; FOR (x=xs;x<=xe;x++) {
		; c=map[yy][x];
		c ( I? yy ). map @ -> =.
		ELIF	; if (c==0) PaintMap(x,yy);
			( I? yy ) PaintMap.
		ELSE	; else if ((c&OPBIT)==0) map[yy][x]|=OPBIT; 境界も開く
			c OPBIT &
			ELIF
				( I? yy ). map @ . -> OPBIT | <-
			ENDIF
		ENDIF
	NEXT
}
RETURN

;****************************************************************

InitScreen:
; 初期画面表示
	CLS
	( "−" "｜" "＋" ) WakuPrint.
	0 MapPrintAll. ; そのまま全表示
	15 0 LOCATE
;	 0         1           2         3   	   4
;	+012345678901 2345 678901234567890123456789012
	"マップ幅：" PRINTS
	wx 10 ' ' 2 PRINTNF '×' PRINTC wy 10 ' ' 2 PRINTNF
	'　' PRINTC
	"爆弾数：" PRINTS
	bomb 10 ' ' 3 PRINTNF '／' PRINTC seed 10 ' ' 3 PRINTNF
	'　' PRINTC
	"時間：" PRINTS
	dt PRINTN10
	RETURN

;****************************************************************

PrintBombCount:
; 残り爆弾数表示
	41 0 LOCATE
	bomb 10 ' ' 3 PRINTNF
	RETURN

;****************************************************************

PrintTime:
; 経過時間表示
	57 0 LOCATE
	dt printn10
	RETURN

;****************************************************************

GameClear:
; ゲームクリア時画面表示
	; 枠の模様を代える
	BEEP
	( "◇" . . ) WakuPrint.
	BEEP
	6 1 LOCATE
	"§§ おめでとう！ あなたは総ての爆弾を捜し当てました §§" PRINTS
	RETURN

;****************************************************************

GameOver:
; ゲームオーバー時画面表示
	; 枠の模様を代える
	BEEP BEEP
	( "☆" . . ) WakuPrint.
	BEEP BEEP
	10 1 LOCATE
	"＠＠　あなたは爆弾にぶち当たってしまいました ＠＠" PRINTS
	RETURN

;****************************************************************

BombCount:
; マップ内の爆弾状況を書き込む
; 各ポイント毎の周りの爆弾の数をあらかじめ書き込む
{
( xs ys xe ye c ). VARS
	"BombCount " PRINTS
	0 wy FOR.
		ys I? -- = ys 0 < IF ys 0 = ENDIF
		ye I? ++ = ye wy -- > IF ye wy -- = ENDIF
		0 wx FOR.
			; 爆弾の所はスキップ
			( I? J? ). map @ -> BOMB == IF. CONTINUE
			xs I? -- = xs 0 < IF xs 0 = ENDIF
			xe I? ++ = xe wx -- > IF xe wx -- = ENDIF
			; 周りをカウント
			c 0 =
			ys ye FOR
				xs xe FOR
					( I? J? ). map @ ->
					BOMB == IF c . ++ = ENDIF
				NEXT
			NEXT
			; c=0〜8
			( I? J? ). map  @ c <- ; map[y][x] =c ; マップ
			( I? J? ). map0 @ c <- ; map0[y][x]=c ; 仮想画面
		NEXT
	NEXT
}
RETURN

;****************************************************************

PrintChr:
; キャラクター表示 
; c(0):bit6,7 = 開き済み(11)/チェックマーク(10)/爆弾マーク(01)/閉じている(00)
;      bit0-5 = マップ状態 0x00〜0x3f
{
( c ). VARS=
	c OPBIT & SWITCH
		0 case ; 閉じている
			'□' PRINTC
			BREAK
		CHBIT case ; チェックマーキング中
			'？' PRINTC
			BREAK
		BMBIT case ; 爆弾ありマーキング中
			'☆' PRINTC
			BREAK
		OPBIT case ; 開いている
			; 開いた部分は反転表示
			REVERSE TWHITE | COLOR ,
			c NOPBIT & SWITCH
				0 case '・' PRINTC BREAK
				1 case '１' PRINTC BREAK
				2 case '２' PRINTC BREAK
				3 case '３' PRINTC BREAK
				4 case '４' PRINTC BREAK
				5 case '５' PRINTC BREAK
				6 case '６' PRINTC BREAK
				7 case '７' PRINTC BREAK
				8 case '８' PRINTC BREAK
				ERR case ; 間違い
					TWHITE COLOR ,
					'×' PRINTC
					BREAK
				BOMB case ; 爆弾
					TWHITE COLOR ,
					'★' PRINTC
					BREAK
			SEND
			TWHITE COLOR ,
			BREAK;
	SEND
}
RETURN

;****************************************************************

MapPrintAll:
; マップ全表示
; mode(0) : 0=そのままで , 1=すべて開いて（間違い判定付き）
{
( mode ). VARS=
c VAR ,
	0 wy FOR.
		( 0 I? ) Locate2.
		0 wx FOR.
			c ( I? J? ). map @ -> =
			mode IF ; if (mode) {
				c OPBIT & BMBIT == IF
				; if ((c&OPBIT)==BMBIT) {
					; 爆弾ありとしているが・・
					; そこに爆弾がなければ間違い
					; if ((c&NOPBIT)!=BOMB) c=ERR;
					c NOPBIT & BOMB != IF
						c ERR =
					ENDIF
				ENDIF
				c . OPBIT | = ; 開く
			ENDIF
			c PrintChr.
		NEXT
	NEXT
}
RETURN

;****************************************************************

BombSeed:
; マップ内に爆弾をまく
; seeds(0) : 爆弾個数 < wx*wyにすること
; 乱数の初期化は外部で行なうこと
{
( seeds ). VARS=
( all x y ). VARS
	; マップクリア
	"マップクリア " PRINTS
	0 wy FOR.
		0 wx FOR.
			( I? J? ). map @ 0 <-
		NEXT
	NEXT
	
	; 要開き数：爆弾以外すべて
	; all=wx*wy-seeds
	all wx wy * seeds - =
	
	; 爆弾配置
	"爆弾配置 " PRINTS
	WHILE
		seeds 0 > EL. BREAK
		x wx RND =
		y wy RND =
		( x y ). map @ ->
		ELIF ;	if (map[y][x]==0) {
			; 爆弾を置く
			( x y ). map  @ BOMB <- ; map[y][x] =BOMB ; マップ
			( x y ). map0 @ BOMB <- ; map0[y][x]=BOMB ; 仮想画面
			seeds . -- = ; 残りを１つ減らす
		ENDIF ; そこにすでに爆弾がある時は別の場所を探す
	WEND
	all ; リターン値
}
RETURN

;****************************************************************

WakuPrint:
; マップ枠表示
; kado(NSTR1:2) tate(NSTR1:1) yoko(NSTR1:0) WakuPrint
{
( yoko tate kado ). SVARS=
	; 横線
	0 wx FOR.
		( I? -1 ) Locate2. yoko PRINTS
		( I? wy ) Locate2. yoko PRINTS
	NEXT
	
	; 縦線
	0 wy FOR.
		( -1 I? ) Locate2. tate PRINTS
		( wx I? ) Locate2. tate PRINTS
	NEXT
	
	; 角
	( -1 -1 ) Locate2. kado PRINTS
	( -1 wy ) Locate2. kado PRINTS
	( wx -1 ) Locate2. kado PRINTS
	( wx wy ) Locate2. kado PRINTS
}
RETURN

;****************************************************************

Locate2:
{
( x y ). VARS=
	x 2 * SX + /* *2 for 全角文字 */
	y SY +
	LOCATE
}
RETURN

;****************************************************************

onbrk:
	"BREAKが押されました\n" PRINTS
	END

;****************************************************************
