GoLang写一个火星漫游行动

文章目录

v1

go 复制代码
package main

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

func main() {
	gridSize := image.Point{X: 20, Y: 10}
	grid := NewMarsGrid(gridSize)
	rover := make([]*RoverDriver, 5)
	for i := range rover {
		rover[i] = startDriver(fmt.Sprint("rover", i), grid)
	}
	time.Sleep(10 * time.Second)
}

func startDriver(name string, grid *MarsGrid) *RoverDriver {
	var o *Occupier
	// Try a random point; continue until we've found one that's
	// not currently occupied.
	for o == nil {
		startPoint := image.Point{X: rand.Intn(grid.Size().X), Y: rand.Intn(grid.Size().Y)}
		o = grid.Occupy(startPoint)
	}
	return NewRoverDriver(name, o)
}

// RoverDriver驾驶火星探测车在火星表面行驶。
type RoverDriver struct {
	commandc chan command
	occupier *Occupier
	name     string
}

// NewRoverDriver starts a new RoverDriver and returns it.
func NewRoverDriver(name string, occupier *Occupier) *RoverDriver {
	r := &RoverDriver{
		commandc: make(chan command),
		occupier: occupier,
		name:     name,
	}
	go r.drive()
	return r
}

type command int

const (
	right command = 0
	left  command = 1
)

// drive is responsible for driving the rover. It
// is expected to be started in a goroutine.
func (r *RoverDriver) drive() {
	log.Printf("%s initial position %v", r.name, r.occupier.Pos())
	direction := image.Point{X: 1, Y: 0}
	updateInterval := 250 * time.Millisecond
	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,
				}
			}
			log.Printf("%s new direction %v", r.name, direction)
		case <-nextMove:
			nextMove = time.After(updateInterval)
			newPos := r.occupier.Pos().Add(direction)
			if r.occupier.MoveTo(newPos) {
				log.Printf("%s moved to %v", r.name, newPos)
				// Successfully moved to new position.
				break
			}
			log.Printf("%s blocked trying to move from %v to %v", r.name, r.occupier.Pos(), newPos)
			// Pick one of the other directions randomly.
			// Next time round, we'll try to move in the new
			// direction.
			dir := rand.Intn(3) + 1
			for i := 0; i < dir; i++ {
				direction = image.Point{
					X: -direction.Y,
					Y: direction.X,
				}
			}
			log.Printf("%s new random direction %v", r.name, direction)
		}
	}
}

// Left turns the rover left (90° anticlockwise).
func (r *RoverDriver) Left() {
	r.commandc <- left
}

// Right turns the rover right (90° clockwise).
func (r *RoverDriver) Right() {
	r.commandc <- right
}

// MarsGrid网格用于表示火星的某些表面。它可能会被多个不同的goroutine并发使用
type MarsGrid struct {
	bounds image.Rectangle
	mu     sync.Mutex
	cells  [][]cell
}

// SensorData holds information about whats in
// a point in the grid.
type SensorData struct {
	Life int
}

// 单元格包含有关网格中给定单元格的信息。
type cell struct {
	groundData SensorData
	occupier   *Occupier
}

// NewMarsGrid 返回给定大小的新 MarsGrid。
func NewMarsGrid(size image.Point) *MarsGrid {
	grid := &MarsGrid{
		bounds: image.Rectangle{
			Max: size,
		},
		cells: make([][]cell, size.Y),
	}
	for i := range grid.cells {
		grid.cells[i] = make([]cell, size.X)
	}
	return grid
}

// size 返回网格的大小。
func (g *MarsGrid) Size() image.Point {
	return g.bounds.Max
}

// Occupy 函数会占据网格中指定位置的单元格。如果该点已在网格内或位于网格外,则返回 nil。否则,它会返回一个可用于在网格上移动到不同位置的值。
func (g *MarsGrid) Occupy(p image.Point) *Occupier {
	g.mu.Lock()
	defer g.mu.Unlock()
	cell := g.cell(p)
	if cell == nil || cell.occupier != nil {
		return nil
	}
	cell.occupier = &Occupier{
		grid: g,
		pos:  p,
	}
	return cell.occupier
}

// cell 返回指向给定位置单元格的指针,如果该位置在网格之外,则返回 nil。
func (g *MarsGrid) cell(p image.Point) *cell {
	if !p.In(g.bounds) {
		return nil
	}
	return &g.cells[p.Y][p.X]
}

// 占用者表示网格中已被占用的单元格。
type Occupier struct {
	grid *MarsGrid
	pos  image.Point
}

// MoveTo 函数将占用者移动到网格中的另一个单元格。会报告移动是否成功。移动可能失败,原因可能是尝试移动到网格之外,或者尝试移动到的单元格已被占用。如果失败,占用者将留在原地。
func (o *Occupier) MoveTo(p image.Point) bool {
	o.grid.mu.Lock()
	defer o.grid.mu.Unlock()
	newCell := o.grid.cell(p)
	if newCell == nil || newCell.occupier != nil {
		return false
	}
	o.grid.cell(o.pos).occupier = nil
	newCell.occupier = o
	o.pos = p
	return true
}

