最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

日麻折騰筆記Java篇(2)-向聽數(shù)和牌效率

2020-11-01 21:58 作者:天羽ちよこ  | 我要投稿

思考

向聽數(shù)比較通俗易懂的解釋是還差幾張牌聽牌,用程序能理解的話就是:

1.?將手牌的n張牌替換成任意一張牌,能夠滿足聽牌的n的最小值
2.?此時,手牌就是n向聽

向聽數(shù)可以由下面的公式快速算出(暫時不考慮七對和國士):

向聽數(shù)=8-2x(順子數(shù)量+刻子數(shù)量)-對子數(shù)量-搭子數(shù)量

所以只需要算出順子、刻子、對子以及搭子的數(shù)量即可

我在這里將對子單獨從搭子中列出來,本文以及本系列文章所說的“搭子”均不包含“對子”

但是要注意的是,很多牌形是有多種拆分方式的,諸如下面的示例

1234s

可以認(rèn)為是123的順子加上孤張4,也可以認(rèn)為是1的孤張加上234的順子,還可以認(rèn)為是1234兩個搭子……


我完善了上一章中的代碼,將對子、刻子、順子等概念抽象成類,每種拆分情況都由這四種組成,部分示例代碼如下

package io.****.mj.util.analyze;


import io.****.mj.util.HandPai;

import java.util.ArrayList;

import java.util.List;


/**

?* 手牌分解的每種組合

?* 每種組合都由a個順子、b個刻子、c個對子、d個搭子、e個孤張組合而成

?*/

public class SplitCondition {

? ? private List<Duizi> duiziList = new ArrayList<>();

? ? private List<Shunzi> shunziList = new ArrayList<>();

? ? private List<Dazi> daziList = new ArrayList<>();

? ? private List<Kezi> keziList = new ArrayList<>();

? ? private List<HandPai> guzhangList = new ArrayList<>();


? ? public int xiangTing() {

? ? ? ? return 8 - 2 * (keziList.size() + shunziList.size()) - daziList.size() - duiziList.size();

? ? }

? ? public void addDuizi(Duizi duizi) {

? ? ? ? duiziList.add(duizi);

? ? }

? ? public void addShunzi(Shunzi shunzi) {

? ? ? ? shunziList.add(shunzi);

? ? }

? ? public void addDazi(Dazi dazi) {

? ? ? ? daziList.add(dazi);

? ? }

? ? public void addKezi(Kezi kezi) {

? ? ? ? keziList.add(kezi);

? ? }

? ? public void addGuzhang(HandPai guzhang) {

? ? ? ? guzhangList.add(guzhang);

? ? }

? ? public List<Duizi> getDuiziList() {

? ? ? ? return duiziList;

? ? }

? ? public List<Shunzi> getShunziList() {

? ? ? ? return shunziList;

? ? }

? ? public List<Dazi> getDaziList() {

? ? ? ? return daziList;

? ? }

? ? public List<Kezi> getKeziList() {

? ? ? ? return keziList;

? ? }

? ? public List<HandPai> getGuzhangList() {

? ? ? ? return guzhangList;

? ? }


? ? public void setGuzhangList(List<HandPai> guzhangList) {

? ? ? ? this.guzhangList = guzhangList;

? ? }

? ? public SplitCondition copy() {

? ? ? ? SplitCondition condition = new SplitCondition();

? ? ? ? condition.duiziList = new ArrayList<>(getDuiziList());

? ? ? ? condition.daziList = new ArrayList<>(getDaziList());

? ? ? ? condition.keziList = new ArrayList<>(getKeziList());

? ? ? ? condition.shunziList = new ArrayList<>(getShunziList());

? ? ? ? return condition;

? ? }

? ? @Override

? ? public String toString() {

? ? ? ? return "[" +

? ? ? ? ? ? ? ? duiziList +

? ? ? ? ? ? ? ? shunziList +

? ? ? ? ? ? ? ? daziList +

? ? ? ? ? ? ? ? keziList +

? ? ? ? ? ? ? ? guzhangList + "]";

? ? }

}

package io.****.mj.util.analyze;


import io.****.mj.util.HandAnalyze;

import io.****.mj.util.HandPai;

import io.****.mj.util.Pai;


import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

import java.util.stream.Collectors;


