chiachan
chiachan
Published on 2020-08-29 / 726 Visits
0

Strategy模式(策略模式)

什么是策略模式?

  • 策略模式即整体地替换算法
  • 能够整体地替换算法,让我们轻松地以不同的算法去解决相一个问题
  • 动态替换

示范代码(strategy.go)

猜拳游戏

package Strategy

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

const (
	// 石头
	HANDVALUE_GUU int = 0
	// 剪刀
	HANDVALUE_CHO int = 1
	// 布
	HANDVALUE_PAA int = 2
)

var (
	hands = []*Hand{
		NewHand(HANDVALUE_GUU),
		NewHand(HANDVALUE_CHO),
		NewHand(HANDVALUE_PAA),
	}
	names = []string{
		"石头",
		"剪刀",
		"布",
	}
)

type Hand struct {
	handvalue int
}

func NewHand(handvalue int) *Hand {
	return &Hand{handvalue: handvalue}
}

func (h *Hand) getHand() *Hand {
	return hands[h.handvalue]
}

// 如果h胜过了_h,返回true
func (h *Hand) isStrongerThan(_h *Hand) bool {
	return h.fight(_h) == 1
}

// 如果h输给了_h,返回true
func (h *Hand) isWeakerThan(_h *Hand) bool {
	return h.fight(_h) == -1
}

// 计分: 平局0 胜利1 失败-1
func (h *Hand) fight(_h *Hand) int {
	if h.handvalue == _h.handvalue {
		return 0
	} else if (h.handvalue+1)%3 == _h.handvalue {
		return 1
	} else {
		return -1
	}
}

func (h *Hand) toString() string {
	return names[h.handvalue]
}

// 猜拳策略的抽闲方法接口
type Strategy interface {
	nextHand() *Hand
	study(win bool)
}

// 策略: 如果上一局的手赢了,则下一局手势与上局相同。否则,随机出手势
type WinningStrategy struct {
	won      bool
	prevHand *Hand
}

func NewWinningStrategy() *WinningStrategy {
	return &WinningStrategy{
		won:      false,
		prevHand: nil,
	}
}

func (s *WinningStrategy) nextHand() *Hand {
	if !s.won {
		rand.Seed(time.Now().UnixNano())
		hand := NewHand(rand.Intn(3))
		s.prevHand = hand.getHand()
	}
	return s.prevHand
}

func (s *WinningStrategy) study(win bool) {
	s.won = win
}

// 根据过去的胜负来进行概率计算
type ProbStratege struct {
	prevHandValue    int
	currentHandValue int
	// history[上一局出的手势][这一局所处的手势]
	history [][]int
}

func NewProbStratege() *ProbStratege {
	return &ProbStratege{
		prevHandValue:    0,
		currentHandValue: 0,
		history: [][]int{
			{1, 1, 1}, // 连续两次出石头胜利次数 | 连续石头、剪刀胜利次数 | 连续石头、布胜利次数
			{1, 1, 1}, // 连续剪刀、石头胜利次数 | 连续两次剪刀胜利次数 | 连续剪刀、布胜利次数
			{1, 1, 1}, // 连续布、石头胜利次数 | 连续布、剪刀胜利次数 | 连续两次布胜利次数
		},
	}
}

func (s *ProbStratege) nextHand() *Hand {
	rand.Seed(time.Now().UnixNano())
	bet := rand.Intn(s.getSum(s.currentHandValue))
	handvalue := 0
	if bet < s.history[s.currentHandValue][0] {
		handvalue = 0
	} else if bet < s.history[s.currentHandValue][0]+s.history[s.currentHandValue][1] {
		handvalue = 1
	} else {
		handvalue = 2
	}
	s.prevHandValue = s.currentHandValue
	s.currentHandValue = handvalue
	hand := NewHand(handvalue)
	return hand.getHand()
}

func (s *ProbStratege) study(win bool) {
	if win {
		s.history[s.prevHandValue][s.currentHandValue]++
	} else {
		s.history[s.prevHandValue][(s.currentHandValue+1)%3]++
		s.history[s.prevHandValue][(s.currentHandValue+2)%3]++
	}
}

func (s *ProbStratege) getSum(hv int) int {
	sum := 0
	for i := 0; i < 3; i++ {
		sum += s.history[hv][i]
	}
	return sum
}

type Player struct {
	name      string
	strategy  Strategy
	winCount  int
	loseCount int
	gameCount int
}

func NewPlayer(name string, strategy Strategy) *Player {
	return &Player{
		name:      name,
		strategy:  strategy,
		winCount:  0,
		loseCount: 0,
		gameCount: 0,
	}
}

func (p *Player) nextHand() *Hand {
	return p.strategy.nextHand()
}

func (p *Player) win() {
	p.strategy.study(true)
	p.gameCount++
	p.winCount++
}

func (p *Player) lose() {
	p.strategy.study(false)
	p.gameCount++
	p.loseCount++
}

func (p *Player) even() {
	p.gameCount++
}

func (p *Player) toString() string {
	return fmt.Sprintf("[%s:%d games,%d win,%d lose]", p.name, p.gameCount, p.winCount, p.loseCount)
}

测试用例(strategy_test.go)

package Strategy

import (
	"fmt"
	"testing"
)

func TestName(t *testing.T) {
	player1 := NewPlayer("Taro", NewWinningStrategy())
	player2 := NewPlayer("Hana", NewProbStratege())
	for i := 0; i < 1000; i++ {
		nextHand1 := player1.nextHand()
		nextHand2 := player2.nextHand()
		if nextHand1.isStrongerThan(nextHand2) {
			fmt.Println("Winner:" + player1.toString())
			player1.win()
			player2.lose()
		} else if nextHand2.isStrongerThan(nextHand1) {
			fmt.Println("Winner:" + player2.toString())
			player1.lose()
			player2.win()
		} else {
			//fmt.Println("Even")
			player1.even()
			player2.even()
		}
	}
	fmt.Println("Total result:")
	fmt.Println(player1.toString())
	fmt.Println(player2.toString())
}