// Sense returns sensory data from the current cell.
func (o *Occupier) Sense() SensorData {
	o.grid.mu.Lock()
	defer o.grid.mu.Unlock()
	return o.grid.cell(o.pos).groundData
}

// Pos 返回占用者的当前网格位置
func (o *Occupier) Pos() image.Point {
	return o.pos
}

v2

go 复制代码
package main

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

func main() {
	marsToEarth := make(chan []Message)
	go earthReceiver(marsToEarth)

	gridSize := image.Point{X: 20, Y: 10}
	grid := NewMarsGrid(gridSize)
	rover := make([]*RoverDriver, 5)
	for i := range rover {
		rover[i] = startDriver(fmt.Sprint("rover", i), grid, marsToEarth)
	}
	time.Sleep(60 * time.Second)
}

// 消息中包含一条从火星发送到地球的信息。
type Message struct {
	Pos       image.Point
	LifeSigns int
	Rover     string
}

const (
	// The length of a Mars day.
	dayLength = 24 * time.Second
	// The length of time per day during which
	// messages can be transmitted from a rover to Earth.
	receiveTimePerDay = 2 * time.Second
)

// earthReceiver接收来自火星的消息。由于连接受限,它只能在每个火星日的一段时间内接收消息。
func earthReceiver(msgc chan []Message) {
	for {
		time.Sleep(dayLength - receiveTimePerDay)
		receiveMarsMessages(msgc)
	}
}

// receiveMarsMessages receives messages sent from Mars
// for the given duration.
func receiveMarsMessages(msgc chan []Message) {
	finished := time.After(receiveTimePerDay)
	for {
		select {
		case <-finished:
			return
		case ms := <-msgc:
			for _, m := range ms {
				log.Printf("earth received report of life sign level %d from %s at %v", m.LifeSigns, m.Rover, m.Pos)
			}
		}
	}
}

// 无线电指的是可以向地球发送信息的无线电发射器。
type Radio struct {
	fromRover chan Message
}

// SendToEarth 函数向地球发送消息。它总是立即成功------实际的消息可能会被缓冲,稍后才会真正发送。
func (r *Radio) SendToEarth(m Message) {
	r.fromRover <- m
}

// NewRadio returns a new Radio instance that sends
// messages on the toEarth channel.
func NewRadio(toEarth chan []Message) *Radio {
	r := &Radio{
		fromRover: make(chan Message),
	}
	go r.run(toEarth)
	return r
}

// 运行缓冲区,将探测车发送的消息缓冲到可以发送到地球为止。
func (r *Radio) run(toEarth chan []Message) {
	var buffered []Message
	for {
		toEarth1 := toEarth
		if len(buffered) == 0 {
			toEarth1 = nil
		}
		select {
		case m := <-r.fromRover:
			buffered = append(buffered, m)
		case toEarth1 <- buffered:
			buffered = nil
		}
	}
}

func startDriver(name string, grid *MarsGrid, marsToEarth chan []Message) *RoverDriver {
	var o *Occupier
	// Try a random point; continue until we've found one that's
	// not currently occupied.
	for o == nil {
		startPoint := image.Point{X: rand.Intn(grid.Size().X), Y: rand.Intn(grid.Size().Y)}
		o = grid.Occupy(startPoint)
	}
	return NewRoverDriver(name, o, marsToEarth)
}

// RoverDriver驾驶火星探测车在火星表面行驶。
type RoverDriver struct {
	commandc chan command
	occupier *Occupier
	name     string
	radio    *Radio
}

// NewRoverDriver starts a new RoverDriver and returns it.
func NewRoverDriver(name string, occupier *Occupier, marsToEarth chan []Message) *RoverDriver {
	r := &RoverDriver{
		commandc: make(chan command),
		occupier: occupier,
		name:     name,
		radio:    NewRadio(marsToEarth),
	}
	go r.drive()
	return r
}

type command int

const (
	right command = 0
	left  command = 1
)

// drive is responsible for driving the rover. It
// is expected to be started in a goroutine.
func (r *RoverDriver) drive() {
	log.Printf("%s initial position %v", r.name, r.occupier.Pos())
	direction := image.Point{X: 1, Y: 0}
	updateInterval := 250 * time.Millisecond
	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,
				}
			}
			log.Printf("%s new direction %v", r.name, direction)
		case <-nextMove:
			nextMove = time.After(updateInterval)
			newPos := r.occupier.Pos().Add(direction)
			if r.occupier.MoveTo(newPos) {
				log.Printf("%s moved to %v", r.name, newPos)
				r.checkForLife()
				break
			}
			log.Printf("%s blocked trying to move from %v to %v", r.name, r.occupier.Pos(), newPos)
			// Pick one of the other directions randomly.
			// Next time round, we'll try to move in the new
			// direction.
			dir := rand.Intn(3) + 1
			for i := 0; i < dir; i++ {
				direction = image.Point{
					X: -direction.Y,
					Y: direction.X,
				}
			}
			log.Printf("%s new random direction %v", r.name, direction)
		}
	}
}

