Microsoft Small Basic

Program Listing: HHF931-0
' Igo04.smallbasic - Igo v0.4
'
' 履歴:
' v0.1 2010/07/21 初版:碁盤の表示 (243行 NBK992)
' v0.2 2010/07/23 手数の表示 (265行 JVR185)
' v0.3 2010/07/24 人による次の手の処理 (378行 HHF931)
' v0.4 2010/07/31 棋譜の再生 (706行)
'
' ---------------------------------
' メインプログラム
' ---------------------------------
' work: boolean bProgramEnd - プログラム終了
' work: boolean bGameEnd - 終局
' work: integer iMove - 手数
' work: boolean bDebug - デバッグ中
'
bDebug = "False"
' 画面設計用格子の表示
If bDebug Then
DrawGrid()
EndIf
' プログラムの初期化
InitProgram()
' 盤の初期化
InitBoard()
' コントロール部の初期化
InitControls()
' 盤面のクリア
ClearBoard()
' 盤の表示
ShowBoard()
' 対戦者名(または棋譜名)の入力
InputGameInfo()
' プログラム終了が指定されるまで{}内繰り返し
While (bProgramEnd = "False")
'{
' 盤面のクリア
ClearBoard()
If (bReplay = "False") Then
' 棋譜の初期化
InitRecord()
EndIf
' 盤の表示
ShowBoard()
iMove = 0
If (bReplay = "False") Then
SaveGameDate()
EndIf
' 終局になるまで{}内繰り返し
While (bGameEnd = "False")
'{
iMove = iMove + 1
' 先手ランプ表示
iX = iBLX
iY = iBLY
bOn = "True"
DrawLamp()
iX = iWLX
iY = iWLY
bOn = "False"
DrawLamp()
' 先手が打つ
If (bReplay) Then
Replay()
Else
' 着手可能な手を調べる
GetPossiblePuts()
Human()
EndIf
' 投了なら終局
If bResign Then
Goto lGameEnd
EndIf
' パスならパス回数をカウントアップ
If bPass Then
iPass = iPass + 1
Else
iPass = 0
EndIf
' 棋譜の記録
If (bReplay = "False") Then
Record()
EndIf
' 盤の表示
If bPass = "False" Then
DrawStone()
Sound.PlayClickAndWait()
EndIf
' 終局の判定
Judge()
If bGameEnd Then
Goto lGameEnd
EndIf
'
iMove = iMove + 1
' 先手ランプ表示
iX = iBLX
iY = iBLY
bOn = "False"
DrawLamp()
iX = iWLX
iY = iWLY
bOn = "True"
DrawLamp()
' 後手が打つ
If (bReplay) Then
Replay()
Else
' 着手可能な手を調べる
GetPossiblePuts()
Human()
EndIf
' 投了なら終局
If bResign Then
Goto lGameEnd
EndIf
' パスならパス回数をカウントアップ
If bPass Then
iPass = iPass + 1
Else
iPass = 0
EndIf
' 棋譜の記録
If (bReplay = "False") Then
Record()
EndIf
' 盤の表示
If bPass = "False" Then
DrawStone()
Sound.PlayClickAndWait()
EndIf
' 終局の判定
Judge()
' }
EndWhile
lGameEnd:
' 対戦者名(または棋譜名)の入力
bProgramEnd = "True"
InputGameInfo()
' }
EndWhile
'
' ---------------------------------
' デバッグ関連
' ---------------------------------
' 画面設計用の格子の表示
Sub DrawGrid
cx = 15.6 ' 43 chars / line
cy = 24 ' 18 lines / 443 height
GraphicsWindow.FontSize = cy
GraphicsWindow.FontName = "Courier New"
GraphicsWindow.BrushColor = "SlateBlue"
GraphicsWindow.PenColor = "Cyan"
GraphicsWindow.Clear()
x = 0
y = 0
text = "----+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8"
GraphicsWindow.DrawText(x, y - 3, text)
For i = 0 To 19
text = i
GraphicsWindow.DrawText(x, y - 3, text)
y = y + cy
EndFor
w = GraphicsWindow.Width
h = GraphicsWindow.Height
text = w + "," + h + " "
GraphicsWindow.DrawText(x + cx * 4, cy * 17 - 3, text)
For i = 0 To 624 Step cx
GraphicsWindow.DrawLine(i, 0, i, 443)
EndFor
For i = 0 To 443 Step cy
GraphicsWindow.DrawLine(0, i, 624, i)
EndFor
EndSub
' ---------------------------------
' プログラム関連
' ---------------------------------
' プログラムの初期化
' work: integer i
' out: constant SPACE - 空点
' out: constant BLACK - 黒
' out: constant WHITE - 白
' out: constant OB - 盤外
' out: integer iRo - 何路か
' out: integer iPass - パス回数
' out: real rCX - 文字幅
' out: real rCY - 文字の高さ
' out: integer iMove - 手数
' out: string sBoardColor - 碁盤の色
' out: string sAlpha[] - SGF棋譜のためのアルファベット
' out: string sStone[] - 石の名前
' out: string sPass - パス
' out: string sResign - 投了
' out: string sReplay - 再生
' out: string sNew - 新規
Sub InitProgram
SPACE = 0 ' 空点
BLACK = 1 ' 黒
WHITE = 2 ' 白
OB = 3 ' 盤外
iRo = 6
rCX = 15.6
rCY = 24
GraphicsWindow.FontSize = rCY
sBoardColor = "Wheat"
For i = 0 To iRo
sAlpha[i] = Text.GetSubText(" abcdefghijklmnopqrs", i + 1, 1)
EndFor
sAlpha[i] = "t" ' パス
iPass = 0
sStone[SPACE] = "SPACE"
sStone[BLACK] = "BLACK"
sStone[WHITE] = "WHITE"
sStone[OB] = "OB"
sNew = "新規"
sReplay = "再生"
sPass = "パス"
sResign = "投了"
EndSub
'
' コントロール部の初期化
' in: real rCX - 文字幅
' in: real rCY - 文字の高さ
' in: integer idLX, idLY - 線の間隔
' in: integer iSR - 碁石の半径
' in: integer iBX0, iBY0, iBX1, iBY1 - 盤の左端, 上端, 右端, 下端
' in: string sNew, sReplay, sPass, sResign - ボタン用テキスト
' work: integer iBHX, iBHY - 黒アゲハマ表示位置
' work: integer iWHX, iWHX - 白アゲハマ表示位置
' out: object oBlack - 黒プレイヤー名のテキストボックス
' out: object oWhite - 白プレイヤー名のテキストボックス
' out: object oPass - [パス]ボタン
' out: object oHama[] - アゲハマ表示用テキスト
' out: object oSGF - SGF棋譜ファイル名のテキストボックス
' out: iBLX, iBLY - 黒番を示すランプの位置
' out: iWLX, iWLY - 白番を示すランプの位置
Sub InitControls
GraphicsWindow.BackgroundColor = "Silver"
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FillEllipse(iBX1 + rCX, iBY0 + idLY, iSR * 2, iSR * 2)
iBHX = iBX1 + rCX + iSR * 2.5
iBHY = iBY0 + rCY * 2.5
oHama[BLACK] = Shapes.AddText(0)
Shapes.Move(oHama[BLACK], iBHX, iBHY)
iBLX = iBHX + rCX * 5
iBLY = iBHY
bOn = "True"
iX = iBLX
iY = iBLY
DrawLamp()
sBlack = "Human"
oBlack = Controls.AddTextBox(iBX1 + rCX, iBY0)
Controls.SetTextBoxText(oBlack, sBlack)
oWhite = Controls.AddTextBox(iBX1 + rCX, iBY0 + idLY * iRo / 2)
sWhite = "Human"
Controls.SetTextBoxText(oWhite, sWhite)
oPass = Controls.AddButton(sPass, iBX1 + rCX, iBY0 + idLY * iRo)
oResign = Controls.AddButton(sResign, iBX1 + rCX * 6, iBY0 + idLY * iRo)
Shapes.HideShape(oPass)
Shapes.HideShape(oResign)
oSGF = Controls.AddTextBox(iBX1 + rCX, iBY0 + idLY * iRo)
sSGF = "temp.sgf"
Controls.SetTextBoxText(oSGF, sSGF)
oNewGame = Controls.AddButton(sNew, iBX1 + rCX, iBY0 + idLY * iRo + rCY * 2)
oReplay = Controls.AddButton(sReplay, iBX1 + rCX * 6, iBY0 + idLY * iRo + rCY * 2)
iWHX = iBX1 + rCX + iSR * 2.5
iWHY = iBY0 + idLY * (iRo / 2) + rCY * 2.5
oHama[WHITE] = Shapes.AddText(0)
Shapes.Move(oHama[WHITE], iWHX, iWHY)
iWLX = iWHX + rCX * 5
iWLY = iWHY
bOn = "False"
iX = iWLX
iY = iWLY
DrawLamp()
GraphicsWindow.BrushColor = "White"
GraphicsWindow.FillEllipse(iBX1 + rCX, iBY0 + idLY * (iRo / 2 + 1), iSR * 2, iSR * 2)
EndSub
'
' ランプの表示
' in: integer iX, iY - ランプの表示位置
' in: boolean bOn - ランプON
Sub DrawLamp
sSavedColor = GraphicsWindow.BrushColor
GraphicsWindow.BrushColor = "White"
GraphicsWindow.FillRectangle(iX + 2, iY + 2, 20, 6)
GraphicsWindow.BrushColor = "DimGray"
GraphicsWindow.FillRectangle(iX - 2, iY - 2, 20, 6)
If bOn Then
GraphicsWindow.BrushColor = "Lime"
Else
GraphicsWindow.BrushColor = "Black"
EndIf
GraphicsWindow.FillRectangle(iX, iY, 20, 6)
GraphicsWindow.BrushColor = sSavedColor
EndSub
' ---------------------------------
' ゲーム関連
' ---------------------------------
' 対戦者名(または棋譜名)の入力
' out: boolean bShowMove - 手数を表示する
' out: boolean bGameEnd - 終局
' out: boolean bProgramEnd - プログラム終了
' out: boolean bReplay - 再生モード
Sub InputGameInfo
bShowMove = "True"
bGameEnd = "False"
bProgramEnd = "False"
bReplay = "False"
bResign = "False"
Controls.HideControl(oPass)
Controls.HideControl(oResign)
Controls.ShowControl(oNewGame)
Controls.ShowControl(oReplay)
Controls.HideControl(oSGF)
Controls.ButtonClicked = OnButtonClicked1
bNotClicked = "True"
While bNotClicked
Program.Delay(200)
EndWhile
Controls.HideControl(oNewGame)
Controls.HideControl(oReplay)
Controls.ShowControl(oPass)
Controls.ShowControl(oResign)
EndSub
'
Sub OnButtonClicked1
If Controls.LastClickedButton = oNewGame Then
bNotClicked = "False"
bProgramEnd = "False"
ElseIf Controls.LastClickedButton = oReplay Then
If iNumRec > 0 Then
bNotClicked = "False"
bReplay = "True"
Else
Sound.PlayBellRingAndWait()
EndIf
EndIf
EndSub
'
' 終局の判定
' out: boolean bGameEnd - 終局
Sub Judge
bGameEnd = "False"
If iPass >= 2 Or bResign Then
bGameEnd = "True"
EndIf
EndSub
'
' 盤の初期化
' in: integer iRo - 何路か
' in: real rCX - 文字幅
' in: real rCY - 文字の高さ
' work: integer i, iX, iY
' out: integer iBoard[][] - 碁盤
' out: integer idLX, idLY - 線の間隔
' out: integer iSR - 碁石の半径
' out: integer iLX0, iLY0, iLX1, iLY1 - 左端, 上端, 右端, 下端の線
' out: integer iBX0, iBY0, iBX1, iBY1 - 盤の左端, 上端, 右端, 下端
' out: integer iHama[] - アゲハマ
Sub InitBoard
For i = 0 To iRo + 1
iBoard[0][i] = OB
iBoard[iRo + 1][i] = OB
iBoard[i][0] = OB
iBoard[i][iRo + 1] = OB
EndFor
idLX = rCX * 3 ' 線の間隔
iSR = idLX / 2 - 2 ' 碁石の半径
idLY = rCY * 2 ' 線の間隔
iLX0 = rCX * 6.5 ' 左端の線
iLY0 = rCY * 4.5 ' 上端の線
iLX1 = iLX0 + idLX * (iRo - 1) ' 右端の線
iLY1 = iLY0 + idLY * (iRo - 1) ' 下端の線
iBX0 = iLX0 - idLX * 1.5 ' 盤の左端
iBY0 = iLY0 - idLY * 1.5 ' 盤の上端
iBX1 = iLX1 + idLX ' 盤の右端
iBY1 = iLY1 + idLY ' 盤の下端
EndSub
'
' 盤面のクリア
' in: integer iRo - 何路か
' work: integer i, iX, iY
' out: integer iBoard[][] - 碁盤
' out: integer iHama[] - アゲハマ
Sub ClearBoard
For iX = 1 To iRo
For iY = 1 To iRo
iBoard[iX][iY] = SPACE
EndFor
EndFor
iHama[BLACK] = 0
iHama[WHITE] = 0
Controls.SetTextBoxText(oHama[BLACK], 0)
Controls.SetTextBoxText(oHama[WHITE], 0)
EndSub
'
' 盤の表示
' in: integer idLX, idLY - 線の間隔
' in: integer iSR - 碁石の半径
' in: integer iLX0, iLY0, iLX1, iLY1 - 左端, 上端, 右端, 下端の線
' in: integer iBX0, iBY0, iBX1, iBY1 - 盤の左端, 上端, 右端, 下端
' work: integer iX, iY
Sub ShowBoard
' 盤の色を塗る
GraphicsWindow.BrushColor = sBoardColor
GraphicsWindow.FillRectangle(iBX0, iBY0, iBX1 - iBX0, iBY1 - iBY0)
' 線を描く
GraphicsWindow.PenColor = "Black"
For iX = iLX0 To iLX1 Step idLX
GraphicsWindow.DrawLine(iX, iLY0, iX, iLY1)
EndFor
For iY = iLY0 To iLY1 Step idLY
GraphicsWindow.DrawLine(iLX0, iY, iLX1, iY)
EndFor
' 数字を書く
GraphicsWindow.BrushColor = "Black"
For iX = 1 To iRo
GraphicsWindow.DrawText(iLX0 + (iX - 1.2) * idLX, iLY0 - idLY - 3, iX)
EndFor
For iY = 1 To iRo
GraphicsWindow.DrawText(iLX0 - idLX, iLY0 + (iY - 1.25) * idLY - 3, iY)
EndFor
EndSub
'
' 碁石を描く
' in: integer iX, iY - 碁石の座標
' in: integer iColor - 碁石の色
' in: boolean bShowMove - 手数を表示する
' in: integer iMove - 手数
' in: integer iLX0 - 左端の縦線のX座標
' in: integer iLY0 - 上端の横線のY座標
' in: integer idLX - 縦線の間隔
' in: integer idLY - 横線の間隔
' in: integer iSR - 碁石の半径
' work: integer iNX, iNY - 手数の座標
' work: integer iND - 手数の桁数
Sub DrawStone
If iColor = WHITE Then
GraphicsWindow.BrushColor = "White"
GraphicsWindow.FillEllipse(iLX0 + (iX - 1) * idLX - iSR, iLY0 + (iY - 1) * idLY - iSR, iSR * 2, iSR * 2)
If bShowMove Then
GraphicsWindow.BrushColor = "Black"
If iMove > 9 Then
iND = 2
Else
iND = 1
EndIf
iNX = iLX0 + (iX - 1) * idLX - rCX / (3 - iND)
iNY = iLY0 + (iY - 1) * idLY - rCY / 2 - 3
GraphicsWindow.DrawText(iNX, iNY, iMove)
EndIf
ElseIf iColor = BLACK Then
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FillEllipse(iLX0 + (iX - 1) * idLX - iSR, iLY0 + (iY - 1) * idLY - iSR, iSR * 2, iSR * 2)
If bShowMove Then
GraphicsWindow.BrushColor = "White"
If iMove > 9 Then
iND = 2
Else
iND = 1
EndIf
iNX = iLX0 + (iX - 1) * idLX - rCX / (3 - iND)
iNY = iLY0 + (iY - 1) * idLY - rCY / 2 - 3
GraphicsWindow.DrawText(iNX, iNY, iMove)
EndIf
EndIf
EndSub
'
' 碁石を消す
' in: integer iX, iY - 碁石の座標
' work: integer iEX0, iEY0, iEX1, iEY1 - 書きなおす線の座標
Sub EraseStone
' 碁石を碁盤の色で消す
GraphicsWindow.BrushColor = sBoardColor
GraphicsWindow.FillRectangle(iLX0 + (iX - 1) * idLX - iSR, iLY0 + (iY - 1) * idLY - iSR, iSR * 2, iSR * 2)
GraphicsWindow.PenColor = "Black"
' 横線の引き直し
If iX = 1 Then
iEX0 = iLX0
Else
iEX0 = iLX0 + (iX - 1) * idLX - iSR
EndIf
If iX = iRo Then
iEX1 = iLX0 + (iX - 1) * idLX
Else
iEX1 = iLX0 + (iX - 1) * idLX + iSR
EndIf
iEY0 = iLY0 + (iY - 1) * idLY
iEY1 = iLY0 + (iY - 1) * idLY
GraphicsWindow.DrawLine(iEX0, iEY0, iEX1, iEY1)
' 縦線の引き直し
iEX0 = iLX0 + (iX - 1) * idLX
iEX1 = iLX0 + (iX - 1) * idLX
If iY = 1 Then
iEY0 = iLY0
Else
iEY0 = iLY0 + (iY - 1) * idLY - iSR
EndIf
If iY = iRo Then
iEY1 = iLY0 + (iY - 1) * idLY
Else
iEY1 = iLY0 + (iY - 1) * idLY + iSR
EndIf
GraphicsWindow.DrawLine(iEX0, iEY0, iEX1, iEY1)
EndSub
'
' ---------------------------------
' 棋譜ファイル関連
' ---------------------------------
' 棋譜の初期化
' out: integer iNumRec - 棋譜の手数
Sub InitRecord
iNumRec = 0
EndSub
'
' 棋譜の記録
' in: integer iX, iY - 次の手(碁盤の座標)
' in: integer iColor - 碁石の色
' in: boolean bPass - パス
' out: integer iRecord[][] - 棋譜
' out: integer iNumRec - 棋譜の手数
Sub Record
iNumRec = iNumRec + 1
If bPass Then
iX = iRo + 1
iY = iRo + 1
EndIf
iRecord[iNumRec]["x"] = iX
iRecord[iNumRec]["y"] = iY
iRecord[iNumRec]["turn"] = iColor
EndSub
'
' 棋譜の出力
' in: integer iNumRec - 棋譜の手数
' in: integer iRecord[][] - 棋譜
' work: integer i, iX, iY, iColor
Sub WriteRecord
iSGFLine = 1
TextWindow.WriteLine("(GM[1]FF[1]SZ[" + iRo + "]PB[" + sBlack + "]PW[" + sWhite + "]")
iSGFLine = 2
TextWindow.WriteLine("DT[" + sDate + "]KM[6.5]RU[Japanese]")
iSGFLine = 3
For i = 1 To iNumRec
iX = iRecord[i]["x"]
iY = iRecord[i]["y"]
iColor = iRecord[i]["turn"]
If (iColor = BLACK) Then
TextWindow.Write(";B[" + sAlpha[iX] + sAlpha[iY] + "]")
ElseIf (iColor = WHITE) Then
TextWindow.Write(";W[" + sAlpha[iX] + sAlpha[iY] + "]")
ElseIf (iColor = SPACE) Then
TextWindow.Write(";E[" + sAlpha[iX] + sAlpha[iY] + "]")
EndIf
If Math.Remainder(iNumRec, 10) = 0 Then
TextWindow.WriteLine("")
iSGFLine = iSGFLine +1
EndIf
EndFor
EndSub
'
' 棋譜出力用の日付を保存
' out: string sDate - SFG棋譜用日付
Sub SaveGameDate
sDate = Clock.Year + "-"
If Clock.Month <= 9 Then
sDate = sDate + "0"
EndIf
sDate = sDate + Clock.Month + "-"
If Clock.Day <= 9 Then
sDate = sDate + "0"
EndIf
sDate = sDate + Clock.Day
EndSub
'
' ---------------------------------
' 思考ルーチン関連
' ---------------------------------
' 着手可能な手のリスト作成
' in: integer iboard[][] - 碁盤
' out: integer iPossible - 着手可能な手の数
' out: integer iPX[], iPX[] - 着手可能な手
' work: integer i - iBoard[][]の座標 x, yの代わり
Sub GetPossiblePuts
iPossible = 0
For i = 0 To iRo * iRo - 1
If iBoard[Math.Remainder(i, iRo) + 1][Math.Floor(i / iRo) + 1] = SPACE Then
iPossible = iPossible + 1
iPX[iPossible] = Math.Remainder(i, iRo) + 1
iPY[iPossible] = Math.Floor(i / iRo) + 1
EndIf
EndFor
EndSub
'
' 次の手を打つ - Human (人)
' in: integer iPossible - 着手可能な手
' in: integer iMove - 手数
' work: i - 着手可能な手の添字
' work: integer iMX, iMY - マウス座標
' work: boolean bNotClicked - マウスがクリックされていない
' out: integer iX, iY - 次の手(碁盤の座標)
' out: integer iColor - 碁石の色
' out: integer iBoard[][] - 碁盤
' out: integer iHama[] - アゲハマ
' out: boolean bPass - パス
' out: boolean bResign - 投了
Sub Human
bPass = "False"
bResign = "False"
GraphicsWindow.MouseDown = OnMouseDown
Controls.ButtonClicked = OnButtonClicked
While "True"
bNotClicked = "True"
While bNotClicked
Program.Delay(200)
EndWhile
If bPass Or bResign Then
Goto lPossiblePut
EndIf
GetPosition()
For i = 1 To iPossible
If iPX[i] = iX And iPY[i] = iY Then
Goto lPossiblePut
EndIf
EndFor
' 碁石を取り除く
If iBoard[iX][iY] = BLACK Then
iHama[WHITE] = iHama[WHITE] + 1
Shapes.SetText(oHama[WHITE], iHama[WHITE])
ElseIf iBoard[iX][iY] = WHITE Then
iHama[BLACK] = iHama[BLACK] + 1
Shapes.SetText(oHama[BLACK], iHama[BLACK])
EndIf
EraseStone()
iColor = SPACE
iBoard[iX][iY] = iColor
Record()
GetPossiblePuts() ' 着手可能な手を再計算
EndWhile
lPossiblePut:
iColor = Math.Remainder((iMove - 1), 2) + 1
If bPass = "False" And bResign = "False" Then
iBoard[iX][iY] = iColor
EndIf
EndSub
'
' マウスが押されたときの処理
' out: integer iMX, iMY - マウス座標
' out: boolean bNotClicked - マウスがクリックされていない
Sub OnMouseDown
iMX = GraphicsWindow.MouseX
iMY = GraphicsWindow.MouseY
bNotClicked = "False"
EndSub
'
' マウスが押されたときの処理
' out: integer iMX, iMY - マウス座標
' out: boolean bNotClicked - マウスがクリックされていない
Sub OnButtonClicked
If Controls.LastClickedButton = oResign Then
bResign = "True"
bNotClicked = "False"
ElseIf Controls.LastClickedButton = oPass Then
bPass = "True"
bNotClicked = "False"
EndIf
EndSub
'
' マウスをクリックした座標から碁盤の座標を得る
' in: integer iMX, iMY - マウス座標
' out: integer iX, iY - 碁盤の座標
Sub GetPosition
iX = Math.Floor((iMX - iLX0 + idLX / 2) / idLX) + 1
iY = Math.Floor((iMY - iLY0 + idLY / 2) / idLY) + 1
If iX < 1 Then
iX = 1
EndIf
If iY < 1 Then
iY = 1
EndIf
If iX > iRo Then
iX = iRo
EndIf
If iY > iRo Then
iY = iRo
EndIf
EndSub
'
' 次の手を打つ - Replay (棋譜の再生)
' in: integer iMove - 手番
' in: integer iRecord - 棋譜
' out: integer iX, iY - 次の手(碁盤の座標)
' out: integer iColor - 碁石の色
' out: integer iBoard[][] - 碁盤
' out: boolean bPass - パス
Sub Replay
iX = iRecord[iMove]["x"]
iY = iRecord[iMove]["y"]
iColor = iRecord[iMove]["turn"]
If iX = iRo + 1 Then
bPass = "True"
Else
iBoard[iX][iY] = iColor
bPass = "False"
EndIf
EndSub