Over The Wire Advent 2019 Battle of the Galaxies
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

236 lines
5.7KB

  1. package strategies
  2. import (
  3. "botg/models"
  4. "fmt"
  5. )
  6. const (
  7. capturingShipThreshold int = 10
  8. )
  9. type LinkerStrategy struct {
  10. }
  11. func (ls *LinkerStrategy) Execute(stars []*models.Star, flights []models.Flight, links []models.Link) int {
  12. // Send back ships, attack, then link
  13. issuedCommands := 0
  14. issuedCommands += ls.sendHomiesBack(stars, flights, links)
  15. issuedCommands += ls.attack(stars, flights, links)
  16. issuedCommands += ls.linkFriendlies(stars, flights, links)
  17. // Do something randomly, whatever now
  18. if issuedCommands == 0 {
  19. // Not sure what
  20. }
  21. return issuedCommands
  22. }
  23. func (ls *LinkerStrategy) attack(stars []*models.Star, flights []models.Flight, links []models.Link) int {
  24. issuedCommands := 0
  25. for _, s := range stars {
  26. // No more flights allowed for that node
  27. if s.FlightsAllowed <= 0 {
  28. continue
  29. }
  30. // Only process owned stars
  31. if s.Owner != 0 || s.Ships == 0 {
  32. continue
  33. }
  34. // Wait till we have a sufficient number of ships
  35. if ls.insufficientShipsForCapturing(s) {
  36. continue
  37. }
  38. //fmt.Printf("Links: %d\n", len(links))
  39. //fmt.Printf("SOURCE => IDX=%d, X=%d, Y=%d, OWNER=%d, RICH=%d, SHIPS=%d\n", s.Idx, s.X, s.Y, s.Owner, s.Richness, s.Ships)
  40. // Determine target for this source
  41. for _, t := range stars {
  42. // Only target non-owned or enemy stars
  43. if !t.Unowned() && !t.Enemy() {
  44. continue
  45. }
  46. // In the process of capturing the star anyway
  47. if t.Capturing {
  48. continue
  49. }
  50. // Check if ship is within distance
  51. //fmt.Printf("TARGET => IDX=%d, X=%d, Y=%d, OWNER=%d, RICH=%d, SHIPS=%d\n", t.Idx, t.X, t.Y, t.Owner, t.Richness, t.Ships)
  52. flightTurnDistance := flightTurnDistance(s, t)
  53. if flightTurnDistance > 6 {
  54. continue
  55. }
  56. shipClause := (s.Ships / 2)
  57. if s.Ships > 20 {
  58. shipClause = s.Ships - 10
  59. }
  60. enemyShipCountWithinFlight := t.Ships
  61. turns := flightTurnDistance
  62. if t.Turns < turns {
  63. turns -= t.Turns
  64. enemyShipCountWithinFlight += t.Richness
  65. }
  66. for 5 < turns {
  67. enemyShipCountWithinFlight += t.Richness
  68. turns -= 5
  69. }
  70. insufficientShips := shipClause <= (enemyShipCountWithinFlight + 10)
  71. if t.Enemy() && insufficientShips {
  72. continue
  73. }
  74. shipCount := shipClause
  75. if shipCount < 6 {
  76. shipCount = 6
  77. }
  78. // TODO: Find best destination
  79. // Fly to this target, and skip processing all remaining potential target stars
  80. fmt.Printf("fly %d %d %d\n", s.Idx, t.Idx, shipCount)
  81. t.Capturing = true
  82. t.CapturedBy = s.Idx
  83. issuedCommands++
  84. s.FlightsAllowed--
  85. break
  86. }
  87. }
  88. return issuedCommands
  89. }
  90. func (ls *LinkerStrategy) linkFriendlies(stars []*models.Star, flights []models.Flight, links []models.Link) int {
  91. issuedCommands := 0
  92. friendly := make([]int, 10)
  93. friendlyDistanceMatrix := make(map[*models.Star]*models.Star)
  94. for i, s := range stars {
  95. if s.OwnedByMe() || s.Friendly() {
  96. friendly = append(friendly, i)
  97. }
  98. }
  99. // Keep a map of all the distances
  100. for i := 0; i < len(friendly); i++ {
  101. src := stars[friendly[i]]
  102. if src.FlightsAllowed <= 0 {
  103. continue
  104. }
  105. for j := i + 1; j < len(friendly); j++ {
  106. dst, distance := stars[friendly[j]], 0
  107. if src.Idx == dst.Idx {
  108. // Shouldn't happen, but oh well
  109. continue
  110. }
  111. if ls.linkedOrInProgress(src, dst, links, flights) {
  112. continue
  113. }
  114. // We only want to map to friendlies, not own
  115. if !dst.Friendly() {
  116. continue
  117. }
  118. distance = calculateDistanceCeiling(src, dst)
  119. currentLowest, exists := friendlyDistanceMatrix[src]
  120. if exists {
  121. currentDistance := calculateDistanceCeiling(src, currentLowest)
  122. if currentDistance < distance {
  123. continue
  124. }
  125. }
  126. // Only add it if it's the currently closest friendly star from source
  127. friendlyDistanceMatrix[src] = dst
  128. }
  129. }
  130. for src, dst := range friendlyDistanceMatrix {
  131. // If the source is not owned by me, must be a different closer commander
  132. // so skip it
  133. if !src.OwnedByMe() {
  134. continue
  135. }
  136. if ls.insufficientShipsForLinking(src) {
  137. continue
  138. }
  139. if src.FlightsAllowed > 0 {
  140. // Send a couple of ships out to link
  141. issuedCommands++
  142. fmt.Printf("fly %d %d %d\n", src.Idx, dst.Idx, 2)
  143. src.FlightsAllowed--
  144. }
  145. }
  146. return issuedCommands
  147. }
  148. func (ls *LinkerStrategy) sendHomiesBack(stars []*models.Star, flights []models.Flight, links []models.Link) int {
  149. issuedCommands := 0
  150. for _, s := range stars {
  151. if s.OwnedByMe() && s.Captured() && ls.insufficientShipsForCapturing(s) && s.FlightsAllowed > 0 {
  152. if s.Ships <= 5 {
  153. continue
  154. }
  155. richnessRoundsToCapture := capturingShipThreshold / s.Richness
  156. if richnessRoundsToCapture == 0 {
  157. richnessRoundsToCapture = 1
  158. }
  159. timeToCapture := s.Turns + (richnessRoundsToCapture - 1) * 5
  160. capturingStar := stars[s.CapturedBy]
  161. if !ls.insufficientShipsForCapturing(capturingStar) {
  162. continue
  163. }
  164. distanceToOrigin := flightTurnDistance(capturingStar, s)
  165. if distanceToOrigin < timeToCapture {
  166. s.FlightsAllowed--
  167. issuedCommands++
  168. fmt.Printf("fly %d %d %d\n", s.Idx, capturingStar.Idx, s.Ships - 3)
  169. }
  170. }
  171. }
  172. return issuedCommands
  173. }
  174. func (s *LinkerStrategy) insufficientShipsForLinking(star *models.Star) bool {
  175. return star.Ships < 5
  176. }
  177. func (s* LinkerStrategy) insufficientShipsForCapturing(star *models.Star) bool {
  178. return star.Ships < capturingShipThreshold
  179. }
  180. func (s *LinkerStrategy) linkedOrInProgress(src *models.Star, dst *models.Star, links []models.Link, flights []models.Flight) bool {
  181. linking := false
  182. for _, l := range links {
  183. if (l.SourceStar == src.Idx && l.TargetStar == dst.Idx) || (l.SourceStar == dst.Idx && l.TargetStar == src.Idx) {
  184. linking = true
  185. break
  186. }
  187. }
  188. for _, f := range flights {
  189. if (f.SourceStar == src.Idx && f.TargetStar == dst.Idx) || (f.SourceStar == dst.Idx && f.TargetStar == src.Idx) {
  190. linking = true
  191. break
  192. }
  193. }
  194. return linking
  195. }