마작 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 는 자주 이렇게 합 니 다).
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
【Codility Lesson3】FrogJmpA small frog wants to get to the other side of the road. The frog is currently located at position X and wants to get to...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.