ラベル BASIC の投稿を表示しています。 すべての投稿を表示
ラベル BASIC の投稿を表示しています。 すべての投稿を表示

2013年9月16日月曜日

サンプル:ゲーム「digman」

 X1用のゲーム「DIGMAN」を移植しました。
 どことなくpitmanに似てますが、気のせいです(^_^;)。
「Oh!X 1990/5」よりの移植です。
もとネタがあるので、転載禁止です。


 Zipファイルはこちら:XBdigman.zip

2013年9月6日金曜日

サンプル:トランプゲーム 一人占い「TEN」






iPad縦画面専用です(横画面は未検証)。



Zipファイル :XBten.zip

cTen.basがiOS用です。ten.basは、X-BASIC/68またはぺけ-BASIC用です。
どのように移植したかを示すための参照用として入れてあります。

2013年8月29日木曜日

サンプル:トランプゲーム7並べ


 


iPad縦画面専用です(横画面は未検証)。


Zipファイル :XB7.zip
c7.basがiOS用です。7.basは、X-BASIC/68またはぺけ-BASIC用です。
どのように移植したかを示すための参照用として入れてあります。

2013年8月23日金曜日

サンプル:トランプゲーム99


 

iPad縦画面専用です(横画面は未検証)。


Zipファイル :XB99.zip

c99.basがiOS用です。99.basと99patch.basは、X-BASIC/68またはぺけ-BASIC用です。
どのように移植したかを示すための参照用として入れてあります。

2013年8月8日木曜日

サンプル:Z80アセンブラー「SuperBASE」


Z80アセンブラー「SuperBASE」を移植しました。

 「I/O別冊 WICS・BASEプログラム集」のMZ-2000用をX1に移植したのが元で、
さらにそれをX68000のCに移植し、今回X-BASIC for iOSに移植しました。
このくらいのサイズのプログラムでも動くという証明用です。

BASICで記述しているのでそれなりに遅いですが、ちゃんとアセンブルできます。
遅いと言っても、実はX68000のC版よりか速かったりします。

SuperBASEはキャリーラボという会社が作った独自ニーモニックのZ80アセンブラーです。


それがどう言うものかは説明書を読んでください。

・・・こんな感じ・・・
;
; DSK-CZ V4.2
;
DRIVE EQU $FF87
ERRJP EQU $FF8A
DSKLD EQU $021A
PRET  EQU $01FB
RDDSK EQU $FF00
IBUFF EQU $0FE3
;
 OFFSET $A000
 START $0EF2 ;HTAB
;
REW
 CALL CDIR
 GOTO $0DEC
;
APSS1
 A=(DIR)
 A+ IF A<8 GOTO APS1
 A=(RDIR)
 A+ IF A>=32 GOTO APS2
 (RDIR)=A
APS2 A=0
APS1
 (DIR)=A
 RET
