Skip to content

Instantly share code, notes, and snippets.

@cjeon
Created November 4, 2023 18:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cjeon/6d8b834a2ea53652670d37a960e4de8a to your computer and use it in GitHub Desktop.
Save cjeon/6d8b834a2ea53652670d37a960e4de8a to your computer and use it in GitHub Desktop.
Bad move Korean
// テンプレートリテラル用
const getTemplate = raw => {
const rawObj = { raw: raw };
return (...prm) => String.raw(rawObj, ...prm);
}
// 打牌に関わる文字列を成形する
const getDahaiStr = (playerSelectHtmlStr) => {
return playerSelectHtmlStr.innerHTML
.replaceAll('<span class="role">プレイヤー: </span>', '')
.replaceAll('<span class="role">Player: </span>', '')
.replaceAll('<span class="role">작사: </span>', '')
.replaceAll('<svg class="tile">', '')
.replaceAll('<use class="face" href="', '')
.replaceAll('"></use>', '')
.replaceAll('</svg>', '')
.replaceAll(' ', '')
}
// モータルの選択一覧を作成する
const getMortalSelects = (mortalSelectElements) => {
const mortalSelects = [];
mortalSelectElements.forEach((mortalSelect) => {
const recommendInt = mortalSelect.querySelectorAll('td')[2].querySelectorAll('span')[0].innerHTML;
const recommendFrac = mortalSelect.querySelectorAll('td')[2].querySelectorAll('span')[1].innerHTML;
mortalSelects.push({
dahai: getDahaiStr(mortalSelect.querySelectorAll('td')[0]),
recommendation: parseFloat(recommendInt + recommendFrac)
});
});
return mortalSelects
}
// 手牌の情報を整理して返却する。
const getTehaiState = (paiElements) => {
let tehai = [];
let draw = { kind: 'after-fu-ro', from: null, pai: null };
paiElements.forEach(pai => {
const from = pai.getAttribute('before')
if (from == '自摸 ' || from == 'Draw ' || from == '쯔모 ') {
draw = {
kind: 'tsumo',
from: 'jicha',
pai: pai.querySelector('svg > use').getAttribute('href')
}
} else if (from == '上家打 ' || from == 'Kamicha👈 cuts ' || from == '상가👈 타 ') {
draw = {
kind: 'fu-ro',
from: 'kamicha',
pai: pai.querySelector('svg > use').getAttribute('href')
}
} else if (from == '対面打 ' || from == 'Toimen👆 cuts ' || from == '대면👆 타 ') {
draw = {
kind: 'fu-ro',
from: 'toimen',
pai: pai.querySelector('svg > use').getAttribute('href')
}
} else if (from == '下家打 ' || from == 'Shimocha👉 cuts ' || from == '하가👉 타 ') {
draw = {
kind: 'fu-ro',
from: 'shimocha',
pai: pai.querySelector('svg > use').getAttribute('href')
}
} else {
tehai.push(pai.querySelector('svg > use').getAttribute('href'))
};
});
return {
tehai: tehai,
draw: draw
}
}
// ページ全体
const pageBody = document.querySelector('body')
// 半荘全体のデータを格納
const hanchan = {}
// メタデータを取得
hanchan.url = location.href
const rounds = pageBody.querySelectorAll('section')
const paifuJsonStr = rounds[0].querySelector('div > details > iframe').getAttribute('src')
.replace(/https:\/\/tenhou\.net\/.*json=/, '');
const daniData = JSON.parse(paifuJsonStr)
hanchan.daniData = {
dan: daniData.dan,
name: daniData.name,
rate: daniData.rate,
sx: daniData.sx
}
hanchan.playerId = pageBody.querySelectorAll('details > dl > dd')[4].innerHTML
hanchan.rounds = []
rounds.forEach(round => {
const turns = []
const turnDetails = Object.values(round.querySelectorAll('div > details')).slice(3)
turnDetails.forEach(turnDetail => {
// 手牌情報取得用に、手牌のHTMLElemenstsを取得する
const paiElements = turnDetail.querySelectorAll('ul > li')
// プレイヤーの打牌のHTMLElementsを取得
const dahaiElement = Object.values(turnDetail.querySelectorAll('span')).filter(e => e.className == 'role')[0].parentElement
// mortalの選択一覧のテーブルのtrタグをすべて取得
const mortalSelectElements = turnDetail.querySelectorAll('details > table > tbody > tr')
const textFragment = turnDetail
.querySelector('summary').innerHTML
.replace('<span class="turn-info">', '')
.replaceAll('&nbsp;', ' ')
.replace('<span class="order-loss">', '')
.replaceAll('</span>', '')
// 集めた情報をターンの情報として格納していく
turns.push(
{
summury: turnDetail.querySelector('summary').innerHTML.replace(/<span.*/, ''),
url: hanchan.url + '#:~:text=' + encodeURI(textFragment),
linkName: textFragment.replaceAll(' ', ' '),
tehaiState: getTehaiState(paiElements),
dahai: getDahaiStr(dahaiElement),
mortalSelects: getMortalSelects(mortalSelectElements)
}
)
})
// 集めた局のデータを半荘データとして格納していく
hanchan.rounds.push({
id: round.querySelector('h1 > div > a').getAttribute('href'),
name: round.querySelector('h1 > div > a').innerHTML,
turns: turns
})
});
let dahaiCount = 0
let missCount = 0
let furoCount = 0
const missList = pageBody.querySelectorAll('fieldset > label')[4]
if (document.querySelector("body > h1").innerHTML == 'AI 복기') {
missList.insertAdjacentHTML('beforeend', '<br><span style="font-weight:700">악수:</span>')
} else if (document.querySelector("body > h1").innerHTML == 'Replay Examination') {
missList.insertAdjacentHTML('beforeend', '<br><span style="font-weight:700">Bad move:</span>')
} else {
missList.insertAdjacentHTML('beforeend', '<br><span style="font-weight:700">悪手リスト:</span>')
}
hanchan.rounds.forEach(round => {
round.turns.forEach(turn => {
// 推奨度の取得
let recommendation = 0;
turn.mortalSelects.forEach(mortalSelect => {
if (turn.dahai == mortalSelect.dahai) {
recommendation = mortalSelect.recommendation
}
})
dahaiCount++
// 副露判定
if (turn.tehaiState.draw.kind != "fu-ro") {
if (recommendation <= 5) {
missCount++
console.debug(`【悪手】局:${round.name}, 巡目:${turn.summury}, 打牌:${turn.dahai}, 推奨度:${recommendation}, ${round.id}:~:text=${turn.summury}`)
missList.insertAdjacentHTML('beforeend', `<br><a href="${turn.url}">${round.name}${turn.linkName}</a>`);
} else {
console.debug(`局:${round.name}, 巡目:${turn.summury}, 打牌:${turn.dahai}, 推奨度:${recommendation}`)
}
} else {
furoCount++
}
})
})
const missRate = new Intl.NumberFormat('ja', { style: 'percent', minimumFractionDigits: 2 }).format(missCount / (dahaiCount - furoCount));
const missRateWithfuro = new Intl.NumberFormat('ja', { style: 'percent', minimumFractionDigits: 2 }).format(missCount / (dahaiCount));
console.debug(`打牌選択:${dahaiCount}, 副露数:${furoCount}, 悪手率:${missCount}, 悪手率:${missRate}`)
pageBody.querySelectorAll('details > dl > dt').forEach(metadata => {
if (metadata.innerHTML == 'mjai-reviewerバージョン') {
metadata.insertAdjacentHTML('beforebegin', `<dt>悪手率</dt><dd>${missRate}</dd>`);
} else if (metadata.innerHTML == 'mjai-reviewer version') {
metadata.insertAdjacentHTML('beforebegin', `<dt>Bad move rate</dt><dd>${missRate}</dd>`);
} else if (metadata.innerHTML == 'mjai-reviewer 버전') {
metadata.insertAdjacentHTML('beforebegin', `<dt>악수율</dt><dd>${missRate}</dd>`);
}
})
console.debug(hanchan)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment