package main import ( "fmt" "math/rand" "red-green/door" "strings" ) type StarPos struct { Y int X int } type StarInfo struct { Symbol int Color bool } /* StarField holds the information needed to render a starfield. */ type StarField struct { MX int MY int Sky map[StarPos]StarInfo } // Make a new StarPos in the StarField Sky. func (s *StarField) make_pos(RNG *rand.Rand) StarPos { for { pos := StarPos{X: RNG.Intn(s.MX), Y: RNG.Intn(s.MY)} _, found := (*s).Sky[pos] if !found { return pos } } } // Regenerate a StarField structure. // This uses make_pos to find a new unused position. // This needs a seeded rand.Rand. func (s *StarField) Regenerate(RNG *rand.Rand) { s.MX = door.Width s.MY = door.Height max_stars := ((s.MX * s.MY) / 40) s.Sky = make(map[StarPos]StarInfo, max_stars) for i := 0; i < max_stars; i++ { pos := s.make_pos(RNG) info := StarInfo{Symbol: i % 2, Color: i%5 < 2} s.Sky[pos] = info } } // Display StarField. // This optimizes the output. Uses CURSOR LEFT/RIGHT // \x1b[C or \x1b[D to optimize output. func (s *StarField) Display(d *door.Door) { white := door.ColorText("WHITE") dark := door.ColorText("BOLD BLACK") d.Write(door.Reset + door.Clrscr) var stars [2]string stars[0] = "." if door.Unicode { stars[1] = "\u2219" } else { stars[1] = "\xf9" } var i int = 0 var last_pos StarPos // maps in go are not sorted for pos, info := range s.Sky { use_goto := true if i != 0 { if pos.Y == last_pos.Y { dx := pos.X - last_pos.X // Sometimes pos.X < last_pos.X if dx == 0 { use_goto = false } else { if dx < 0 { // handle negative // fmt.Printf("from %#v to %#v\n", last_pos, pos) dx = -dx if dx < 5 { d.Write(strings.Repeat("\x08", dx)) } else { d.Write(fmt.Sprintf("\x1b[%dD", dx)) } use_goto = false } else { if dx < 5 { d.Write(strings.Repeat(" ", dx)) } else { d.Write(fmt.Sprintf("\x1b[%dC", dx)) } use_goto = false } } } } if use_goto { d.Write(door.Goto(pos.X, pos.Y)) } if info.Color { d.Write(dark) } else { d.Write(white) } d.Write(stars[info.Symbol]) last_pos = pos last_pos.X++ i++ } }