;
DLOAD
 [BC [HL [DE
 CALL TRNS
 DE=(REC)
 A=B IF A=0 GOTO DLAST
 CALL RDDSK IF CY GOTO DL3
 A=B A=A+H H=A
 DO !() DE+ UNTIL DEC(B)=0
 GOTO DLAST
;
FILE
 A=(7) IF A=40 GOTO $6200
 CALL $143C
 GOTO $61D3
;
; 0FE3-10E2 IBUFF
 START $10E8 ;Screen Edit
;
RDS
 DINT
 [BC [HL [DE
 [HL [AF
DN A=00 (DRIVE)=A
 HL=DSKER-RDS+RDDSK (ERRJP)=HL
 A=$1D PORT(0)=A
 ]AF ]HL
 CALL DSKLD
 CY=0
RD1
 A=$1E PORT(0)=A
 EINT
 GOTO PRET
DSKER
 CY=1
 GOTO RD1
RDE

・・・


X68版を作ったとき、関係者の人にお見せしたら喜んでらしたし、もう今更なので許していただきたいかと。

なお、V2.3以前のX-BASIC for iOSではバグのため正常に走りません。
必ず、V2.4以降にバージョンアップしてください。


Zipファイル :XBsbase.zip


転載禁止です。

2013年8月1日木曜日

サンプル:レーダースネーキー


MZ-80Kの傑作ゲーム、「レーダースネーキー」を移植しました。
しかも、ベタ移植です(本当は少しだけアレンジしてあるんだけど)。

昔はこんなゲームを楽しんでいたんだということを、
ある人はなつかしく、ある人とは新鮮な気持ちで知ってください。

「I/O別冊 WICS・BASEプログラム集」よりの移植です。
もとネタがあるので、転載禁止です。

iPad縦画面のみ対応+日英対応です。



Zipファイル :XBrs.zip

2013年7月23日火曜日

サンプル:人間対戦チェスプログラム


人間対戦のチェスプログラムです。

間違ってもコンピューターとの対戦など出来ません。
見た目も非常にシンプルです。

結局、中途半端なプログラムに終わっています。
適当に改良してください(^_^;)。

iPadの縦横画面および日英に対応しています。



Zipファイル :XBches.zip

これは珍しく完全自作なので、転載ほか、いかように使っていただいても結構です。

駒のパターンは、実はUnicodeにある文字フォントです。
Unicodeではこんなものまであるのです。
(チェスがあるのですから、トランプなんて当然あります。)

2013年7月18日木曜日

「グラフィックが表示できん」とお嘆きの貴兄に

外人で、「グラフィックが表示されんから金返せ」などと言う奴が居たのですよ。

取説にはグラフィックの表示手順についてちゃんとく書いたし、サンプルも多数出している。
その上ここでも何も聞かずにこんな馬鹿なこという奴は、きっと何を触っても何も出来ないのだろうと、あきれかえってしまうわけです。

ってなわけで、そんな奴は無視して、日本語だけで、グラフィック表示の仕方を簡単に説明します。

・・・まずは、取説からの抜粋・・・

グラフィック画面は4画面あります。それをページと呼びます。
どのページに描画するかはapage()、どのページを表示するかはvpage()、
テキスト画面、グラフィック画面およびスプライト&BG画面の表示順位はvpriority()で
設定します。

従って、グラフィックの描画プログラムは基本的に、
vpage() 表示ページ指定 vpriority() 表示優先順位指定 apage() 描画対象ページ指定 wipe() クリア gColor() 描画色指定 描画命令群
という順で記述することになります。
 ・・・
ということです。この手順を守れば、確実にグラフィックは表示できます。
これはX-BASIC/68でもほとんど同じです(優先順位指定と色指定が違うけど)。 
 
・・・例・・・
vpage(B_TPAGE+B_GPAGE0,YES) vpriority(GPAGE0,TPAGE,GPAGE1,GPAGE2,GPAGE3) apage(GPAGE0) wipe() // gColor(255,255,255,255) line(0,0,100,100) // end
これで、(0,0)~(100,100)の間に白の直線を引きます。
描画しているのは、4ページあるグラフィック画面の内、GPAGE0のページです。

・・・

グラフィックを描画するプログラムを書く場合、グラフィックを1画面しか使わないのであれば、変更すべきはgColor()とline()の部分だけであって、他は定型として使い回せば良いのです。
何も難しいことはありません。

ある程度の手順を覚えなければならないことは、どの言語を使っても同じです。
いや、言語じゃなくてもアプリでも、それどころか世の中にある物全て、全く何も覚えずに使える物なんてないのです。

くれぐれも、何も努力をしないで「出来ないのは相手が悪い」などという愚かな発想に至らないように。


2013年7月13日土曜日

サンプル:ルナ・レスキュー

MZ-80Kの傑作ゲーム、「ルナ・レスキュー」を移植しました。
しかも、ベタ移植です(本当は少しだけアレンジしてあるんだけど)。

昔はこんなゲームを楽しんでいたんだということを、
ある人はなつかしく、ある人とは新鮮な気持ちで知ってください。

基本的には「I/O別冊 MZ活用研究」よりの移植です。
もとネタがあるので、転載禁止です。


iPad縦画面のみ対応、日英対応です。





Zipファイル :XBlunar.zip

2013年7月5日金曜日

サンプル:新説?!桃太郎ゲーム

MZ-80K用の古いゲームの移植です。
 iPad縦画面のみ対応、日英対応です。




「I/O別冊 MZ活用研究」からの移植で、転載禁止です。


Zipファイル :XBmomo.zip

2013年7月2日火曜日

サンプル:地底最大の作戦


MZ-80K用の古いゲームの移植です。
iPad縦画面のみ対応、日英対応です。



Oh!X1988/9からの移植で、転載禁止です。


Zipファイル :XBsnake.zip

2013年6月23日日曜日

サンプル:バネの物理的挙動をシミュレートするプログラム?

バネの物理的挙動をシミュレートするプログラム・・・だそうです。
// http://d.hatena.ne.jp/x68000forever/20101123/1290520018 より
// 画面初期化 / screen initialize
apage(0)
wipe()
vpriority(TPAGE,SPAGE,GPAGE0,GPAGE1,GPAGE2,GPAGE3)
vpage(B_TPAGE+B_SPAGE+B_GPAGE0)
gborder(1,CTHYELLOW)
int by=0
switch deviceType()
 case DEVICE_IPODTOUCH: // iPod touch
 case DEVICE_IPHONE: // iPhone
 case DEVIDE_IPHONE_SIMURATOR
  width(40)
  by=32
endswitch
//
int c1,c2,t
float l1,l2,l3,k,m
float a,b,c,w1,w2,w3,xx1,xx2,xx3,x1,x2,x3,r2=1.4142135623731#
//
print localizedString("変位1=","Displacement1=");:input l1
print localizedString("変位2=","Displacement2=");:input l2
print localizedString("変位3=","Displacement2=");:input l3
print localizedString("バネ定数=","Rate of spring=");:input k
repeat
 print localizedString("質量(>0)=","Mass(>0)=");:input m
until m>0
//
w1=pow((2-r2)*k/m,0.5#)
w2=pow(2*k/m,0.5#)
w3=pow((2+r2)*k/m,0.5#)
a=(l1+r2*l2+l3)/4
b=(l1-l3)/2
c=(l1-r2*l2+l3)/4
//
c1=x68Color2iOSColor(61376):// hsv(30,31,31))
c2=x68Color2iOSColor(55358):// hsv(100,31,31))
gColor(c2)
line(  0,288+by,511,288+by)
gColor(c1)
line(130,285+by,130,291+by)
line(255,285+by,255,291+by)
line(380,285+by,380,291+by)
sp_clr()
sp_disp(0)
sprite_pattern():sprite_pallet()
sp_disp(1)
//
repeat
 x1=a*cos(w1*t)+b*cos(w2*t)+c*cos(w3*t)
 x2=r2*a*cos(w1*t)-r2*c*cos(w3*t)
 x3=a*cos(w1*t)-b*cos(w2*t)+c*cos(w3*t)
 locate(0,5):print "t=";t
 locate(0,6):print "x1=";x1,"x2=";x2,"x3=";x3
 xx1=130+x1:xx2=255+x2:xx3=380+x3
 locate(0,7):print "xx1=";xx1,"xx2=";xx2,"xx3=";xx3
 sp_halt(YES)
 sp_move( 0,xx1-16,256+by, 0)
 sp_move( 1,xx1   ,256+by, 1)
 sp_move( 2,xx1   ,272+by, 2)
 sp_move( 3,xx1-16,272+by, 3)
 sp_move( 4,xx2-16,256+by, 4)
 sp_move( 5,xx2   ,256+by, 5)
 sp_move( 6,xx2   ,272+by, 6)
 sp_move( 7,xx2-16,272+by, 7)
 sp_move( 8,xx3-16,256+by, 8)
 sp_move( 9,xx3   ,256+by, 9)
 sp_move(10,xx3   ,272+by,10)
 sp_move(11,xx3-16,272+by,11)
 sp_halt(NO)
 if t=0 then wait(1)
 t=t+1
 wait(0.1)
until t>1000
end
//
func sprite_pattern()
 int i,j
 dim char sp1(255),sp2(255),sp3(255),sp4(255)
 sp1={
  0,0,0,0,0,0,0,0,0,0,4,4,4,4,5,5,
  0,0,0,0,0,0,0,0,4,5,5,5,5,5,6,6,
  0,0,0,0,0,0,0,4,5,6,6,6,6,6,6,6,
  0,0,0,0,0,4,5,6,6,6,6,7,7,7,7,7,
  0,0,0,0,4,5,6,6,7,7,7,7,8,8,8,8,
  0,0,0,4,5,6,7,7,7,8,8,8,8,8,9,9,
  0,0,4,4,6,6,7,8,8,8,8,9,9,9,9,10,
  0,0,4,5,6,7,8,8,9,9,9,9,10,10,10,10,
  0,4,4,6,6,7,8,9,9,10,10,10,10,11,11,11,
  0,4,5,6,7,8,8,9,10,10,11,11,11,11,12,12,
  4,5,6,6,7,8,9,9,10,11,11,12,12,12,12,13,
  4,5,6,7,7,8,9,10,10,11,12,12,13,13,13,13,
  4,5,6,7,8,8,9,10,10,11,12,13,13,14,14,14,
  4,5,6,7,8,8,9,10,11,11,12,13,14,14,15,15,
  4,6,6,7,8,9,9,10,11,12,12,13,14,15,15,15,
  5,6,6,7,8,9,10,10,11,12,13,13,14,15,15,15
 }
 sp4={
  5,6,6,7,8,9,10,10,11,12,13,13,14,15,15,15,
  4,6,6,7,8,9,9,10,11,12,12,13,14,15,15,15,
  4,5,6,7,8,8,9,10,11,11,12,13,14,14,15,15,
  4,5,6,7,8,8,9,10,10,11,12,13,13,14,14,14,
  4,5,6,7,7,8,9,10,10,11,12,12,13,13,13,13,
  4,5,6,6,7,8,9,9,10,11,11,12,12,12,12,13,
  0,4,5,6,7,8,8,9,10,10,11,11,11,11,12,12,
  0,4,4,6,6,7,8,9,9,10,10,10,10,11,11,11,
  0,0,4,5,6,7,8,8,9,9,9,9,10,10,10,10,
  0,0,4,4,6,6,7,8,8,8,8,9,9,9,9,10,
  0,0,0,4,5,6,7,7,7,8,8,8,8,8,9,9,
  0,0,0,0,4,5,6,6,7,7,7,7,8,8,8,8,
  0,0,0,0,0,4,5,6,6,6,6,7,7,7,7,7,
  0,0,0,0,0,0,0,4,5,6,6,6,6,6,6,6,
  0,0,0,0,0,0,0,0,4,5,5,5,5,5,6,6,
  0,0,0,0,0,0,0,0,0,0,4,4,4,4,5,5
 }
 for i=0 to 255
  sp3(i)=sp1(255-i)
  sp2(i)=sp4(255-i)
 next
 sp_def(0 ,sp1)
 sp_def(1 ,sp2)
 sp_def(2 ,sp3)
 sp_def(3 ,sp4)
 sp_def(4 ,sp1)
 sp_def(5 ,sp2)
 sp_def(6 ,sp3)
 sp_def(7 ,sp4)
 sp_def(8 ,sp1)
 sp_def(9 ,sp2)
 sp_def(10,sp3)
 sp_def(11,sp4)
endfunc
//
func sp_color2(pal;int,colX68;int,pb;int)
// X68の色コードでスプライトの色を設定する / set sprite color by X68 color
 sp_color(pal,x68Color2iOSColor(colX68),pb)
endfunc
//
func sprite_pallet()
 sp_color(  0,    0,1)://sp_color2( 0,    1,1)
 sp_color2( 1, 4229,1)
 sp_color2( 2, 8457,1)
 sp_color2( 3,12685,1)
 sp_color2( 4,19027,1)
 sp_color2( 5,23255,1)
 sp_color2( 6,27483,1)
 sp_color2( 7,31711,1)
 sp_color2( 8,35939,1)
 sp_color2( 9,40167,1)
 sp_color2(10,44395,1)
 sp_color2(11,48623,1)
 sp_color2(12,52851,1)
 sp_color2(13,57079,1)
 sp_color2(14,61307,1)
 sp_color2(15,65535,1)
endfunc

func str localizedString(js;str,es;str)
 if isLocalizeJapan() then return(js)
 return (es)
endfunc

Zip archive file: XBetc.zip

2013年6月18日火曜日

サンプル:信州


 
どこかで見たことがあるようなゲームです。

説明書は書いてませんが、.要するに同じ柄の牌を取っていくだけです。

iPad専用縦横、英語対応。

タッチ座標から牌位置を算出処理に手抜きがあるので、牌の真中付近でないと間違えることがあります(checkPoint())。

やり直しや得点もないので、改良してやってください。

現在のX-BASIC for iOSのスプライトは、ここで使われている程度がちょうど良い、という例でもあります。

Oh!X1988/6からの移植で、転載禁止です。

Zipファイル :XBsinshu.zip

2013/07/16追記
V2.3では配列周りのエラーチェックが正しくなった関係で、信州がエラーで落ちるようになってしまいました。V2.3でも動くサンプルに更新しました。

2013年6月7日金曜日

実行時におかしい動作をする/コンパイルエラーが出る時に調べる点(随時追記)

実行時におかしい動作をした時に調べる点をあげておきます。

・表示がおかしい(1)
文字、文字列の表示そのものや、tatrb()/tcolor()で表示属性をつけた時に、おかしな表示になる時は、まずは文字コードを疑ってください。

X-BASIC for iOSの標準の文字コードはUTF-8です。
このコードはASCII範囲(&h20〜&h7e)こそ1バイトでSHIFT-JISと同じですが、半角カナや全角文字は全くコードが異なります。

このため、ASCIIにしか対応してない文字列関数やstr[i]による文字列内1バイト取り出しを使うと、予期しないコードを返すことがあり、またそれを表示しようとすると、表示そのものだけでなく属性も正常に処理されません。

どうしても内部処理を SHIFT−JISで行いたいときは、強制SHIFT-JISモードをオンにしてください。

なお、X-BASIC for iOSにはX68にあった、複数バイトコードは1バイトずつ出力しても正常表示される、という機能はありません。これは強制SHIFT-JISモードでも同じです。


・表示がおかしい(2)
タッチエリアを画面外にまたぐように設定すると、以後のテキスト表示がおかしくなることがあるようです。
タッチエリアは必ず画面内に収まるように設定してください。

・表示がおかしい(3)
画面がちらつくときは、vpriority()の引数も確認してみてください。
例えば、vpriority(TPAGE,B_SPAGE,GPAGE0,GPAGE1,GPAGE2,GPAGE3)とかすると画面がちらつく上テキスト画面が表示されなくなります。定数名が間違っている=引数の値が異常になっているのが原因です。
エラーが出ないのでわかりにくいですが、おかしい時は、まず引数を確認して下さい。

・画面が表示されない
グラフィックもしくはスプライト 画面を非透明の黒で描画していませんか?
X68では黒=透明でしたが、iOSでは黒と透明は厳密に異なります。
黒は透過しませんので、それでうめつくされた画面より優先順位が下になる画面は見えなくなります。特にBG画面は、設定によっては1スプライトの設定で全画面非透明になることがあるので要注意です。
vpriority()で優先順位を入れ替えて表示が見えるようになる場合は、100%これが原因です。

・TABをはじめコントロールコードが表示されてない
仕様書をよく読みましょう。
iOS版では高速化のため、コントロールコードはその1文字だけ表示する場合のみ処理されます。
従って、freads()などで読み込んだテキストファイル内文字列をそのままprintすると、結果が異なることになります。
内部的には、';'や','で区切った場合も別表示と見なしますので、うまく処理するとよいでしょう。

・ case 定数=値と書いていてもエラーにならない
これは、定数と値の比較式を評価した結果をcase値にしているからです。人間の理解上では間違っているように見えますが、BASICの式としては正しいのでエラーにはなりません。
すなわち、定数=値は比較式であり、このように実質上定数の比較式はコンパイル時に演算されてしまいます。
この場合、もし定数=値ならばcase -1相当で有り、定数<>値ならcase 0相当となります。
だから、エラーにならないのです。
もし、case -1またはcase 0相当式が同一swicth~endswitch内にあれば、エラーになります。

・bitmapでfdirect=YESなのに描画データが読めない
100%、apage()が描画対象のページ以外になっています。
確認してください。

・タッチキーでエリア外なのにキーコードが返ってくる
タッチエリアを、全く同じ座標域で重複設定している場合に発生します。
ループで処理を戻す場合などは、必ず、前回の設定をremoveTouchArea()で解除してから再設定してください。

・line()などでstyleを設定しても思った通りに描画されない。
styleの設定値にはiOSによる制約があります。そのためX-BASIC/68と同じ値を設定しても同じ描画になるとは限りません。
詳しくは取説をご覧ください。

・a=b=cの結果が違う
C言語ではこの式は
     b=c
     a=b
と等価で、a/b共にcの値が代入されますが、X-BASICでは
     a=(b=c)
と等価であり、(b=c)すなわち、bとcが等しいかどうかの評価結果がaに代入されます。
(その値はtrue=-1またはfalse=0です。)
Cから移植すると間違いやすいので注意してください。

・if 文でandを使う時の式評価順に注意
X-BASICのandは、C言語の&&とは意味が異なります。それは&に相当します。それぞれの意味は以下のようになります。
X-BASIC;if A and B
AとBを両方評価し、結果をandする。
だから、常にA式とB式両方が評価されます。

C ; if (A && B)
Aを評価し、成立したらBを評価する。
だから、Bは評価されないことがあります。

C ; if (A & B)
AとBを評価し、結果を&する。
だから、常にA式とB式両方が評価されます。
X-BASICのandはこちらに相当します。

ということなので、X-BASICでAが成立する時だけBが評価されることを想定するなら、
if A then {
    if B then ...
}
と記述する必要があります。

・func文の所で変数二重宣言エラーが発生する
関数の引数名が関数名と同じだったとき発生します。
関数の引数名は関数名と同じにしないでください。

なおこれはX-BASIC/68では発生しませんが、ぺけ-BASICで発生します。
両者の数少ない非互換部分です。


・落ちる/iOSに戻ってしまう
V2.4ではかなり安定しています。
この状態でも落ちるのは、再帰呼び出しが深すぎることがほとんどです。
例えば
func f()
  f()
endfunc
とすれば、確実に落ちます。再帰呼び出しでスタックを使い尽くすためです。残念ながら、iOSレベルでスタックの残り量を知るすべが無いため、安全性のチェックはなされていません。
V2.6では再帰呼び出し段数(正確には再起でなく異なる関数の呼び出し続けも同じ)に制約をつけましたので、関数内部でのローカル変数定義数にもよりますが、およそ落ちることはなくなるはずです。

あともう1つ、X-BASIC/68で書き込まれたfloatのデータを(ファイルを経由して)そのままiOS版で表示しようとすると落ちることがあります。浮動小数点数値フォーマットが異なるため、表示できずこうなります。
float6880()で変換して表示してください。


・関数呼び出しのある行で未宣言変数、関数、配列のエラーが出る
これはほぼ100%関数宣言側に問題があります。
funcがfuneになっているとか。


・X-BASIC/68作られたファイルを読むと値が異なる
この理由はメモリ上におけるデータのバイト並びの違いによります。68000はいわゆるビッグエンディアンでH→Lの順で格納されますが、iOS機器のARMはリトルエンディアンでL→H順です。
交互変換にはint6880()やfloat6880()関数を使います(V2.5以降で有効)。
なお、スプライトなどのパレットデータについては、さらに色コードの違いもあります。
x68Color2iOSColor()で変換します。

2013年6月1日土曜日

エラーメッセージ別対処法(随時追記)

X-BASIC for iOSのエラーメッセージは、残念ながらエラーの発生している場所や原因を的確に表していない場合があります。

理由の1つは、「エラーが発生したらその行の解析を飛ばし、次の行からコンパイルを継続するから」です。コンパイル時には1つのエラーでいちいち止めていては全体のデバッグに時間がかかりすぎるため、エラー発生行をなかったことにして無理やりコンパイルを継続して、全体のエラーを「制限付きながら」洗い出します。
そのためにこの現象が起きます。

このため、対となる構文がある行では、見かけ上間違いがなくても必ずエラーが発生します。例えば、switchの行でエラーが発生すれば、endswitchの行でも必ず(場合によってはcase/defaultの文でも)エラーが発生するわけです。

オリジナルのぺけ-BASICやX-BASIC/68は1つエラーが発生すればそこで止まるため、それ以降の解釈の継続という考え方がなく、故に同様なことが発生しません。

・・・

X-BASIC for iOSではできるだけエラーを細分化しましたが、解釈の都合上、エラー直接原因とは場所でエラーが発生することもあります。
この場合、発生するエラーからは原因がわかりづらいこともあります。


その例をここにまとめておきます。
(今後、発見次第随時紹介。)

・未宣言変数/Undefined variable.   (日本語/英語の順)
 
未定義関数を呼びだそうとした時もこれが発生することがあります。
関数を配列と認識してしまった場合です。
C言語と異なり、BASICでは関数も配列も()で括るため、こうなります。

・文字列が閉じてないときは、その影響で色々とエラーが発生することがあります
文字列のある行でエラーが発生している場合、まずは文字列が閉じられているか確認してください。

X-BASIC for iOSのごめんなさい(随時追記)

X-BASIC for iOSの、各種「ごめんなさい」部分を述べておきます。

・関数の命名規則がバラバラ
pic_load()だったりpicLoader()だったり。前者はX68に多く、後者はApple的です。
今後、X68にない関数はApple的に統一していく予定です(V2.1でだいぶ行いました)。
すでに存在する関数名についも、互換性のためそのまま残します。
同一機能の関数に複数名称あるのは主にそのためです。


・関数動作がX-BASIC/68と微妙に異なる
X-BASIC for iOS は基本的にX-BASIC/68やぺけーBASICと互換性がありますが、それは内部ステートメントと機種依存しない外部関数だけで、機種やOS依存の部分は、同名関数であっても同一動作をするとは限りません。

特に表示系はiOSや機種に強く依存するため、表示結果がX68と微妙に異なることが多々あります。おそらく特にわかりにくいのが透明の概念でしょう。

 X68では黒=透明であり、ページを重ねている場合はその部分は透けてみます。
逆に言えば、助けさせるためには黒で塗れば良いです。
ところが、iOSでは黒と透明は全く異なるものです。黒で塗った部分は、重ねあわさせても黒であり、透過しません。その上、透明度を持った色で「そのまま」描画しようとすると、元々そこにあったパターンと合成してしまいます。単純に 置き換わらないのです。
これを回避するにはblend()で合成方法を変更する必要があります。

X−BASIC for iOSはBASIC/68のプログラムをそのまま動かすことを目的としたのではなく、「iOSに比較的簡単に移植する」ことを目的としたため、必要以上にX68と動作が一緒になるようにはしていません。そのため、こういう部分があります。

もし「完全互換動作」を求める声が多ければ、それを別の外部関数として実装するという手もあるでしょうが、現在の状況では(時間的そして経済的に)不可能です。


・意図しない範囲まで描画される
iOS版のグラフィック座標は実数です。これは何を意味するかというと、物理的なドットの間にも仮想的なドットが存在すると想定していると言うことです。これにより、アンチエイリアスなどを実現しています。

しかしこれがゆえに、指定した範囲よりも広く実描画されることがあります。なので、X68でドット単位で正確に領域を限定して描画していた物は、それが維持できなることがあります。具体的には、ある領域に描画して、その後そこを消そうとした場合、消したはずなのに一部残る、という現象が発生します。

残念ながら、この現象はアンチエイリアス機能をOFFにしても解消しません。描画方法をプログラム的に調整するしかありません。

一番手っ取り早いのは、描画をbitmap関数で行うことです。bitmap関数は整数座標であり、アンチエイリアスもないので、指定した座標に正確に描画されます。


・fopen()のリターンが異なる
開発時に参考にしていた文献の間違いのため、X68版とリターン値の範囲が異なります。
X-BASIC/68では正常で>=0ですが、iOS版では>=1です。ただし、X68版のソースを動かす場合は範囲が広いだけなので問題にはなりません(だから余計に気がつかなかった)。
万が一、X68に移植することがあれば注意してください。

すでにiOS上の多数のサンプルで使われているため、変更できませんでした。

V2.5からfclose()/fcloseall()のリターン値の値が取説上(定数名として)変わっていますが、値そのものは同じです。


・外部関数のエラーチェックが異なる
外部関数内におけるエラーチェックに関しては、重要なもの以外、ほとんど再現していません。理由は簡単で「いちいち調べるのが面倒だった」からです。
基本的には、X68版のほうがエラーチェックが厳しいので、それで動いていたものはiOS版でも大丈夫と思われます。
iOS版は引数の範囲が規定外だった時、多くの場合規定範囲内に自動的に補正するのでエラーは発生しません。
エラーが発生することを想定している場合は、呼び出し側でチェックしてください。

注意すべきはfopen()で、X68版ではファイルがオープンできない時外部関数エラーが発生して停止しますが、iOS版では標準ではリターン値としてエラーコードを返してくるだけでエラーは発生しません。
したがって、エラーで止まることを前提にして作られたプログラムは、必ずエラーチェックを追加する必要があります。
X68版iOS版
fp=fopen("file","r") fp=fopen("file","r")
if fp<=0 then print "file error":end
V2.6からはerror on2で、fopen()のファイルオープンエラーでも、エラーで停止させるように出来ます。

なお、X-BASIC/68のプログラムではfopen()のリターン値=ファイルポインタを得ずにfread()などのfp値に固定数値を与えている行儀の悪いものが散見されます(Oh!X掲載のプログラムにも結構あった)。
そういうものはすべからく誤動作しますので、必ずfopen()でfpを得てそれを与えるようにしてください(そういうプログラムはBCを通してCに変換するとほぼ確実に誤動作します)。



・スプライト機能は重い
これはひとえに私の技量不足からくるものですが、X68のスプライト、特にBG画面との重ね合わせは、実現するにはかなり面倒な処理が必要です。スプライト機能をX68互換を捨てて実装すればもう少し高速化も可能だったかもしれませんが、ここだけは資産を生かすため出来るだけX68互換を維持したかったので、現在のようになっています。

BGを使わないようにすれば、多少省メモリと高速化されます。

現在のスプライトでは、iPad1においてはスプライトのみ(BGなし)を、移動せず固定的に表示するのが精一杯です。iPad2以降やiPod touch4ではCPUやGPUの性能向上や、画面サイズによりおよそ問題ない性能が出ています。

ただ、やはり重いのは重いので、電池の消耗が激しいです。

BGを使わなくても遅いです。BGとの重ね合わせは処理が重いのはわかっているのですが、スプライトのみで使っても異様に遅い原因はわかっていません。
シミュレーター上では十分な速度で動いていても、実機;特にiPad1/2ではものすごく遅いです。スプライト使わず、グラフィックで描いたほうが早いくらい。

移動せず、固定的に、重ねあわせられるもう1画面として使うのが関の山かもしれません。
現状では、限定された利用しかできないようです。ごめんなさい。
OpenGLなどで書きなおせば早くなるのかしら?
今のところ、その技量はないですが。

・・・

今後、思い出し次第追記します。

2013年5月26日日曜日

その他サンプルプログラム

その他のサンプルプログラムをまとめました。
これらは主に、各ステートメントや関数の試験用です。

Zipファイル : XBtests.zip

2013年5月18日土曜日

スプライトテスト(2)

スプライト関数のテストプログラムその2です。 画像からスプライトを一括定義できるsp_image()が特徴です。 そして、大量のスプライトを一度に動かすときは、ちらつき防止のためにsp_halt()が必須であることを示しています。

注意:現在のスプライト機能は非常に重いので、使える場所が限られます。


// スプライトテスト2
// V2.00以降専用

// グラフィックモードに設定
width(0)
int wgx,wgy
getWidth(wgx,wgy):// グラフィックモードで画面幅を取得
//locate(0,2):print "Graphic Width:";wgx;"*";wgy
//
sp_init():// ここでスプライト画面は非表示になる
width(32)
vpage(B_SPAGE+B_TPAGE,YES):// display sprite screen
vpriority(TPAGE,SPAGE,GPAGE0,GPAGE1,GPAGE2,GPAGE3)
cls()
tborder(1,CTHCYAN)
//
// タッチした位置からにスプライトパターンを表示する
//
str exit$,mes$
if isLocalizeJapan() then {
 exit$="中断"
 mes$="画面にタッチ&ドラッグしてください"
} else {
 exit$="exit"
 mes$="Please touch & drag"
}
setFunctionKey(0, exit$ ,'!')
setFunctionKey(1,"SPst" ,'S')
setFunctionKey(2,"BGst" ,'B')
setFunctionKey(3,"Dprw" ,'p')
setFunctionKey(4,"disp" ,'d')
setFunctionKey(5,"OFF" ,'F')
setFunctionKey(6,"ON" ,'O')
setFunctionKey(7,"BGget",'G')
setFunctionKeyBackgroundImage("funcBack.png")
displayFunctionKeyAll(YES,YES):// v2.0
//
setTouchArea(0,0,0,0,0,YES):// これがないと任意のタッチ座標を得られない
//
// パターンは512個定義できるが、表示=プレーンは256個まで
sp_halt(YES):// しばらくスプライトの表示を中止
int pwx=16,pwy=16
int tsp=0,esp=0
esp=sp_image("IMG1.JPG",110,260,pwx,pwy,tsp,tsp,wgx/3,wgy/3,SPRITE_PRW_BG0SPBG1,YES):// pwx,pwyは補正される
locate(12,0):print "SP:";tsp;"〜";esp-1

//---------------------
// 関数テスト
//dim int ptn(255)
//sp_pat(tsp,ptn)
//int fp
//fp=fopen("pat.dat","w")
//fwrite(ptn,255,fp)
//fclose(fp)
//---------------------
//
// sprite pattern (X68 type)
dim char ca(255) = { // 網目
     0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1
                , 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0
    , 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1
                , 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0
    , 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1
                , 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0
    , 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1
                , 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0
    , 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1
                , 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0
    , 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1
                , 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0
    , 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1
                , 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0
    , 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1
                , 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0
}
int pb1=3:// パレットブロック番号
int pb2=2:// パレットブロック番号
sp_color(0,&h00000000,pb1):// 透明
sp_color(0,&h00000000,pb2):// 透明
sp_color(1,&h000080ff,pb1)
sp_color(1,&h800000ff,pb2)
sp_def(esp,ca)
sp_clr(esp+1)
//
int pg0=0:// BGテキストページ番号
int pg1=1:// BGテキストページ番号
int x,y
bg_fill(pg0,esp,pb1,SPRITE_FLIP_NONE):// 一旦全体を埋める
// wg?/16 : 画面幅のスプライト単位の数
// 中央を抜く
int bwx=wgx/16,bwy=wgy/16
for y=bwy/4 to bwy*3/4
 for x=bwx/4 to bwx*3/4
  bg_put(pg0,x,y,esp+1,pb1)
 next
next
bg_set(1,pg0,YES)
//
bg_fill(pg1,esp+1,pb1,SPRITE_FLIP_NONE):// 一旦全体をクリアする
// 中央に描画
for y=bwy*4/10 to bwy*5/10
 for x=0 to bwx
  bg_put(pg1,x,y,esp,pb2)
 next
next
bg_set(0,pg1,YES)
//
//mapSprite(wgx/3,wgy/3,tsp,pwx,pwy)
sp_halt(NO):// スプライトの表示を再開

//
dim float tpoint(2)
int touchCnt
str cs
int dt=0,t
int ts=time():/* 開始時間
locate(0,1):print mes$
//
int i
int x0=-1,y0=-1
int fDisableBG0SPBG1=NO
prfDisableBG0SPBG1(fDisableBG0SPBG1)
int fdisp=YES
sp_disp(fdisp)
repeat
 // キー入力待ちしながら時計を回す
 while (not kbhit())
  t=time()
  if t-ts>dt then { :/* 秒が変った
   dt=dt+1
  // locate(20,0):print dt;
  // beep2(0)
  // locate(20,1)
  }
  touchCnt=getTouchPoint(tpoint)
  if (touchCnt>0) then {
   x=tpoint(0):// float → int
   y=tpoint(1)
   if x<>x0 or y<>y0 then {
    locate(0,0):print x;",";y
    mapSprite(x,y,tsp,pwx,pwy)
    //
    x0=x
    y0=y
   }
   // 同じ座標の時は再表示しないようにしないとちらつく
  }
 endwhile
 // キー操作判定
 cs=inkey$()
 switch cs
  case "S":// Sprite stat
   for i=0 to 5
    locate(0,2+i)
    print "i=";i;":";sp_stat(tsp,i)
   next
   break
  case "B":// BG stat
   for i=0 to 3
    locate(16,2+i)
    print "i=";i;":";bg_stat(0,i)
   next
   break
  case "p":// Disable BG0SPBG1
   if fDisableBG0SPBG1 then fDisableBG0SPBG1=NO else fDisableBG0SPBG1=YES
   prfDisableBG0SPBG1(fDisableBG0SPBG1)
   break
  case "d":// sp_disp
   if fdisp then fdisp=NO else fdisp=YES
   sp_disp(fdisp)
   break
  case "F":// sp_off
   sp_off(tsp,esp-1):// このプログラム中ではプレーン番号とパターン番号を合わせてあるから
   break
  case "O":// sp_on
   sp_on(tsp,esp-1,SPRITE_PRW_BG0SPBG1):// このプログラム中ではプレーン番号とパターン番号を合わせてあるから
   break
  case "G":// bg_get
   getBG(pg0,bwx,bwy)
   getBG(pg1,bwx,bwy)
   break
 endswitch
until cs="!"

print "Sprite version=";versionXBiOSSprite$()
//
end

func mapSprite(gx;int,gy;int,tsp;int,pwx;int,pwy;int)
int wx,wy
 sp_halt(YES):// 以下一括変更するので終了まで表示停止
 for wy=0 to pwy-1
  for wx=0 to pwx-1
   // sp_image()では全てiOS型で定義されるのでパレットは無効
   sp_setting(tsp,gx+wx*16,gy+wy*16)
   tsp=tsp+1
  next
 next
 sp_halt(NO):// 表示再開
endfunc

func prfDisableBG0SPBG1(fDisableBG0SPBG1;int)
 dim str strfDisableBG0SPBG1(1)={"-","D"}
 sp_disableBG0SPBG1(fDisableBG0SPBG1)
 locate(31,0):print strfDisableBG0SPBG1(-fDisableBG0SPBG1)
endfunc

func getBG(pg;int,x;int,y;int)
 int pb,flip,ptn
 ptn=bg_get(pg,x,y,pb,flip)
 locate(0,10+pg*2)  :print "pg=";pg;":x=";x;"/y=";y
 locate(0,10+pg*2+1):print "pb=";pb;"/flip=";flip;"/ptn=";ptn
endfunc

画像ファイル入りZipファイル : XBsprite.zip

2013年5月16日木曜日

スプライトテスト(1)

スプライト関数のテストプログラムその1です。


注意:スプライト機能は重いので、iPad1ではちょっとしんどいです。


// スプライトテスト1
// V2.00以降専用

// グラフィックモードに設定
width(0)
int wgx,wgy
getWidth(wgx,wgy):// グラフィックモードで画面幅を取得
locate(0,2):print "Graphic Width:";wgx;"*";wgy

// sprite pattern (X68 type)
dim char ptnA(255) = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
                   ,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0
                   ,0,0,2,0,2,0,2,0,0,2,0,2,0,2,0,0
                   ,0,0,0,3,0,3,0,3,3,0,3,0,3,0,0,0
                   ,0,1,2,0,4,0,0,4,4,0,0,4,0,2,1,0
                   ,0,0,0,3,0,5,0,5,5,0,5,0,3,0,0,0
                   ,0,0,2,0,0,0,6,0,0,6,0,0,0,2,0,0
                   ,0,1,0,3,4,5,0,7,7,0,5,4,3,0,1,0
                   ,0,1,0,3,4,5,0,7,7,0,5,4,3,0,1,0
                   ,0,0,2,0,0,0,6,0,0,6,0,0,0,2,0,0
                   ,0,0,0,3,0,5,0,5,5,0,5,0,3,0,0,0
                   ,0,1,2,0,4,0,0,4,4,0,0,4,0,2,1,0
                   ,0,0,0,3,0,3,0,3,3,0,3,0,3,0,0,0
                   ,0,0,2,0,2,0,2,0,0,2,0,2,0,2,0,0
                   ,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0
                   ,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
}
int spA=16:// パターン番号
int pbA=1://3 :// パレットブロック番号
int planeA=0:// プレーン番号
//
dim char ptnB(255)={ // あ(複数色)
   0,0,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0
  ,0,0,0,0,0,0,1,0 ,0,0,0,0,0,0,0,0
  ,0,0,0,0,0,0,1,0 ,0,0,0,0,0,0,0,0
  ,0,0,0,1,2,3,4,5 ,6,7,8,9,1,0,0,0
  ,0,0,0,0,0,0,1,0 ,0,0,0,0,0,0,0,0
  ,0,0,0,0,0,0,1,0 ,0,0,0,0,0,0,0,0
  ,0,0,0,0,0,0,1,2 ,3,4,5,0,0,0,0,0
  ,0,0,0,0,1,2,3,0 ,0,0,4,5,6,0,0,0
  ,0,0,0,1,0,0,2,0 ,0,3,0,0,0,4,0,0
  ,0,0,1,0,0,0,2,0 ,0,3,0,0,0,4,0,0
  ,0,0,1,0,0,0,2,0 ,3,0,0,0,0,4,0,0
  ,0,0,1,0,0,0,2,1 ,0,0,0,0,0,3,0,0
  ,0,0,0,1,0,0,2,0 ,0,0,0,3,4,0,0,0
  ,0,0,0,0,1,2,0,0 ,0,3,4,0,0,0,0,0
  ,0,0,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0
  ,0,0,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0
}
int spB=spA+1:// パターン番号
int pbB=pbA+1:// パレットブロック番号
int planeB=planeA+1:// プレーン番号
//
int i,x,y
sp_init():// ここでスプライト画面は非表示になる
sp_clr()
sp_def(spA,ptnA)
sp_def(spB,ptnB)
for i = 0 to 7
    sp_color(i,rgb(0,i*256/8,0),pbA)
next
//
 sp_color(0,0,pbB)
 sp_color(1,&hff00ffff,pbB)
 sp_color(2,&hffff00ff,pbB)
 sp_color(3,&h800000ff,pbB)
 sp_color(4,&h8080ffff,pbB)
 sp_color(5,&hff8080ff,pbB)
 sp_color(6,&hff0000ff,pbB)
 sp_color(7,&h00ff00ff,pbB)
 sp_color(8,&h0080ffff,pbB)
 sp_color(9,&hffffffff,pbB)
//
int pbA2=pbB+1 :// パレットブロック番号
for i = 0 to 7
    sp_color(i,rgb(i*256/8,0,0),pbA2)
next
//
width(32)
vpage(B_SPAGE+B_TPAGE,YES):// display sprite screen
//vpriority(TPAGE,SPAGE,GPAGE0,GPAGE1,GPAGE2,GPAGE3)
vpriority(SPAGE,TPAGE,GPAGE0,GPAGE1,GPAGE2,GPAGE3)
cls()
tborder(1,CTHRED)
//
// タッチした位置にスプライトパターンを表示する
//
str exit$,mes$
if isLocalizeJapan() then {
 exit$="中断"
 mes$="画面にタッチ&ドラッグしてください"
} else {
 exit$="exit"
 mes$="Please touch & drag"
}
setFunctionKey(0,exit$,'!')
setFunctionKeyBackgroundImage("funcBack.png")
displayFunctionKeyAll(YES,YES):// v2.0
//
setTouchArea(0,0,0,0,0,YES):// これがないと任意のタッチ座標を得られない
//
dim float tpoint(2)
int touchCnt
str cs
int dt=0,t
int ts=time():/* 開始時間
locate(0,1):print mes$
//
sp_disp(YES)
//
if NO then {
 sp_halt(YES):// 以下一括変更するので終了まで表示停止
 sp_setting(planeA+0,0,0,spA,pbA2,SPRITE_FLIP_NONE,SPRITE_PRW_SPBG0BG1)
 sp_setting(planeA+1,16,16,spA,pbA2,SPRITE_FLIP_NONE,SPRITE_PRW_SPBG0BG1)
 sp_setting(planeA+2,32,32,spA,pbA2,SPRITE_FLIP_NONE,SPRITE_PRW_SPBG0BG1)
 sp_halt(NO):// 表示再開
 end
}
//
int x0=-1,y0=-1
repeat
 // キー入力待ちしながら時計を回す
 while (not kbhit())
  t=time()
  if t-ts>dt then { :/* 秒が変った
   dt=dt+1
  // locate(20,0):print dt;
  // beep2(0)
  // locate(20,1)
  }
  touchCnt=getTouchPoint(tpoint)
  if (touchCnt>0) then {
   x=tpoint(0):// float → int
   y=tpoint(1)
   if x<>x0 or y<>y0 then {
    locate(0,0):print x;",";y
    sp_halt(YES):// 以下一括変更するので終了まで表示停止
    // 普通 / normal
    if pbA<>1 then {
     sp_setting(planeA  ,    x,    y,spA,pbA ,SPRITE_FLIP_NONE,SPRITE_PRW_SPBG0BG1)
    } else {
     // sp_move()はパレットブロック1専用
     sp_move(   planeA  ,    x,    y,spA)
    }
    // 反転
    sp_setting(planeB  , x+16, y+16,spB,pbB ,SPRITE_FLIP_NONE  ,SPRITE_PRW_SPBG0BG1)
    sp_setting(planeB+1, x-16, y+16,spB,pbB ,SPRITE_FLIP_HORIZONTAL ,SPRITE_PRW_SPBG0BG1)
    sp_setting(planeB+2, x+16, y-16,spB,pbB ,SPRITE_FLIP_VERTICAL ,SPRITE_PRW_SPBG0BG1)
    sp_setting(planeB+3, x-16, y-16,spB,pbB ,SPRITE_FLIP_HORIZONTAL_VERTICAL,SPRITE_PRW_SPBG0BG1)
    // 同一パターンを別パレットブロックで
    sp_setting(planeB+4,wgx-x,wgy-y,spA,pbA2,SPRITE_FLIP_NONE,SPRITE_PRW_SPBG0BG1)
    sp_halt(NO):// 表示再開
    //
    x0=x:y0=y
   }
   // Do not re-displayed when the coordinate is same. Sprite would flicker Failure to do so. 
  }
 endwhile
 // キー操作判定
 cs=inkey$()
until cs="!"

float ver=osVersion()
print "iOS=";ver
//
end


Zipファイル : XBsprite.zip

2013年3月5日火曜日

爆弾探しゲーム

いわゆる「マインスイーパー」です。
bomb11.aiffも入れる必要があります。
/*****************************************************************
// 爆弾捜しゲーム
//    アイディアは違うけど、プログラムは
//    Copyright (C) 1993,2012 by AIG-SOFT
//        うわぁ、約20年ぶりに移植したのか!
/*****************************************************************
/* 定数 じゃなくて 変数

font("IPAGothic")
int fzenhan
int WX=64
int WY=width(WX,,fzenhan)
int xw=1:/* 全角1文字の桁数
if fzenhan then xw=2

/*****************************************************************

int MX =(WX/2)-2    :/* マップ最大横幅(-2は枠用)
int MY =19          :/* マップ最大縦幅
int MXX=5           :/* マップ最小横幅
int MYY=4           :/* マップ最小縦幅

int BOMB='*'        :/* 爆弾コード
int ERR=9           :/* 判定間違い
int BMBIT=bit(7)    :/* 爆弾在るはずビット
int CHBIT=bit(6)    :/* わかんないビット
int OPBIT=(BMBIT or CHBIT)    :/* オープン状態を示すビット
int NOPBIT=(not OPBIT)        :/* キャラクターのみを取り出すビット

/* 画面表示ホーム座標
int SX=2    :/* sx>=2 & sx+(MX+1)*2<画面x幅
int SY=4    :/* sy>=4 & sy+MY+1    <画面y幅

int ESC=&h1B
int CR =&h0d

/*****************************************************************
/* グローバル変数

dim char map( (64/2),19)    :/* (MX,MY)だけど変数だから使えない 現在マップ
dim char map0((64/2),19)    :/* (MX,MY) 表示用仮想画面

dim int    touchNo(10)      :// タッチエリア番号
int touchCnt    :// タッチエリア数

int wx,wy       :/* マップ幅
int seed,bomb   :/* 全爆弾数,爆弾ありそう数
int all         :/* 開けるべき全ますの数
int ts,dt       :/* 開始時間、経過時間

/*****************************************************************

int ret
int minbomb,maxbomb
/* フィールド幅、爆弾個数設定
str widthStr,heightStr,bombStr,defaultStr,messageStr
if isLocalizeJapan() then {
    widthStr ="横幅"
    heightStr="縦幅"
    minbomb=MXX*MYY/8 :/* 最小1/8
    maxbomb=wx*wy/2     :/* 最大1/2
    bombStr="爆弾数"
    defaultStr="標準"
    messageStr="メッセージ"
} else {
    widthStr ="width "
    heightStr="height"
    minbomb=MXX*MYY/8 :/* 最小1/8
    maxbomb=wx*wy/2     :/* 最大1/2
    bombStr="bombs"
    defaultStr="default"
    messageStr="message"
}
    print widthStr ;"(";MXX;"~";MX;")";
    dim str menues(3)={"10","20","30"}
    menues(3)=defaultStr
    wx=selectMenu(widthStr,menues):// selectMenu()の最終項目は特別
    if wx=3 then wx=2
    wx=(wx+1)*10
    print wx
//    input wx
    print heightStr;"(";MYY;"~";MY;")";
    menues={"5","10","15","19"}
    repeat
        wy=selectMenu2(heightStr,menues)
    until wy<>-1
    wy=5*(wy+1)
    print wy
//    input wy
    
    if wx<MXX then wx=MXX
    if wx>MX  then wx=MX
    if wy<MYY then wy=MYY
    if wy>MY  then wy=MY
    minbomb=MXX*MYY/8 :/* 最小1/8
    maxbomb=wx*wy/2     :/* 最大1/2
    print bombStr;"(";minbomb;"~";maxbomb")";
    menues={"50","100","150","200"}
    seed=selectMenuWithMessage(bombStr,messageStr,menues)
    seed=50*(seed+1)
    print seed
//    input seed
    if seed<minbomb then seed=minbomb
    if seed>maxbomb then seed=maxbomb

int sno=a_setPlayData("bomb11.aiff")
a_play(sno)

/* メインルーチン
repeat
    /* 乱数初期化
    srand2()
    
    /* 爆弾配置
    all=BombSeed(seed)
    bomb=0
    BombCount()
    dt=0:/* 経過時間
    
    /* 初期画面表示
    InitScreen()

    /* キー操作
    ret=CursorMove()
    
    /* 終わり判定
    if ret=-2 then break:/* 強制終了
    switch ret
        case -1:/* やり直し
            MapPrintAll(YES):/* 全表示
            break
        case 0: /* Game clear
            GameClear()
            break
        case 1: /* Game over
            MapPrintAll(YES):/* 全表示
            GameOver()
            break
    endswitch

    /* 再開処理
    locate(20,2)
    if isLocalizeJapan() then {
        print "《もう一度しますか?》";
        setFunctionKey(0,"はい"  ,CR)
        setFunctionKey(1,"いいえ",ESC)
    } else {
        print "<< Try Again ? >>";
        setFunctionKey(0,"YES",CR)
        setFunctionKey(1,"NO" ,ESC)
    }
    displayFunctionKey(YES,0,1)
    repeat
        ret=inkey()
    until ret<>0
    displayFunctionKey(NO,0,1)
    // タッチエリアの解除
    // 一旦解除してから再設定しないと重なり続ける
    resetTouchArea()
until (ret=ESC)
a_end(sno):// 音声バッファー解放
if isLocalizeJapan() then {
    print "終了"
} else {
    print "end"
}
end

/*****************************************************************
/* 関数群
/*****************************************************************

func int CursorMove()
/* カーソル移動&操作
int x,y,c
int t
    /* 初期座標
    /* 爆弾が無い座標に置く
    for x=0 to wx-1
        for y=0 to wy-1
            if ((map(x,y) and NOPBIT)<>BOMB) then goto "CM1"
        next
    next
label "CM1"
    ts=time():/* 開始時間
    while YES
        /* カーソル位置を反転表示
        c=map(x,y)
        tatrb(ATRB_REVERSE)
        locate2(x,y)
        print printChr$(c);
        tatrb(ATRB_NORMAL)
        
        // キー入力待ちしながら時計を回す
//        int cnt=0
        while (not kbhit())
            /* キー入力の無い間
            t=time()
            if t-ts>dt then { :/* 秒が変った
                dt=dt+1
                PrintTime(dt)
//                beep2(0)
//                print "cnt=";cnt
            }
//            inc(cnt)
        endwhile
        
        /* カーソル位置を普通表示
        locate2(x,y)
        print printChr$(c);
        //
        // キー操作判定
        c=inkey()
        beep2(0)    :// click sound
        switch c
            case &h1b: /* やり直し
                return(-1)
            
            /* カーソルは画面端でロールする
            case '2': /* ↓
                if y<wy-1 then y=y+1 else y=0
                break
            case '4': /* ←
                if x>0 then x=x-1 else x=wx-1
                break
            case '6': /* →
                if x<wx-1 then x=x+1 else x=0
                break
            case '8': /* ↑
                if y>0 then y=y-1 else y=wy-1
                break
            
            case '5': /* Check
                CheckMapPoint(x,y)
                MapPrint()
                /* チェックでは全開きにはならない
                break
            
            case ' ': /* Open
                if OpenMapPoint(x,y) then return(YES):/* game over
                /* 表示
                if MapPrint() then return(NO)
                break
        endswitch
    endwhile
    return (YES)
endfunc

/*****************************************************************

func int MapPrint()
/* マップを表示する&仮想画面変更
/* リターン値:0=まだ全ては開いていない , 1=全部開いた
int x,y,c
int all0=0
    for y=0 to wy-1
        for x=0 to wx-1
            c=map(x,y)
            if map0(x,y)<>c then {
                /* 画面に変更がある
                locate2(x,y)
                print printChr$(c)
                map0(x,y)=c:/* 仮想画面も変更
            }
            /* 開いているポイントを数える
            if ((c and OPBIT)=OPBIT) then all0=all0+1
        next
    next
    return(all0=all)
endfunc

/*****************************************************************

func MapPrintAll(mode;int)
/* マップ全表示
/* mode : 0=そのままで , 1=すべて開いて(間違い判定付き)
int x,y,c
str ls
    for y=0 to wy-1
        ls=""
        for x=0 to wx-1
            c=map(x,y)
            if mode then {
                if (c and OPBIT)=BMBIT then {
                    /* 爆弾ありとしているが・・
                    /* そこに爆弾がなければ間違い
                    if (c and NOPBIT)<>BOMB then c=ERR
                }
                c=(c or OPBIT):/* 開く
            }
            ls=ls+printChr$(c)
        next
        locate2(0,y)
        print ls
    next
    // color(7)
endfunc

/*****************************************************************

func str printChr$(c;int)
/* キャラクター表示
/* c : bit6,7 = 開き済み(11)/チェックマーク(10)/爆弾マーク(01)/閉じている(00)
/*     bit0-5 = マップ状態 &h00~&h3f
str cs
    int cc=(c and OPBIT)
    if cc=0 then {
        /* 閉じている
        return ("□")
    }
    if cc=CHBIT then {
        /* CHBIT チェックマーキング中
        return ("?")
    }
    if cc=BMBIT then {
        /* 爆弾ありマーキング中
        return ("☆")
    }
    if cc=OPBIT then {
        /* 開いている
        switch (c and NOPBIT)
            case  0:cs= "・":break
            case  1:cs= "1":break
            case  2:cs= "2":break
            case  3:cs= "3":break
            case  4:cs= "4":break
            case  5:cs= "5":break
            case  6:cs= "6":break
            case  7:cs= "7":break
            case  8:cs= "8":break
            case  9:/* 間違い
                cs= "×"
                break
            case '*':/* 爆弾
                cs= "★"
                break
        endswitch
    }
    return (cs)
endfunc

/*****************************************************************

func locate2(x;int,y;int)
    locate(SX+(x)*xw,SY+y)    :/* *xw for 全角文字
endfunc

/*****************************************************************

func WakuPrint(yoko;str,tate;str,kado;str,fall;int)
/* マップ枠表示
int i
    /* 横線
    str yokos=""
    for i=0 to wx-1:yokos=yokos+yoko:next
    locate2(0,-1):print yokos
    locate2(0,wy):print yokos

    /* 縦線
    if fall then {
        // 内側も消す=速い
        str tates=tate+space$(wx*xw)+tate
        for i=0 to wy-1
            locate2(-1,i):print tates;
        next
    } else {
        // 枠だけ=遅い
        for i=0 to wy-1
            locate2(-1,i):print tate;
            locate2(wx,i):print tate;
        next
    }
    
    /* 角
    locate2(-1,-1):print kado;
    locate2(-1,wy):print kado;
    locate2(wx,-1):print kado;
    locate2(wx,wy):print kado;
endfunc

/*****************************************************************

func InitScreen()
/* 初期画面表示
    cls()
    WakuPrint("-","|","+",YES)
    MapPrintAll(NO)
    locate(5,0)
    if isLocalizeJapan() then {
        print using "マップ幅:##×##  爆弾数:###/###  時間:#####";wx,wy,bomb,seed,dt
            /*         0         1           2         3          4    
            /*        +0123456789012345678901234567890123456789012    
            /*        +locate
    } else {
        print using "map size:##×##  bombs :###/###  time:#####";wx,wy,bomb,seed,dt
    }
    str open$,exit$
    if isLocalizeJapan() then {
        open$="開く"
        exit$="中断"
    } else {
        open$="open"
        exit$="exit"
    }
    int x=WX/2-18
    int y=SY+wy+2
                    //     012345678901234567890123456789
    locate(x,y+0):print "      ┏━┓"
    locate(x,y+1):print "      ┃↑┃"
    locate(x,y+2):print "      ┗━┛"
    locate(x,y+3):print "┏━┓┏━┓┏━┓    ┏━━┓"
    locate(x,y+4):print "┃←┃┃?┃┃→┃    ┃";open$;"┃"
    locate(x,y+5):print "┗━┛┗━┛┗━┛    ┗━━┛"
    locate(x,y+6):print "      ┏━┓"
    locate(x,y+7):print "      ┃↓┃"
    locate(x,y+8):print "      ┗━┛"
    
    int i=0
    touchNo(inc(i))=setTouchAreaWithText(x+ 6,y  ,xw*3,3,'8',YES,125,0,5,100)
    touchNo(inc(i))=setTouchAreaWithText(x+ 6,y+6,xw*3,3,'2',YES,125,0,5,100)
    touchNo(inc(i))=setTouchAreaWithText(x   ,y+3,xw*3,3,'4',YES,125,0,5,100)
    touchNo(inc(i))=setTouchAreaWithText(x+12,y+3,xw*3,3,'6',YES,125,0,5,100)
    //
    touchNo(inc(i))=setTouchAreaWithText(x+ 6,y+3,xw*3,3,'5',YES,150,50, 5,120):/* check
    touchNo(inc(i))=setTouchAreaWithText(x+22,y+3,xw*4,3,' ',YES,150, 5 ,5,120):/* open
    touchCnt=i
    //
    setFunctionKey(0,exit$,ESC)
    displayFunctionKey(YES,0,0)
endfunc

func resetTouchArea()
// タッチエリアの解除
int i
    for i=0 to touchCnt
        removeTouchArea(touchNo(i))
    next
    touchCnt=0
endfunc

/*****************************************************************

func PrintBombCount()
/* 残り爆弾数表示
    locate(5+26,0)
    print using "###";bomb
endfunc

/*****************************************************************

func PrintTime(dt;int)
/* 経過時間表示
    locate(5+42,0)
    print using "#####";dt
endfunc

/*****************************************************************

func GameClear()
/* ゲームクリア時画面表示
    /* 枠の模様を代える
    beep()
    WakuPrint("◇","◇","◇",NO)
    beep()
    locate(6,1)
    if isLocalizeJapan() then {
        print "§§ おめでとう! あなたは総ての爆弾を捜し当てました §§";
    } else {
        print "<< Congratulations! You've turned up the bomb of all >>";
    }
endfunc

/*****************************************************************

func GameOver()
/* ゲームオーバー時画面表示
    /* 枠の模様を代える
    a_play(sno)
    WakuPrint("☆","☆","☆",NO)
    a_play(sno)
    locate(10,1)
    if isLocalizeJapan() then {
        print "@@ あなたは爆弾にぶち当たってしまいました @@";
    } else {
        print "@@ You've hit a bomb @@";
    }
endfunc

/*****************************************************************

func CheckMapPoint(x;int,y;int)
/* マップ上のポイントをチェックする
int c
    c=map(x,y)
    c=(c and OPBIT)
    if c=OPBIT then {
        /* すでに開いている
        /* 開いている所を調べることは不用
        beep()
        return()
    }
    if c=0 then {
        /* 閉じている -> 爆弾ありそう
        map(x,y)=(BMBIT or map(x,y))
        bomb=bomb+1
        PrintBombCount()
        return()
    }
    if c=BMBIT then {
        /* 爆弾ありそう -> チェックしてね
        map(x,y)=(c and NOPBIT)or CHBIT
        return()
    }
    if c=CHBIT then {
        /* チェックしてね -> 閉じる
        map(x,y)=(NOPBIT and map(x,y))
        bomb=bomb-1
        PrintBombCount()
    }
endfunc

/*****************************************************************

func int OpenMapPoint(x;int,y;int)
/* マップ上のポイントを開く
int c
    c=map(x,y)
    if ((c and OPBIT)=OPBIT) then {
        /* すでに開いている
        /* 開いているところを再度開くことは不用
        beep()
        return(NO):/* 警告以外は何もしない
    }
    if ((c and NOPBIT)=BOMB) then {
        /* 爆弾を開けてしまった
        a_play(sno)
        return(YES):/* game over
    }
    /* そこはまだ開いていない所なので開ける
    if (c=0) then {
        /* 周りに爆弾がない所ならペイント型で開く
        PaintMap(x,y)
    } else {
        /* 周りに1つでも爆弾がある時は1ポイントだけ開ける
        map(x,y)=(OPBIT or map(x,y)):/* 開けた印
    }
    return(NO)
endfunc

/*****************************************************************

func PaintMap(x;int,y;int)
/* マップの(x,y)から0の点を開く
/* 基本的にはペイントと同じルーチンになるが、境界まで開くのが違う
int c,xs,xe
    if map(x,y)<>0 then {
        /* 最初の点が0でなければ終わり
        return()
    }
    /* 0 <- x : 左サーチ
    xe=x+1 :/* 右へ捜し始めるポイント
    while x>=0
        c=map(x,y)
        if ((c and OPBIT)=0) then {
            map(x,y)=(OPBIT or map(x,y))
        }
        if (c<>0) then break:/* 境界
        /* 境界も開かれる
        //    境界は爆弾ではない。/ border is not bomb
        //    なぜなら爆弾の周りには1~8が必ずあるから。
        x=x-1:/* 左へ
    endwhile
    if x<0 then xs=0 else xs=x
    /* xs=線分左端(境界も含む)

    /* x -> wx-1 : 右サーチ
    while xe<=wx-1
        c=map(xe,y)
        if (c and OPBIT)=0 then map(xe,y)=(OPBIT or map(xe,y))
        if c<>0 then break:/* 境界
        xe=xe+1 :/* 右へ
    endwhile
    if xe=wx then xe=xe-1
    /* xe=線分右端(境界を含む)
    if y>0    then PaintMapSub(xs,xe,y-1):/* 上チェック
    if y<wy-1 then PaintMapSub(xs,xe,y+1):/* 下チェック
endfunc

/*****************************************************************

func PaintMapSub(xs;int,xe;int,yy;int)
/* PaintMap()サブルーチン:上下チェック用
int x,c
    for x=xs to xe
        c=map(x,yy)
        if c=0 then {
            PaintMap(x,yy)
        } else {
            if (c and OPBIT)=0 then map(x,yy)=(OPBIT or map(x,yy)):/* 境界も開く
        }
    next
endfunc

/*****************************************************************

func int BombSeed(seeds;int)
/* マップ内に爆弾をまく
/* seeds : 爆弾個数 < wx*wyにすること
/* 乱数の初期化は外部で行なうこと
int x,y
int all
    /* マップクリア
    for y=0 to wy-1
        for x=0 to wx-1
            map(x,y)=0
        next
    next
    
    /* 要開き数:爆弾以外すべて
    all=wx*wy-seeds
    
    /* 爆弾配置
    while seeds>0
        x=rand2(wx)
        y=rand2(wy)
        if map(x,y)=0 then {
            /* 爆弾を置く
             map(x,y)=BOMB:/* マップ
            map0(x,y)=BOMB:/* 仮想画面
            seeds=seeds-1 :/* 残りを1つ減らす
        }:/* そこにすでに爆弾がある時は別の場所を探す
    endwhile
    
    return(all)
endfunc

/*****************************************************************

func BombCount()
/* マップ内の爆弾状況を書き込む
/* 各ポイント毎の周りの爆弾の数をあらかじめ書き込む
int x,y,xx,yy,xs,ys,xe,ye,c
    for y=0 to wy-1
        ys=y-1:if ys<0    then ys=0
        ye=y+1:if ye>wy-1 then ye=wy-1
        for x=0 to wx-1
            if map(x,y)=BOMB then continue:/* 爆弾の所はスキップ
            xs=x-1:if xs<0    then xs=0
            xe=x+1:if xe>wx-1 then xe=wx-1
            /* 周りをカウント
            c=0
            for yy=ys to ye
                for xx=xs to xe
                    if map(xx,yy)=BOMB then c=c+1
                next
            next
            /* c=0~8
             map(x,y)=c:/* マップ
            map0(x,y)=c:/* 仮想画面
        next
    next
endfunc

/*****************************************************************
/* 汎用関数
/*****************************************************************

func srand2()
/* rand()乱数を初期化する
int tm=time()
    tm=tm and &h0fff
    tm=tm * 16
    srand(tm)
endfunc

func rand2(seed;int)
    return (rand() mod seed):/* 0~seed-1の乱数を発生
endfunc

/*****************************************************************
音声ファイル入り圧縮ファイル: XBbs.zip