今回は部品クラスを作ります。その前にペントミノを簡単な文字列で表現する方法について説明します(平面版ペントミノ2Dのときにも使いました)。
図の折れ線矢印は起点から上下左右に図形を一筆書きのようにたどります。1マスの移動を
アルファベット1文字("U", "D", "L", "R")で表します。それぞれ "Up", "Down", "Left", "Right" の頭文字です。移動後部品を外れるときは小文字を使います。
こうすると図の部品 "F" は "RDRdL" という文字列で表現できます(表し方は一意ではありません)。他の部品 "L", "T", "X" もそれぞれ "DDDR", "RRdLD", "DLdRrU" と表せます。初期化ではこの文字列を引数として受けとり、座標のコレクションに変換します。
・クラス宣言
部品クラスの名前は "Piece" です。
Public Class Piece
・プロパティ:Coords
座標クラス(Coord)のコレクションです。ペントミノでは5個の コレクションとなります。
ReadOnly Property Coords As List(Of Coord)
・初期化
冒頭で述べたとおり部品内の起点からの移動を文字列にしたものを引数にします。引数を省略するとインスタンスのみ確保します。
Sub New(Optional expression As String = "") Coords = New List(Of Coord) If expression <> "" Then With New Coord(0, 0, 0) Coords.Add(.Clone) For Each c As Char In expression Dim s = CStr(c) Select Case s Case "R", "r" '右 .MoveTo(.X + 1, .Y, 0) Case "D", "d" '手前 .MoveTo(.X, .Y + 1, 0) Case "L", "l" '左 .MoveTo(.X - 1, .Y, 0) Case "U", "u" '奥 .MoveTo(.X, .Y - 1, 0) End Select '大文字のときはリストに加える If "RDLU".Contains(s) Then Coords.Add(.Clone) End If Next End With End If End Sub '=====(例)===== Dim piece As New Piece("RDRdL") '部品"F"を作成します
・プロパティ:Size
部品の大きさ(Coordsの要素数=立方体の個数)を返します。ペントミノの場合は 5 。
ReadOnly Property Size As Integer Get Return Coords.Count End Get End Property
・プロパティ:最小値 MinX, MinY, MinZ
Coordsの座標でそれぞれの最小値を返します。
ReadOnly Property MinX As Integer Get Return Coords.Min(Function(s) s.X) End Get End Property ReadOnly Property MinY As Integer Get Return Coords.Min(Function(s) s.Y) End Get End Property ReadOnly Property MinZ As Integer Get Return Coords.Min(Function(s) s.Z) End Get End Property
・プロパティ:長さ LenX, LenY, LenZ
Coordsの座標でそれぞれの軸方向の長さを返します。
ReadOnly Property LenX As Integer Get Return Coords.Max(Function(s) s.X) - MinX + 1 End Get End Property ReadOnly Property LenY As Integer Get Return Coords.Max(Function(s) s.Y) - MinY + 1 End Get End Property ReadOnly Property LenZ As Integer Get Return Coords.Max(Function(s) s.Z) - MinZ + 1 End Get End Property
・プロパティ:重心 CentroidX, CentroidY, CentroidZ
Coordsの座標でそれぞれの軸方向の重心座標を返します。
Public Function CentroidX() As Single Return Coords.Sum(Function(c) c.X) / Size End Function Public Function CentroidY() As Single Return Coords.Sum(Function(c) c.Y) / Size End Function Public Function CentroidZ() As Single Return Coords.Sum(Function(c) c.Z) / Size End Function
・複製(関数)
座標クラスのときと同様に、同じプロパティを持つインスタンスをもう一つ作ります。
Public Function Clone() As Piece Dim newPiece As New Piece() For Each c In Coords newPiece.Coords.Add(c.Clone) Next Return newPiece End Function
・演算子定義
プロパティの一致の判定用に演算子("=", "<>")を定義します。Coords の要素の順序は不一致でも構わない仕様です。
Public Shared Operator =(p1 As Piece, p2 As Piece) As Boolean If p1.Coords.Count <> p2.Coords.Count Then Return False Else For Each c1 In p1.Coords Dim f = False For Each c2 In p2.Coords If c1 = c2 Then f = True Exit For End If Next If f = False Then Return False End If Next End If Return True End Operator Public Shared Operator <>(p1 As Piece, p2 As Piece) As Boolean Return Not (p1 = p2) End Operator '=====(例)===== Dim p1 As New Piece("RDRdL") Dim p2 As New Piece("RDDrU") MsgBox(If(p1 = p2, "True", "False")) 'Trueが表示されます
・座標の回転
反時計回りに90度単位で回転させます。引数には90度単位の回転回数を指定します。
'** XY平面で反時計回りに回転 Public Sub RotateXY(ct As Integer) 'ct:1=90度,2=180度,・・・ For Each c In Coords c.RotateXY(ct) Next End Sub '** XZ平面で反時計回りに回転 Public Sub RotateXZ(ct As Integer) 'ct:1=90度,2=180度,・・・ For Each c In Coords c.RotateXZ(ct) Next End Sub '** YZ平面で反時計回りに回転 Public Sub RotateYZ(ct As Integer) 'ct:1=90度,2=180度,・・・ For Each c In Coords c.RotateYZ(ct) Next End Sub
・原点にシフト
部品を原点(0, 0, 0)にシフトする。
Public Sub ShiftToOrigin() 'YZ平面に接地(X座標=0)させる Dim dX = Coords.Min(Function(p) p.X) For Each c In Coords c.MoveTo(c.X - dX, c.Y, c.Z) Next '上で接地した面をXZ平面に接地(Y座標=0)させる Dim dY = Coords.Where(Function(p) p.X = 0).ToList.Min(Function(p) p.Y) For Each c In Coords c.MoveTo(c.X, c.Y - dY, c.Z) Next '上で接地した面をXY平面に接地(Z座標=0)させる Dim dZ = Coords.Where(Function(p) p.X = 0 And p.Y = 0).ToList.Min(Function(p) p.Z) For Each c In Coords c.MoveTo(c.X, c.Y, c.Z - dZ) Next End Sub
以上が「部品クラス」です。クラス全体のソースは以下のとおりです。
Public Class Piece '********************************************** '* Piece (部品クラス) '********************************************** ReadOnly Property Coords As List(Of Coord) '座標 ReadOnly Property Size As Integer Get Return Coords.Count End Get End Property #Region "最小値: MinX, MinY, MinZ" ReadOnly Property MinX As Integer Get Return Coords.Min(Function(s) s.X) End Get End Property ReadOnly Property MinY As Integer Get Return Coords.Min(Function(s) s.Y) End Get End Property ReadOnly Property MinZ As Integer Get Return Coords.Min(Function(s) s.Z) End Get End Property #End Region #Region "長さ: LenX, LenY, LenZ" ReadOnly Property LenX As Integer Get Return Coords.Max(Function(s) s.X) - MinX + 1 End Get End Property ReadOnly Property LenY As Integer Get Return Coords.Max(Function(s) s.Y) - MinY + 1 End Get End Property ReadOnly Property LenZ As Integer Get Return Coords.Max(Function(s) s.Z) - MinZ + 1 End Get End Property #End Region #Region "重心: CentroidX, CentroidY, CentroidZ" Public Function CentroidX() As Single Return Coords.Sum(Function(c) c.X) / Size End Function Public Function CentroidY() As Single Return Coords.Sum(Function(c) c.Y) / Size End Function Public Function CentroidZ() As Single Return Coords.Sum(Function(c) c.Z) / Size End Function #End Region '** 初期化 Sub New(Optional expression As String = "") Coords = New List(Of Coord) If expression <> "" Then With New Coord(0, 0, 0) Coords.Add(.Clone) For Each c As Char In expression Dim s = CStr(c) Select Case s Case "R", "r" '右 .MoveTo(.X + 1, .Y, 0) Case "D", "d" '手前 .MoveTo(.X, .Y + 1, 0) Case "L", "l" '左 .MoveTo(.X - 1, .Y, 0) Case "U", "u" '奥 .MoveTo(.X, .Y - 1, 0) End Select '大文字のときはリストに加える If "RDLU".Contains(s) Then Coords.Add(.Clone) End If Next End With End If End Sub '** 複製 Public Function Clone() As Piece Dim newPiece As New Piece() For Each c In Coords newPiece.Coords.Add(c.Clone) Next Return newPiece End Function '** 演算子定義(=) Public Shared Operator =(p1 As Piece, p2 As Piece) As Boolean If p1.Coords.Count <> p2.Coords.Count Then Return False Else For Each c1 In p1.Coords Dim f = False For Each c2 In p2.Coords If c1 = c2 Then f = True Exit For End If Next If f = False Then Return False End If Next End If Return True End Operator Public Shared Operator <>(p1 As Piece, p2 As Piece) As Boolean Return Not (p1 = p2) End Operator #Region "回転: Rotate" '** XY平面で反時計回りに回転 Public Sub RotateXY(ct As Integer) 'ct:1=90度,2=180度,・・・ For Each c In Coords c.RotateXY(ct) Next End Sub '** XZ平面で反時計回りに回転 Public Sub RotateXZ(ct As Integer) 'ct:1=90度,2=180度,・・・ For Each c In Coords c.RotateXZ(ct) Next End Sub '** YZ平面で反時計回りに回転 Public Sub RotateYZ(ct As Integer) 'ct:1=90度,2=180度,・・・ For Each c In Coords c.RotateYZ(ct) Next End Sub #End Region '** ShiftToOrigin:座標(0,0,0)に部品をシフトする Public Sub ShiftToOrigin() 'YZ平面に接地(X座標=0)させる Dim dX = Coords.Min(Function(p) p.X) For Each c In Coords c.MoveTo(c.X - dX, c.Y, c.Z) Next '上で接地した面をXZ平面に接地(Y座標=0)させる Dim dY = Coords.Where(Function(p) p.X = 0).ToList.Min(Function(p) p.Y) For Each c In Coords c.MoveTo(c.X, c.Y - dY, c.Z) Next '上で接地した面をXY平面に接地(Z座標=0)させる Dim dZ = Coords.Where(Function(p) p.X = 0 And p.Y = 0).ToList.Min(Function(p) p.Z) For Each c In Coords c.MoveTo(c.X, c.Y, c.Z - dZ) Next End Sub End Class
次回は「棚クラス」です。