【解答】Go言語初心者向けハンズオン #2

※これは解答です。どうしても課題が解けない場合、ご利用ください。

# 課題１ - 基本問題

```golang
package main

import (
	"fmt"
	"math/rand"
	"time"
)

const (
	PAA   = 1
	GUU   = 2
	CHOKI = 3
)

func main() {
	COUNT := 1000000
	rand.Seed(time.Now().UnixNano())

	result := [3]int{}
	for i := 0; i < COUNT; i++ {
		a := yukpiz()
		b := kent()

		switch {
		case a == b:
			result[0]++
		case a == PAA && b == GUU, a == GUU && b == CHOKI, a == CHOKI && b == PAA:
			result[1]++
		case a == PAA && b == CHOKI, a == GUU && b == PAA, a == CHOKI && b == GUU:
			result[2]++
		default:
			panic("(´・ω・｀)")
		}
	}

	fmt.Printf("引分: %d\n", result[0])
	fmt.Printf("勝利: %d\n", result[1])
	fmt.Printf("敗北: %d\n", result[2])
}

func yukpiz() int {
	s := []int{PAA, GUU, CHOKI}
	return s[rand.Intn(len(s))]
}

func kent() int {
	s := []int{PAA, GUU, CHOKI}
	return s[rand.Intn(len(s))]
}
```

# 課題１ - 発展問題

※変更箇所のみ記載しています。

```golang
func kent() int {
	s := []int{PAA, PAA, PAA, GUU, CHOKI}
	return s[rand.Intn(len(s))]
}
```


# 課題２ - 基本問題

```golang
package main

import (
	"fmt"
)

type MessageTo int

const (
	MESSAGE_TO_KENT    MessageTo = 0
	MESSAGE_TO_YUKPIZ  MessageTo = 1
	MESSAGE_END_ME     MessageTo = 3
	MESSAGE_END_ME_TOO MessageTo = 4
)

func main() {
	ch := make(chan MessageTo) // 相手にボールを渡す為のチャネル
	done := make(chan bool)    // 全ての会話完了を検知するチャネル

	go kent(ch, done)
	go yukpiz(ch, done)

	// 会話が終わるまでブロック
	<-done

	// 閉じているかを確認
	<-ch
	fmt.Println("会話完了")
}

func kent(ch chan MessageTo, done chan bool) {
	texts := []string{"あなたの名前を教えてください", "私はけんとです", "生ハム食べに行きましょう"}
	for v := range ch {
		switch v {
		case MESSAGE_END_ME_TOO:
			// 終了通知が来たらチャネルを閉じる
			close(ch)
			close(done)
		case MESSAGE_END_ME:
			if len(texts) == 0 {
				// セリフがなくなったら完了
				ch <- MESSAGE_END_ME_TOO
				break
			}
			fallthrough
		case MESSAGE_TO_KENT:
			if len(texts) > 0 {
				fmt.Println("kent:" + texts[0]) // 次のセリフを発言する
				texts = texts[1:]               // 発言済みのセリフを消す
				ch <- MESSAGE_TO_YUKPIZ         // 相手にボールを渡す
			} else {
				ch <- MESSAGE_END_ME
			}
		case MESSAGE_TO_YUKPIZ:
			ch <- MESSAGE_TO_YUKPIZ
		}
	}
}

func yukpiz(ch chan MessageTo, done chan bool) {
	texts := []string{"私はゆくぴずです", "よろしくおねがいします", "行きましょう"}
	ch <- MESSAGE_TO_KENT
	for v := range ch {
		switch v {
		case MESSAGE_END_ME_TOO:
			close(ch)
			close(done)
		case MESSAGE_END_ME:
			if len(texts) == 0 {
				ch <- MESSAGE_END_ME_TOO
				break
			}
			fallthrough
		case MESSAGE_TO_KENT:
			ch <- MESSAGE_TO_KENT
		case MESSAGE_TO_YUKPIZ:
			if len(texts) > 0 {
				fmt.Println("yukpiz:" + texts[0])
				texts = texts[1:]
				ch <- MESSAGE_TO_KENT
			} else {
				ch <- MESSAGE_END_ME
			}
		}
	}
}
```

# 課題２ - 発展問題

チャネルは1対1での送信と受信をサポートしていますが、
ブロードキャストのような1対多のような通信をサポートするには工夫が必要です。

```golang
package main

import (
	"fmt"
)

type Worker struct {
	Name          string
	BroadcastChan chan int
	DoneChan      chan int
	ReceiveChan   chan int
	Receive       func(*Worker)
}

func main() {
	bch := make(chan int)  //ブロードキャスト用のチャネル
	done := make(chan int) //終了判定用のチャネル

	var workers []*Worker
	workers = append(workers, &Worker{
		Name:          "yukpiz",
		BroadcastChan: bch,
		ReceiveChan:   make(chan int),
		Receive:       yukpiz,
	})
	workers = append(workers, &Worker{
		Name:          "kent",
		BroadcastChan: bch,
		ReceiveChan:   make(chan int),
		Receive:       kent,
	})
	workers = append(workers, &Worker{
		Name:          "ariaki",
		BroadcastChan: bch,
		ReceiveChan:   make(chan int),
		Receive:       ariaki,
	})

	go BroadcastObserve(workers, bch, done)
	for _, w := range workers {
		go w.Receive(w)
	}

	// 全てのチャネルが閉じられている事を確認
	<-done
	<-bch
	for _, w := range workers {
		<-w.ReceiveChan
	}
	fmt.Println("exit!")
}

func BroadcastObserve(workers []*Worker, bch chan int, done chan int) {
	defer close(done)
	for v := range bch {
		for _, w := range workers {
			w.ReceiveChan <- v
		}
	}

	fmt.Println("closed broad cast")
	for _, w := range workers {
		close(w.ReceiveChan)
	}
}

func yukpiz(me *Worker) {
	fmt.Printf("%s: 皆さんの名前を教えてください\n", me.Name)
	me.BroadcastChan <- 1
	for v := range me.ReceiveChan {
		switch v {
		case 3:
			fmt.Printf("%s: ありがとうございます\n", me.Name)
			fmt.Printf("%s: 好きなモノを教えてください\n", me.Name)
			v++
			me.BroadcastChan <- v
		case 5:
			fmt.Printf("%s: 日本酒が好きなんですね\n", me.Name)
			v++
			me.BroadcastChan <- v
		case 7:
			fmt.Printf("%s: 生ハムを食べにいきましょう\n", me.Name)
			v++
			me.BroadcastChan <- v

		}
	}
}

func kent(me *Worker) {
	for v := range me.ReceiveChan {
		switch v {
		case 1:
			fmt.Printf("%s: 私はけんとです\n", me.Name)
			v++
			me.BroadcastChan <- v
		case 6:
			fmt.Printf("%s: 私は生ハムが好きです\n", me.Name)
			v++
			me.BroadcastChan <- v
		case 8:
			fmt.Printf("%s: 行きましょう\n", me.Name)
			close(me.BroadcastChan)
		}
	}
}

func ariaki(me *Worker) {
	for v := range me.ReceiveChan {
		switch v {
		case 2:
			fmt.Printf("%s: 私はありあきです\n", me.Name)
			v++
			me.BroadcastChan <- v
		case 4:
			fmt.Printf("%s: 私は日本酒です\n", me.Name)
			v++
			me.BroadcastChan <- v
		}
	}
}
```