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:
32-bit Ubuntu one core
go version go1.7 linux/386


Tue, 16 Aug 2016 03:39:04 GMT

MAKE:
/usr/local/src/go/bin/go build -o chameneosredux.go-5.go_run
0.38s 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
4082778 zero
4242296 zero
3674926 zero
 one two zero zero zero zero zero zero

 blue red yellow red yellow blue red yellow red blue
1011307 zero
1368061 zero
1460410 zero
1037198 zero
1001365 zero
1227009 zero
1099859 zero
993368 zero
1373101 zero
1428322 zero
 one two zero zero zero zero zero zero