Guest User

Untitled

a guest
Aug 22nd, 2018
108
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.57 KB | None | 0 0
  1. '@Folder("Battleship.Model.Ship")
  2. '@Interface
  3. Option Explicit
  4.  
  5. Public Enum ShipType
  6. Carrier
  7. Battleship
  8. Submarine
  9. Cruiser
  10. Destroyer
  11. End Enum
  12.  
  13. Public Enum ShipOrientation
  14. Horizontal
  15. Vertical
  16. End Enum
  17.  
  18. '@Description("Gets the type of the ship.")
  19. Public Property Get ShipKind() As ShipType
  20. End Property
  21.  
  22. '@Description("The name/description of the ship. Must be unique in a grid.")
  23. Public Property Get Name() As String
  24. End Property
  25.  
  26. '@Description("Use in 'With' blocks to get a reference to the scope variable.")
  27. Public Property Get GridPosition() As GridCoord
  28. End Property
  29.  
  30. '@Description("The number of grid squares (1-5) occupied by this ship.")
  31. Public Property Get Size() As Byte
  32. End Property
  33.  
  34. '@Description("The orientation of the ship.")
  35. Public Property Get Orientation() As ShipOrientation
  36. End Property
  37.  
  38. '@Description("True if this ship is sunken.")
  39. Public Property Get IsSunken() As Boolean
  40. End Property
  41.  
  42. '@Description("Gets all grid coordinates this ship was hit at.")
  43. Public Property Get HitArea() As Collection
  44. End Property
  45.  
  46. '@Description("Gets an array containing the state of each grid coordinate of the ship.")
  47. Public Property Get StateArray() As Variant
  48. End Property
  49.  
  50. '@Description("If the specified coordinate hits this ship, marks coordinate as a hit and returns True.")
  51. Public Function Hit(ByVal coord As GridCoord) As Boolean
  52. End Function
  53.  
  54. '@Description("Returns intersection coordinate if specified ship intersects with this instance.")
  55. Public Function Intersects(ByVal shipSize As Byte, ByVal direction As ShipOrientation, ByVal position As GridCoord) As GridCoord
  56. End Function
  57.  
  58. '@Folder("Battleship.Model.Ship")
  59. Option Explicit
  60.  
  61. Private Const MinimumShipSize As Byte = 2
  62. Private Const MaximumShipSize As Byte = 5
  63.  
  64. Private ShipSizes As Scripting.Dictionary
  65. Private ShipNames As Scripting.Dictionary
  66. Private Const ShipNameCarrier As String = "Aircraft Carrier"
  67. Private Const ShipNameBattleship As String = "Battleship"
  68. Private Const ShipNameSubmarine As String = "Submarine"
  69. Private Const ShipNameCruiser As String = "Cruiser"
  70. Private Const ShipNameDestroyer As String = "Destroyer"
  71.  
  72. Private Type TShip
  73. ShipKind As ShipType
  74. Name As String
  75. GridPosition As GridCoord
  76. Orientation As ShipOrientation
  77. State As Scripting.Dictionary
  78. IsHit As Boolean
  79. End Type
  80.  
  81. Private this As TShip
  82. Implements IShip
  83.  
  84. '@Description("Gets a dictionary associating all ship names with their respective size.")
  85. Public Property Get Fleet() As Scripting.Dictionary
  86. Dim names As Variant
  87. names = ShipNames.Items
  88.  
  89. Dim sizes As Variant
  90. sizes = ShipSizes.Items
  91.  
  92. Dim result As Scripting.Dictionary
  93. Set result = New Scripting.Dictionary
  94. Dim i As Long
  95. For i = LBound(names) To UBound(names)
  96. result.Add names(i), sizes(i)
  97. Next
  98. Set Fleet = result
  99. End Property
  100.  
  101. '@Description("Gets an array of all valid ShipKind enum values.")
  102. Public Property Get ShipKinds() As Variant
  103. ShipKinds = ShipNames.Keys
  104. End Property
  105.  
  106. '@Description("The minimum valid ship size. Invoke from the default instance.")
  107. Public Property Get MinimumSize() As Byte
  108. MinimumSize = MinimumShipSize
  109. End Property
  110.  
  111. '@Description("The maximum valid ship size. Invoke from the default instance.")
  112. Public Property Get MaximumSize() As Byte
  113. MaximumSize = MaximumShipSize
  114. End Property
  115.  
  116. '@Description("Use from the class' default instance to create a new ship instance using parameters.")
  117. Public Function Create(ByVal kind As ShipType, ByVal direction As ShipOrientation, ByVal position As GridCoord) As Ship
  118. ValidateInputs kind, direction, position
  119. With New Ship
  120. .ShipKind = kind
  121. .Name = ShipNames(kind)
  122. .Orientation = direction
  123. Set .GridPosition = position
  124. Dim Offset As Byte
  125. For Offset = 0 To ShipSizes(kind) - 1
  126.  
  127. Dim currentPoint As GridCoord
  128. Set currentPoint = New GridCoord
  129.  
  130. currentPoint.X = position.X + IIf(direction = Horizontal, Offset, 0)
  131. currentPoint.Y = position.Y + IIf(direction = Vertical, Offset, 0)
  132.  
  133. ' each element is a Boolean, keyed with a grid coordinate:
  134. .State.Add item:=False, Key:=currentPoint.ToString
  135. Next
  136. Set Create = .Self
  137. End With
  138. End Function
  139.  
  140. Private Sub ValidateInputs(ByVal kind As ShipType, ByVal Orientation As ShipOrientation, ByVal position As GridCoord)
  141. Dim shipSize As Byte
  142. shipSize = ShipSizes(kind)
  143.  
  144. Select Case True
  145.  
  146. Case Orientation <> Horizontal And Orientation <> Vertical
  147. OnInvalidArgument "orientation", "Invalid orientation."
  148.  
  149. Case Orientation = Horizontal And position.X + shipSize - 1 > PlayerGrid.Size
  150. OnInvalidArgument "position", "Invalid position; ship exceeds right edge of the grid."
  151.  
  152. Case Orientation = Vertical And position.Y + shipSize - 1 > PlayerGrid.Size
  153. OnInvalidArgument "position", "Invalid position; ship exceeds bottom edge of the grid."
  154.  
  155. End Select
  156.  
  157. End Sub
  158.  
  159. Private Sub OnInvalidArgument(ByVal argName As String, ByVal message As String)
  160. Err.Raise 5, TypeName(Me), message
  161. End Sub
  162.  
  163. Public Property Get Self() As Ship
  164. Set Self = Me
  165. End Property
  166.  
  167. Public Property Get ShipKind() As ShipType
  168. ShipKind = this.ShipKind
  169. End Property
  170.  
  171. Public Property Let ShipKind(ByVal value As ShipType)
  172. this.ShipKind = value
  173. End Property
  174.  
  175. Public Property Get Name() As String
  176. Name = this.Name
  177. End Property
  178.  
  179. Public Property Let Name(ByVal value As String)
  180. this.Name = value
  181. End Property
  182.  
  183. Public Property Get Orientation() As ShipOrientation
  184. Orientation = this.Orientation
  185. End Property
  186.  
  187. Public Property Let Orientation(ByVal value As ShipOrientation)
  188. this.Orientation = value
  189. End Property
  190.  
  191. Public Property Get GridPosition() As GridCoord
  192. Set GridPosition = this.GridPosition
  193. End Property
  194.  
  195. Public Property Set GridPosition(ByVal value As GridCoord)
  196. Set this.GridPosition = value
  197. End Property
  198.  
  199. Public Property Get State() As Scripting.Dictionary
  200. Set State = this.State
  201. End Property
  202.  
  203. Private Sub Class_Initialize()
  204. If Me Is Ship Then
  205. 'default instance
  206. Set ShipSizes = New Scripting.Dictionary
  207. With ShipSizes
  208. .Add ShipType.Carrier, 5
  209. .Add ShipType.Battleship, 4
  210. .Add ShipType.Submarine, 3
  211. .Add ShipType.Cruiser, 3
  212. .Add ShipType.Destroyer, 2
  213. End With
  214. Set ShipNames = New Scripting.Dictionary
  215. With ShipNames
  216. .Add ShipType.Carrier, ShipNameCarrier
  217. .Add ShipType.Battleship, ShipNameBattleship
  218. .Add ShipType.Submarine, ShipNameSubmarine
  219. .Add ShipType.Cruiser, ShipNameCruiser
  220. .Add ShipType.Destroyer, ShipNameDestroyer
  221. End With
  222. Else
  223. Set this.State = New Scripting.Dictionary
  224. End If
  225. End Sub
  226.  
  227. Private Sub Class_Terminate()
  228. Set ShipSizes = Nothing
  229. Set ShipNames = Nothing
  230. Set this.State = Nothing
  231. End Sub
  232.  
  233. Private Property Get IShip_GridPosition() As GridCoord
  234. Set IShip_GridPosition = this.GridPosition
  235. End Property
  236.  
  237. Private Function IShip_Hit(ByVal coord As GridCoord) As Boolean
  238. Dim coordString As String
  239. coordString = coord.ToString
  240. If this.State.Exists(coordString) Then
  241. 'this.State.Remove coordString
  242. this.State(coordString) = True
  243. this.IsHit = True
  244. IShip_Hit = this.State(coordString)
  245. End If
  246. End Function
  247.  
  248. Private Function IShip_Intersects(ByVal shipSize As Byte, ByVal direction As ShipOrientation, ByVal position As GridCoord) As GridCoord
  249. Dim gridOffset As Long
  250. For gridOffset = 0 To shipSize - 1
  251. Dim current As GridCoord
  252. Set current = position.Offset( _
  253. IIf(direction = Horizontal, gridOffset, 0), _
  254. IIf(direction = Vertical, gridOffset, 0))
  255. If this.State.Exists(current.ToString) Then
  256. Set IShip_Intersects = current
  257. Exit Function
  258. End If
  259. Next
  260. End Function
  261.  
  262. Private Property Get IShip_HitArea() As VBA.Collection
  263. Dim result As VBA.Collection
  264. Set result = New VBA.Collection
  265. Dim currentPoint As Variant
  266. For Each currentPoint In this.State.Keys
  267. If this.State(currentPoint) Then
  268. result.Add GridCoord.FromString(currentPoint)
  269. End If
  270. Next
  271. Set IShip_HitArea = result
  272. End Property
  273.  
  274. Private Property Get IShip_IsSunken() As Boolean
  275. If Not this.IsHit Then Exit Property
  276. Dim currentPoint As Variant
  277. For Each currentPoint In this.State.Items
  278. If Not currentPoint Then Exit Property
  279. Next
  280. IShip_IsSunken = True
  281. End Property
  282.  
  283. Private Property Get IShip_Name() As String
  284. IShip_Name = this.Name
  285. End Property
  286.  
  287. Private Property Get IShip_Orientation() As ShipOrientation
  288. IShip_Orientation = this.Orientation
  289. End Property
  290.  
  291. Private Property Get IShip_ShipKind() As ShipType
  292. IShip_ShipKind = this.ShipKind
  293. End Property
  294.  
  295. Private Property Get IShip_Size() As Byte
  296. IShip_Size = this.State.Count
  297. End Property
  298.  
  299. Private Property Get IShip_StateArray() As Variant
  300. IShip_StateArray = this.State.Items
  301. End Property
  302.  
  303. '@Folder("Battleship.Model.Player")
  304. Option Explicit
  305.  
  306. Public Enum PlayerType
  307. HumanControlled
  308. ComputerControlled
  309. End Enum
  310.  
  311. '@Description("Gets the player's grid/state.")
  312. Public Property Get PlayGrid() As PlayerGrid
  313. End Property
  314.  
  315. '@Description("Identifies the player class implementation.")
  316. Public Property Get PlayerType() As PlayerType
  317. End Property
  318.  
  319. '@Description("Attempts to make a hit on the enemy grid.")
  320. Public Function Play(ByVal enemyGrid As PlayerGrid) As GridCoord
  321. End Function
  322.  
  323. '@Description("Places specified ship on game grid.")
  324. Public Sub PlaceShip(ByVal currentShip As IShip)
  325. End Sub
  326.  
  327. '@Folder("Battleship.Model.Player")
  328. Option Explicit
  329. Implements IPlayer
  330.  
  331. Private Type TPlayer
  332. GridIndex As Byte
  333. PlayerType As PlayerType
  334. PlayGrid As PlayerGrid
  335. Strategy As IGameStrategy
  336. End Type
  337.  
  338. Private this As TPlayer
  339.  
  340. Public Function Create(ByVal grid As Byte, ByVal gameStrategy As IGameStrategy) As AIPlayer
  341. With New AIPlayer
  342. .PlayerType = ComputerControlled
  343. .GridIndex = grid
  344. Set .Strategy = gameStrategy
  345. Set .PlayGrid = PlayerGrid.Create(grid)
  346. Set Create = .Self
  347. End With
  348. End Function
  349.  
  350. Public Property Get Self() As AIPlayer
  351. Set Self = Me
  352. End Property
  353.  
  354. Public Property Get Strategy() As IGameStrategy
  355. Set Strategy = this.Strategy
  356. End Property
  357.  
  358. Public Property Set Strategy(ByVal value As IGameStrategy)
  359. Set this.Strategy = value
  360. End Property
  361.  
  362. Public Property Get PlayGrid() As PlayerGrid
  363. Set PlayGrid = this.PlayGrid
  364. End Property
  365.  
  366. Public Property Set PlayGrid(ByVal value As PlayerGrid)
  367. Set this.PlayGrid = value
  368. End Property
  369.  
  370. Public Property Get GridIndex() As Byte
  371. GridIndex = this.GridIndex
  372. End Property
  373.  
  374. Public Property Let GridIndex(ByVal value As Byte)
  375. this.GridIndex = value
  376. End Property
  377.  
  378. Public Property Get PlayerType() As PlayerType
  379. PlayerType = this.PlayerType
  380. End Property
  381.  
  382. Public Property Let PlayerType(ByVal value As PlayerType)
  383. this.PlayerType = value
  384. End Property
  385.  
  386. Private Property Get IPlayer_PlayGrid() As PlayerGrid
  387. Set IPlayer_PlayGrid = this.PlayGrid
  388. End Property
  389.  
  390. Private Sub IPlayer_PlaceShip(ByVal currentShip As IShip)
  391. this.Strategy.PlaceShip this.PlayGrid, currentShip
  392. End Sub
  393.  
  394. Private Function IPlayer_Play(ByVal enemyGrid As PlayerGrid) As GridCoord
  395. Set IPlayer_Play = this.Strategy.Play(enemyGrid)
  396. End Function
  397.  
  398. Private Property Get IPlayer_PlayerType() As PlayerType
  399. IPlayer_PlayerType = this.PlayerType
  400. End Property
  401.  
  402. '@Folder("Battleship.AI")
  403. '@Interface
  404. Option Explicit
  405.  
  406. Public Enum AIDifficulty
  407. Unspecified
  408. RandomAI
  409. FairplayAI
  410. MercilessAI
  411. End Enum
  412.  
  413. '@Description("Places the specified ship on the specified grid.")
  414. Public Sub PlaceShip(ByVal grid As PlayerGrid, ByVal currentShip As IShip)
  415. End Sub
  416.  
  417. '@Description("Gets the grid coordinate to attack on the specified enemy grid.")
  418. Public Function Play(ByVal enemyGrid As PlayerGrid) As GridCoord
  419. End Function
  420.  
  421. '@Folder("Battleship.AI")
  422. ' dumbest possible AI game strategy: randomly places ships, randomly shoots
  423. Option Explicit
  424. Implements IGameStrategy
  425.  
  426. Private Type TStrategy
  427. RNG As IRandomizer
  428. End Type
  429.  
  430. Private this As TStrategy
  431.  
  432. Public Function Create(ByVal randomizer As IRandomizer) As IGameStrategy
  433. With New RandomShotStrategy
  434. Set .RNG = randomizer
  435. Set Create = .Self
  436. End With
  437. End Function
  438.  
  439. Public Property Get Self() As RandomShotStrategy
  440. Set Self = Me
  441. End Property
  442.  
  443. Public Property Get RNG() As IRandomizer
  444. Set RNG = this.RNG
  445. End Property
  446.  
  447. Public Property Set RNG(ByVal value As IRandomizer)
  448. Set this.RNG = value
  449. End Property
  450.  
  451. Private Sub IGameStrategy_PlaceShip(ByVal grid As PlayerGrid, ByVal currentShip As IShip)
  452.  
  453. Do
  454. Dim gridX As Long
  455. gridX = this.RNG.Between(1, PlayerGrid.Size)
  456.  
  457. Dim direction As ShipOrientation
  458. If gridX + currentShip.Size - 1 > PlayerGrid.Size Then
  459. direction = Vertical
  460. Else
  461. direction = IIf(this.RNG.NextSingle < 0.5, Horizontal, Vertical)
  462. End If
  463.  
  464. Dim gridY As Long
  465. If direction = Horizontal Then
  466. gridY = this.RNG.Between(1, PlayerGrid.Size)
  467. Else
  468. gridY = this.RNG.Between(1, PlayerGrid.Size - currentShip.Size)
  469. End If
  470.  
  471. Dim position As GridCoord
  472. Set position = GridCoord.Create(gridX, gridY)
  473.  
  474. Loop Until grid.CanAddShip(position, direction, currentShip.Size)
  475. grid.AddShip Ship.Create(currentShip.ShipKind, direction, position)
  476.  
  477. If grid.ShipCount = PlayerGrid.ShipsPerGrid Then grid.Scramble
  478.  
  479. End Sub
  480.  
  481. Private Function IGameStrategy_Play(ByVal enemyGrid As PlayerGrid) As GridCoord
  482.  
  483. Do
  484. Dim position As GridCoord
  485. Set position = GridCoord.Create( _
  486. xPosition:=this.RNG.Between(1, PlayerGrid.Size), _
  487. yPosition:=this.RNG.Between(1, PlayerGrid.Size))
  488.  
  489. Loop Until enemyGrid.State(position) <> PreviousHit And _
  490. enemyGrid.State(position) <> PreviousMiss
  491.  
  492. Set IGameStrategy_Play = position
  493.  
  494. End Function
  495.  
  496. '@Folder("Battleship.AI")
  497. Option Explicit
  498. Implements IGameStrategy
  499.  
  500. Private Type TStrategy
  501. RNG As IRandomizer
  502. End Type
  503.  
  504. Private this As TStrategy
  505.  
  506. Public Function Create(ByVal randomizer As IRandomizer) As IGameStrategy
  507. With New FairPlayStrategy
  508. Set .RNG = randomizer
  509. Set Create = .Self
  510. End With
  511. End Function
  512.  
  513. Public Property Get Self() As FairPlayStrategy
  514. Set Self = Me
  515. End Property
  516.  
  517. Public Property Get RNG() As IRandomizer
  518. Set RNG = this.RNG
  519. End Property
  520.  
  521. Public Property Set RNG(ByVal value As IRandomizer)
  522. Set this.RNG = value
  523. End Property
  524.  
  525. Private Sub IGameStrategy_PlaceShip(ByVal grid As PlayerGrid, ByVal currentShip As IShip)
  526.  
  527. Do
  528. Dim gridX As Long
  529. gridX = this.RNG.Between(1, PlayerGrid.Size)
  530.  
  531. Dim direction As ShipOrientation
  532. If gridX + currentShip.Size - 1 > PlayerGrid.Size Then
  533. direction = Vertical
  534. Else
  535. direction = IIf(this.RNG.NextSingle < 0.5, Horizontal, Vertical)
  536. End If
  537.  
  538. Dim gridY As Long
  539. If direction = Horizontal Then
  540. gridY = this.RNG.Between(1, PlayerGrid.Size)
  541. Else
  542. gridY = this.RNG.Between(1, PlayerGrid.Size - currentShip.Size)
  543. End If
  544.  
  545. Dim position As GridCoord
  546. Set position = GridCoord.Create(gridX, gridY)
  547.  
  548. DoEvents
  549.  
  550. Loop Until grid.CanAddShip(position, direction, currentShip.Size) And _
  551. Not grid.HasAdjacentShip(position, direction, currentShip.Size)
  552.  
  553. grid.AddShip Ship.Create(currentShip.ShipKind, direction, position)
  554. If grid.ShipCount = PlayerGrid.ShipsPerGrid Then grid.Scramble
  555.  
  556. End Sub
  557.  
  558. Private Function IGameStrategy_Play(ByVal enemyGrid As PlayerGrid) As GridCoord
  559. Dim result As GridCoord
  560. Do
  561. Dim area As Collection
  562. Set area = enemyGrid.FindHitArea
  563.  
  564. If Not area Is Nothing Then
  565.  
  566. Dim inferredDirection As ShipOrientation
  567. inferredDirection = TryInferDirection(area)
  568.  
  569. If inferredDirection = Horizontal Then
  570. If this.RNG.NextSingle < 0.5 Then
  571. Set result = FindLeftMostHit(area).Offset(xOffset:=-1)
  572. If result.X = 1 Or enemyGrid.State(result) = PreviousMiss Then
  573. Set result = FindRightMostHit(area).Offset(xOffset:=1)
  574. End If
  575. Else
  576. Set result = FindRightMostHit(area).Offset(xOffset:=1)
  577. If result.X = PlayerGrid.Size Or enemyGrid.State(result) = PreviousMiss Then
  578. Set result = FindLeftMostHit(area).Offset(xOffset:=-1)
  579. End If
  580. End If
  581. Else
  582. If this.RNG.NextSingle < 0.5 Then
  583. Set result = FindTopMostHit(area).Offset(yOffset:=-1)
  584. If result.Y = 1 Or enemyGrid.State(result) = PreviousMiss Then
  585. Set result = FindBottomMostHit(area).Offset(yOffset:=1)
  586. End If
  587. Else
  588. Set result = FindBottomMostHit(area).Offset(yOffset:=1)
  589. If result.Y = PlayerGrid.Size Or enemyGrid.State(result) = PreviousMiss Then
  590. Set result = FindTopMostHit(area).Offset(yOffset:=-1)
  591. End If
  592. End If
  593. End If
  594.  
  595. Else
  596. 'no hit area: just shoot *somewhere*
  597. Set result = ShootRandom
  598. End If
  599.  
  600. Loop Until result.X >= 1 And result.X <= PlayerGrid.Size And _
  601. result.Y >= 1 And result.Y <= PlayerGrid.Size And _
  602. enemyGrid.State(result) <> PreviousHit And _
  603. enemyGrid.State(result) <> PreviousMiss
  604.  
  605. Set IGameStrategy_Play = result
  606. Exit Function
  607. End Function
  608.  
  609. Private Function ShootRandom() As GridCoord
  610. Set ShootRandom = GridCoord.Create( _
  611. xPosition:=this.RNG.Between(1, PlayerGrid.Size), _
  612. yPosition:=this.RNG.Between(1, PlayerGrid.Size))
  613. End Function
  614.  
  615. Private Function TryInferDirection(ByVal area As Collection) As ShipOrientation
  616. Dim previousPosition As GridCoord
  617. Dim currentPosition As GridCoord
  618. For Each currentPosition In area
  619. If previousPosition Is Nothing Then
  620. Set previousPosition = currentPosition
  621. 'could be either (ignoring remaining enemy ships)
  622. TryInferDirection = IIf(this.RNG.NextSingle < 0.5, Horizontal, Vertical)
  623. Else
  624. If currentPosition.Y = previousPosition.Y Then
  625. TryInferDirection = Horizontal
  626. Else
  627. TryInferDirection = Vertical
  628. End If
  629. End If
  630. Next
  631. End Function
  632.  
  633. Private Function FindLeftMostHit(ByVal area As Collection) As GridCoord
  634. Dim leftMost As GridCoord
  635. Set leftMost = area(1)
  636.  
  637. Dim current As GridCoord
  638. For Each current In area
  639. If current.X < leftMost.X Then Set leftMost = current
  640. Next
  641.  
  642. Set FindLeftMostHit = leftMost
  643. End Function
  644.  
  645. Private Function FindRightMostHit(ByVal area As Collection) As GridCoord
  646. Dim rightMost As GridCoord
  647. Set rightMost = area(1)
  648.  
  649. Dim current As GridCoord
  650. For Each current In area
  651. If current.X > rightMost.X Then Set rightMost = current
  652. Next
  653.  
  654. Set FindRightMostHit = rightMost
  655. End Function
  656.  
  657. Private Function FindTopMostHit(ByVal area As Collection) As GridCoord
  658. Dim topMost As GridCoord
  659. Set topMost = area(1)
  660.  
  661. Dim current As GridCoord
  662. For Each current In area
  663. If current.Y < topMost.Y Then Set topMost = current
  664. Next
  665.  
  666. Set FindTopMostHit = topMost
  667. End Function
  668.  
  669. Private Function FindBottomMostHit(ByVal area As Collection) As GridCoord
  670. Dim bottomMost As GridCoord
  671. Set bottomMost = area(1)
  672.  
  673. Dim current As GridCoord
  674. For Each current In area
  675. If current.Y > bottomMost.Y Then Set bottomMost = current
  676. Next
  677.  
  678. Set FindBottomMostHit = bottomMost
  679. End Function
Add Comment
Please, Sign In to add comment