마작 Ai 디자인 사고방식 (go 언어 실현)

97500 단어 알고리즘
디자인 아이디어
마작 호 패 규칙 이 고정 되 어 있 기 때문에 3m + 2 (3 은 순자, 각 자 를 가리 키 고 2 는 패 (한 쌍) 를 가리킨다.예 를 들 어 123 만 789 개의 백 백 백 중 에 이 손 패 는 바로 후 패 의 패 형 인 데 그 중에서 m = 3 이다.호 패 의 패 형 을 형성 하려 면 현재 패 형 중의 순자, 각 자 와 쌍, 그리고 성 순자 의 차 이 를 형성 할 수 있 는 패 (예 를 들 어 13, 차 이 는 2, 예 를 들 어 34, 차 이 는 2 또는 5) 를 보존 해 야 한다.이렇게 분 리 된 패 는 한 패 밖 에 남지 않 았 기 때문에 우 리 는 카드 를 낼 때 먼저 한 패 를 친 다음 에 새로운 패 를 만 진 것 에 따라 재 분석 한 다음 에 듣 기 패 형 을 형성 할 수 있다.사건 (터치 바) 이 있 을 때 우 리 는 모든 사건 에 대해 한 번 의 패 형 을 분석 한 다음 에 형 성 된 패 형 에 따라 발생 하 는 단일 패, 그리고 단일 패 를 형성 할 수 있 는 패 의 가중치 비 를 계산 하여 현재 사건 이 발생 한 후에 패 형 변화 에 대한 우선 순 위 를 계산 할 수 있다.(예 를 들 어 단판 권 에 대한 중요 한 점용 비율 이 비교적 크 고 0.6 이상 일 수 있다. 그 다음 에 13 패 의 비율 이 그 다음 에 0.25 이 고 마지막 23 패 는 0.15 일 수 있다).패 형 을 분석 하려 면 우 리 는 패 형 을 위해 구조 체 를 새로 만들어 야 한다.
//      ,         ,          ,            (   23 ,13   ),      ,        
//    (       )
type TileType struct {
	Array_S          []byte //    ,      
	Array_k          []byte //    ,      
	Array_j          []byte //       ,      
	Array_d          []byte //  
	Array_1_3        []byte //    1    ,     
	Array_34         []byte //         ,     
	Array_tile_index []byte //      
	mask             byte   //           。  ,          ,     
	tile             byte   //    
}

위의 구조 체 에서 우 리 는 순자 자리 1 순위 이 고 그 다음 에 야 각 자, 대 자, 34, 13 이다.다음은 카드 형 추출:
//    
func AnaTileType(tileIndexs []byte, mask byte, tile byte) *TileType {
	tt := &TileType{}
	tt.mask = mask
	tt.tile = tile
	tt.Array_tile_index = append(tt.Array_tile_index, tileIndexs...)
	ti := tt.Array_tile_index
	//    123 23  13
	for i := byte(0); i < 7; i++ {
		v := ti[i]
		if v != 0 && ti[i+1] != 0 && ti[i+2] != 0 { //  
			tt.Array_S = append(tt.Array_S, i)
			ti[i]--
			ti[i+1]--
			ti[i+2]--
		}
	}
	//      
	for i, v := range ti {
		if v == 2 {
			tt.Array_j = append(tt.Array_j, byte(i))
			ti[i] = 0
			continue
		}
		if v == 3 {
			tt.Array_k = append(tt.Array_k, byte(i))
			ti[i] = 0
		}
	}
	//      34      
	for i := byte(0); i < 7; i++ {
		if ti[i] != 0 && ti[i+1] != 0 && ti[i+2] == 0 {
			tt.Array_34 = append(tt.Array_34, i)
			ti[i]--
			ti[i+1]--
		}
	}
	//     13      
	for i := byte(0); i < 7; i++ {
		if ti[i] != 0 && ti[i+1] == 0 && ti[i+2] != 0 {
			tt.Array_1_3 = append(tt.Array_1_3, i)
			ti[i]--
			ti[i+2]--
		}
	}
	//    
	for i, v := range ti {
		if v == 1 {
			tt.Array_d = append(tt.Array_d, byte(i))
		}
	}
	share.LOG.Debug("---  :%s", tt.ToString())
	return tt
}


다음은 카드 에 대한 분석 이다.
/*
ai  
1      
2       
3     2         
4     1         
5        4、7      ,          。 2344 ,    
6        3、6、9      ,        ,       ( 233 ,    );           ( 233 ,  2 )
7        2、5、8     ,          。 23445 ,  5 
8          
 */

//       (      :   )
func (this *ErMahjong_AI) PlayLogic(msg *PGAME.Data_Play, play_res *PGAME.Play) {
	t1 := time.Now().Unix()

	//      
	if msg.Ext != nil && len(msg.Ext) != 0 {
		this.hand.leftTiles = msg.Ext
	}
	share.LOG.Debug("-------    :%v", this.hand.leftTiles)

	//    
	if msg.GetDraw() { //   ,       
		share.LOG.Debug("---------  :%d", msg.Tiles.Value[0])
		if !this.isTing { //    ,          
			n := 0
			//         
			if num, found := this.hand.tiles_num[msg.Tiles.GetValue()[0]]; found {
				n = num + 1
			}
			this.hand.tiles_num[msg.Tiles.GetValue()[0]] = n
			this.hand.tiles[msg.Tiles.GetId()[0]] = msg.Tiles.Value[0]
			this.hand.tilesIndex[ValueToIndex(msg.Tiles.Value[0])]++
			share.LOG.Debug("------------    :%v", this.hand.tilesIndex)
		}
	}

	var drawIds []byte
	if msg.GetDraw() {
		drawIds = msg.GetTiles().Id
	}
	//    id
	play_id, tingSend := this.PlayByTing(drawIds)

	share.LOG.Debug("    tile:%v", this.hand.tiles)
	fmt.Println("--------  id:", play_id)
	play_res.Action = PGAME.Action_ACTION_NULL.Enum()
	play_res.Id = proto.Int32(int32(play_id))

	if tingSend {
		play_res.Action = PGAME.Action_ACTION_TING.Enum()
	}
	share.LOG.Debug("          :%d", time.Now().Unix()-t1)
	return
}

카드 를 들 을 지 여부 에 따라 카드 를 내야 합 니 다.
//      ,    id
/*
              ,            
      ,           
params:
	ids:    
returns:
	playId:  id
	tingSend :        
 */
func (this *ErMahjong_AI) PlayByTing(ids []byte) (playId byte, tingSend bool) {
	//      
	if this.isTing {
		playId = ids[0]
	} else { //    ,         
		tingMap := CheckTing(this.hand.tilesIndex)
		index := -1 //    
		if len(tingMap) > 0 { //     ,          ,             
			maxLen := 0
			tingSend = true
			for k, v := range tingMap {
				if len(v) > maxLen {
					index = int(k)
					maxLen = len(v)
				}
			}
			this.isTing = true
			this.tilesTing = tingMap[byte(index)]
			share.LOG.Debug("--------     ,%v", this.tilesTing)
		} else { //    ,           
			this.TileType = AnaTileType(this.hand.tilesIndex, 0, 0)
			//        
			index = PlayByTileType(this.hand.tilesIndex, this.TileType)
		}
		b := byte(index)
		va := IndexToValue(b) //           
		flag := false
		//    ,       id
		for id, v := range this.hand.tiles {
			if v == va {
				playId = id
				flag = true
				break
			}
		}
		if !flag {//             ,       ,             
			if len(ids) ==0 {//      ,           
				maxValue:=byte(0)
				maxId := byte(0)
				for id,v := range this.hand.tiles {
					if v>maxValue {
						maxId = id
					}
				}
				playId = maxId
			}else {
				playId = ids[0]
			}
		}
	}
	return
}

체크 카드:
//    
/**
params:
	tileIndexs:      ,           
returns:
	[]byte:         
 */
func CheckTing(tileIndexs []byte) map[byte][]byte {
	//key-       ,value:       
	tingM := make(map[byte][]byte, 0)
	for i, v := range tileIndexs {
		if v != 0 {
			tileIndexs[i]--
			ts := make([]byte, 0)
			//share.LOG.Debug("------checkTing--i:%d",i)
			//fmt.Println("i:",i)
			//      (            ,        )
			for j := byte(0); j < 34; j++ {
				//             
				if j == 9 {
					j = 27
				}
				if byte(i) == j { //         
					continue
				}
				tileIndexs[j]++
				//share.LOG.Debug("------*(*(**(**&**(**(**:%d",j)
				//fmt.Println("j:",j)

				isHu := AnalyseTilesIndex(tileIndexs, mahjongTable) //      
				if isHu { //    ,     ,        
					ts = append(ts, IndexToValue(j))
				}
				tileIndexs[j]--
			}
			if len(ts) > 0 { //          0,    ,        map ,
				tingM[byte(i)] = ts
			}
			tileIndexs[i]++
		}
	}
	return tingM
}

분석 한 패 형 에 따라 패 를 낸다.
//  TileType    ,      
func PlayByTileType(tilesIndexs []byte, tt *TileType) int {
	share.LOG.Debug("  :%v, :%d,  :%v,34:%v,13:%v,  :%v", tt.Array_S, tt.Array_j, tt.Array_k, tt.Array_34, tt.Array_1_3, tt.Array_d)
	//        
	if len(tt.Array_d) > 0 {
		return int(tt.Array_d[len(tt.Array_d)-1])
	}
	//      ,     13    
	if len(tt.Array_1_3) > 0 {
		return int(tt.Array_1_3[len(tt.Array_1_3)-1])
	}
	//  13   ,    23
	if len(tt.Array_34) > 0 {
		return int(tt.Array_34[len(tt.Array_34)-1])
	}
	//  23  ,     
	if len(tt.Array_j) > 0 {
		return int(tt.Array_j[len(tt.Array_j)-1])
	}
	//        ,          
	i := len(tilesIndexs) - 1
	for ; i >= 0; i-- {
		if tilesIndexs[i] != 0 {
			break
		}
	}
	return i
}

다음은 사건 에 대한 처리 입 니 다.
//event      ,        ,       ,     
/*
	   0x01
	   0x02
	   0x04
	  0x08
	  0x10
  :
	1.        :m*AAA + n*ABC +AA      (         ,          )
	2.         ,        ,      ,       ,          ,        ,         ,    。       1 3、78     5,  78     13   ,     
             。

 */
/**
params
	mask:    
	tileValues           
 */
func (this *ErMahjong_AI) AiEventLogic(mask int32, tileValues []byte) int {
	value := tileValues[0]       //        
	index := ValueToIndex(value) //        
	var handIndexs []byte
	handIndexs = append(handIndexs, this.hand.tilesIndex...)
	resultAn := make(map[byte]*TileType, 0) //           key      ,value TileTYpe  
	i := byte(3)
	iMax := byte(8)
	if this.isTing { //     ,              
		iMax = byte(4)
	}
	for ; i < iMax; i++ {

		if mask&(int32(0x80)>>i) > 0 { //            
			switch i {
			case 3: // 
				share.LOG.Debug("   ,   :%d", value)
				handIndexs[index] = 0
				//    
				tt := AnaTileType(handIndexs, 0x10, value)
				handIndexs[index] = 3
				resultAn[0x10] = tt
			case 4: // 
				share.LOG.Debug("   ,   :%d", value)
				handIndexs[index] = 0
				tt := AnaTileType(handIndexs, 0x08, value)
				handIndexs[index] = 2
				resultAn[0x08] = tt
			case 5: //  
				share.LOG.Debug("    ,    :%d", value)
				handIndexs[index] --
				handIndexs[index-1] --
				handIndexs[index-2]--
				tt := AnaTileType(handIndexs, 0x04, value)
				handIndexs[index] ++
				handIndexs[index-1]++
				handIndexs[index-2]++
				resultAn[0x04] = tt
			case 6: //  
				share.LOG.Debug("    ,    :%d", value)
				handIndexs[index] --
				handIndexs[index-1] --
				handIndexs[index+1]--
				tt := AnaTileType(handIndexs, 0x02, value)
				handIndexs[index] ++
				handIndexs[index-1] ++
				handIndexs[index+1]++
				resultAn[0x02] = tt
			case 7: //  
				share.LOG.Debug("    ,    :%d", value)
				handIndexs[index] --
				handIndexs[index+1] --
				handIndexs[index+2]--
				tt := AnaTileType(handIndexs, 0x01, value)
				handIndexs[index] ++
				handIndexs[index+1] ++
				handIndexs[index+2]++
				resultAn[0x01] = tt
			}
		}
	}
	return int(AnalysisEventTileTypeMap(resultAn, this.TileType))

}

//           TileType
/**
params
	tts:          map  
	lastTileType :         
returns
	byte:           
 */
func AnalysisEventTileTypeMap(tts map[byte]*TileType, lastTileType *TileType) byte {
	//            ,  ,         
	/**
		             0  23、13     0,
	                
		      ,    、13、23                    ,  :0.5,0.3,0.2
	        : 0x5*     + 0.3*13   + 0.2*23  
	        
	 */
	//           
	lastQ := 0.5*float32(len(lastTileType.Array_d)) + 0.3*float32(len(lastTileType.Array_1_3)) + 0.2*float32(len(lastTileType.Array_34))
	share.LOG.Debug("    :%d", lastQ)
	//        
	resultMask := byte(0x00)
	//          
	for mask, v := range tts {
		nowQ := 0.5*float32(len(v.Array_d)) + 0.3*float32(len(v.Array_1_3)) + 0.2*float32(len(v.Array_34))
		share.LOG.Debug("      :%d,       :%d", nowQ, lastQ)
		if nowQ <= lastQ {
			resultMask = mask
			lastQ = nowQ
		}
	}
	share.LOG.Debug("----------ailogic:      :%d",resultMask)
	return resultMask
}


다음은 도구 입 니 다.
//    tile_num       
func mapToIndex(tile_num map[byte]int) []byte {
	tiles := make([]byte, 0)
	for value, num := range tile_num {
		for i := 0; i < num; i++ {
			tiles = append(tiles, value)
		}
	}
	return ToIndexArray(tiles)
}

//       
func IndexToValues(indexs []byte) ([]byte) {
	res := make([]byte, 0)
	for i := byte(0); i < byte(len(indexs)); i++ {
		v := indexs[i]
		if v == 0 {
			continue
		}
		for j := byte(0); j < v; j++ {
			if i < 27 {
				res = append(res, (i/9)<<4|(i%9+1))
			} else {
				res = append(res, byte(0x30|(i-27+1)))
			}
		}

	}
	return res
}

//     
func IndexToValue(index byte) (byte) {
	if index < 27 {
		var color byte = index / 9
		var color2 byte = color << 4
		var val byte = index%9 + 1
		var ret = color2 | val
		return ret
	} else {
		return byte(0x30 | (index - 27 + 1))
	}
}

//     
func ValueToIndex(tile byte) (byte) {
	return ((tile&0xF0)>>4)*9 + (tile & 0x0F) - 1
}

//         
func ToIndexArray(array []byte) ([]byte) {
	var indexArray [34]byte
	for _, v := range array {
		i := ((v&0xF0)>>4)*9 + (v & 0x0F) - 1
		indexArray[i] ++
	}
	return indexArray[:]
}

//    ,        
/**
params:
	tileIndexs:         
	table;     
 */
func AnalyseTilesIndex(tileIndexs []byte, table map[uint32][]uint32) (bool) {
	pos := make([]int, 14)
	key := CalCulateKey(tileIndexs, pos)
	_, found := table[key]
	return found
}

//    key
func CalCulateKey(tiles []byte, pos []int) uint32 {
	p := -1
	var x uint32 = 0
	pos_p := 0
	b := false
	for i := 0; i < 3; i++ {
		for j := 0; j < 9; j++ {
			if tiles[i*9+j] == 0 {
				if b {
					b = false
					x |= 0x1 << uint(p)
					p++
				}
			} else {
				p++
				b = true
				pos[pos_p] = i*9 + j
				pos_p ++
				switch tiles[i*9+j] {
				case 2:
					x |= 0x3 << uint(p)
					p += 2
				case 3:
					x |= 0xF << uint(p)
					p += 4
				case 4:
					x |= 0x3F << uint(p)
					p += 6
				}
			}
		}
		if b {
			b = false
			x |= 0x1 << uint(p)
			p++
		}
	}
	for i := 27; i <= 33; i ++ {
		if tiles[i] > 0 {
			p ++
			pos[pos_p] = i
			pos_p ++
			switch tiles[i] {
			case 2:
				x |= 0x3 << uint(p)
				p += 2
			case 3:
				x |= 0xF << uint(p)
				p += 4
			case 4:
				x |= 0x3F << uint(p)
				p += 6
			}
			x |= 0x1 << uint(p)
			p ++
		}
	}
	return x
}

//          
func MahjongSToChinese(tiles []byte) string {
	str := ""
	for _, v := range tiles {
		cbValue := int(v & 0x0F)
		cbColor := int((v & 0xF0) >> 4)
		if cbColor < 3 {
			str += " " + fmt.Sprintf("%s%s", ValueToChinese(cbValue), ColorToChinese(cbColor))
		} else {
			str += " " + HonorToChinese(cbValue)
		}
	}
	return str
}

//           
func MahjongToChinese(tile byte) string {
	cbValue := int(tile & 0x0F)
	cbColor := int((tile & 0xF0) >> 4)
	if cbColor < 3 {
		return fmt.Sprintf("%s%s ", ValueToChinese(cbValue), ColorToChinese(cbColor))
	} else {
		return HonorToChinese(cbValue)
	}
}

func ValueToChinese(v int) string {
	switch v {
	case 1:
		return " "
	case 2:
		return " "
	case 3:
		return " "
	case 4:
		return " "
	case 5:
		return " "
	case 6:
		return " "
	case 7:
		return " "
	case 8:
		return " "
	case 9:
		return " "
	}
	return " "
}

func ColorToChinese(c int) string {
	switch c {
	case 0:
		return " "
	case 1:
		return " "
	case 2:
		return " "
	}
	return " "
}

func HonorToChinese(h int) string {
	switch h {
	case 1:
		return " "
	case 2:
		return " "
	case 3:
		return " "
	case 4:
		return " "
	case 5:
		return " "
	case 6:
		return " "
	case 7:
		return " "
	}
	return " "
}

테스트 를 통 해 ai 대 인류 경기 에서 ai 승 률 은 기본적으로 65 이상이다.이상 의 알고리즘 에는 데스크 톱 에 이미 나 온 카드 에 대한 분석 이 추가 되 지 않 았 습 니 다. 그 다음 에 우 리 는 데스크 톱 에 나 온 카드 를 분석 할 수 있 습 니 다. 예 를 들 어 카드 를 분석 할 때 나머지 가 비교적 많은 것 을 선택 할 수 있 습 니 다.이것 은 로봇 의 난이도 가 높 습 니 다. 난이 도 를 강화 하려 면 게임 서버 논리 에서 카드 를 보 내 는 것 을 제어 할 수 있 고 게이머 의 승 패 를 제어 할 수 있 습 니 다 (물론 이것 은 비교적 저렴 합 니 다. 특정한 t 는 자주 이렇게 합 니 다).

좋은 웹페이지 즐겨찾기