31204 プログラミング 3605 井上寛晶 3531 松井佑樹 3635 宮地翼 要旨各自でフリーソフトを作成 インターネット上に公開することを目的とし Visual Basic2008 2010 を使い 二年生までは ちんちろりん という簡単なゲームを作ったが 今回はより難度が高い オセロ の作成に成功した 本文 1. 目的情報化が進んだ現代において 社会に出ていくためにはパソコンの一つや二つ 軽く扱えなければならない さらに 資源の乏しい日本においては今後 情報技術の発展することが望ましいと考える 私たちのグループでは SE( システムエンジニア ) 等の職に就き 今後の日本のみならず情報技術によって世界に貢献できる人材となるべく プログラミング技術の習得を目的とした また 今回は IA( 人工知能 ) が必要となる対戦型オセロゲームを作成することを目的とし より高度なプログラミング技術の取得を目的とした 2. 使用した器具 装置など (1) 使用言語 Visual Basic (2) 開発ソフト Microsoft Visual Studio 2008 Microsoft Visual Studio 2010 使用マシン FMV K5290 Intel LB-D701S 3. 研究 実験の手順一年生から積み上げてきた Visual Basic の知識を生かし 以下のソフトを開発 対戦型オセロゲーム 4. 結果 プログラムの説明 Cell,txt のプログラム Public Class cell Public Status As CellStatus ' セルの状態 黒 白 空 04-1
Public Grid As ReverseGrid Public Position As Point ' 論理位置 Public Rectangle As Rectangle ' 物理位置 Status; 黒 白 なしの状態 Grid; オセロの盤面 Position; 盤面内の説明 Rectangle; 描画を行う四角形の領域 ' コンストラクタ ''' <summary> 論理位置を指定してセルを作成します </summary> ''' <param name="grid"> セルが属するグリッドを指定します </param> ''' <param name="position"> セルの論理位置を指定します </param> Public Sub New(ByVal Grid As ReverseGrid, ByVal Position As Point) Me.Grid = Grid Me.Position = Position Dim Rect As New Rectangle ' 論理位置から物理位置を求めます Rect.X = Position.X * ReverseGrid.CellSize Rect.Y = Position.Y * ReverseGrid.CellSize Rect.Width = ReverseGrid.CellSize Rect.Height = ReverseGrid.CellSize Me.Rectangle = Rect Dim FrontBrush As New SolidBrush(Color.Black) Dim BackBrush As New SolidBrush(Color.White) ' Draw ''' <summary> 現在の状態を描画します </summary> ''' <param name="g"> 描画対象の Graphics オブジェクトを指定します </param> Public Sub Draw(ByVal g As Graphics) Dim CellRect As Rectangle ' 描画領域 CellRect は描画領域であると宣言 04-2
' 描画領域の算定 ' セルいっぱいに描画するとぎちぎちになるので範囲を -2 する CellRect = Me.Rectangle CellRect.Inflate(-2, -2) ' 描画状態の設定 Select Case Me.Status Case CellStatus.Black FrontBrush.Color = Color.Black ' 表を黒に設定 BackBrush.Color = Color.White ' 裏を白に設定 Case CellStatus.White FrontBrush.Color = Color.White ' 表を白に設定 BackBrush.Color = Color.Black ' 裏を黒に設定 End Select 黒の石は表を黒 裏を白に 白の石は表を白 裏を黒に設定する ' 描画実行 If Me.Status <> CellStatus.Nothing Then ' 裏面描画 CellRect.Y += 2 ' 裏と表をちょっとずらして立体的に見せる g.fillellipse(backbrush, CellRect) ' 表面描画 CellRect.Y -= 2 g.fillellipse(frontbrush, CellRect) 石の状態が有色 ( 石が置かれている ) であれば 石の表 裏を描画する この時 裏表を若干ずらして立体的に見せる ' アクティブな場合は枠を描画する If Me.Focused Then g.drawrectangle(pens.orange, CellRect) 下記の赤色の変更を適応するコード 04-3
Public Focused As Boolean ' Focus ''' <summary> セルをアクティブにします </summary> ''' <remarks> アクティブなセルとは Focused プロパティが True のセルです ''' このメソッドを呼び出すと同じ盤に属するその他のセルのFocused プロパティを Falseにします ''' アクティブであることにそれ以上の効果はありませんが ''' 描画の際に Focused プロパティが True のセルに枠線を描画します ''' </remarks> Public Sub Focus() Dim X As Integer Dim Y As Integer ' 同じグリッドに属する自分以外のセルを非アクティブにする For X = 0 To ReverseGrid.XCount - 1 For Y = 0 To ReverseGrid.YCount - 1 Grid.Cells(X, Y).Focused = False Next Next ' 自分自身をアクティブにする Me.Focused = True マスの上にマウスを置いたときに枠線を表示する (Form1 の MouseMove イベントと対応 ) * こちらではマウスが来たときに枠を描く準備を行う 実際に描画しているのは Paint イベントの Draw イベント ( 上の青色の部分 ) 04-4
computer3 のプログラム Public Class Computer3 Dim Grid As ReverseGrid Public Standard As CellStatus ReverseGrid; オセロの盤面を表し 石を置く ひっくり返すことや それに関するイベント セルの管理 描画処理を行う Cellstatus; 石のもと ( 白 黒 なし の三つの状態を表す ) Grid はオセロの盤面 Standard は黒 白 なしの三色と宣言 Public Sub New(ByVal Grid As ReverseGrid, ByVal Standard As CellStatus) Me.Grid = Grid Me.Standard = Standard Public Sub Put() コンピュータに石を置かせる Dim X As Integer Dim Y As Integer Dim PlayerColor As CellStatus ' 角に置けるなら角におく If Grid.CanPut(Standard, 0, 0) Then Grid.Put(Standard, 0, 0) If Grid.CanPut(Standard, 0, ReverseGrid.YCount - 1) Then Grid.Put(Standard, 0, ReverseGrid.YCount - 1) If Grid.CanPut(Standard, ReverseGrid.XCount - 1, 0) Then Grid.Put(Standard, ReverseGrid.XCount - 1, 0) 04-5
If Grid.CanPut(Standard, ReverseGrid.XCount - 1, ReverseGrid.YCount - 1) Then Grid.Put(Standard, ReverseGrid.XCount - 1, ReverseGrid.YCount - 1) 右上 右下 左上 左下に置くことが可能な時はその置けるところに石を置かせるなお 角を図 1のように定義している図 1 If Standard = CellStatus.Black Then PlayerColor = CellStatus.White Else PlayerColor = CellStatus.Black コンピュータの使う色が黒ならプレイヤーの石の色は白 それ以外 ( 白 ) なら プレイヤーの石は黒 ' 順番に見ていってはじめに置けるところを探す Dim ImageGrid As ReverseGrid Dim Puts As New ArrayList ImageGrid はオセロの盤面 Puts は新しいコレクションと宣言 For Y = 0 To ReverseGrid.YCount - 1 For X = 0 To ReverseGrid.XCount - 1 If Grid.CanPut(Standard, X, Y) Then Puts.Add(New Point(X, Y)) y 座標の上端から下端 x 座標の左端から右端をみて置けるマスがあればそこに石を置く 04-6
' まず コピーのグリッドに石を置いてみる ImageGrid = Grid.Copy ImageGrid.Put(Standard, X, Y) ' この状態で相手に角を取られる可能性があるか検証する Select Case True Case ImageGrid.CanPut(PlayerColor, 0, 0) ' 左上の角を取られてしまうので何もしない Case ImageGrid.CanPut(PlayerColor, 0, ReverseGrid.YCount - 1) ' 左下の角を取られてしまうので何もしない Case ImageGrid.CanPut(PlayerColor, ReverseGrid.XCount - 1, 0) ' 右上の角を取られてしまうので何もしない Case ImageGrid.CanPut(PlayerColor, ReverseGrid.XCount - 1, ReverseGrid.YCount - 1) ' 右下の角を取られてしまうので何もしない Case Else ' 角を取られる心配がないのでこの位置に石を置く Grid.Put(Standard, X, Y) End Select Next Next ' 角を取られる位置にしか置けない場合 仕方ないので置く Dim Pos As Point = DirectCast(Puts(0), Point) Grid.Put(Standard, Pos.X, Pos.Y) End Class 置く場所を考える場合 コピーの盤面を AI が考え それぞれの角を取られる場合を考える それ 以外の条件 ( 角を取られない ) なら そこに石を置く 04-7
Constant のプログラム ' CellStatus ''' <summary> セルの状態を表します </summary> Public Enum CellStatus [Nothing] ' なし Black ' 黒 White ' 白 End Enum ' ScanDirection ''' <summary> 方向を表します </summary> Public Enum ScanDirection Left Right Up Down LeftUp LeftDown RightUp RightDown End Enum form1 のプログラム Public Class Form1 Dim WithEvents Grid As New ReverseGrid Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Grid.Initialize() lblblackcount.text = Grid.Count(CellStatus.Black) lblwhitecount.text = Grid.Count(CellStatus.White) lblwhiteturn.visible = False ゲーム開始時に現在のターンをラベルを使って表示 04-8
Private Sub PictureBox1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PictureBox1.Click ' マウスの座標を PictureBox1 のコントロール座標に変換する Dim Pos As Point = PictureBox1.PointToClient(Windows.Forms.Cursor.Position) マウスで表される座標 (PC 上の画面での座標 ) を盤面上のマスの座標に変換 Dim ThisCell As cell ThisCell = Grid.CellFromPoint(Pos.X, Pos.Y) If Grid.Put(Turn, ThisCell.Position.X, ThisCell.Position.Y) Then ChangeTurn() 石を置いたときにターンを交代させる また ReverseGrid の Canput で設定した置けるマスのみに石を置けるようにする ''' <summary> マウスの移動に伴ってセルにアクティブを示す枠を描画する </summary> Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove Dim ThisCell As cell ' マウスがある位置のセルを取得 ThisCell = Grid.CellFromPoint(e.X, e.y) If Not IsNothing(ThisCell) Then ' セルが取得できた場合は セルにアクティブを示す枠を描画 ThisCell.Focus() ' 現在の状態を描画 (PictureBox1 の Paint イベントを発生させる ) PictureBox1.Invalidate() ' 実際の描画はすべてここで行う Cell の Focus と対応 ( こっちでは マウスがある位置等を取得 ) 04-9
Dim Turn As CellStatus = CellStatus.Black ' 今どっちの順番か Dim PlayerColor As CellStatus = CellStatus.Black ' プレイヤーの色 ' ChangeTurn ''' <summary> ターン交代 </summary> Public Sub ChangeTurn() ' 現在の状態を描画 (PictureBox1 の Paint イベントを発生させる ) PictureBox1.Invalidate() paint イベントを強制的に発生させる 勝敗判定 1すべてのマスに石が置かれる 2マスはまだ空いているが石を置けない状況 3すべての色が同じ色になる 1 If Grid.Count(CellStatus.Nothing) = 0 Then 石の置けるマスが0になり ' 全セルへの配置が終了した場合は勝敗判定して終了 If Grid.Count(CellStatus.Black) > Grid.Count(CellStatus.White) Then 黒のほうが多かったら MsgBox(" 黒の勝ちです!") ElseIf Grid.Count(CellStatus.Black) < Grid.Count(CellStatus.White) Then 白のほうが多かったら MsgBox(" 白の勝ちです!") Else MsgBox(" 引き分けです!!") マスすべてに石が置かれ どちらの石の数が多いか調べた後 それぞれのメッセージを表示 ElseIf Grid.PuttableCount(CellStatus.Black) = 0 AndAlso Grid.PuttableCount(CellStatus.White) = 0 Then 2 ' 空いているセルがあるのに黒も白も置けない場合 If Grid.Count(CellStatus.Black) > Grid.Count(CellStatus.White) Then MsgBox(" 黒の勝ちです!") ElseIf Grid.Count(CellStatus.Black) < Grid.Count(CellStatus.White) Then MsgBox(" 白の勝ちです!") Else MsgBox(" 引き分けです!!") 04-10
石を置くことのできるマスがなくなり どちらが多いかを調べた後 それぞれのメッセージを表示 ElseIf Grid.Count(CellStatus.Black) = 0 Then 3 ' すべての石が白になった場合 (= 黒の石が 0 個の場合 ) MsgBox(" 白の勝ちです!") ElseIf Grid.Count(CellStatus.White) = 0 Then ' すべての石が黒になった場合 (= 白の石が 0 個の場合 ) MsgBox(" 黒の勝ちです!") 石すべてが白または黒になったらそれぞれのメッセージを表示 ' 次のターンの決定 If Turn = CellStatus.Black Then Turn = CellStatus.White lblblackturn.visible = False lblwhiteturn.visible = True Else Turn = CellStatus.Black lblblackturn.visible = True lblwhiteturn.visible = False 石が黒のターンなら次は白に 白なら次は黒に ターンの交代を処理また 黒のターン 白のターンであることを示す線を表示させる ' 置ける場所があるか判定 If Grid.PuttableCount(Turn) = 0 Then ' 置く場所がなければパスして次のターン ChangeTurn() ' 人間かコンピュータかで処理を分岐 If Turn = PlayerColor Then ' 人間の番ならば PictureBox を使用可能にする PictureBox1.Enabled = True Else プレイヤーの番は盤面の操作を可能にし コンピュータの番のときは盤面の操作を不可能にする ' コンピュータの番ならば PictureBox を使用不可にする PictureBox1.Enabled = False 04-11
' ちょっと時間をおく Application.DoEvents() System.Threading.Thread.Sleep(500) 0.5 秒の間を空ける ' コンピュータに石を置かせる どのセルに置くかはコンピュータ (AI) が決定する Computer.Put() ChangeTurn() ' プレイヤーの番へ コンピュータが石を置きターンを交代する Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint Grid.Draw(e.Graphics) ''' <summary> 石を置いたときに発生するイベント </summary> Private Sub Grid_PutNew(ByVal sender As Object, ByVal e As System.EventArgs) Handles Grid.PutNew Call Grid_Reversed(sender, e) ''' <summary> 石がひっくり返されたときに発生するイベント </summary> Private Sub Grid_Reversed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Grid.Reversed ' 現在の状態を描画 (PictureBox1 の Paint イベントを発生させる ) PictureBox1.Invalidate() ' 現在の黒と白の石の数を表示する 04-12
lblblackcount.text = Grid.Count(CellStatus.Black) lblwhitecount.text = Grid.Count(CellStatus.White) 黒石 白石の数をラベルに表示し paint イベントを発生させ 描画を行う ' ちょっと時間をおく Application.DoEvents() System.Threading.Thread.Sleep(500) 石をひっくり返す動作をする際に 毎回 0.5 秒の間を空ける 瞬時に変わるとゲーム性が損なわれる! ' Start ''' <summary> ゲームを開始します </summary> ''' <param name="playercolor"> 人間の石の色を指定します </param> ''' <remarks> 黒が先手になります </remarks> Private Sub Start(ByVal PlayerColor As CellStatus) Grid.Initialize() Me.PlayerColor = PlayerColor ' 人間の色 If PlayerColor = CellStatus.Black Then Computer.Standard = CellStatus.White ' コンピュータの色は白 Else Computer.Standard = CellStatus.Black ' コンピュータの色は黒 プレイヤー コンピュータの色を設定 ' 現在の黒と白の駒の数を表示する lblblackcount.text = Grid.Count(CellStatus.Black) lblwhitecount.text = Grid.Count(CellStatus.White) ラベルに黒石 白石の数を表示 'ChangeTurn を呼び出して黒の番を開始する そのために仮に今は白の番であることにする Turn = CellStatus.White ChangeTurn() Private Sub btnstartblack_click(byval sender As System.Object, ByVal e As System.EventArgs) Handles btnstartblack.click 04-13
Start(CellStatus.Black) Private Sub btnstartwhite_click(byval sender As System.Object, ByVal e As System.EventArgs) Handles btnstartwhite.click Start(CellStatus.White) 黒 白で始まるかを選択するボタンの処理 5. 結果に対する考察 わかったこと 感想人工知能というと 難しいイメージがあったが一つ一つ見ていくと角に置けるかを確認するプログラム 右上から左下まで置けるところを探すプログラム 実際に仮の盤面に置くプログラム 相手に角を取られないことを確認するプログラム 実際に置くプログラムと 単純なことの積み重ねだったので 理解できた この経験を生かして今後もプログラムの道に精進したい 6. 参考文献 引用文献 http://homepage1.nifty.com/rucio/main/main.htm 04-14