Greasy Fork is available in English.
天鳳牌理で一向聴の好形率を表示する
当前为
// ==UserScript==
// @name 天鳳牌理好形表示
// @namespace http://tanimodori.com/
// @version 0.0.3
// @description 天鳳牌理で一向聴の好形率を表示する
// @author Tanimodori
// @match https://tenhou.net/2/*
// @grant none
// @license MIT
// ==/UserScript==
const S_P_QUERY = "一般形(七対国士を含まない)の計算結果 / 標準形";
const S_Q_QUERY = "標準形(七対国士を含む)の計算結果 / 一般形";
const S_YIISHANTEN = "1向聴";
const S_YIISHANTEN_ALL = "標準形1向聴";
const MJ_RE = /([0-9]+[mpsz])+/gm;
let mjtiles = (input) => {
let result = [];
let stk = "";
for(let tile_i of input) {
if("0" <= tile_i && tile_i <= "9") {
stk += tile_i;
} else {
for(let tile_j of stk) {
result.push(tile_j + tile_i);
}
stk = "";
}
}
return result;
}
let mjcomp = (a, b) => {
let [a_n, a_t] = a;
let [b_n, b_t] = b;
if(a_n === "0") a_n = 5.5;
if(b_n === "0") b_n = 5.5;
return a_t !== b_t ? (a_t < b_t ? -1 : 1) : Number(a_n) - Number(b_n);
}
const MJ_13ORPHAN_TILES = mjtiles("19m19s19p1234567z");
const MJ_TILES = mjtiles("123456789m123456789s123456789p1234567z");
let global_show_all_result = false;
let mjaka = (tile) => {
if(tile[0] === "0" || tile[0] === "5") {
return String(5-Number(tile[0])) + tile[1];
}
return tile;
}
let mjsub = (mjarr, ...tiles) => {
if(mjarr.mjfail) return;
for(let tile of tiles) {
let index = mjarr.indexOf(tile);
if(index != -1) {
mjarr.splice(index, 1);
continue;
}
index = mjarr.indexOf(mjaka(tile));
if(index != -1) {
mjarr.splice(index, 1);
continue;
}
mjarr.mjfail = true;
return mjarr
}
return mjarr
}
let mj7toi = (mjarr) => {
if(mjarr.length != 14) {
return false;
}
mjarr.sort(mjcomp);
for(let i = 0; i <= 12; i += 2) {
if(mjarr[i] !== mjarr[i+1] && mjarr[i] !== mjaka(mjarr[i+1])) {
return false;
}
}
return true;
}
let mj13orphan = (mjarr) => {
if(mjarr.length != 14) {
return false;
}
mjsub(mjarr,MJ_13ORPHAN_TILES);
if(!mjarr.mjfail && MJ_13ORPHAN_TILES.indexOf(mjarr[0]) !== -1) {
return true;
}
return false;
}
let mjagaricache = {};
let mjagari = (mjarr) => {
if(mjarr.mjfail || mjarr.length % 3 === 1) return false;
if(mjarr.length == 0) {
return true;
}
if(global_show_all_result) {
if(mj7toi([...mjarr])) return true;
if(mj13orphan([...mjarr])) return true;
}
let joined_result = mjarr.sort(mjcomp).join("").replace("0","5");
if(joined_result in mjagaricache) {
return mjagaricache[joined_result];
}
let result = mjagari_raw(mjarr);
mjagaricache[joined_result] = result;
return result;
}
let mjagari_raw = (mjarr) => {
let tile = mjarr[0];
let [tile_num, tile_type] = tile;
// toitsu
if(mjarr.length % 3 === 2) {
if(mjagari(mjsub([...mjarr],tile,tile),false)) return true;
}
// kootsu
if(mjagari(mjsub([...mjarr],tile,tile,tile),false)) return true;
// shuntsu
if(tile_type !== "z") {
let tile_number = Number(tile_num);
if(tile_number === 0) tile_number = 5;
let shuntsu_tiles_groups = [tile_number-2, tile_number-1, tile_number].filter(x=>x>=1&&x<=7).map(x=>[x+tile_type,x+1+tile_type,x+2+tile_type]);
for(let s_tiles of shuntsu_tiles_groups) {
if(mjagari(mjsub([...mjarr],...s_tiles),false)) return true;
}
}
return false;
}
let mjnokori = (mjarr, tile) => {
return 4 - mjarr.filter(x => x===tile||x===mjaka(tile)).length;
}
let mjmachi = (mjarr) => {
if(mjarr.length % 3 !== 1) return [];
return MJ_TILES.filter(tile => {
if(mjnokori(mjarr,tile) === 0) return false; // ikasama
return mjagari([...mjarr, tile]);
});
}
let mjtenpaikei = (mjarr) => {
if(mjarr.length % 3 === 1) return {};
let result = {}
result.nokori_max = 0;
let unique = (value, index, self) => self.indexOf(value) === index;
for(let tile of mjarr.filter(unique)) {
let machi = mjmachi(mjsub([...mjarr],tile));
if (machi.length > 0) {
result[tile] = {};
result[tile].nokori = 0;
for(let machihai of machi) {
let nokori = mjnokori([...mjarr,tile], machihai);
result[tile][machihai] = nokori;
result[tile].nokori += nokori;
}
result.nokori_max = Math.max(result.nokori_max, result[tile].nokori);
}
}
return result;
}
let inject_css = () => {
let styles = `
.D {
position: relative;
}
.D .popup {
visibility: hidden;
width: 300px;
background-color: #ddd;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 8px 0;
position: absolute;
z-index: 1;
top: 125%;
left: 50%;
margin-left: -150px;
}
.D .popup::before {
content: "";
position: absolute;
top: calc(0% - 10px);
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: transparent transparent #ddd transparent;
}
.D .popup.show {
visibility: visible;
}
.D .popup table {
margin-left: auto;
margin-right: auto;
}
`
let styleSheet = document.createElement("style");
styleSheet.type = "text/css";
styleSheet.innerText = styles;
document.head.appendChild(styleSheet);
}
let create_node_tile_img = (tile) => {
let img_node = document.createElement("img");
img_node.setAttribute("src", "https://cdn.tenhou.net/2/a/"+tile+".gif");
img_node.setAttribute("border", "0");
return img_node
}
let create_node_tile = (tile, link) => {
let a_node = document.createElement("a");
if(link) a_node.setAttribute("href", link);
a_node.setAttribute("class", "D");
a_node.appendChild(create_node_tile_img(tile));
return a_node;
}
let create_node_td = (...children) => {
let td = document.createElement("td");
for(let child of children) {
td.appendChild(child);
}
return td;
}
let mouse_over_node = (node, info) => {
let popups = node.getElementsByClassName("popup");
let popup;
if(popups.length === 0) {
popup = document.createElement("div");
let tiles = mjtiles(info.link.substring(3));
for(let tile of tiles){
popup.appendChild(create_node_tile_img(tile));
}
popup.appendChild(document.createElement("br"));
let table = document.createElement("table");
table.setAttribute("cellpadding", 2);
table.setAttribute("cellspacing", 0);
let tbody = document.createElement("tbody");
table.appendChild(tbody);
let keys_valid = Object.keys(info).filter(x => MJ_TILES.indexOf(x.replace("0","5")) !== -1).sort((x,y) => {
let nokori_diff = info[y].nokori-info[x].nokori;
return nokori_diff === 0 ? mjcomp(x,y) : nokori_diff;
});
for(let key of keys_valid) {
let tr = document.createElement("tr");
let info_local = info[key];
tr.appendChild(create_node_td(document.createTextNode("打")));
tr.appendChild(create_node_td(create_node_tile_img(key)));
tr.appendChild(create_node_td(document.createTextNode("待ち[")));
let machis = []
for(let key_local of Object.keys(info_local)) {
if(MJ_TILES.indexOf(key_local) === -1) continue;
machis.push(create_node_tile_img(key_local));
}
tr.appendChild(create_node_td(...machis));
tr.appendChild(create_node_td(document.createTextNode(info_local.nokori + "枚 ]")));
tbody.appendChild(tr);
}
popup.appendChild(table);
popup.classList.add("popup");
node.appendChild(popup);
} else {
popup = popups[0];
}
popup.classList.toggle("show");
}
let mouse_out_node = (node) => {
let popup = node.getElementsByClassName('popup')[0];
popup.classList.toggle("show");
}
let run = () => {
// check
let tehai = document.getElementById("tehai");
let m2 = document.getElementById("m2");
if(!tehai) return;
if(!m2) return;
if(m2.textContent.startsWith(S_P_QUERY)){
global_show_all_result = false;
} else if (m2.textContent.startsWith(S_Q_QUERY)){
global_show_all_result = true;
} else {
return;
}
if(!tehai.textContent.startsWith(S_YIISHANTEN)) {
if(!global_show_all_result || !tehai.textContent.startsWith(S_YIISHANTEN_ALL)) {
return;
}
}
// parse hands
let info = m2.getElementsByTagName("textarea")[0].textContent;
let matches = info.match(MJ_RE).map(mjtiles);
// calculate tenpaikei
let hands = matches[0].sort(mjcomp);
let tenpaikeis = {};
for(let i = 1; i < matches.length; i += 2) {
let sutehai = matches[i][0];
let tsumohais = matches[i+1];
let tenpaikeis_local = {};
tenpaikeis_local.koukei = 0;
tenpaikeis_local.gukei = 0;
tenpaikeis_local.koukeihais = [];
tenpaikeis_local.gukeihais = [];
for(let tsumohai of tsumohais) {
let hands_local = [...mjsub([...hands], sutehai), tsumohai];
let tenpaikei_local = mjtenpaikei(hands_local);
tenpaikei_local.link = "?" + (global_show_all_result ? "q" : "p" ) + "=" + hands_local.join("");
tenpaikei_local.nokori_self = mjnokori(hands, tsumohai);
if(tenpaikei_local.nokori_max > 4) {
tenpaikeis_local.koukei += tenpaikei_local.nokori_self;
tenpaikeis_local.koukeihais.push(tsumohai);
} else {
tenpaikeis_local.gukei += tenpaikei_local.nokori_self;
tenpaikeis_local.gukeihais.push(tsumohai);
}
tenpaikeis_local[tsumohai] = tenpaikei_local;
}
tenpaikeis[sutehai] = tenpaikeis_local;
}
// display tenpaikei
inject_css();
let trs = m2.getElementsByTagName("tr");
let sutehais = Object.keys(tenpaikeis);
for(let i = 0; i < sutehais.length; ++i) {
let tr = trs[i];
let tds = tr.children;
let tenpaikeis_local = tenpaikeis[sutehais[i]];
tr.removeChild(tds[3]);
tr.removeChild(tds[3]);
let td_anchor = tds[3];
let nokori_all = tenpaikeis_local.koukei+tenpaikeis_local.gukei;
// modify
// koukei
if(tenpaikeis_local.koukei > 0) {
let td_node = document.createElement("td");
for(let koukeihai of tenpaikeis_local.koukeihais) {
td_node.appendChild(create_node_tile(koukeihai, tenpaikeis_local[koukeihai].link));
}
tr.insertBefore(td_node, td_anchor);
for(let i = 0; i < tenpaikeis_local.koukeihais.length; ++i) {
let a_node = td_node.children[i];
let koukeihai = tenpaikeis_local.koukeihais[i];
a_node.addEventListener("mouseover", e => mouse_over_node(a_node, tenpaikeis_local[koukeihai]));
a_node.addEventListener("mouseout", e => mouse_out_node(a_node));
}
let td_node2 = document.createElement("td");
td_node2.appendChild(document.createTextNode("好形"+tenpaikeis_local.koukei+"枚"));
tr.insertBefore(td_node2, td_anchor);
} else {
tr.insertBefore(document.createElement("td"), td_anchor);
tr.insertBefore(document.createElement("td"), td_anchor);
}
// koukei - gukei
if(tenpaikeis_local.koukei > 0 && tenpaikeis_local.gukei > 0) {
let td_node3 = document.createElement("td");
td_node3.appendChild(document.createTextNode("+"));
tr.insertBefore(td_node3, td_anchor);
} else {
tr.insertBefore(document.createElement("td"), td_anchor);
}
// gukei
if(tenpaikeis_local.gukei > 0) {
let td_node = document.createElement("td");
for(let gukeihai of tenpaikeis_local.gukeihais) {
td_node.appendChild(create_node_tile(gukeihai, tenpaikeis_local[gukeihai].link));
}
tr.insertBefore(td_node, td_anchor);
for(let i = 0; i < tenpaikeis_local.gukeihais.length; ++i) {
let a_node = td_node.children[i];
let gukeihai = tenpaikeis_local.gukeihais[i];
a_node.addEventListener("mouseover", e => mouse_over_node(a_node, tenpaikeis_local[gukeihai]));
a_node.addEventListener("mouseout", e => mouse_out_node(a_node));
}
let td_node2 = document.createElement("td");
td_node2.appendChild(document.createTextNode("愚形"+tenpaikeis_local.gukei+"枚"));
tr.insertBefore(td_node2, td_anchor);
} else {
tr.insertBefore(document.createElement("td"), td_anchor);
tr.insertBefore(document.createElement("td"), td_anchor);
}
// all
let td_node_all = document.createElement("td");
td_node_all.appendChild(document.createTextNode("="+(nokori_all)+"枚 (" + Math.round(100 * tenpaikeis_local.koukei / nokori_all) + "%)"));
tr.insertBefore(td_node_all, td_anchor);
}
}
run();