1  package main
   2  import ( "fmt"; "os"; "flag"; "strconv"; "rand"; "time" )
   3  
   4  /* Rock, Paper, or Scissors */
   5  type Throw int
   6  const ( ROCK Throw = iota; PAPER; SCISSORS; NUM_THROWS )
   7  
   8  /* Returns true if I beat you */
   9  func beats(me Throw, you Throw) bool {
  10    return (me == ROCK     && you == SCISSORS) ||
  11           (me == SCISSORS && you == PAPER)    ||
  12           (me == PAPER    && you == ROCK)
  13  }
  14  
  15  /* Returns a random throw */
  16  func random_throw() Throw { return Throw(rand.Intn(int(NUM_THROWS))) }
  17  
  18  /* A tuple: (throw, lifetime), used for communicating on the ring network. */
  19  type Packet struct { throw Throw; lifetime int }
  20  
  21  /* A tuple: (id, score), used for reporting a player's score */
  22  type Result struct { player int; score int }
  23  
  24  /*
  25   * Play 1000 rounds of Jan-Ken-Pon in the current thread.
  26   *
  27   * Plays are sent around the ring so everyone can score
  28   * themselves. 'read' and 'write' connect this player to
  29   * the rest.
  30   *
  31   * The number of other players is also given.
  32   *
  33   * This returns the score for the player after the rounds
  34   * are finished.
  35   */
  36  func play_rounds(read chan Packet, write chan Packet, n int) int {
  37    score := 0
  38    
  39    for r := 0; r < 1000; r++ {
  40      throw := random_throw()
  41      write <- Packet { throw, n - 1 }
  42      
  43      /* Read other n-1 plays */
  44      for got := 1; got < n; got++ {
  45        opp := <- read
  46        if beats(throw, opp.throw) { score++ }
  47        
  48        /* If the lifetime is not 0, pass it along the ring */
  49        opp.lifetime--
  50        if opp.lifetime != 0 { write <- opp }
  51      }
  52    }
  53    
  54    return score
  55  }
  56  
  57  /*
  58   * Make a channel and a goroutine. The goroutine will
  59   * play 1000 rounds of Jan-Ken-Pon, sending its plays
  60   * around the ring (via 'write') and reading other
  61   * player's plays coming around the ring (via 'read').
  62   *
  63   * Once the rounds are done, the score for this player
  64   * is written to 'report' and this player's goroutine
  65   * will exit.
  66   *
  67   * 'id' is this player's id, and 'n' is the number of
  68   * players. This returns the read channel for this player.
  69   */
  70  func player(write chan Packet, report chan Result, id int, n int) chan Packet {
  71    read := make(chan Packet, n)
  72    
  73    go func() {
  74      score := play_rounds(read, write, n);
  75      report <- Result { id, score }
  76    }()
  77    
  78    return read
  79  }
  80  
  81  /*
  82   * Process the command-line and spawn the game
  83   */
  84  func main() {
  85    if flag.NArg() != 1 {
  86      os.Stderr.WriteString("Usage: jkp <N>\n")
  87      os.Exit(1)
  88    }
  89  
  90    n, err := strconv.Atoi(flag.Arg(0))
  91    
  92    if err != nil {
  93      fmt.Fprintf(os.Stderr, "The argument must be a number (%v)\n", err)
  94      os.Exit(1)
  95    }
  96    
  97    if n < 2 {
  98      fmt.Fprintf(os.Stderr, "The argument must be 2 or greater (not %d)\n", n)
  99      os.Exit(1)
 100    }
 101    
 102    rand.Seed(time.Nanoseconds())
 103    
 104    /*
 105     * Create the other players, each playing the game. This
 106     * main thread will play too; it is player 0.
 107     *
 108     * The players communicate via a ring topology.
 109     */
 110  
 111    report := make(chan Result)
 112    ring := make(chan Packet, n)
 113    
 114    /* This thread will read from the first and write to the last. */
 115    read := ring
 116    
 117    for i := 1; i < n; i++ {
 118      ring = player(ring, report, i, n)
 119    }
 120    
 121    write := ring
 122    
 123    /* Play */
 124    score := play_rounds(read, write, n);
 125  
 126    /* Tally */
 127    fmt.Printf("Player %d  Score %d\n", 0, score)
 128    
 129    for i := 1; i < n; i++ {
 130      r := <- report
 131      fmt.Printf("Player %d  Score %d\n", r.player, r.score)
 132    }
 133  }