about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEmile <HanEmile@users.noreply.github.com>2023-04-10 20:23:07 +0200
committerGitHub <noreply@github.com>2023-04-10 20:23:07 +0200
commit355ef9cd8e328a1e4eb584f590cd54fdf1675a60 (patch)
treedba21a9946e6ab4906c4216cf8c62fe60f4e2ce7
parent3c59964278f427ca0eb5f96b77eb3e3c62a9aacd (diff)
parentaaf716b690b23fcde0bccaf8d0a6afb1bd7b5cf5 (diff)
Merge pull request #3 from pesco/placebots easterhegg
place bots fairly
-rw-r--r--main.go134
1 files changed, 60 insertions, 74 deletions
diff --git a/main.go b/main.go
index 2a742f7..0004ca8 100644
--- a/main.go
+++ b/main.go
@@ -1,6 +1,7 @@
 package main
 
 import (
+	"container/heap"
 	"flag"
 	"fmt"
 	"math/rand"
@@ -213,80 +214,77 @@ func initArena(config *Config) *r2pipe.Pipe {
 	return r2p
 }
 
+// a heap of integers (adapted from the container/heap documentation)
+type IntHeap []int
+
+func (h IntHeap) Len() int		{ return len(h) }
+func (h IntHeap) Less(i, j int) bool	{ return h[i] < h[j] }
+func (h IntHeap) Swap(i, j int)		{ h[i], h[j] = h[j], h[i] }
+
+func (h *IntHeap) Push(x any) {
+	*h = append(*h, x.(int))
+}
+
+func (h *IntHeap) Pop() any {
+	n := len(*h)
+	x := (*h)[n - 1]
+	*h = (*h)[0 : n - 1]
+	return x
+}
+
 // genRandomOffsets returns random offsets for all bots
 // This is used to get the offset the bots are initially placed in
 func genRandomOffsets(config *Config) {
 
 	logrus.Info("Generating random bot offsets")
 
-	// define the amount of bots, an array to store the offsets in and a counter
-	// to store the amount of tries it took to find a random positon for the bots
-	var amountOfBots int = len(config.Bots)
-	var offsets []int
-	var roundCounter int = 0
-
 	// seed the random number generator
 	rand.Seed(time.Now().UTC().UnixNano())
 
-	for {
-
-		// reset the offsets array
-		offsets = []int{}
-
-		// define a random address
-		// | ------------------------------------- | ----- |
-		// | generate an address in this space
-		address := rand.Intn(config.Memsize - config.MaxProgSize)
-		logrus.Tracef("%d", address)
-
-		// for all bots, try to generate another random address after the intially
-		// generated address and test if it fits in memory
-		for i := 0; i < amountOfBots; i++ {
-
-			// append the address to the offsets array
-			offsets = append(offsets, address)
-
-			// define a min and max bound
-			//
-			// | ------|-|----------------------------------|-|
-			// |       | |           in this space          | |
-			// a       b c                                  d e
-			//
-			// a = 0x0
-			// b = address
-			// c = address + config.MaxProcSize (min)
-			// d = config.Memsize - config.MaxProgSize (max)
-			// e = config.Memsize
-			min := address + config.MaxProgSize
-			max := config.Memsize - config.MaxProgSize
-
-			// if the new minimum bound is bigger or equal to the maximum bound,
-			// discard this try and start with a fresh new initial address
-			if min >= max {
-				roundCounter++
-				break
-			}
-
-			// generate a new address in the [min, max) range defined above
-			address = rand.Intn(max-min) + min
-			logrus.Tracef("%d", address)
-
-			// If there isn't enough space inbetween the address and the biggest
-			// possible address, as in, the biggest possible bot can't fit in that
-			// space, discard and start with a new fresh initial address
-			if (config.Memsize-config.MaxProgSize)-address < config.MaxProgSize {
-				roundCounter++
-				break
-			}
-		}
+	nbots := len(config.Bots)
 
-		// if the needed amount of offsets has been found, break out of the infinite loop
-		if len(offsets) == amountOfBots {
-			break
-		}
+	// calculate final occupied and remaining free space
+	needed := 0
+	for i := 0; i < nbots; i++ {
+		needed += len(config.Bots[i].Code)
+	}
+	if needed > config.Memsize {
+		panic("total bot size exceeds available memory")
 	}
+	free := config.Memsize - needed
+
+	// pick nbots random places among chunks of free memory.
+	// we consider as possible places the spaces between individual free
+	// bytes of memory and pick one uniformly at random for each bot.
+	// the results are stored in a heap so we can easily go through them
+	// from first to last later.
+	// it is okay to pick the same place multiple times, which just means
+	// that the corresponding bots will be adjacent.
+	places := &IntHeap{}
+	for i := 0; i < nbots; i++ {
+		// there are free + 1 possible places, one in front of each
+		// free byte, plus one after the last.
+		heap.Push(places, rand.Intn(free + 1))
+	}
+
+	// randomly choose the order in which the bots should appear
+	perm := rand.Perm(nbots)
+
+	// calculate the resulting bot offsets
+	offsets := make([]int, nbots)
+	occupied := 0;				// space allocated so far
+	for i := 0; i < nbots; i++ {
+		bot := perm[i]
+		size := len(config.Bots[bot].Code)
+		place := heap.Pop(places).(int)
+
+		offsets[bot] = place + occupied
+		if offsets[bot] + size > config.Memsize {
+			panic("BUG: bot placed out of bounds")
+		}
 
-	logrus.Infof("Initial bot positions found after %d tries", roundCounter)
+		occupied += size
+	}
 
 	// debug print all offsets
 	var fields0 logrus.Fields = make(logrus.Fields)
@@ -295,18 +293,6 @@ func genRandomOffsets(config *Config) {
 	}
 	logrus.WithFields(fields0).Debug("Offsets")
 
-	// shuffle the offsets
-	rand.Shuffle(len(offsets), func(i, j int) {
-		offsets[i], offsets[j] = offsets[j], offsets[i]
-	})
-
-	// debug print the shuffled offsets
-	var fields1 logrus.Fields = make(logrus.Fields)
-	for i := 0; i < len(offsets); i++ {
-		fields1[fmt.Sprintf("%d", i)] = offsets[i]
-	}
-	logrus.WithFields(fields1).Debug("Shuffled offsets")
-
 	config.RandomOffsets = offsets
 }