UNPKG

tsgammon-core

Version:

A Backgammon library for Typescript, formerly developed as a part of tsgammon-ui

99 lines (98 loc) 4.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.toPositionIDFromArray = exports.toPositionID = void 0; const base64_1 = require("@borderless/base64"); /** * 盤面からGNU Backgammon仕様のPositionIDを生成する。 * * @param board 盤面 * @returns 生成されたPositionID */ function toPositionID(board) { return toPositionIDFromArray(board.points); } exports.toPositionID = toPositionID; /** * 盤面を表す配列からGNU Backgammon仕様のPositionIDを生成する。 * * @param posArr 盤面を表す配列 * @returns 生成されたPositionID */ function toPositionIDFromArray(posArr) { // PositionIDのエンコーディングは、インデックスを遡って駒を数えていく // [24,23,...,1,0 = bar] const myIndex = [...Array(25)].map((_, i, arr) => arr.length - i - 1); const myPieces = myIndex .map((pos) => posArr[pos]) .map((n) => (n > 0 ? n : 0)); // [1,2,...,24, 25=bar] const oppIndex = [...Array(25)].map((_, i) => i + 1); const oppPieces = oppIndex .map((pos) => posArr[pos]) .map((n) => (n < 0 ? -n : 0)); const buffer = new ArrayBuffer(10); // GNU Backgammonの説明と順序が違うが、これでよいようだ const v = oppPieces .concat(myPieces) // reduceにより、各ポイントの駒数nをn+1個のbit列に変換し(e.g. 3=>0111)、 // それを順次8bitずつにまとめてbufferに詰め込んでいく .reduce((prev, n) => { // もうバッファに書き込めない場合(コマの数が多すぎる)は、何もしない if (prev.pos >= 10) { return prev; } const { lastBit, lastLen, dataView, pos } = prev; // バッファに書き込む量に達しない場合は、次に引き継ぐ if (n + lastLen < 8) { // n1個の1(0->0, 1->1, 2->11, 3->111, 4->1111...)を用意 const bitForNext = (1 << n) - 1; const bitForNextLen = n + 1; // 先頭には0をつけるので、長さは上記のbit列より1bit長くなる // この先頭の0は、必ず次回のループ初回で書き込まれるか、 // ループをスルーしてその次へ引き継がれるかのいずれか return { // オリジナルの解説ではリトルエンディアンで処理しているが、 // ビッグエンディアンで処理したいので、左に追加している lastBit: (bitForNext << lastLen) | lastBit, lastLen: bitForNextLen + lastLen, dataView, pos, }; } // 前回の残りと新規分とを合わせ、8bit分のデータを用意する const n1 = 8 - lastLen; const oct = (((1 << n1) - 1) << lastLen) | lastBit; // 8bitをバッファに書き込み let curPos = pos; dataView.setUint8(curPos, oct); curPos++; // 残りのbitが8個より多いなら、8bit分の1を書いてしまう let bitLen = n - n1; if (bitLen > 8) { dataView.setUint8(curPos, 255); curPos++; bitLen -= 8; } // バックギャモンの駒は1プレイヤー15個なので、bitLen < 8が保証される return { lastBit: (1 << bitLen) - 1, lastLen: bitLen + 1, dataView, pos: curPos, }; }, { lastBit: 0, lastLen: 0, dataView: new DataView(buffer), pos: 0 }); // バッファがオーバーフローしていなければ、 // 未出力の最後のバイトを書く if (v.pos < 10) { // 最後に残ったデータは、そのまま1バイトとして扱う v.dataView.setUint8(v.pos, v.lastBit); // 一応、バッファの空きも詰める let pos = v.pos + 1; while (pos < 10) { v.dataView.setUint8(pos, 0); pos++; } } return (0, base64_1.encode)(buffer).substring(0, 14); } exports.toPositionIDFromArray = toPositionIDFromArray;