func printLoop(ch <-chan []grid2d.Update, g grid2d.Grid, cns *census.DirCensus, cond *sync.Cond, numUpdates *int64, clearScreen bool) { // Try to keep rendering smooth. runtime.LockOSThread() for _ = range ch { if clearScreen { // TODO(dnesting): use termcap/terminfo to do this more portably. fmt.Print("[H") } term.PrintWorld(os.Stdout, g) fmt.Println() if clearScreen { fmt.Print("[J") } // Write some summary stats after the rendering. fmt.Printf("%d updates\n", atomic.LoadInt64(numUpdates)) fmt.Printf("%d/%d orgs (%d/%d species, %d recorded)\n", cns.Count(), cns.CountAllTime(), cns.Distinct(), cns.DistinctAllTime(), cns.NumRecorded()) if loc := g.Get(0, 0); loc != nil { fmt.Printf("random: %v\n", loc.Value()) } // If we're running with --sync, signal to any goroutines waiting on a rendering that // it's OK for them to continue again. if cond != nil { cond.Broadcast() } } }
func startUpdateTracker(g grid2d.Grid, numUpdates *int64) { ch := make(chan []grid2d.Update, 0) go func() { for updates := range ch { atomic.AddInt64(numUpdates, int64(len(updates))) } }() g.Subscribe(ch) }
// StartAll finds all organisms driven by Cpu instances, and spawns a goroutine // to begin executing each Cpu instance found. func StartAll(g grid2d.Grid) { var locs []grid2d.Point g.Locations(&locs) for _, p := range locs { if o, ok := p.V.(*org.Organism); ok { if c, ok := o.Driver.(*Cpu); ok { go c.Run(o) } } } }
// Count counts the number of occupants in g that satisfy fn. func Count(g grid2d.Grid, fn CounterFunc) int { var locs []grid2d.Point g.Locations(&locs) var count int for _, p := range locs { if fn(p.V) { count += 1 } } return count }
func startOrg(g grid2d.Grid) { c := cpu1.Random() o := org.Random() o.Driver = c o.AddEnergy(initialEnergy) for { // PutRandomly might fail if there's no room, so just keep trying. if _, loc := g.PutRandomly(o, org.PutWhenFood); loc != nil { go c.Run(o) break } } }
func startAndMaintainOrgs(g grid2d.Grid) { // Obtain an initial count before we start anything executing. mCount := maintain.Count(g, isOrg) ch := make(chan []grid2d.Update, 0) g.Subscribe(ch) // Start all organisms currently existing in the Grid. We do this *after* // subscribing ch so that we don't end up with a wrong count if any organisms // divide or die. cpu1.StartAll(g) go maintain.Maintain(ch, isOrg, func() { startOrg(g) }, minOrgs, mCount) }
// PrintWorld renders g to w. func PrintWorld(w io.Writer, g grid2d.Grid) { points := locPool.Get().([]grid2d.Point) width, height, _ := g.Locations(&points) sort.Sort(byCoordinate(points)) iy, ix := 0, -1 addHeader(w, width) for _, p := range points { fillBefore(w, p.X, p.Y, width, &ix, &iy) writeRune(w, RuneForOccupant(p.V)) ix += 1 } locPool.Put(points) fillBefore(w, width, height-1, width, &ix, &iy) writeRune(w, rightRune) writeRune(w, '\n') addFooter(w, width) }
func startPrintLoop(g grid2d.Grid, cns *census.DirCensus, cond *sync.Cond, numUpdates *int64, clearScreen bool) { // We want to use chanbuf.Tick to ensure renders occur at specific intervals regardless // of the rate at which updates arrive. To prevent the notification channel from backing up // and causing deadlock, we buffer using a chanbuf.Trigger (since we don't care about the // update messages themselves). We could have used a time.Tick instead, but we'd need to // do something special to ensure this loop exits when the notifications stop. // grid notifier -> updateCh -> trigger -> tick -> print world // grid notifier first dispatches an update the moment it occurs // trigger consumes the event and flags that an update occurred // tick fires every freq, halting when trigger reports it's closed // printLoop renders the grid every time tick fires updateCh := make(chan []grid2d.Update, 0) trigger := chanbuf.Trigger() freq := time.Duration(1000000.0/printRate) * time.Microsecond go chanbuf.Feed(trigger, grid2d.NotifyToInterface(updateCh)) tickCh := chanbuf.Tick(trigger, freq, true) g.Subscribe(updateCh) go printLoop(grid2d.NotifyFromInterface(tickCh), g, cns, cond, numUpdates, clearScreen) }
func startCensus(g grid2d.Grid) *census.DirCensus { // Create a new Census that writes to /tmp/census when a population grows to 40. cns, err := census.NewDirCensus("/tmp/census", func(p census.Population) bool { return p.Count > 40 }) if err != nil { fmt.Printf("Error creating census: %v\n", err) os.Exit(1) } // Use human times. timeNow := func(interface{}) interface{} { return time.Now() } ch := make(chan []grid2d.Update, 0) g.Subscribe(ch) // Populate the Census with what's already in the world (perhaps restored from an autosave). // Assumes nothing in the world is changing yet. grid2d.ScanForCensus(cns, g, timeNow, orgHash) // Start monitoring for changes go grid2d.WatchForCensus(cns, ch, timeNow, orgHash) return cns }