## 2020年2月20日木曜日

### Go - 並行プログラミング - チャレンジ:火星で生きるもの - 発見を報告する、ゴルーチン、syncパッケージ、Mutex型、Lock関数、Unlock関数、defer

コード

```package main

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

type marsGrid struct {
mu         sync.Mutex
rows, cols int
cells      [][]cell
}
type cell struct {
o    *occupier
life int
}
type occupier struct {
gridPtr *marsGrid
pos     image.Point
}

func newMarsGrid(rows, cols int) *marsGrid {
cells := [][]cell{}
for i := 0; i < rows; i++ {
cells = append(cells, []cell{})
for j := 0; j < cols; j++ {
cells[i] = append(cells[i], cell{o: nil, life: rand.Intn(1001)})
}
}
return &marsGrid{rows: rows, cols: cols, cells: cells}
}

func (g *marsGrid) in(p image.Point) bool {
return p.X >= 0 && p.X < g.cols && p.Y >= 0 && p.Y < g.rows
}
func (g *marsGrid) cell(p image.Point) *cell {
if !g.in(p) {
return nil
}
return &g.cells[p.Y][p.X]
}
func (g *marsGrid) occupy(p image.Point) *occupier {
defer g.mu.Unlock()
g.mu.Lock()
cellPtr := g.cell(p)
if cellPtr == nil || cellPtr.o != nil {
return nil
}
cellPtr.o = &occupier{gridPtr: g, pos: p}
return cellPtr.o
}

func (o *occupier) moveTo(p image.Point) bool {
defer o.gridPtr.mu.Unlock()
o.gridPtr.mu.Lock()
cellPtr := o.gridPtr.cell(p)
if cellPtr == nil || cellPtr.o != nil {
return false
}
o.gridPtr.cell(o.pos).o = nil
cellPtr.o = o
o.pos = p
return true
}

type command int

const (
right = command(0)
left  = command(1)
start = command(2)
stop  = command(3)
)

type roverDriver struct {
commandc chan command
n        int
o        *occupier
working  bool
}

func newRoverDriver(n int, o *occupier, msgc chan string) *roverDriver {
r := &roverDriver{
commandc: make(chan command),
n:        n,
o:        o,
}
go r.drive(msgc)
return r
}

func (r *roverDriver) right() {
r.commandc <- right
}
func (r *roverDriver) left() {
r.commandc <- left
}
func (r *roverDriver) start() {
r.commandc <- start
}
func (r *roverDriver) stop() {
r.commandc <- stop
}
func (r *roverDriver) drive(msgc chan string) {
direction := image.Point{X: 1, Y: 0}
updateInterval := time.Second
nextMove := time.After(updateInterval / 2)
for {
select {
case c := <-r.commandc:
switch c {
case right:
direction = image.Point{
X: -direction.Y,
Y: direction.X,
}
case left:
direction = image.Point{
X: direction.Y,
Y: -direction.X,
}
case start:
r.working = true
default:
r.working = false
}
case <-nextMove:
if r.working {
moved := r.o.moveTo(p)
if moved {
fmt.Printf("%v号 %vへ移動\n", r.n, r.o.pos)
life := r.o.gridPtr.cell(p).life
if life >= 900 {
msgc <- fmt.Sprintf(
"%v号 位置%vに生命が存在する可能性があります。生命値%v",
r.n, r.o.pos, life)
}
}
}
nextMove = time.After(updateInterval)
}
}
}

time.Sleep(time.Second * 10)
fmt.Println(<-msgc)
}
func main() {
rand.Seed(time.Now().UnixNano())
rows := 50
cols := 50
gridPtr := newMarsGrid(rows, cols)
roverDrivers := []*roverDriver{}
msgc := make(chan string)

for i := 1; i < 6; i++ {
var o *occupier = nil
for o == nil {
y := rand.Intn(rows)
x := rand.Intn(cols)
o = gridPtr.occupy(image.Point{X: x, Y: y})
}
roverDrivers = append(roverDrivers, newRoverDriver(i, o, msgc))
}
for {
r := roverDrivers[rand.Intn(len(roverDrivers))]
switch command(rand.Intn(4)) {
case right:
r.right()
case left:
r.left()
case start:
r.start()
default:
r.stop()
}
time.Sleep(time.Second)
}
}
```

```% go build rover.go
% ./rover
3号 (45,2)へ移動
3号 (45,1)へ移動
3号 (45,0)へ移動
3号 位置(45,0)に生命が存在する可能性があります。生命値951
3号 (44,0)へ移動
2号 (47,9)へ移動
3号 (43,0)へ移動
2号 (46,9)へ移動
3号 (42,0)へ移動
2号 (45,9)へ移動
3号 (42,1)へ移動
2号 (44,9)へ移動
3号 (41,1)へ移動
3号 (40,1)へ移動
3号 (39,1)へ移動
3号 (38,1)へ移動
3号 (37,1)へ移動
3号 (36,1)へ移動
3号 (35,1)へ移動
3号 (34,1)へ移動
3号 (33,1)へ移動
^C
% ./rover
4号 (30,48)へ移動
4号 (31,48)へ移動
3号 (16,5)へ移動
4号 (31,47)へ移動
4号 (31,46)へ移動
4号 (31,45)へ移動
4号 (31,44)へ移動
4号 (31,43)へ移動
4号 (31,42)へ移動
3号 位置(16,5)に生命が存在する可能性があります。生命値956
3号 (16,6)へ移動
3号 (16,7)へ移動
3号 (16,8)へ移動
1号 (28,4)へ移動
1号 (29,4)へ移動
1号 (30,4)へ移動
^C
%
```