(2018/01/20 公開)
ボードゲームです。正しくは "Quoridor" で、Gigamic 社が開発・販売しているゲームです。
2人で交代しながらプレイしてください。コンピュータとの対戦は(まだ)できません。対戦プログラムを BASIC で書ける気はあまりしませんが、誰かチャレンジしてみます?
' Corridor for Smile Basic on Pasocom Mini
' 2018.1.20. Toshi Nagata
' UTF-8 encoding
'
dim board[9, 9] ' bit0-1:下の壁、1=左半分、2=右半分;bit2-3:右の壁、1=上半分、2=下半分
dim cx[2], cy[2] ' プレイヤー n のコマの現在位置
dim walls[2] ' プレイヤー n が持っている壁の残り数(初期値は10)
tile = 25 ' コマが乗るタイルの大きさ(正方形)
groove = 5 ' ミゾの幅
posx = -1 ' 現在のカーソル位置 (x)
posy = -1 ' 現在のカーソル位置 (y)
posn = -1 ' 0: コマ,1: 水平壁、2: 垂直壁
cando = 0 ' この位置にコマ/壁を置けるか
' 盤面のサイズ
wid = tile * 9 + groove * 8
' 盤面の左上の座標
xbase = floor((640 - wid) / 2)
ybase = floor((360 - wid) / 2)
' 盤面の初期化
def init_board
var i, j
for i = 0 to 8
for j = 0 to 8
board[i, j] = 0
next
next
cx[0] = 0
cy[0] = 4
cx[1] = 8
cy[1] = 4
walls[0] = 10
walls[1] = 10
end
' 太めの四角形を描く
def gbox2 x1, y1, x2, y2, col
gbox x1, y1, x2, y2, col
gbox x1 + 1, y1 + 1, x2 - 1, y2 - 1, col
end
' (x, y) と (x + dx, y + dy) の間に壁があるか
def has_wall(x, y, dx, dy)
if x < 0 || x >= 9 || y < 0 || y >= 9 then return 0
if dx == 1 then
' (x, y) の右に壁があるか
if x < 8 && (board[x, y] and 12) != 0 then return 1 else return 0
elseif dx == -1 then
' (x - 1, y) の右に壁があるか
if x > 0 && (board[x - 1, y] and 12) != 0 then return 1 else return 0
elseif dy == 1 then
' (x, y) の下に壁があるか
if y < 8 && (board[x, y] and 3) != 0 then return 1 else return 0
elseif dy == -1 then
' (x, y - 1) の下に壁があるか
if y > 0 && (board[x, y - 1] and 3) != 0 then return 1 else return 0
else
return 0
endif
end
' プレーヤー turn が位置 (nx, ny) に動けるか
def is_movable(turn, nx, ny)
var x, y, dx, dy, ox, oy, b, s
x = cx[turn]
y = cy[turn]
dx = nx - x
dy = ny - y
ox = cx[1 - turn]
oy = cy[1 - turn]
' 盤面の外?
if nx < 0 || nx >= 9 || ny < 0 || ny >= 9 then return 0
' 相手と同じ位置?
if nx == ox && ny == oy then return 0
' 今と同じ位置?
if dx == 0 && dy == 0 then return 0
if dx != 0 && dy != 0 then
' (dx, dy) のどちらも0でない場合
' 斜め1マスの移動のみ可能
if abs(dx) != 1 || abs(dy) != 1 then return 0
' (x + dx, y) に相手がいて、(x + dx, y) と (x + 2*dx, y) の間に壁がある
if x + dx == ox && y == oy && has_wall(ox, oy, ox + dx, oy) then return 1
' (x, y + dy) に相手がいて、(x, y + dy) と (x, y + dy) の間に壁がある
if x == ox && y + dy == oy && has_wall(ox, oy, ox, oy + dy) then return 1
return 0
elseif dx == 0 then
' 3マス以上の移動は不可
if abs(dy) >= 3 then return 0
' 隣に壁があれば不可
s = sgn(dy)
if has_wall(x, y, 0, s) then return 0
' 1マスの移動は OK
if abs(dy) == 1 then return 1
' 2マスの移動は (x, y + s) に相手がいて、そこと (x, y + dy) の間に壁がなければ OK
if x == ox && y + s == oy && !has_wall(x, oy, 0, s) then return 1
return 0
else ' dy == 0
' 3マス以上の移動は不可
if abs(dx) >= 3 then return 0
' 隣に壁があれば不可
s = sgn(dx)
if has_wall(x, y, s, 0) then return 0
' 1マスの移動は OK
if abs(dx) == 1 then return 1
' 2マスの移動は (x + s, y) に相手がいて、そこと (x + dx, y) の間に壁がなければ OK
if x + s == ox && y == oy && !has_wall(ox, y, s, 0) then return 1
return 0
endif
end
' (x, y) の下に横壁を置けるか
def can_place_hwall(x, y)
var b
if x < 0 || x >= 8 || y < 0 || y >= 8 then return 0
b = board[x, y]
' すでに壁がある
if (b and 3) != 0 then return 0
' 右タイルの下に壁がある
if (board[x + 1, y] and 3) != 0 then return 0
' 右に縦壁の上半分がある
if (b and 12) == 4 then return 0
return 1
end
' (x, y) の右に縦壁を置けるか
def can_place_vwall(x, y)
var b
if x < 0 || x >= 8 || y < 0 || y >= 8 then return 0
b = board[x, y]
' すでに壁がある
if (b and 12) != 0 then return 0
' 下タイルの右に壁がある
if (board[x, y + 1] and 12) != 0 then return 0
' 下に横壁の左半分がある
if (b and 3) == 1 then return 0
return 1
end
' 横壁を置く/消す
def place_hwall x, y, flag
if flag then
board[x, y] = board[x, y] or 1
board[x + 1, y] = board[x + 1, y] or 2
else
board[x, y] = board[x, y] and 254
board[x + 1, y] = board[x + 1, y] and 253
endif
end
' 縦壁を置く/消す
def place_vwall x, y, flag
if flag then
board[x, y] = board[x, y] or 4
board[x, y + 1] = board[x, y + 1] or 8
else
board[x, y] = board[x, y] and 251
board[x, y + 1] = board[x, y + 1] and 247
endif
end
'
def clear_marks
var x, y
for x = 0 to 8
for y = 0 to 8
board[x, y] = board[x, y] and 15
next
next
end
' ゴールインは可能か
' (現在のコマの位置からシード・フィル法で塗りつぶしを行い、ゴールラインに達するかどうか調べる)
dim bufx[45], bufy[45]
def can_goal(turn)
var x, y, st, ed, xl, xr, b, ret, yy, bb
' いったんマークをすべて消す
clear_marks
x = cx[turn]
y = cy[turn]
bufx[0] = x
bufy[0] = y
st = 0
ed = 1
ret = 0
while st != ed
' シードを取り出す
x = bufx[st]
y = bufy[st]
st = (st + 1) mod 45
' マーク済みならスキップ
b = board[x, y]
if (b and 16) != 0 then
continue
endif
' 現在位置をマーク
board[x, y] = b or 16
' 左に行けるところまで行く
xl = x
while xl > 0
xl = xl - 1
b = board[xl, y]
if (b and 12) != 0 then
xl = xl + 1
break
endif
board[xl, y] = b or 16
wend
' 右に行けるところまで行く
xr = x
b = board[xr, y]
while xr < 8
if (b and 12) != 0 then break
xr = xr + 1
b = board[xr, y]
board[xr, y] = (b or 16)
wend
if (turn == 0 && xr == 8) || (turn == 1 && xl == 0) then
ret = 1 ' ゴール可能
break ' これ以上処理する必要なし
endif
' 一つ上/一つ下のラインで、xl..xr から行けるところを探す
for dy = -1 to 1 step 2
yy = y + dy
if yy < 0 || yy >= 9 then continue
x = xl
while x <= xr
b = board[x, yy]
if (b and 16) != 0 then @next ' 処理済み
if dy == -1 then
if (b and 3) != 0 then @next ' 上に行けない
else
if (board[x, y] and 3) != 0 then @next ' 下に行けない
endif
' 1つ上/下に行けて、かつ未処理
' この位置から右に行けるところまで行く(ただし xr が限度)
while x < xr
if (b and 12) != 0 then break
x = x + 1
b = board[x, yy]
wend
' (x, yy) をシードとして登録
bufx[ed] = x
bufy[ed] = yy
ed = (ed + 1) mod 45
' 次の x はここの右隣から探す
@next: x = x + 1
wend
next
wend
' ret == 1 ならマークを消す
if ret == 1 then
for x = 0 to 8
for y = 0 to 8
board[x, y] = board[x, y] and 15
next
next
endif
return ret
end
' 画面を更新する
def display turn
var i, j, k, x, y, n, col, ccol, tcol
gfill 0, ybase, 639, ybase + wid - 1, rgb(0, 0, 0)
' 手持ちの壁の数を表示
for i = 0 to 1
for n = 0 to walls[i] - 1
if i == 0 then
x = xbase - (tile * 3 + groove * 2 - 2)
else
x = xbase + wid + (tile + groove)
endif
y = ybase + 10 + (tile + groove - 3) * n
gfill x, y, x + tile * 2 + groove - 3, y + groove - 1, #yellow
next
next
clear_marks
' 「次の手」が打てるかどうかを判断
if posn == 0 then
' コマが動かせるか
cando = is_movable(turn, posx, posy)
elseif posn == 1 then
' 横壁が置けるか
cando = can_place_hwall(posx, posy)
if cando then
' ゴールを妨げないか
place_hwall posx, posy, 1
cando = can_goal(turn) && can_goal(1 - turn)
place_hwall posx, posy, 0
endif
else
' 縦壁が置けるか
cando = can_place_vwall(posx, posy)
if cando then
' ゴールを妨げないか
place_vwall posx, posy, 1
cando = can_goal(turn) && can_goal(1 - turn)
place_vwall posx, posy, 0
endif
endif
if cando then
if turn == 0 then ccol = #red else ccol = #blue
else
ccol = rgb(180, 180, 180)
endif
for i = 0 to 8
for j = 0 to 8
x = xbase + (tile + groove) * i
y = ybase + (tile + groove) * j
if posn != 0 && (board[i, j] and 16) != 0 then
tcol = rgb(0, 84, 12) ' ゴールに到達できないタイルは色を変える
else
tcol = rgb(144, 84, 12)
endif
gfill x, y, x + tile - 1, y + tile - 1, tcol
if posn == 0 && posx == i && posy == j then
gbox2 x, y, x + tile - 1, y + tile - 1, ccol
endif
n = board[i, j] and 3
if n == 1 then
gfill x + 2, y + tile, x + tile + groove - 1, y + tile + groove - 1, rgb(255, 255, 0)
elseif n == 2 then
gfill x, y + tile, x + tile - 3, y + tile + groove - 1, rgb(255, 255, 0)
endif
if posn == 1 && posx == i && posy == j then
gbox2 x + 2, y + tile, x + tile * 2 + groove - 3, y + tile + groove - 1, ccol
endif
n = board[i, j] and 12
if n == 4 then
gfill x + tile, y + 2, x + tile + groove - 1, y + tile + groove - 1, rgb(255, 255, 0)
elseif n == 8 then
gfill x + tile, y, x + tile + groove - 1, y + tile - 3, rgb(255, 255, 0)
endif
if posn == 2 && posx == i && posy == j then
gbox2 x + tile, y + 2, x + tile + groove - 1, y + tile * 2 + groove - 3, ccol
endif
x = x + floor(tile / 2)
y = y + floor(tile / 2)
for k = 0 to 1
if k == 0 then col = #red else col = #blue
if i == cx[k] && j == cy[k] then
gcircle x, y, 7, col
gpaint x, y, col
endif
next
next
next
end
def move(turn)
var col1, col2, lim
if turn == 0 then
col1 = rgb(200, 0, 0)
col2 = rgb(0, 0, 0)
else
col1 = rgb(0, 0, 0)
col2 = rgb(0, 0, 200)
endif
gbox2 0, ybase, xbase - 1, ybase + wid - 1, col1
gbox2 xbase + wid, ybase, 639, ybase + wid - 1, col2
posn = 0
posx = cx[turn]
posy = cy[turn]
while 1
display turn
repeat : in$ = inkey$() : until len(in$) > 0
ch = asc(in$)
if posn == 0 then lim = 9 else lim = 8
if ch == 32 then
if walls[turn] > 0 then
posn = (posn + 1) mod 3
if posn != 0 then
' 壁は (0, 0)-(7, 7) の範囲にしか置けない
if posx == 8 then posx = 7
if posy == 8 then posy = 7
endif
endif
elseif ch == 28 then
posx = (posx + 1) mod lim
elseif ch == 29 then
posx = (posx - 1 + lim) mod lim
elseif ch == 30 then
posy = (posy - 1 + lim) mod lim
elseif ch == 31 then
posy = (posy + 1) mod lim
elseif ch == 13 && cando then
if posn == 0 then
' コマを動かす
cx[turn] = posx
cy[turn] = posy
if (turn == 0 && posx == 8) || (turn == 1 && posx == 0) then
return 1 ' ゴールイン
endif
elseif posn == 1 then
' 横壁を置く
place_hwall posx, posy, 1
inc walls[turn], -1
else
' 縦壁を置く
place_vwall posx, posy, 1
inc walls[turn], -1
endif
return 0
endif
wend
end
cls : gcls
init_board
this_turn = 0
while move(this_turn) == 0
this_turn = 1 - this_turn
wend
if this_turn == 0 then s$ = "赤" else s$ = "青"
print s$; "がゴールしました!"