starfield.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. package main
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "red-green/door"
  6. "strings"
  7. )
  8. type StarPos struct {
  9. Y int
  10. X int
  11. }
  12. type StarInfo struct {
  13. Symbol int
  14. Color bool
  15. }
  16. /*
  17. StarField holds the information needed to render a starfield.
  18. */
  19. type StarField struct {
  20. MX int
  21. MY int
  22. Sky map[StarPos]StarInfo
  23. }
  24. // Make a new StarPos in the StarField Sky.
  25. func (s *StarField) make_pos(RNG *rand.Rand) StarPos {
  26. for {
  27. pos := StarPos{X: RNG.Intn(s.MX), Y: RNG.Intn(s.MY)}
  28. _, found := (*s).Sky[pos]
  29. if !found {
  30. return pos
  31. }
  32. }
  33. }
  34. // Regenerate a StarField structure.
  35. // This uses make_pos to find a new unused position.
  36. // This needs a seeded rand.Rand.
  37. func (s *StarField) Regenerate(RNG *rand.Rand) {
  38. s.MX = door.Width
  39. s.MY = door.Height
  40. max_stars := ((s.MX * s.MY) / 40)
  41. s.Sky = make(map[StarPos]StarInfo, max_stars)
  42. for i := 0; i < max_stars; i++ {
  43. pos := s.make_pos(RNG)
  44. info := StarInfo{Symbol: i % 2, Color: i%5 < 2}
  45. s.Sky[pos] = info
  46. }
  47. }
  48. // Display StarField.
  49. // This optimizes the output. Uses CURSOR LEFT/RIGHT
  50. // \x1b[C or \x1b[D to optimize output.
  51. func (s *StarField) Display(d *door.Door) {
  52. white := door.ColorText("WHITE")
  53. dark := door.ColorText("BOLD BLACK")
  54. d.Write(door.Reset + door.Clrscr)
  55. var stars [2]string
  56. stars[0] = "."
  57. if door.Unicode {
  58. stars[1] = "\u2219"
  59. } else {
  60. stars[1] = "\xf9"
  61. }
  62. var i int = 0
  63. var last_pos StarPos
  64. // maps in go are not sorted
  65. for pos, info := range s.Sky {
  66. use_goto := true
  67. if i != 0 {
  68. if pos.Y == last_pos.Y {
  69. dx := pos.X - last_pos.X
  70. // Sometimes pos.X < last_pos.X
  71. if dx == 0 {
  72. use_goto = false
  73. } else {
  74. if dx < 0 {
  75. // handle negative
  76. // fmt.Printf("from %#v to %#v\n", last_pos, pos)
  77. dx = -dx
  78. if dx < 5 {
  79. d.Write(strings.Repeat("\x08", dx))
  80. } else {
  81. d.Write(fmt.Sprintf("\x1b[%dD", dx))
  82. }
  83. use_goto = false
  84. } else {
  85. if dx < 5 {
  86. d.Write(strings.Repeat(" ", dx))
  87. } else {
  88. d.Write(fmt.Sprintf("\x1b[%dC", dx))
  89. }
  90. use_goto = false
  91. }
  92. }
  93. }
  94. }
  95. if use_goto {
  96. d.Write(door.Goto(pos.X, pos.Y))
  97. }
  98. if info.Color {
  99. d.Write(dark)
  100. } else {
  101. d.Write(white)
  102. }
  103. d.Write(stars[info.Symbol])
  104. last_pos = pos
  105. last_pos.X++
  106. i++
  107. }
  108. }