The Computer Language
Benchmarks Game

chameneos-redux Go program

source code

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

package main

import (
    "flag"
    "fmt"
    "runtime"
    "strconv"
    atom "sync/atomic"
)

const (
    // three colors in alphabetic order
    blue = 1 + iota
    red
    yellow
)

func complement(col1, col2 int) int {
    switch col1 {
    case blue:
        switch col2 {
        case blue:
            return blue
        case red:
            return yellow
        case yellow:
            return red
        }
    case red:
        switch col2 {
        case blue:
            return yellow
        case red:
            return red
        case yellow:
            return blue
        }
    case yellow:
        switch col2 {
        case blue:
            return red
        case red:
            return blue
        case yellow:
            return yellow
        }
    }
    panic("Invalid colour in complement")
}

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

// result sent by each creature at the end of processing.
type result struct {
    met  int // number of meetings with any
    same int // number of meetings with self
}

// creature's state needed for a meeting
type creature struct {
    selfColour int     // my current colour
    mateColour int     // colour of mate
    handshake  *uint32 // name of mate when first
}

// mall organizes meetings between creatures
type mall struct {
    meetings  *uint32    // count number of meetings
    waiter    *uint32    // waiting creature if non-zero
    creatures []creature // all creatures indexed by name
}

// total meetings per game of pallmall
var meetings = 600

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

    for col1 := blue; col1 <= yellow; col1++ {
        for col2 := blue; col2 <= yellow; col2++ {
            fmt.Printf("%s + %s -> %s\n",
                colname[col1], colname[col2],
                colname[complement(col1, col2)])
        }
    }
    fmt.Print("\n")

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

// pallmall starts a new game and reports statistics
func pallmall(cols []int) {
    // construct a new game
    creatures := make([]creature, len(cols)+1)
    mall := &mall{new(uint32), new(uint32), creatures}
    ended := make(chan result)
    msg := ""
    for i, col := range cols {
        name := i + 1
        go play(name, col, mall, ended)
        msg += " " + colname[col]
    }
    fmt.Println(msg)

    // wait for all results
    total := 0
    for _ = range cols {
        result := <-ended
        total += result.met
        fmt.Printf("%v%v\n", result.met, spell(result.same))
    }
    fmt.Printf("%v\n\n", spell(total))
}

func play(selfName int, startColour int, mall *mall, ended chan<- result) {
    atomName := uint32(selfName)
    self := &mall.creatures[selfName]
    self.selfColour = startColour
    self.handshake = new(uint32)
    const noone = 0
    result := result{}
    // obtain a license for a new meeting, until meetings are over
    for atom.AddUint32(mall.meetings, 1) <= uint32(2*meetings) {
        var other uint32
        for {
            other = atom.LoadUint32(mall.waiter)
            if other == noone {
                // nobody is waiting; try to become the first to enter
                if atom.CompareAndSwapUint32(mall.waiter, noone, atomName) {
                    // success; now wait for second creature
                    for i := 0; other == noone; i++ {
                        if i >= 50 {
                            // limit idle spinning
                            runtime.Gosched()
                        }
                        other = atom.LoadUint32(self.handshake)
                    }
                    // second creature is done; clear flag
                    *self.handshake = noone
                    break
                }
            } else if atom.CompareAndSwapUint32(mall.waiter, other, noone) {
                // we found a mate and are the second to enter
                // swap colour info with our mate
                self.mateColour = mall.creatures[other].selfColour
                mall.creatures[other].mateColour = self.selfColour
                // tell waiting mate that we are done
                atom.StoreUint32(mall.creatures[other].handshake, atomName)
                break
            }
        }
        self.selfColour = complement(self.selfColour, self.mateColour)
        if other == atomName {
            result.same++
        }
        result.met++
    }
    ended <- result
}

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

func spell(n int) string {
    msg := digits[n%10]
    for n /= 10; n != 0; n /= 10 {
        msg = digits[n%10] + msg
    }
    return msg
}
    

notes, command-line, and program output

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


Sat, 17 Feb 2018 18:47:17 GMT

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

0.41s to complete and log all make actions

COMMAND LINE:
./chameneosredux.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
4062651 zero
3917127 zero
4020222 zero
 one two zero zero zero zero zero zero

 blue red yellow red yellow blue red yellow red blue
1231177 zero
1225398 zero
1167338 zero
1221098 zero
1115632 zero
1221727 zero
1209429 zero
1215713 zero
1158916 zero
1233572 zero
 one two zero zero zero zero zero zero