(2018/01/13 公開)
同じ牌を選んで消していきます。牌は、左または右と上が空いているものしか取れません。全部とったらクリアです。
このプログラムも、動作させるための最低限の実装しかしていません。クリアメッセージすらありません(ctrl-Cで止めるしかない)。気が向いたら改造するということで。
牌の画像データを mahjong.png
という名前で、SmileBASIC のディレクトリに置いておいてください。このデータはゲームデザイン (http://www.gamedesign.jp/) ご提供の「フリー素材」からいただきました。(現在はリンクがなくなっているようです。)
' マージャンソリティア
' 2018.1.13. Toshi Nagata
' UTF-8 encoding
' 牌のレイアウト (x1,y1)-(x2,y2),z
' レイアウトは x, y, z の昇順になっていること
@layout1
data 0, 0, 8, 0, 0
data 1, 1, 10, 1, 0
data 2, 2, 11, 2, 0
data 3, 3, 12, 3, 0
data 4, 4, 13, 4, 0
data 3, 5, 12, 5, 0
data 2, 6, 11, 6, 0
data 1, 7, 10, 7, 0
data 3.5, 1, 8.5, 3, 1
data 4.5, 4, 9.5, 6, 1
data 5, 2, 8, 5, 2
data 6, 3, 7, 4, 3
data 6.5, 3.5, 6.5, 3.5, 4
dim posx[136], posy[136], posz[136]
dim tiles[136], flags[136]
cenx = 0
ceny = 0
maxz = 0
wid = 28
high = 40
wid0 = 27
high0 = 34
curn = -1
' スプライトの初期化
def init_sprite
var nx, ny, i, j
' GRP2 をスプライト用に使う
sppage 2
' 画像読み込み
load "grp2:mahjong.png"
' カーソル位置表示用の四角を GRP2 に描く
gpage 0, 2
for i = 0 to 3
gbox i, high * 5 + i, wid0 + 2 - i, high * 5 + high0 + 2 - i, rgb(160, 50, 50, 255)
next
gpage 0, 0
' スプライトの定義
for j = 0 to 33 ' (0, 0) はマスク用(このプログラムでは使わない)
nx = (j + 1) mod 7
ny = (j + 1) div 7
spdef j, nx * wid, ny * high, wid, high
for i = 0 to 3
spset j * 4 + i, j
next
next
' カーソル位置表示用スプライト
spdef 34, 0, high * 5, wid0 + 2, high0 + 2
spset 200, 34
end
' レイアウトを読みこむ
def read_layout
var x1, y1, x2, y2, z, i, j, n
restore @layout1
n = 0
while 1
read x1, y1, x2, y2, z
x2 = x2 - x1
y2 = y2 - y1
for i = 0 to y2
for j = 0 to x2
posx[n] = x1 + j
posy[n] = y1 + i
posz[n] = z
n = n + 1
if n >= 136 then @break
next
next
wend
@break
cenx = (max(posx) + min(posx)) / 2
ceny = (max(posy) + min(posy)) / 2
maxz = max(posz)
end
' 牌をシャッフル
def shuffle
var i, j, k, w
for i = 0 to 135
tiles[i] = i
flags[i] = 1 ' すべて通常表示
next
for i = 0 to 100
j = rnd(136)
k = rnd(136)
w = tiles[j]
tiles[j] = tiles[k]
tiles[k] = w
next
end
' n 番の牌の上に牌があるかどうかチェック
def is_hidden(n)
var x1, y1, z1, m
x1 = posx[n]
y1 = posy[n]
z1 = posz[n]
for m = n + 1 to 135
if (flags[m] and 1) == 0 then continue
if posz[m] != z1 + 1 then continue
if abs(x1 - posx[m]) < 1 && abs(y1 - posy[m]) < 1 then return 1
next
return 0
end
' カーソル移動処理:n から (dx, dy) 方向で一番近い牌を探す
def search_tile(n, dx, dy)
var m, ret, d, dd, x, y, wx, wy
ret = n
if n < 0 then
x = 0
y = 0
else
x = posx[n]
y = posy[n]
endif
for m = 0 to 135
if m == n || (flags[m] and 2) == 0 then continue
wx = posx[m] - x
wy = posy[m] - y
if dx != 0 && sgn(wx) != dx then continue
if dy != 0 && sgn(wy) != dy then continue
dd = wx * wx + wy * wy
if ret == n || dd < d then
ret = m
d = dd
endif
next
return ret
end
' 取れる牌をマーク
def update_selectable
var n, m, c
dim count[34]
for n = 0 to 33
count[n] = 0
next
for n = 0 to 135
flags[n] = flags[n] and 1
' すでに消されている牌
if flags[n] == 0 then continue
' 左に同じ行・高さの牌がある
if n > 0 && (flags[n - 1] and 1) != 0 && posy[n - 1] == posy[n] && posz[n - 1] == posz[n] then
' 右に同じ行・高さの牌がある
if n < 135 && (flags[n + 1] and 1) != 0 && posy[n + 1] == posy[n] && posz[n + 1] == posz[n] then
continue
endif
endif
' 上に牌がある
if is_hidden(n) then continue
' 取れる牌としてマーク
flags[n] = flags[n] or 2
' 取れる牌の種類をカウント
m = tiles[n] div 4
inc count[m]
next
c = 0
for n = 0 to 135
' 「取れる」とした牌をもう一度調べる
if flags[n] != 3 then continue
' カウントが1の牌は実際には取れない
m = tiles[n] div 4
if count[m] <= 1 then
flags[n] = 1
else
inc c ' 取れる牌の数を数える
endif
next
' カーソル位置を更新
if curn < 0 then
curn = search_tile(-1, 0, 0)
else
curn = search_tile(curn, 0, 0)
endif
end
' 牌を選択する
def select_tile n
var m
' n がすでに選択されていたら選択を外す
if (flags[n] and 4) != 0 then
flags[n] = flags[n] and 3
return
endif
' すでに選択されているものがあるかどうかチェック
for m = 0 to 135
if (flags[m] and 4) != 0 then
' 同じタイルか?
if (tiles[m] div 4) == (tiles[n] div 4) then
' 消す
flags[m] = 0
flags[n] = 0
update_selectable
return
else
' 前の選択を解除してこの牌だけを選択
flags[m] = flags[m] and 3
flags[n] = flags[n] or 4
return
endif
endif
next
' この牌を選択
flags[n] = flags[n] or 4
end
' 盤面を表示
def display
var px, py, n, cn, col, spn, flg
gfill 0, 36, 639, 359, rgb(0, 64, 0)
for n = 0 to 135
spn = tiles[n]
flg = flags[n]
if (flg and 1) == 0 then
sphide spn
continue
endif
px = (posx[n] - cenx - 0.5) * wid0 + 320
py = (posy[n] - ceny - 0.5) * high0 + 36 + 162
if (flg and 4) != 0 then
col = #red
elseif (flg and 2) != 0 then
col = #yellow
else
cn = 255 - (maxz - posz[n]) / maxz * 60
col = rgb(cn, cn, cn)
endif
spcolor spn, col
spofs spn, px - posz[n] * 3, py - posz[n] * 7, -n - 1
spshow spn
if n == curn then
spofs 200, px - posz[n] * 3 - 2, py - posz[n] * 7 - 2, -200
spshow 200
endif
next
if curn < 0 then sphide 200
end
init_sprite
read_layout
shuffle
update_selectable
display
while curn >= 0
repeat : in$ = inkey$() : until len(in$) > 0
ch = asc(in$)
if ch == 13 then
select_tile curn
display
continue
endif
x = posx[curn]
y = posy[curn]
if ch == 28 then
dx = 1
dy = 0
elseif ch == 29 then
dx = -1
dy = 0
elseif ch == 30 then
dx = 0
dy = -1
elseif ch == 31 then
dx = 0
dy = 1
endif
curn = search_tile(curn, dx, dy)
display
wend