public class PaiSplitter {

? ? public static List<SplitCondition> split(String str) {

? ? ? ? List<HandPai> handPais = HandAnalyze.strToHandPai(str);

? ? ? ? List<SplitCondition> conditions = new ArrayList<>();

? ? ? ? return split(handPais, conditions, new SplitCondition(), 0);

? ? }


? ? private static List<SplitCondition> split(List<HandPai> handPais, List<SplitCondition> conditions, SplitCondition condition, int depth) {

? ? ? ? if (handPais.size() <= 1) {

? ? ? ? ? ? conditions.add(condition);

? ? ? ? ? ? return conditions;

? ? ? ? }

? ? ? ? HandPai current = null;

? ? ? ? HandPai next = null;

? ? ? ? HandPai nextNext = null;

? ? ? ? current = handPais.get(0);

? ? ? ? next = handPais.get(1);

? ? ? ? if (0 != handPais.size() - 2) {

? ? ? ? ? ? nextNext = handPais.get(2);

? ? ? ? }

? ? ? ? // 處理對子

? ? ? ? if (current.getPai() == next.getPai()) {

? ? ? ? ? ? handleDuizi(handPais, conditions, condition.copy(), current, next, depth + 1);

? ? ? ? }

? ? ? ? // 處理刻子

? ? ? ? if (nextNext != null) {

? ? ? ? ? ? if (current.getPai() == next.getPai() && next.getPai() == nextNext.getPai()) {

? ? ? ? ? ? ? ? handleKezi(handPais, conditions, condition.copy(), current, next, nextNext, depth + 1);

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? Pai currentPlusOne = current.getPai().next(false);

? ? ? ? if (currentPlusOne != null) {

? ? ? ? ? ? List<HandPai> filtered = handPais.stream().filter(p -> p.getPai() == currentPlusOne).collect(Collectors.toList());

? ? ? ? ? ? if (filtered.size() > 0) {

? ? ? ? ? ? ? ? HandPai second = filtered.get(0);

? ? ? ? ? ? ? ? handleDazi(handPais, conditions, condition.copy(), current, second, depth + 1);

? ? ? ? ? ? ? ? Pai currentPlusTwo = currentPlusOne.next(false);

? ? ? ? ? ? ? ? filtered = handPais.stream().filter(p -> p.getPai() == currentPlusTwo).collect(Collectors.toList());

? ? ? ? ? ? ? ? if (filtered.size() > 0) {

? ? ? ? ? ? ? ? ? ? HandPai third = filtered.get(0);

? ? ? ? ? ? ? ? ? ? handleShunzi(handPais, conditions, condition.copy(), current, second, third, depth + 1);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? handleGuzhang(handPais, conditions, condition.copy(), current, depth + 1);

? ? ? ? return conditions;

? ? }



? ? private static void handleGuzhang(List<HandPai> currentHandPai, List<SplitCondition> conditions, SplitCondition condition, HandPai first, int depth) {

? ? ? ? currentHandPai = new ArrayList<>(currentHandPai);

? ? ? ? condition.addGuzhang(first);

? ? ? ? currentHandPai.remove(first);

? ? ? ? split(currentHandPai, conditions, condition, depth);

? ? }


? ? private static void handleDazi(List<HandPai> currentHandPai, List<SplitCondition> conditions, SplitCondition condition, HandPai first, HandPai second, int depth) {

? ? ? ? currentHandPai = new ArrayList<>(currentHandPai);

? ? ? ? Dazi dazi = new Dazi(Arrays.asList(first, second));

? ? ? ? condition.addDazi(dazi);

? ? ? ? currentHandPai.remove(first);

? ? ? ? currentHandPai.remove(second);

? ? ? ? split(currentHandPai, conditions, condition, depth);

? ? }


? ? private static void handleDuizi(List<HandPai> currentHandPai, List<SplitCondition> conditions, SplitCondition condition, HandPai first, HandPai second, int depth) {

? ? ? ? currentHandPai = new ArrayList<>(currentHandPai);

? ? ? ? Duizi duizi = new Duizi(Arrays.asList(first, second));

? ? ? ? condition.addDuizi(duizi);

? ? ? ? currentHandPai.remove(first);

? ? ? ? currentHandPai.remove(second);

? ? ? ? split(currentHandPai, conditions, condition, depth);

? ? }


? ? private static void handleShunzi(List<HandPai> currentHandPai, List<SplitCondition> conditions, SplitCondition condition, HandPai first, HandPai second,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?HandPai third, int depth) {

? ? ? ? currentHandPai = new ArrayList<>(currentHandPai);

? ? ? ? Shunzi shunzi = new Shunzi(Arrays.asList(first, second, third));

? ? ? ? condition.addShunzi(shunzi);

? ? ? ? currentHandPai.remove(first);

? ? ? ? currentHandPai.remove(second);

? ? ? ? currentHandPai.remove(third);

? ? ? ? split(currentHandPai, conditions, condition, depth);

? ? }


? ? private static void handleKezi(List<HandPai> currentHandPai, List<SplitCondition> conditions, SplitCondition condition, HandPai first, HandPai second,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?HandPai third, int depth) {

? ? ? ? currentHandPai = new ArrayList<>(currentHandPai);

? ? ? ? Kezi kezi = new Kezi(Arrays.asList(first, second, third));

? ? ? ? condition.addKezi(kezi);

? ? ? ? currentHandPai.remove(first);

? ? ? ? currentHandPai.remove(second);

? ? ? ? currentHandPai.remove(third);

? ? ? ? split(currentHandPai, conditions, condition, depth);

? ? }

}


相關(guān)測試代碼

@Test
public void splitDuizi() {
? ?List<SplitCondition> split = PaiSplitter.split("11223344556677s");
? ?Assert.assertTrue(split.stream().anyMatch(c -> c.getDuiziList().size() == 7));
}

@Test
public void testHe() {
? ?List<SplitCondition> split = PaiSplitter.split("11223344556677s");
? ?Assert.assertTrue(split.stream().mapToInt(SplitCondition::xiangTing)
? ? ? ? ? ?.min().getAsInt() < 1);
}

@Test
public void testKeziAndShunzi() {
? ?List<SplitCondition> split = PaiSplitter.split("11155s134m579p122z");
? ?Assert.assertEquals(3, split.stream().mapToInt(SplitCondition::xiangTing)
? ? ? ? ? ?.min().getAsInt());
}


牌效率是什么

經(jīng)營自己的手牌,以達(dá)成和牌為目標(biāo)
我們的任何操作(切牌、碰、吃等)都是為了最終能夠和牌服務(wù)的,也就是提高和了率。

在只考慮自摸的情況下,我們可以快速的計算出打哪張牌能夠使向聽數(shù)前進(jìn)、有效牌和改良牌變多。

有效牌

能夠減少向聽數(shù)的牌

通過向聽數(shù)的計算公式8-2*(面子數(shù))-對子數(shù)-搭子數(shù),我們發(fā)現(xiàn),能夠減少向聽數(shù)的牌有以下幾種:

  1. 能夠與搭子組成一個順子,此時面子數(shù)量+1,搭子數(shù)量-1,向聽數(shù)+1

  2. 能夠與對子組成一個刻子,此時面子數(shù)量+1,對子數(shù)量-1,向聽數(shù)+1

  3. 能夠與孤張組成一個對子,此時對子數(shù)量+1,向聽數(shù)+1

  4. 能夠與孤張組成一個搭子,此時搭子數(shù)量+1,向聽數(shù)+1

這樣一來就很清晰了,我們來編碼,計算以下兩種情況

  1. 在向聽數(shù)減少的情況下,打出哪張牌后的有效牌最多

  2. 在打出任何牌都不能導(dǎo)致向聽數(shù)減少的情況下,打出哪張牌后的有效牌最多(也就是指改良)

編碼

我們需要有個工具來記錄當(dāng)前玩家視角內(nèi)牌的數(shù)量情況:

/**

?* 對局中牌出現(xiàn)數(shù)量的計數(shù)器

?*/

public class PaiCounter {

? ? private Map<Pai, AtomicInteger> paiCount = new HashMap<>();


? ? public PaiCounter() {

? ? ? ? Pai.ALL.forEach(p -> {

? ? ? ? ? ? paiCount.put(p, new AtomicInteger(0));

? ? ? ? });

? ? }


? ? public PaiCounter(Map<Pai, AtomicInteger> paiCount) {

? ? ? ? this.paiCount = paiCount;

? ? }

? ? /**

? ? ?* 查詢當(dāng)前玩家視角內(nèi)某張牌已經(jīng)出現(xiàn)的數(shù)量

? ? ?* @param pai 需要查詢的牌

? ? ?* @return 數(shù)量

? ? ?*/

? ? public int getCount(Pai pai) {

? ? ? ? return paiCount.computeIfAbsent(pai, p -> new AtomicInteger(0)).get();

? ? }

? ? /**

? ? ?* 玩家新摸到、對手打出、或者吃碰杠等操作,導(dǎo)致當(dāng)前玩家視角內(nèi)出現(xiàn)新的牌,請調(diào)用此方法計數(shù)

? ? ?*

? ? ?* @param pai 新出現(xiàn)的牌

? ? ?*/

? ? public void addCount(Pai pai) {

? ? ? ? paiCount.computeIfAbsent(pai, p -> new AtomicInteger(0)).incrementAndGet();

? ? }

}


計算當(dāng)前手牌打出哪張后的有效進(jìn)張

/**
* 計算有效牌的工具類
*/
public class EffectSelector {
? ?public static List<EffectCondition> calculate(String pais, PaiCounter counter) {
? ? ? ?List<HandPai> handPais = HandAnalyze.strToHandPai(pais);
? ? ? ?handPais.forEach(p -> {
? ? ? ? ? ?counter.addCount(p.getPai());
? ? ? ?});
? ? ? ?return handPais.stream()
? ? ? ? ? ? ? ?.map(p -> {
? ? ? ? ? ? ? ? ? ?EffectCondition condition = new EffectCondition();
? ? ? ? ? ? ? ? ? ?condition.setThro(p);
? ? ? ? ? ? ? ? ? ?List<HandPai> tmp = new ArrayList<>(handPais);
? ? ? ? ? ? ? ? ? ?tmp.remove(p);
? ? ? ? ? ? ? ? ? ?List<SplitCondition> split = PaiSplitter.split(tmp, 0);
? ? ? ? ? ? ? ? ? ?// 過濾出向聽數(shù)最少的
? ? ? ? ? ? ? ? ? ?int min = split.stream().mapToInt(SplitCondition::xiangTing).min().getAsInt();
? ? ? ? ? ? ? ? ? ?split = split.stream()
? ? ? ? ? ? ? ? ? ? ? ? ? ?.filter(d -> d.xiangTing() == min)
? ? ? ? ? ? ? ? ? ? ? ? ? ?.collect(Collectors.toList());
? ? ? ? ? ? ? ? ? ?Map<Pai, Integer> collect = calculate(split)
? ? ? ? ? ? ? ? ? ? ? ? ? ?.stream().map(a -> new AbstractMap.SimpleEntry<>(a, 4 - counter.getCount(a)))
? ? ? ? ? ? ? ? ? ? ? ? ? ?.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
? ? ? ? ? ? ? ? ? ?condition.setEffectivePais(new TreeMap<>(collect));
? ? ? ? ? ? ? ? ? ?return condition;
? ? ? ? ? ? ? ?}).sorted((o1, o2) -> o2.getEffectivePais().values().stream().mapToInt(d -> d).sum()
? ? ? ? ? ? ? ? ? ? ? ?- o1.getEffectivePais().values().stream().mapToInt(d -> d).sum()).collect(Collectors.toList());
? ?}

? ?public static List<Pai> calculate(List<SplitCondition> conditions) {
? ? ? ?return conditions.stream().map(d -> {
? ? ? ? ? ?List<Pai> daziData = d.getDaziList().stream()
? ? ? ? ? ? ? ? ? ?.flatMap(m -> {
? ? ? ? ? ? ? ? ? ? ? ?List<HandPai> tmp = m.getData();
? ? ? ? ? ? ? ? ? ? ? ?Collections.sort(tmp);
? ? ? ? ? ? ? ? ? ? ? ?HandPai first = m.getData().get(0);
? ? ? ? ? ? ? ? ? ? ? ?HandPai second = m.getData().get(1);
? ? ? ? ? ? ? ? ? ? ? ?List<Pai> res = new ArrayList<>();
? ? ? ? ? ? ? ? ? ? ? ?if (first.getPai().getNumber() == second.getPai().getNumber() - 1) {
? ? ? ? ? ? ? ? ? ? ? ? ? ?res.add(first.getPai().prev(false));
? ? ? ? ? ? ? ? ? ? ? ? ? ?res.add(second.getPai().next(false));
? ? ? ? ? ? ? ? ? ? ? ?} else {
? ? ? ? ? ? ? ? ? ? ? ? ? ?res.add(first.getPai().next(false));
? ? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? ? ? ?return res.stream().filter(Objects::nonNull);
? ? ? ? ? ? ? ? ? ?}).collect(Collectors.toList());
? ? ? ? ? ?List<Pai> duiziData = d.getDuiziList()
? ? ? ? ? ? ? ? ? ?.stream()
? ? ? ? ? ? ? ? ? ?.map(a -> a.getData().get(0).getPai()).collect(Collectors.toList());
? ? ? ? ? ?List<Pai> guzhangData = d.getGuzhangList()
? ? ? ? ? ? ? ? ? ?.stream()
? ? ? ? ? ? ? ? ? ?.flatMap(a -> {
? ? ? ? ? ? ? ? ? ? ? ?if (a.getPai().getType() == Type.z)
? ? ? ? ? ? ? ? ? ? ? ? ? ?return Stream.of(a.getPai());
? ? ? ? ? ? ? ? ? ? ? ?else
? ? ? ? ? ? ? ? ? ? ? ? ? ?return Stream.of(a.getPai(), a.getPai().next(false), a.getPai().prev(false));
? ? ? ? ? ? ? ? ? ?})
? ? ? ? ? ? ? ? ? ?.filter(Objects::nonNull).collect(Collectors.toList());
? ? ? ? ? ?return Stream.of(daziData, duiziData)
? ? ? ? ? ? ? ? ? ?.flatMap(List::stream);
? ? ? ?}).flatMap(d -> d).distinct().collect(Collectors.toList());
? ?}
}


測試代碼

EffectSelector
? ? ? ?.calculate("34677m67p22577s27z", new PaiCounter())
? ? ? ?.forEach(System.out::println);

輸出如下

打=2z 向聽數(shù)=3 有效牌=30 {2m=4, 5m=4, 7m=2, 8m=4, 5p=4, 8p=4, 2s=2, 6s=4, 7s=2}

打=7z 向聽數(shù)=3 有效牌=30 {2m=4, 5m=4, 7m=2, 8m=4, 5p=4, 8p=4, 2s=2, 6s=4, 7s=2}

打=7m 向聽數(shù)=3 有效牌=28 {2m=4, 5m=4, 8m=4, 5p=4, 8p=4, 2s=2, 6s=4, 7s=2}

打=7m 向聽數(shù)=3 有效牌=28 {2m=4, 5m=4, 8m=4, 5p=4, 8p=4, 2s=2, 6s=4, 7s=2}

打=7s 向聽數(shù)=3 有效牌=28 {2m=4, 5m=4, 7m=2, 8m=4, 5p=4, 8p=4, 2s=2, 6s=4}

打=7s 向聽數(shù)=3 有效牌=28 {2m=4, 5m=4, 7m=2, 8m=4, 5p=4, 8p=4, 2s=2, 6s=4}

打=6m 向聽數(shù)=3 有效牌=26 {2m=4, 5m=4, 7m=2, 5p=4, 8p=4, 2s=2, 6s=4, 7s=2}

打=5s 向聽數(shù)=3 有效牌=26 {2m=4, 5m=4, 7m=2, 8m=4, 5p=4, 8p=4, 2s=2, 7s=2}

打=3m 向聽數(shù)=3 有效牌=22 {5m=4, 7m=2, 5p=4, 8p=4, 2s=2, 6s=4, 7s=2}

天鳳牌理

我們用天鳳牌理來驗證一下我們的程序是否正確:


可以看到是一致的


日麻折騰筆記Java篇(2)-向聽數(shù)和牌效率的評論 (共 條)

分享到微博請遵守國家法律
淮滨县| 临澧县| 邛崃市| 淳安县| 出国| 龙泉市| 永寿县| 嫩江县| 德钦县| 朔州市| 临海市| 兴山县| 航空| 财经| 介休市| 洮南市| 胶南市| 灵武市| 霍州市| 上饶市| 新乡市| 来凤县| 西平县| 彩票| 西城区| 通榆县| 昌乐县| 金坛市| 新竹市| 台南市| 南雄市| 乃东县| 牙克石市| 乌拉特中旗| 苏尼特右旗| 晋宁县| 钟祥市| 蓝山县| 沾化县| 万载县| 高雄市|