beason4251

Rotating galaxy game simulation

Apr 17th, 2020
1,352
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
R 6.26 KB | None | 0 0
  1. # MIT License
  2. #
  3. # Copyright (c) 2020 William Beason
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a copy
  6. # of this software and associated documentation files (the "Software"), to deal
  7. # in the Software without restriction, including without limitation the rights
  8. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. # copies of the Software, and to permit persons to whom the Software is
  10. # furnished to do so, subject to the following conditions:
  11. #  
  12. #   The above copyright notice and this permission notice shall be included in all
  13. # copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. # SOFTWARE.
  22.  
  23. # Generate 100 stars
  24. n <- 100
  25. # Number of players
  26. players <- 6
  27. # stars.m holds our galaxy
  28. stars.m <- matrix(0, n, 5)
  29.  
  30. # Add a star to the galaxy.
  31. genStar <- function(m) {
  32.   # Try 20 times to find a new spot for a star.
  33.   for (tries in 1:20) {
  34.     # Pick a radius and angle.
  35.     # Roughly, stars are a randomly-chosen integer distance from the center.
  36.     # Larger radii have a larger chance of being chosen than smaller.
  37.     radius <- 3+floor((1 - runif(1)) * 20)
  38.     angle <- runif(1)*2*pi
  39.    
  40.     collision = FALSE
  41.     for (i in 1:n) {
  42.       # Check all current stars in the galaxy to make sure there aren't
  43.       # any too close since it looks bad.
  44.       if (m[i, 1] == radius & (abs(m[i, 2] - angle) < (1.0 / radius))) {
  45.         # Too close to a star in the same ring, so try another.
  46.         collision = TRUE
  47.         break
  48.       }
  49.     }
  50.     if (!collision) {
  51.       # No collision, so we've found a new location for a star.
  52.       return(c(radius, angle))
  53.     }
  54.   }
  55.   return(c(0, 0))
  56. }
  57.  
  58. for (i in 1:n) {
  59.   # Assign radius/angle for the new star.
  60.   stars.m[i, 1:2] = genStar(stars.m)
  61.  
  62.   # Calculate x, y positions.
  63.   stars.m[i, 3] = stars.m[i, 1]*sin(stars.m[i, 2])
  64.   stars.m[i, 4] = stars.m[i, 1]*cos(stars.m[i, 2])
  65.   # Every star begins unowned.
  66.   stars.m[i, 5] = 1
  67. }
  68. for (i in 1:players) {
  69.   stars.m[i, 5] = i + 1
  70. }
  71.  
  72. # White, Red, Yellow, Green, Teal, Blue, Purple
  73. cols=c("#FFFFFF", "#FF0000", "#FFFF00", "#00FF00", "#00FFFF", "#0000FF", "#FF00FF")
  74.  
  75. # Advance the galaxy by some time.
  76. advance <- function(m, t) {
  77.   for (i in 1:n) {
  78.     # Stars closer to the center move faster.
  79.     m[i, 2] = m[i, 2] + (t / m[i, 1])
  80.    
  81.     # Update x, y positions.
  82.     m[i, 3] = m[i, 1]*sin(m[i, 2])
  83.     m[i, 4] = m[i, 1]*cos(m[i, 2])
  84.   }
  85.   return(m)
  86. }
  87.  
  88. calcCenter <- function(m) {
  89.   # Get the geometric center of a set of stars.
  90.   cmx <- mean(m[, 3])
  91.   cmy <- mean(m[, 4])
  92.   return(c(cmx, cmy))
  93. }
  94.  
  95. getClaims <- function(m) {
  96.   # Figure out what star the player should attack next.
  97.   claims <- matrix(0, players, 2)
  98.   for (i in 1:players) {
  99.     id <- i+1
  100.     stars <- m[m[, 5] == id,, drop=FALSE]
  101.     # This player has no stars, so don't simulate them.
  102.     if (dim(stars)[1] == 0) next
  103.    
  104.     # Get the geometric center of the player's stars.
  105.     center <- calcCenter(stars)
  106.     cmx <- center[1]
  107.     cmy <- center[2]
  108.    
  109.     claimValue <- -Inf
  110.     # Consider each star to figure out the best next one to claim.
  111.     for (j in 1:(dim(m)[1])) {
  112.       # The star is already owned, so ignore it.
  113.       if (m[j, 5] == id) next
  114.      
  115.       newValue <- 0
  116.       claimDistance <- ((m[j, 3]-cmx)^2 + (m[j, 4] - cmy)^2)
  117.      
  118.       # Value unclaimed stars more than claimed stars.
  119.       # Also, value stars closer to the geometric center of the empire more.
  120.       #
  121.       # Could do with balancing, but the point is to ensure
  122.       # most stars get claimed before fighting starts.
  123.       if (m[j, 5] == 1) {
  124.         newValue <- 1 / (claimDistance + 1)
  125.       } else {
  126.         newValue <- 1 / (claimDistance + 1)^2
  127.       }
  128.      
  129.       # If this system's value is better than the best previously
  130.       # considered, it is now the best option.
  131.       if (newValue > claimValue) {
  132.         claimValue <- newValue
  133.         claims[i, 1] <- j
  134.         claims[i, 2] <- claimDistance
  135.       }
  136.     }
  137.   }
  138.  
  139.   return(claims)
  140. }
  141.  
  142. playgame <- function(m) {
  143.   # Each player gets to attack at most one star each turn.
  144.   claims <- getClaims(m)
  145.  
  146.   # Resolve claims. Later players technically have a slight advantage, but
  147.   # this simulation is just to give a general idea of how things play out.
  148.   for (i in 1:players) {
  149.     id <- i+1
  150.    
  151.     # Didn't claim any stars or is dead.
  152.     if (claims[i, 1] == 0) next
  153.    
  154.     oid <- m[claims[i, 1], 5]
  155.     if (oid == 1) {
  156.       # Unowned star, automatically claimed.
  157.       m[claims[i], 5] <- id
  158.       next
  159.     }
  160.    
  161.     # Fight over star.
  162.     # Roughly, strength is proportional to the number of owned stars and
  163.     # decreases with distance from the faction's center.
  164.     iDist <- claims[i, 2]
  165.     oCenter <- calcCenter(m[m[, 5] == oid,, drop=FALSE])
  166.     oDist <- sqrt((oCenter[1] - m[i, 3])^2 + (oCenter[2] - m[i, 4])^2)
  167.    
  168.     iStrength <- sum(m[, 5, drop=FALSE] == id) / (iDist + 1)
  169.     oStrength <- sum(m[, 5, drop=FALSE] == oid) / (oDist + 1)
  170.    
  171.     if (runif(1) < iStrength / (iStrength + oStrength)) {
  172.       # Player won the battle, so claim star.
  173.       m[claims[i, 1], 5] <- id
  174.     }
  175.   }
  176.  
  177.   return(m)
  178. }
  179.  
  180. # Plot the initial galaxy.
  181. png(filename="game/tick_0.png")
  182. par(bg = "black")
  183. plot(stars.m[, 3:4], col=cols[stars.m[, 5]], main="Tick 0", col.main="white", xlim=c(-23, 23), ylim=c(-23, 23))
  184. dev.off()
  185.  
  186. for (i in 1:160) {
  187.   # Advance the galaxy in quarter-day-increments and plot.
  188.   tick <- 6*i
  189.   fn <- paste0("game/tick_", as.character(tick), ".png")
  190.  
  191.  
  192.   png(filename=fn)
  193.   # Have players act for one turn, then simulate star movement.
  194.   stars.m <- playgame(stars.m)
  195.   stars.m <- advance(stars.m, 0.25)
  196.   par(bg = "black")
  197.   plot(stars.m[, 3:4], col=cols[stars.m[, 5]], main=paste("Tick", tick), col.main="white", xlim=c(-23, 23), ylim=c(-23, 23))
  198.   dev.off()
  199. }
Advertisement
Add Comment
Please, Sign In to add comment