func (r *RoverDriver) checkForLife() {
	// Successfully moved to new position.
	sensorData := r.occupier.Sense()
	if sensorData.LifeSigns < 900 {
		return
	}
	r.radio.SendToEarth(Message{
		Pos:       r.occupier.Pos(),
		LifeSigns: sensorData.LifeSigns,
		Rover:     r.name,
	})
}

// Left turns the rover left (90° anticlockwise).
func (r *RoverDriver) Left() {
	r.commandc <- left
}

// Right turns the rover right (90° clockwise).
func (r *RoverDriver) Right() {
	r.commandc <- right
}

// MarsGrid网格用于表示火星的某些表面。它可能会被多个不同的goroutine并发使用
type MarsGrid struct {
	bounds image.Rectangle
	mu     sync.Mutex
	cells  [][]cell
}

// SensorData 存储着网格中某一点的信息。
type SensorData struct {
	LifeSigns int
}

// 单元格包含有关网格中给定单元格的信息。
type cell struct {
	groundData SensorData
	occupier   *Occupier
}

// NewMarsGrid 返回给定大小的新 MarsGrid。
func NewMarsGrid(size image.Point) *MarsGrid {
	grid := &MarsGrid{
		bounds: image.Rectangle{
			Max: size,
		},
		cells: make([][]cell, size.Y),
	}
	for y := range grid.cells {
		grid.cells[y] = make([]cell, size.X)
		for x := range grid.cells[y] {
			cell := &grid.cells[y][x]
			cell.groundData.LifeSigns = rand.Intn(1000)
		}
	}
	return grid
}

// size 返回网格的大小。
func (g *MarsGrid) Size() image.Point {
	return g.bounds.Max
}

// Occupy 函数会占据网格中指定位置的单元格。如果该点已在网格内或位于网格外,则返回 nil。否则,它会返回一个可用于在网格上移动到不同位置的值。
func (g *MarsGrid) Occupy(p image.Point) *Occupier {
	g.mu.Lock()
	defer g.mu.Unlock()
	cell := g.cell(p)
	if cell == nil || cell.occupier != nil {
		return nil
	}
	cell.occupier = &Occupier{
		grid: g,
		pos:  p,
	}
	return cell.occupier
}

// cell 返回指向给定位置单元格的指针,如果该位置在网格之外,则返回 nil。
func (g *MarsGrid) cell(p image.Point) *cell {
	if !p.In(g.bounds) {
		return nil
	}
	return &g.cells[p.Y][p.X]
}

// 占用者表示网格中已被占用的单元格。
type Occupier struct {
	grid *MarsGrid
	pos  image.Point
}

// MoveTo 函数将占用者移动到网格中的另一个单元格。会报告移动是否成功。移动可能失败,原因可能是尝试移动到网格之外,或者尝试移动到的单元格已被占用。如果失败,占用者将留在原地。
func (o *Occupier) MoveTo(p image.Point) bool {
	o.grid.mu.Lock()
	defer o.grid.mu.Unlock()
	newCell := o.grid.cell(p)
	if newCell == nil || newCell.occupier != nil {
		return false
	}
	o.grid.cell(o.pos).occupier = nil
	newCell.occupier = o
	o.pos = p
	return true
}

// Sense 从当前细胞返回感觉数据。
func (o *Occupier) Sense() SensorData {
	o.grid.mu.Lock()
	defer o.grid.mu.Unlock()
	return o.grid.cell(o.pos).groundData
}

// Pos 返回占用者的当前网格位置
func (o *Occupier) Pos() image.Point {
	return o.pos
}

之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持!

相关推荐
i***13241 小时前
【SpringBoot】单元测试实战演示及心得分享
spring boot·后端·单元测试
小尧嵌入式1 小时前
C++中的封装继承多态
开发语言·arm开发·c++
弘毅 失败的 mian1 小时前
Git 基本操作
大数据·经验分享·笔记·git·elasticsearch
csbysj20201 小时前
Redis 配置详解
开发语言
s***35301 小时前
SpringMVC新版本踩坑[已解决]
android·前端·后端
行走在电子领域的工匠1 小时前
台达ST:自定义串行通讯传送与接收指令COMRS程序范例四
开发语言·台达plc·st语言编程
t198751281 小时前
基于因子图与和积算法的MATLAB实现
开发语言·算法·matlab
霸王大陆1 小时前
《零基础学 PHP:从入门到实战》教程-模块四:数组与函数-1
android·开发语言·php
le serein —f1 小时前
用go实现-回文链表
算法·leetcode·golang