## 2020年2月19日水曜日

### 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
}
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})
}
}
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
}

func newRoverDriver(n int, o *occupier) *roverDriver {
r := &roverDriver{
commandc: make(chan command),
n:        n,
o:        o,
}
go r.drive()
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() {
direction := image.Point{X: 1, Y: 0}
updateInterval := time.Second / 2
nextMove := time.After(updateInterval)
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 <-nextMove:
moved := r.o.moveTo(p)
if moved {
fmt.Printf("%v番 %vへ移動\n", r.n, r.o.pos)
}
nextMove = time.After(updateInterval)
}
}
}

func main() {
rand.Seed(time.Now().UnixNano())
rows := 10
cols := 10
gridPtr := newMarsGrid(rows, cols)
roverDrivers := []*roverDriver{}
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))
}
for i := 0; i < 20; i++ {
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 run ./rover.go
1番 (6,0)へ移動
5番 (5,2)へ移動
2番 (9,3)へ移動
3番 (1,7)へ移動
4番 (3,6)へ移動
5番 (6,2)へ移動
4番 (4,6)へ移動
1番 (7,0)へ移動
3番 (2,7)へ移動
3番 (3,7)へ移動
5番 (7,2)へ移動
4番 (5,6)へ移動
3番 (4,7)へ移動
5番 (8,2)へ移動
4番 (6,6)へ移動
4番 (7,6)へ移動
3番 (5,7)へ移動
1番 (6,0)へ移動
5番 (9,2)へ移動
3番 (6,7)へ移動
1番 (5,0)へ移動
4番 (8,6)へ移動
3番 (7,7)へ移動
4番 (9,6)へ移動
1番 (4,0)へ移動
1番 (3,0)へ移動
3番 (8,7)へ移動
1番 (2,0)へ移動
3番 (8,8)へ移動
3番 (8,9)へ移動
1番 (1,0)へ移動
1番 (0,0)へ移動
1番 (0,1)へ移動
1番 (0,2)へ移動
1番 (0,3)へ移動
1番 (0,4)へ移動
1番 (0,5)へ移動
1番 (0,6)へ移動
4番 (9,7)へ移動
4番 (9,8)へ移動
4番 (9,9)へ移動
5番 (9,1)へ移動
5番 (9,0)へ移動
2番 (9,4)へ移動
2番 (9,5)へ移動
2番 (9,6)へ移動
2番 (9,7)へ移動
2番 (9,8)へ移動
1番 (0,7)へ移動
1番 (0,8)へ移動
1番 (0,9)へ移動
% go run ./rover.go
3番 (1,0)へ移動
4番 (2,7)へ移動
5番 (5,7)へ移動
1番 (1,8)へ移動
2番 (5,4)へ移動
2番 (6,4)へ移動
4番 (3,7)へ移動
1番 (2,8)へ移動
3番 (2,0)へ移動
5番 (6,7)へ移動
2番 (7,4)へ移動
4番 (4,7)へ移動
5番 (7,7)へ移動
1番 (3,8)へ移動
3番 (3,0)へ移動
4番 (5,7)へ移動
2番 (7,3)へ移動
1番 (4,8)へ移動
5番 (8,7)へ移動
3番 (4,0)へ移動
2番 (7,2)へ移動
4番 (6,7)へ移動
5番 (9,7)へ移動
3番 (5,0)へ移動
1番 (5,8)へ移動
3番 (6,0)へ移動
1番 (6,8)へ移動
2番 (7,1)へ移動
4番 (7,7)へ移動
4番 (8,7)へ移動
1番 (7,8)へ移動
2番 (7,0)へ移動
2番 (8,0)へ移動
1番 (8,8)へ移動
2番 (9,0)へ移動
1番 (9,8)へ移動
3番 (7,0)へ移動
3番 (8,0)へ移動
4番 (9,7)へ移動
5番 (9,6)へ移動
5番 (9,5)へ移動
5番 (9,4)へ移動
5番 (9,3)へ移動
5番 (9,2)へ移動
5番 (9,1)へ移動
%