~hokiegeek/life

ref: c41a449353d445a06ec7334b540afb793d577cd8 life/processors.go -rw-r--r-- 2.1 KiB View raw
c41a4493HokieGeek Refactored to host on sr.ht and converted to a mod 10 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package life

import (
	"sync"
)

// SimultaneousProcessor simultaneously applies the given rules to the given pond. This is the default Conway processor.
func SimultaneousProcessor(pond *pond, rules func(int, bool) bool) {
	unprocessed := func() func(Location) bool {
		var mu sync.Mutex
		processed := make(map[int]map[int]struct{}, pond.Dims.Height)
		return func(loc Location) (_ bool) {
			mu.Lock()
			defer mu.Unlock()

			if yLoc, ok := processed[loc.Y]; ok {
				if _, ok = yLoc[loc.X]; ok {
					return
				}
			} else {
				processed[loc.Y] = make(map[int]struct{}, pond.Dims.Width)
			}
			processed[loc.Y][loc.X] = struct{}{}
			return true
		}
	}()

	var wg sync.WaitGroup
	numWorkers := 100
	processing := make(chan Location, numWorkers)

	type ModifiedOrganism struct {
		loc   Location
		alive bool
	}
	modifications := make(chan ModifiedOrganism, pond.Dims.Capacity())

	for w := 1; w <= numWorkers; w++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for organism := range processing {
				// Since this is a new one, go ahead and process it
				if unprocessed(organism) {
					currentStatus := pond.isOrganismAlive(organism)

					// Check with the ruleset what this organism's new status is
					assessedStatus := rules(pond.NumLivingNeighbors(organism), currentStatus)

					// If its status has changed, then we do stuff
					if currentStatus != assessedStatus {
						modifications <- ModifiedOrganism{loc: organism, alive: assessedStatus}
					}
				}
			}
		}()
	}

	// Add living organisms to processing queue
	for _, organism := range pond.living.GetAll() {
		processing <- organism

		// Now process the neighbors!
		if neighbors, err := pond.GetNeighbors(organism); err == nil {
			for _, neighbor := range neighbors {
				processing <- neighbor
			}
		}
	}
	close(processing)

	wg.Wait()
	// Make the actual modifications to the pond
	close(modifications)
	for w := 1; w <= numWorkers; w++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for mod := range modifications {
				pond.setOrganismState(mod.loc, mod.alive)
			}
		}()
	}
	wg.Wait()
}

// vim: set foldmethod=marker: