The Computer Language
Benchmarks Game

chameneos-redux Go #5 program

source code

/* The Computer Language Benchmarks Game
 * http://benchmarksgameshootout.alioth.debian.org/
 *
 * contributed by The Go Authors.
 * modified by roger peppe
 * 
 */

package main

import (
   "flag"
   "fmt"
   "strconv"
   "sync"
)

const (
   blue = iota
   red
   yellow
   ncol
)

var complement = [...]int{
   red | red<<2: red,
   red | yellow<<2: blue,
   red | blue<<2: yellow,
   yellow | red<<2: blue,
   yellow | yellow<<2: yellow,
   yellow | blue<<2: red,
   blue | red<<2: yellow,
   blue | yellow<<2: red,
   blue | blue<<2: blue,
}

var colname = [...]string{
   blue: "blue",
   red: "red",
   yellow: "yellow",
}

// information about the current state of a creature.
type info struct {
   colour int // creature's current colour.
   name   int // creature's name.
}

// if mate is nil, it indicates there's no creature currently waiting
// otherwise the creature's info is stored in info, and
// it is waiting to receive its mate's information on the mate channel.
type Place struct {
   sync.Mutex
   n    int         // current number of encounters.
   mate chan<- info // creature waiting when non-nil.
   info info        // info about creature waiting.
}

// result sent by each creature at the end of processing.
type result struct {
   met  int
   same int
}

var n = 600

func main() {
   flag.Parse()
   if flag.NArg() > 0 {
      n, _ = strconv.Atoi(flag.Arg(0))
   }

   for c0 := 0; c0 < ncol; c0++ {
      for c1 := 0; c1 < ncol; c1++ {
         fmt.Printf("%s + %s -> %s\n", colname[c0], colname[c1], colname[complement[c0|c1<<2]])
      }
   }
   fmt.Print("\n")

   pallmall([]int{blue, red, yellow})
   pallmall([]int{blue, red, yellow, red, yellow, blue, red, yellow, red, blue})
}

func pallmall(cols []int) {

   // invariant: meetingplace always contains a value unless a creature
   // is currently dealing with it (whereupon it must put it back).
   meetingplace := new(Place)
   ended := make(chan result)
   msg := ""
   for i, col := range cols {
      go creature(info{col, i}, meetingplace, ended)
      msg += " " + colname[col]
   }
   fmt.Println(msg)
   tot := 0
   // wait for all results
   for _ = range (cols) {
      result := <-ended
      tot += result.met
      fmt.Printf("%v%v\n", result.met, spell(result.same, true))
   }
   fmt.Printf("%v\n\n", spell(tot, true))
}

// in this function, variables ending in 0 refer to the local creature,
// variables ending in 1 to the creature we've met.
func creature(info0 info, m *Place, ended chan result) {
   c0 := make(chan info)
   met := 0
   same := 0
   for {
      var othername int
      // get access to rendez data and decide what to do.
      m.Lock()
      switch {
      case m.n >= n:
         // if no more meetings left, then send our result data and exit.
         m.Unlock()
         ended <- result{met, same}
         return

      case m.mate == nil:
         // no creature waiting wait for someone to meet us,
         // get their info and send our info in reply.
         m.info = info0
         m.mate = c0
         m.Unlock()
         info1 := <-c0
         othername = info1.name
         info0.colour = complement[info0.colour|info1.colour<<2]

      default:
         // another creature is waiting for us with its info
         // increment meeting count,
         // send them our info in reply.
         mate := m.mate
         m.n++
         m.mate = nil
         info1 := m.info
         m.Unlock()
         mate <- info0
         othername = info1.name
         info0.colour = complement[info0.colour|info1.colour<<2]
      }
      if othername == info0.name {
         same++
      }
      met++
   }
}

var digits = [...]string{"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}

func spell(n int, required bool) string {
   if n == 0 && !required {
      return ""
   }
   return spell(n/10, false) + " " + digits[n%10]
}
    

notes, command-line, and program output

NOTES:
64-bit Ubuntu quad core
go version go1.10 linux/amd64


Sat, 17 Feb 2018 18:46:09 GMT

MAKE:
/opt/src/go1.10.linux-amd64/go/bin/go build -o chameneosredux.go-5.go_run

0.39s to complete and log all make actions

COMMAND LINE:
./chameneosredux.go-5.go_run 6000000

PROGRAM OUTPUT:
blue + blue -> blue
blue + red -> yellow
blue + yellow -> red
red + blue -> yellow
red + red -> red
red + yellow -> blue
yellow + blue -> red
yellow + red -> blue
yellow + yellow -> yellow

 blue red yellow
3968167 zero
4008428 zero
4023405 zero
 one two zero zero zero zero zero zero

 blue red yellow red yellow blue red yellow red blue
1215084 zero
1188280 zero
1210920 zero
1180111 zero
1212895 zero
1190343 zero
1202855 zero
1195551 zero
1194534 zero
1209427 zero
 one two zero zero zero zero zero zero